This extension can quickly convert PCM format sound wave files into WAV format audio files. Currently, it only provides solutions for company projects.
The extended reference is in helviojunior/WaveGenerator, special thanks to you!
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
and WAV
? PCM
: PCM (Pulse Code Modulation----pulse code modulation recording). The so-called PCM
recording is to convert analog signals such as sound into symbolic pulse trains and then record them. PCM
signal is a digital signal composed of 1
, 0
and other symbols without any encoding or compression processing. Compared with analog signals, it is less susceptible to noise and distortion in the transmission system. The dynamic range is wide and the sound quality can be quite good.
WAV
: WAV
is a lossless audio file format. WAV
complies with the PIFF (Resource Interchange File Format) specification. All WAV
have a file header, which contains the encoding parameters of the audio stream. WAV has no hard and fast rules for encoding audio streams. In addition to PCM
, almost all encodings that support ACM
specification can encode WAV audio streams.
PCM
and WAV
Simply put, PCM
is the original data of audio, and WAV
is a container that encapsulates audio data, and its format is very simple. It just adds some header information related to the audio data at the beginning of the data.
First, let’s take a look at the format rules of WAV, as shown below
After understanding these rules, we can start coding
1. ChunkID
occupies 4 bytes, fixed value "RIFF"
$ChunkID = array(0x52, 0x49, 0x46, 0x46); // RIFF 16进制的0x52等于10进制中的82,82对应的ASCII码为R
2. ChunkSize
occupies 4 bytes, and the value is 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size). If the original data is PCM, it is simplified to 36 + SubChunk2Size
$ChunkSize = array(0x0, 0x0, 0x0, 0x0);
$ChunkSize = self::getLittleEndianByteArray(36 + $dataSize);
3. Format
occupies 4byte, fixed value "WAVE"
$FileFormat = array(0x57, 0x41, 0x56, 0x45); // WAVE
4. Subchunk1ID
occupies 4 bytes, fixed value "ftm" (note the space completion of 4 digits)
$Subchunk1ID = array(0x66, 0x6D, 0x74, 0x20); // fmt
5. Subchunk1Size
occupies 4byte, and when the data is PCM, the value is 16
$Subchunk1Size = array(0x10, 0x0, 0x0, 0x0); // 16 little endian
6. AudioFormat
occupies 2 bytes. When the data is PCM, the value is 1. Other values indicate that the data has been compressed in some way.
$AudioFormat = array(0x1, 0x0); // PCM = 1 little endian
7. NumChannels
occupies 2 bytes, corresponding to channelConfig in AudioRecord, Mono = 1, Stereo = 2
if ($numchannels == 2) {
$fmt->NumChannels = array(0x2, 0x0); // 立体声为2
} else {
$fmt->NumChannels = array(0x1, 0x0); // 单声道为1
}
8. SampleRate
occupies 4 bytes, corresponding to sampleRateInHz in AudioRecord, which is the sampling frequency, such as 8000, 16000, 44100
$SampleRate = self::getLittleEndianByteArray($samplerate);
9. ByteRate
occupies 4byte, and the value is SampleRate * BlockAlign
self::getLittleEndianByteArray($samplerate * $numchannels * ($bitspersample / 8));
10. BlockAlign
occupies 2 bytes, and the value is NumChannels * BitsPerSample / 8
self::getLittleEndianByteArray($numchannels * ($bitspersample / 8), true);
11. BitsPerSample
occupies 2byte, corresponding to audioFormat in AudioRecord, 8bits = 8, 16bits = 16
self::getLittleEndianByteArray($bitspersample, true);
12. Subchunk2ID
occupies 4byte, fixed value "data", that is
$Subchunk2ID = array(0x64, 0x61, 0x74, 0x61); // data
13. Subchunk2Size
occupies 4byte, which describes the length of the audio data, which is the size of the pcm file.
self::getLittleEndianByteArray(filesize($filename));
14. Data
accounts for bytes of the pcm file size, representing the original PCM audio data.
getLittleEndianByteArray
method description
getLittleEndianByteArray
mainly processes the passed numbers and converts them into the data that needs to be used. The length of the array will be returned based on the number of bytes.
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;
}
The first 44 bytes of information in the entire file have been basically explained. Let’s talk about the implementation of processing class files. The processing logic here first temporarily creates a file with only 44 bytes, and then appends the data of PCM
file to the file. Finally, according to the WAV format rules, the actual header 44-byte information is actually calculated and the file modification pointer points to the beginning of the file, and then modified to the newly generated data.