此擴充功能能快速將PCM格式的音波文件轉換成WAV格式的音訊文件,目前只為公司專案提供解決方案。
擴充參考於helviojunior/WaveGenerator ,在此特別感謝!
composer require jade/pcm-to-wav
use PcmToWav e P cmToWave ;
$input_file = ' ./file/test.pcm ' ; // 准备输入的文件
$output_file = ' ./file/test.wav ' ; // 预计输出的文件
$data = PcmToWav e::init( $pcm_file , $wav_file ) ; // 调用转换
进入扩展包目录
cd vendor/jade/pcm-to-wav
composer install
cd test
php Test.php
PCM
和WAV
? PCM
:PCM(Pulse Code Modulation----脈碼調變錄音)。所謂PCM
錄音就是將聲音等類比訊號變成符號化的脈衝列,再予以記錄。 PCM
訊號是由1
、 0
等符號構成的數位訊號,而未經過任何編碼和壓縮處理。與類比訊號比,它不易受傳送系統的雜波及失真的影響。動態範圍寬,可得到音質相當好的影響效果。
WAV
: WAV
是一種無損的音訊檔案格式, WAV
符合PIFF(Resource Interchange File Format) 規格。所有的WAV
都有一個檔案頭,這個檔案頭音訊流的編碼參數。 WAV對音訊流的編碼沒有硬性規定,除了PCM
之外,還有幾乎所有支援ACM
規範的編碼都可以為WAV的音訊串流進行編碼。
PCM
和WAV
的關係簡單地說, PCM
是音頻的原始數據, WAV
則是一種封裝音頻數據的容器, 而且它的格式還很簡單, 只是在數據開頭添加一些和音頻數據相關的頭信息。
首先我們來看看WAV的格式規則, 如下圖
了解這些規則後,我們就可以擼程式碼吧
1、 ChunkID
佔4byte, 固定值"RIFF"
$ChunkID = array(0x52, 0x49, 0x46, 0x46); // RIFF 16进制的0x52等于10进制中的82,82对应的ASCII码为R
2. ChunkSize
佔4byte, 值為4 + (8 + SubChunk1Size) + (8 + SubChunk2Size), 其中如果原始資料是PCM, 簡化為36 + SubChunk2Size
$ChunkSize = array(0x0, 0x0, 0x0, 0x0);
$ChunkSize = self::getLittleEndianByteArray(36 + $dataSize);
3、 Format
佔4byte, 固定值"WAVE"
$FileFormat = array(0x57, 0x41, 0x56, 0x45); // WAVE
4. Subchunk1ID
佔4byte, 固定值"ftm "(注意空格補全4位元)
$Subchunk1ID = array(0x66, 0x6D, 0x74, 0x20); // fmt
5. Subchunk1Size
佔4byte, 數據為PCM時, 值為16
$Subchunk1Size = array(0x10, 0x0, 0x0, 0x0); // 16 little endian
6. AudioFormat
佔2byte, 資料為PCM時, 值為1, 其他值表示資料進行過某種壓縮
$AudioFormat = array(0x1, 0x0); // PCM = 1 little endian
7. NumChannels
佔2byte, 對應AudioRecord中的channelConfig, 單聲道Mono = 1, 立體聲Stereo = 2
if ($numchannels == 2) {
$fmt->NumChannels = array(0x2, 0x0); // 立体声为2
} else {
$fmt->NumChannels = array(0x1, 0x0); // 单声道为1
}
8. SampleRate
佔4byte, 對應AudioRecord中的sampleRateInHz, 即取樣頻率, 例如8000, 16000, 44100
$SampleRate = self::getLittleEndianByteArray($samplerate);
9. ByteRate
佔4byte, 值為SampleRate * BlockAlign
self::getLittleEndianByteArray($samplerate * $numchannels * ($bitspersample / 8));
10. BlockAlign
佔2byte, 值為NumChannels * BitsPerSample / 8
self::getLittleEndianByteArray($numchannels * ($bitspersample / 8), true);
11. BitsPerSample
佔2byte, 對應AudioRecord中的audioFormat, 8bits = 8, 16bits = 16
self::getLittleEndianByteArray($bitspersample, true);
12、 Subchunk2ID
佔4byte, 固定值"data", 即
$Subchunk2ID = array(0x64, 0x61, 0x74, 0x61); // data
13. Subchunk2Size
佔4byte, 描述音訊資料的長度, 就是pcm檔案大小
self::getLittleEndianByteArray(filesize($filename));
14. Data
佔pcm檔案大小個byte, 表示原始的PCM音訊數據
getLittleEndianByteArray
方法說明
getLittleEndianByteArray
主要是將傳遞過來的數進行處理已轉換成需要使用的數據,站幾字節,就返回多少長度的數組
private static function getLittleEndianByteArray($lValue, $short = false)
{
$b = array(0, 0, 0, 0);
$running = $lValue / pow(16, 6);
$b[3] = floor($running);
$running -= $b[3];
$running *= 256;
$b[2] = floor($running);
$running -= $b[2];
$running *= 256;
$b[1] = floor($running);
$running -= $b[1];
$running *= 256;
$b[0] = round($running);
if ($short) { // 为 `true` 时返回长度为2的数组
$tmp = array_slice($b, 0, 2);
$b = $tmp;
}
return $b;
}
整個文件的開頭44位元組資訊也基本上說明完了,下面就說下處理類文件的實現,這邊處理的邏輯先臨時創建一個只有44字節的文件,然後將PCM
文件的數據追加進該文件,最終根據WAV的格式規則實際計算出真實的頭部44位元組資訊並將檔案修改指標指向檔案開頭,然後修改為新產生的數據