MP3、PCM、WAV などの基本的なオーディオ形式の概要とコード分析

MP3ファイルは生活の中で非常に身近なものと言えます. Doudou自体はほぼ毎日のようにバイナリファイルです. この記事では、内部でどのようにエンコードされているかを見ていきます.

このプロジェクトで使用されているコードは参照できます (実際には、コアのものは以下にあり、せいぜい移植する必要はありません)。

https://github.com/MY201314MY/Audio.git

1. 基礎知識

まず、オーディオの基本に密接に関連するいくつかのパラメータを見てみましょう.
サンプリング周波数
サンプリング周波数は、1 秒間のサンプル数であり、サンプリング ポイント間の間隔を反映します。間隔が短いほど、失われる情報が少なくなり、デジタル サウンドがよりリアルで詳細になり、必要なストレージが大きくなります。コンピュータの動作速度と記憶容量には限界があり、人間の聴覚の上限は20kHzであるため、サンプリング周波数を高くしすぎることはできませんし、必要もありません。ナイキスト サンプリングの法則によれば、サンプリング周波数が信号の最高周波数の 2 倍よりも高い限り、サンプリングから元の波形を復元できます。したがって、サンプリング周波数は 40kHz 以上で十分です。
測定精度

測定精度とは、サンプルの縦方向の精度であり、波形を縦方向に等分割することで実現されるサンプルの量子化レベルです。デジタル化は最終的に 2 進数で表現されるため、サンプルの量子化レベルを表すために 2 進数の桁数が一般的に使用されます。各サンプルが 8 ビット バイナリで表される場合、合計で 8 つのマグニチュードがあります。各サンプルが 16 ビットの 2 進数で表される場合、合計で 16 の大きさがあります。マグニチュードが大きいほど、サンプルは元の波形に近くなり、デジタル サウンドの品質が高いほど、より多くのストレージが必要になります。現在、マルチメディア コンピュータの標準サンプリング レベルは 8 ビットと 16 ビットです。
チャンネル数

サウンド レコーディングは、モノラルと呼ばれる 1 つの波形のみを生成します。サウンド レコーディングでは、ステレオフォニックと呼ばれる 2 つの波形のみが生成されます (最も基本的なステレオフォニックは、左と右の 2 つのチャネルです)。ステレオ サウンドは、モノラルよりも豊かで広々としていますが、2 倍のストレージ スペースが必要です。
ビットレート
ビットレートとは、1 秒間のサンプリング時間に含まれる音声のデータの流れを指し、単位は bps です。

二、MP3ファイル構成

MP3 ファイルは、タグ ヘッダー (TAG_V2 ID3V2)、オーディオ データ フレーム フレーム、TAG_V1 (ID3V1) の 3 つの部分に分けることができます。フレーム部分が呼び出せるデータフレームです。

Mp3 ファイルには、必ずしもタグ フレームが存在するとは限りませんが、データ フレームが存在する必要があります。

ID3V2

作者、作曲者、アルバムなどの情報が含まれています。長さが固定されていないため、ID3V1 の情報量が拡張されます。

フレーム

このデータフレームは非常に重要で、実際にサウンドカードに送られるデータはこの部分で構成されています。

これは一連のフレームで構成され、各 FRAME の長さは、ビット レートによって決定され、等しくない場合もあります。各 FRAME は、フレーム ヘッダーとデータエンティティの2 つの部分に分割されます。フレーム ヘッダーには、ビット レート、サンプリング レート、mp3 のバージョンなどの情報が記録され、各フレームは互いに独立しています。

ID3V1

ID3V1 は初期のバージョンで、保存できる情報量は限られていますが、プログラミングは ID3V2 よりはるかに簡単で、現在でも多く使用されています。ID3V1はMP3ファイルの末尾に固定的に格納される128バイトで、曲名は30バイト固定で割り当てられ、曲名が短すぎる場合は0で埋められ、長すぎる場合は埋められます。他の情報も同様に保存されます。

