音頻聲音文件MP3和PCM
兩者均是封裝格式,為了分析PCM,先下載一個MP3文件,然后通過ffmpeg將MP3文件轉成PCM文件進行分析,使用分析軟件為audition音頻軟件。
轉換PCM文件
ffmpeg -i hai.mp3 -f s16le audio1.pcm
轉換后可以使用此命令播放看轉換是否成功:ffplay -ar 44100 -ac 2 -f s16le -i audio1.pcm
轉換后的文件放到audition中,並選擇采樣率和位寬后,便可以顯示音頻文件頻譜
代碼分析PCM文件:
PCM的左右聲道是間隔存放的,每個聲道占用2個字節,所以取值時需要間隔進行讀取並存放成左右兩個聲道
源碼如下:
void pcm_spilit_channel(const char* pcmfile) { FILE* pcmFp = fopen(pcmfile, "rb+"); FILE* pcmlFp = fopen("E:\\audio_leftC.pcm", "wb+"); FILE* pcmrFp = fopen("E:\\audio_rightC.pcm", "wb+"); unsigned char* samples = (unsigned char*)malloc(4); while (!feof(pcmFp)) { fread(samples, 1, 4, pcmFp); fwrite(samples, 1, 2, pcmlFp); fwrite(samples + 2, 1, 2, pcmrFp); } free(samples); fclose(pcmrFp); fclose(pcmlFp); fclose(pcmFp); }
分離左右聲道后,音頻文件同樣的設置后通過audition進行查看
WAVE文件格式,包含3個頭部結構和一個PCM數據塊,具體如下:
使用C++將pcm音頻轉換為wav音頻文件
typedef struct { char fccID[4]; unsigned long dwSize; char fccType[4]; }WAV_HEADER; typedef struct { char fccID[4]; unsigned long dwSize; unsigned short wFromatTag; unsigned short wChannels; unsigned long dwSamplesPerSec; unsigned long dwAvgBytesPerSec; unsigned short wBlockAlign; unsigned short uiBitsPerSample; }WAV_FMT; typedef struct { char fccID[4]; unsigned long dwSize; }WAV_DATA; void pcm2wav(const char* pcmfile, int channel, int sampleRate, const char* wavfile) { FILE* pcmFp = fopen(pcmfile, "rb+"); FILE* wavFp = fopen(wavfile, "wb+"); WAV_HEADER wavHeader; WAV_FMT wavFormat; WAV_DATA wavData; unsigned short pcmData; int bits = 16; memcpy(wavHeader.fccID, "RIFF", strlen("RRIF")); //設置RIFF頭 memcpy(wavHeader.fccType, "WAVE", strlen("WAVE")); //設置WAVE標識 memcpy(wavFormat.fccID, "fmt ", strlen("fmt ")); //設置第二塊頭“fmt ” wavFormat.dwSize = 16; //WAV_FORMAT的大小- sizeof(fccID) - sizeof(dwSize) wavFormat.wFromatTag = 1; //PCM格式文件時設置1 wavFormat.wChannels = channel; wavFormat.dwSamplesPerSec = sampleRate; //比特率 wavFormat.dwAvgBytesPerSec = sampleRate * sizeof(pcmData); //碼率 wavFormat.wBlockAlign = sizeof(pcmData); //每個采樣點的字節對齊寬度 wavFormat.uiBitsPerSample = bits; //PCM采樣的位數 memcpy(wavData.fccID, "data", strlen("data")); //設置第三塊頭data int offset = sizeof(WAV_HEADER) + sizeof(WAV_FMT) + sizeof(WAV_DATA); int pcmDataCount = 0; fseek(wavFp, offset, SEEK_CUR); while (!feof(pcmFp)) { fread(&pcmData, sizeof(unsigned short), 1, pcmFp); fwrite(&pcmData, sizeof(unsigned short), 1, wavFp); pcmDataCount++; } pcmDataCount = pcmDataCount << 1; wavData.dwSize = pcmDataCount; //設置大小為PCM數據的大小 wavHeader.dwSize = pcmDataCount + sizeof(WAV_DATA) + sizeof(WAV_FMT) + sizeof(WAV_HEADER) - 8; //頭的大小為所有的大小-sizeof(WAV_HEADER.fccID)-sizeof(WAV_HEADER.dwSize) rewind(wavFp); fwrite(&wavHeader, 1, sizeof(wavHeader), wavFp); fwrite(&wavFormat, 1, sizeof(wavFormat), wavFp); fwrite(&wavData, 1, sizeof(wavData), wavFp); fclose(pcmFp); fclose(wavFp); return; }
注意:轉換后文件大小幾乎相同
轉換后文件可以播放,放到audition中可以看到波形圖正常即可
由此可以了解PCM和wav文件格式,以及音頻在這兩種文件中的存放方式以及順序。