つまり、複数の MP3 ファイルのデータ フレームを抽出し、それらを連続して再生できます。

複数のMP3ファイルを連続再生

埋め込みリソースは限られています。複数のリソースのオーディオを事前に RAM に抽出し、再生時に直接呼び出して、ファイルの読み取り時間を節約します。これは、RAM の読み取り速度が Flash よりもはるかに高速であるためです。

パソコン(Windows)の場合

コンピューター上で正常に実行し、ボードに移植して効率を向上させ、書き込み時間を節約することができます. 提案の根拠は以下にある, または参照することができます 1. 詳細版. 私は彼の記事を読んで、多くを得ました
. 2.
簡易版にはコード戦闘が含まれます

#include <stdio.h>
#include <stdint.h>

int main() {
    
    
    char *array[] = {
    
    
            "../audio/1.mp3", "../audio/bai.mp3", "../audio/5.mp3", "../audio/shi.mp3","../audio/3.mp3", 				"../audio/dian.mp3", "../audio/4.mp3", "../audio/2.mp3", "../audio/yuan.mp3"
    };
    int fileSize = 0;
    uint8_t storeBuff[1024*1024];
    FILE *output = fopen("output.mp3", "wb+");

    for (uint8_t i=0;i<9;i++){
    
    
        FILE *fp= fopen(array[i], "rb+");
        fseek(fp, 0, SEEK_END);
        int temp = ftell(fp);
        printf("temp:%d\r\n", temp);
        if(i != 8)
        {
    
    
            if(i==0) {
    
    
                temp -= 128;
                fseek(fp, 0, SEEK_SET);
                fread((storeBuff + fileSize), temp, 1, fp);
            }else{
    
    
                temp -= 128+5672;
                fseek(fp, 5672, SEEK_SET);
                fread((storeBuff + fileSize), temp, 1, fp);
            }
        }else{
    
    
            temp -= 5672;
            fseek(fp, 5672, SEEK_SET);
            fread((storeBuff+fileSize), temp, 1, fp);
        }
        fclose(fp);
        fileSize += temp;
    }

    fwrite(storeBuff, fileSize, 1, output);
    fclose(output);

    return 0;
}

#### 1、标签帧

编码格式

```c
typedef struct
{
    
    
    /* 必须为字符串"ID3",否则认为标签不存在 对应16进制:49 44 33 */
    char Header[3]; 
    /* 版本号 ID3V2.3就记录0x03 */
    char ver;
    /* 副版本号 */
    char Revision;
    /* 存放标志的字节 */
    char Flag;
    /* 标签帧的大小,不包括标签头的10个字节 */
    char Size[4];
}head_lable;

MP3ファイルの先頭はこのように定義されているので、手元にあるMP3ファイルを見てみましょう。

ubuntu@VM-16-10-ubuntu:~/Audio/search$ ls -l maria.mp3
-rw-rw-r-- 1 ubuntu ubuntu 7240 Apr 13 15:57 maria.mp3
#这个命令就是只看前32个字节,可以看到开头三个字节确实是 0x49 0x44 0x33
ubuntu@VM-16-10-ubuntu:~/Audio/search$ hexdump -C maria.mp3 -n 32
00000000  49 44 33 03 00 00 00 00  2c 1e 54 59 45 52 00 00  |ID3.....,.TYER..|
00000010  00 05 00 00 00 32 30 32  32 54 43 4f 4e 00 00 00  |.....2022TCON...|
00000020

ラベル フレームの長さの計算方法に注目しましょう。

7-10 バイト目はタグフレームのサイズを表しており、合計 4 バイトですが、各バイトは 7 ビットしか使用せず、最上位ビットは使用しない場合は常に 0 です。したがって、フォーマットは次のようになります.サイズを計算する場合、ラベル サイズである 28 ビットの 2 進数を取得するには、0 を削除する必要があります.計算式は次のとおりです。

int label_frame_size=((Size[0]&0x7F)<<21)
                        + ((Size[1]&0x7F)<<14))
                        + ((Size[2]&0x7F)<<7)
                        + (Size[3]&0x7F);

( 0 x 00 ) * ( 1 < < 21 ) + ( 0 x 00 ) * ( 1 < < 14 ) + ( 0 x 2 C ) * ( 1 < < 7 ) + ( 0 x 1 e ) = 5662 / *私たちのファイルの場合* / (0x00)*(1<<21)+(0x00)*(1<<14)+(0x2C)*(1<<7)+(0x1e) = 5662/* 私たちの場合 ファイルの場合*/( 0 × 0 0 )( 1<<2 1 )+( 0 × 0 0 )( 1<<1 4 )+( 0 × 2C ) _( 1<<7 )+( 0 × 1e ) _=5 6 6 2 /私たちファイル_ _/

ここでのサイズには、タグ ヘッダーの 10 バイトと、最初の 10 バイトが 5672 バイト (オーディオ データ フレームのオフセット) は含まれていません。

ファイル情報を記述するために使用されるラベル フレーム内のその他の情報については、分析を行っていませんが、これらの記述情報の割合は、5672/7240=78.34% です。

2. データフレーム

これは非常に重要であり、私たちが必要としている、または焦点を当てているものです

#-s 表示偏移量
ubuntu@VM-16-10-ubuntu:~/Audio/search$ hexdump -C maria.mp3 -s 5672 -n 32
00001628  ff f2 49 c0 67 66 00 13  2a ba 25 9d 43 10 02 be  |..I.gf..*.%.C...|
00001638  eb 6e 36 c2 26 5e fc 44  74 4d e0 44 4a 88 85 bb  |.n6.&^.DtM.DJ...|
00001648

前述のように、データ フレームは複数のフレームで構成されています。最初のフレームを個別に分析してみましょう。

フレームヘッダー

typedef struct frameHeader
{
    
    
    unsigned int sync1:11;                     
    unsigned int version:2;                    //版本
    unsigned int layer:2;                      //层
    unsigned int crc_check:1;                  //CRC校验
    
    unsigned int bit_rate_index:4;             //比特率索引
	unsigned int sample_rate_index:2;          //采样率索引
    unsigned int padding:1;                    //帧长调节位
    unsigned int reserved:1;                   //保留字
    
    unsigned int channel_mode:2;               //声道模式
    unsigned int mode_extension:2;             //扩展模式,仅用于联合立体声
    unsigned int copyright:1;                  //版权标志
    unsigned int original:1;                   //原版标志
    unsigned int emphasis:2;                   //强调方式
}FHEADER, *LPHEADER;

上記のリンクとhexdumpを表示して取得したデータによると、非常に関連性の高い 2 つの情報を照会できます。

  • サンプル数: このフレームで収集されたサンプルの数。
  • サンプリング レート: つまり、サンプリング周波数、1 秒間に何回収集されるか
  • ビット レート: サンプルを表すために使用されるデータ ビット深度の量

これらのパラメーターを取得する方法を見てみましょう。

まず、バージョンを知る必要があります。

  • bit[13:12]=10 は MPEG2
  • bit[11:10]=01 はレイヤ 3 を意味します

これより、ビットレートは32kbps、サンプリングレートは16kHz、サンプル数は576 /frameとなります。

MP3 フレーム長の計算

サイズ=((サンプル数 * (1 / サンプリング レート))* フレーム ビット レート)/8 + フレーム パディング サイズ

整数

この例では、データ フレームの長さは次のとおりです。32000/(16000)x576/8 = 72*2= 144

合計ファイル サイズは 7240、ID3V2 は 5672、IDV31 は 128、合計データ フレーム サイズは 7240-128-5672= 1440です。

驚くべきことに、この例には 10 個のデータ フレームがあります。

MP3 の各フレームの長さの計算

各フレームの長さ (ミリ秒) = フレームあたりのサンプル数 / サンプリング周波数 * 1000

この例では

1 フレーム時間 = 576/(16x1000)/(1000) = 36 ms

3. デコード

目標: サウンド カードが直接認識できる PCM データを取得します。PCM は非常に直接的なバイナリ オーディオ ストリームです。

この部分では、多くの情報を読み、最終的にHelixライブラリを移植してデコードすることにしました. このライブラリは組み込み MCU で広く使用されています. 以下にライブラリの簡単な説明を示します
.詳細情報
Webset : Helix

デコードの詳細は、多くの数学的計算で設計されています. 興味のある友人は自分で理解できます. この記事では主にフォーマットについて説明します.

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "../pub/mp3dec.h"

int main() {
    
    
    int16_t targetPCM[1024];
    unsigned char buffer[10*1024] = {
    
    0};

    memset(targetPCM, 0, sizeof (targetPCM));
	/* 致敬Apple */
    printf("This is designed by Apple in Cal.\n");

    HMP3Decoder hmp3decoder = MP3InitDecoder();
	/* 读取MP3源二进制文件 */
    FILE *fp = fopen("../audio/maria.mp3", "rb+");
    fseek(fp, 0, SEEK_END);
    int count = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    fread(buffer, count, 1, fp);

    int offset = 0;
    unsigned char *p = buffer;
    int left = count;
    static int k = 0;
    while (1) {
    
    
        /* 寻找同步帧0xFF,0xE0 */
        offset = MP3FindSyncWord(p, count);
        if(offset<0) {
    
    printf("offset:%d", offset);break;}

        left -= offset;
        p += offset;
        count -= offset;
        int err = MP3Decode(hmp3decoder, &p, &left, targetPCM, 0);
        /* MP3Decode将解码后的PCM数据保存到targetPCM数组中,这个长度576的计算请看下一个章节 */
        printf("left:%d\r\n", left);
        for (int i = 0; i < 576; i++) {
    
    
            if((i+1)%16 == 0)
                printf(". ");
        }
        k++;
    }

    fclose(fp);
    MP3FreeDecoder(hmp3decoder);

    return 0;
}
デコード後の PCM 長の計算
/**************************************************************************************
 * Function:    MP3Decode
 *
 * Description: decode one frame of MP3 data
 *
 * Inputs:      valid MP3 decoder instance pointer (HMP3Decoder)
 *              double pointer to buffer of MP3 data (containing headers + mainData)
 *              number of valid bytes remaining in inbuf
 *              pointer to outbuf, big enough to hold one frame of decoded PCM samples
 *              flag indicating whether MP3 data is normal MPEG format (useSize = 0)
 *                or reformatted as "self-contained" frames (useSize = 1)
 *
 * Outputs:     PCM data in outbuf, interleaved LRLRLR... if stereo
 *                number of output samples = nGrans * nGranSamps * nChans
 *              updated inbuf pointer, updated bytesLeft
 *
 * Return:      error code, defined in mp3dec.h (0 means no error, < 0 means error)
 *
 * Notes:       switching useSize on and off between frames in the same stream 
 *                is not supported (bit reservoir is not maintained if useSize on)
 **************************************************************************************/
int MP3Decode(HMP3Decoder hMP3Decoder, unsigned char **inbuf, int *bytesLeft, short *outbuf, int useSize);

関数のコメントから、PCM がデュアルチャネルの場合、LR の順に配置され、デフォルトは 16 ビットであることがわかります.PCM 配列は十分な大きさが必要であり、長さは nGrans x nGranSamps です. x nChans. 最終的なサイズ パラメータを渡すことができます *void MP3GetLastFrameInfo (HMP3Decoder hMP3Decoder, MP3FrameInfo *mp3FrameInfo)* 出力サンプルを取得すると、この MP3 ファイルをプレビューできます:

MP3GetLastFrameInfo(hmp3decoder, &frameInfo);
printf("bitrate:%d nChans:%d samprate:%d bitsPerSample:%d outputSamps:%d layer:%d version:%d\r\n",
               frameInfo.bitrate,
               frameInfo.nChans, frameInfo.samprate, frameInfo.bitsPerSample, frameInfo.outputSamps, 		                frameInfo.layer,
               frameInfo.version);
/* bitrate:32000 nChans:1 samprate:16000 bitsPerSample:16 outputSamps:576 layer:3 version:1 */

出力ファイルを見てみましょう

D:\project\CLion\Helix\cmake-build-debug\Helix.exe
This is designed by Apple in Cal.
left:1424
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:1280
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:1136
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:992
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:848
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:704
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:560
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:416
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:272
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . left:128
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . offset:-1
Process finished with exit code 0
結論

10回デコードして、最後に残った128バイト!

データの視覚化

生成された PCM データを印刷し、Python ツールを使用して視覚的な分析を行います。

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "../pub/mp3dec.h"

int main() {
    
    
    int16_t targetPCM[1024];
    unsigned char buffer[10*1024] = {
    
    0};

    memset(targetPCM, 0, sizeof (targetPCM));

    printf("This is designed by Apple in Cal.\n");

    HMP3Decoder hmp3decoder = MP3InitDecoder();

    FILE *fp = fopen("../audio/maria.mp3", "rb+");
    fseek(fp, 0, SEEK_END);
    int count = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    fread(buffer, count, 1, fp);

    int offset = 0;
    unsigned char *p = buffer;
    int left = count;
    static int k = 0;
    while (1) {
    
    
        offset = MP3FindSyncWord(p, count);
        if(offset<0) {
    
    printf("offset:%d", offset);break;}

        left -= offset;
        p += offset;
        count -= offset;
        int err = MP3Decode(hmp3decoder, &p, &left, targetPCM, 0);

        for (int i = 0; i < 576; i++) {
    
    
            printf("i:%04d value:%04d\r\n", i+576*k, *(targetPCM + i));
        }
        /* 每个数据帧递增 */
        k++;
    }

    fclose(fp);
    MP3FreeDecoder(hmp3decoder);

    return 0;
}

印刷されたデータをテキストファイルoutput.txtに保存します. 理解の便宜のために, バイナリ保存は当面行われません.

i:0796 value:-001
i:0800 value:-001
i:0816 value:-001
i:0823 value:-001
i:0837 value:-001
i:0838 value:-001
i:0853 value:-001
i:0902 value:-001
i:0921 value:0001
i:0923 value:-001
i:0925 value:0001
i:0931 value:0001
    ...
i:5742 value:-001
i:5745 value:-001
i:5747 value:-001
i:5749 value:-001
i:5752 value:-001
i:5755 value:0001
i:5759 value:-001

視覚的な分析には、次のコードを使用します。

import matplotlib.pyplot as plt
import numpy as np

time = np.zeros(shape=5760, dtype=np.int16)
value = np.zeros(shape=5760, dtype=np.int16)
with open(file="output.txt", mode="r") as f:
    x = f.readlines()
#AXIS X
for i in range(5760):
    time[i] = i
#AXIS Y
for item in x:
    value[int(item[2:6])] = int(item[13:-1])
    print("{0} -- {1}".format(item[2:6], item[13:-1]))

plt.figure(figsize=(16, 9))
plt.plot(time, value)
plt.show()

結果は

ここに画像の説明を挿入

比較のために成熟した波形ソフトウェアを使用しており、波形が比較的一貫していることがわかります.このソフトウェアはオンラインです.公式ウェブサイトにアクセスして.
ここに画像の説明を挿入

4. PCM を WAV オーディオ形式にカプセル化して再生

PCMとWAVの紹介

WAV: wav はロスレス オーディオ ファイル形式で、WAV は PIFF (Resource Interchange File Format) 仕様に準拠しています。すべての WAV には、オーディオ ストリームのエンコード パラメータを指定するファイル ヘッダーがあります。

PCM: PCM (パルス符号変調 - パルス符号変調)。いわゆるPCMエンコードとは、音声などのアナログ信号を記号化されたパルス列に変換して記録することです。PCM 信号は、符号化や圧縮処理を一切行わずに、1、0、その他の記号で構成されたデジタル信号です。アナログ信号に比べ、伝送系のクラッタや歪みの影響を受けにくい。ダイナミックレンジも広く、音質への影響もなかなか。PCM データは、完全にロスレスの最も生のオーディオ データです。

簡単に言うと、wavはロスレスのオーディオファイル形式、pcmは圧縮を行わないエンコード方式、wav形式はpcmにヘッダーを追加したもので、ヘッダーの長さは44バイトです。

PCM にヘッダーを追加する

その前に、上記のテキストを PCM バイナリ ファイルに変換します。

import numpy as np
import struct

time = np.zeros(shape=5760, dtype=np.int16)
value = np.zeros(shape=5760, dtype=np.int16)
with open(file="output.txt", mode="r") as f:
    x = f.readlines()

for i in range(5760):
    time[i] = i

for item in x:
    value[int(item[2:6])] = int(item[13:-1])
with open(file="maria.pcm", mode="wb+") as pcm:
    for i in range(5760):
        pcm.write(struct.pack("h", value[i]))

今!

import wave

pcmf = open("maria.pcm", 'rb')
pcmdata = pcmf.read()
pcmf.close()

print(pcmdata.__len__())

wavfile = wave.open("output.wav", 'wb')

#可以着重留意一下这几个参数
wavfile.setnchannels(1)
wavfile.setsampwidth(16//8)
wavfile.setframerate(16000)
wavfile.writeframes(pcmdata)

wavfile.close()

追加した後、コンピュータープレーヤーを使用して、取得したWAVファイルを正常に再生できます

WAV ヘッダー

WAV ファイルのヘッダーにある 44 バイトの意味を見てみましょう。

この記事を参照できます。

私たちを見てみましょう:

ubuntu@VM-16-10-ubuntu:hexdump -C output.wav -n 44
00000000  52 49 46 46 24 2d 00 00  57 41 56 45 66 6d 74 20  |RIFF$-..WAVEfmt |
00000010  10 00 00 00 01 00 01 00  80 3e 00 00 00 7d 00 00  |.........>...}..|
00000020  02 00 10 00 64 61 74 61  00 2d 00 00              |....data.-..|
0000002c

5.再生速度を変更するFM

MP3からPCMへ

前のファイルは比較的小さいため、再生速度を調整した効果は明ら​​かではありません.大量の印刷はデータ全体にとって災害です.まず、MP3をテキストではなくPCMバイナリに変換します.

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "../pub/mp3dec.h"

int main() {
    
    
    int16_t targetPCM[10*1024];
    MP3FrameInfo frameInfo;
    unsigned char buffer[20*1024] = {
    
    0};

    memset(targetPCM, 0, sizeof (targetPCM));

    printf("This is designed by Apple in Cal.\n");

    HMP3Decoder hmp3decoder = MP3InitDecoder();

    FILE *fp = fopen("../audio/moon.mp3", "rb+");
    fseek(fp, 0, SEEK_END);
    int count = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    fread(buffer, count, 1, fp);

    int offset = 0;
    unsigned char *p = buffer;
    int left = count;
    static int k = 0;
    FILE *pcm = fopen("../audio/maria.pcm", "wb+");
    while (1) {
    
    
        offset = MP3FindSyncWord(p, count);
        if(offset<0) {
    
    printf("offset:%d", offset);break;}

        left -= offset;
        p += offset;
        count -= offset;
        int err = MP3Decode(hmp3decoder, &p, &left, targetPCM, 0);

        MP3GetLastFrameInfo(hmp3decoder, &frameInfo);
		
        /* 这里直接按照二进制写入 */
        fseek(fp, k*frameInfo.outputSamps*2, SEEK_SET);
        fwrite(targetPCM, frameInfo.outputSamps*2, 1, pcm);
        k++;
    }

    fclose(fp);
    fclose(pcm);
    MP3FreeDecoder(hmp3decoder);

    return 0;
}

フレームレートを変更して高速化する

それからPCMにヘッダを付けてWAVファイルを取得し、フレームレートを変えて再生速度を変えますが、音もかなり変わります

0.8 倍から 1.2 倍の間で速度をコントロールしてください。そうしないと、変化が大きくなり、女の子の声が男の子になってしまう可能性があります。

import wave

pcmf = open("maria.pcm", 'rb')
pcmdata = pcmf.read()
pcmf.close()

print(pcmdata.__len__())

wavfile = wave.open("output.wav", 'wb')
wavfile.setnchannels(1)
wavfile.setsampwidth(16//8)
/* 更改次参数可改变播放时长 帧率16K, 数据总长251136, 播放时长为251136/2/16000 = 7.848s */
wavfile.setframerate(16000)
wavfile.writeframes(pcmdata)
wavfile.close()

成熟したソフトウェアを使用して速度を調整し、再生して効果を確認することもできます
ここに画像の説明を挿入

6.アルゴリズムは再生速度を高速化します

「123.24元」を再生する必要がある場合、この曲は実際には9つの個別の曲で構成されています。最初と最後の無音により、曲の間に明らかな一時停止が生じます。私たちの目標は、無音を除外することですファイルの最初と最後に挿入して、元の音色を維持しながら再生速度を上げます。

アルゴリズム 1

また、これは私がこれまでに考えた中で最も単純で実用的なアルゴリズムでもあります. 興味があれば、他のアルゴリズムを自分で試すことができます.

比較した

複数の MP3 ファイルをフィルタリングせずに直接接続する

  • この wav ファイルのサイズは109484バイトです
  • 持続時間は3.419秒

[外部リンクの画像転送に失敗しました。ソース サイトにアンチリーチング メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-MhvLmcUF-1651022804902) (C:\Users\34776\AppData\Roaming\Typora\ Typora-user-images\ image-20220421152326562.png)]

私たちのアイデアは非常に単純です:無音部分のサンプルを削除することで、音色は変わりませんが、放送速度も向上します

フィルタリングされた波形を見てみましょう
ここに画像の説明を挿入

  • この wav ファイルのサイズは66998バイトです
  • 持続時間は2.092秒

要約すれば:

フィルターアルゴリズムのファイルサイズ(実際はPCMで計算した方が合理的ですが、ヘッダーサイズは固定なのであまり正確ではありません)は38.8%、再生時間は38.8%削減されています。

[外部リンクの画像転送に失敗しました。ソース サイトにアンチリーチング メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-otvUb4yd-1651022804902) (C:\Users\34776\AppData\Roaming\Typora\) Typora-user-images\ image-20220422135515294.png)]

もう一度コードを見てみましょう.サウンドアルゴリズムをフィルタリングするこの粗雑な方法は非常に不合理ですが、私は数学が苦手で、当分の間他のことを行う方法を理解していません.

#include <math.h>
#include <stdio.h>
#include <stdint.h>

int main() {
    
    
    uint8_t buff[128*1024];
    FILE *fp = fopen("../audio/he.pcm", "rb+");
    FILE *output = fopen("../audio/maria.pcm", "wb+");
    int16_t *p = (int16_t *)buff;

    fseek(fp, 0, SEEK_END);
    int temp = ftell(fp);
    printf("temp:%d\r\n", temp/2);

    fseek(fp, 0, SEEK_SET);
    fread(buff, temp, 1, fp);
    /* 这里样本大小大于100才会收集起来,我们可以自由控制这个阈值 */
    for (int i=0;i<temp/2;i++)
    {
    
    
        if(abs(*(p+i)) >= 100){
    
    
            fwrite(p+i, 2, 1, output);
        }
    }

    printf("%2d\r\n");

    fclose(fp);
    fclose(output);

    return 0;
}
知らせ

携帯電話やパソコンで音楽を再生すると必ずフェードイン、フェードアウトが発生するため、このオンライン編集ソフトで再生することをお勧めします。

その他のアルゴリズム

他は分かりませんので追記お願いします。

おすすめ

転載: blog.csdn.net/Cloud_1234_5678/article/details/124443218