Create an audio stitching tool in PHP
Understanding Microsoft PCM WAV files
Microsoft PCM WAV files contain information
such as the audio type, the audio format, and the audio data split into
three "chunks." The first chunk contains ChunkID, ChunkSize, and Format.
In a few bytes, this chunk informs any application reading the media file
that it's a WAVE audio file.
The second chunk contains Subchunk1ID, Subchunk1Size, AudioFormat, NumChannels, SampleRate, ByteRate, BlockAlign, and BitsPerSample. Respectively, this is "fmt"; 16 (for PCM); 1 (no compression); 1 or 2 (Mono or Stereo); 8000, 44100, etc.; SampleRate * NumChannels * BitsPerSample/8; NumChannels * BitsPerSample/8; and 8, 16, etc.
The third chunk contains the Subchunk2ID, Subchunk2Size, and Data: "data", data size (NumSamples * NumChannels * BitsPerSample/8), and the audio data. To retrieve this information, you simply read and store the appropriate bytes in the form of an audio file. With PHP, you do this with the stream functions.
Using PHP's stream functions
By using a combination of fopen(), fread(), and
fclose(), read these values in to local variables. A method that I'm
particularly fond of is storing the file information in a structure for
easy organization. You can do this with a class in PHP:
class FILESTRUCT {
var
$ChunkID;
var
$ChunkSize;
var
$Format;
var
$Subchunk1ID;
var
$Subchunk1Size;
var
$AudioFormat;
var
$NumChannels;
var
$SampleRate;
var
$ByteRate;
var
$BlockAlign;
var
$BitsPerSample;
var
$Subchunk2ID;
var
$Subchunk2Size;
var
$Data;
function FILESTRUCT()
{
$this->ChunkID =
array(0x0, 0x0, 0x0,
0x0);
$this->ChunkSize
= array(0x0, 0x0, 0x0,
0x0);
$this->Format
= array(0x0, 0x0, 0x0,
0x0);
$this->Subchunk1ID
= array(0x0, 0x0, 0x0,
0x0);
$this->Subchunk1Size
= array(0x0, 0x0, 0x0,
0x0);
$this->AudioFormat
= array(0x0,
0x0);
$this->NumChannels
= array(0x0,
0x0);
$this->SampleRate
= array(0x0, 0x0, 0x0,
0x0);
$this->ByteRate
= array(0x0, 0x0, 0x0,
0x0);
$this->BlockAlign
= array(0x0,
0x0);
$this->BitsPerSample
= array(0x0,
0x0);
$this->Subchunk2ID
= array(0x0, 0x0, 0x0,
0x0);
$this->Subchunk2Size
= array(0x0, 0x0, 0x0,
0x0);
$this->Data =
array();
}
}
This file structure class contains information about the size of each section of the file. For instance, the ChunkID property is a four element array for containing four bytes of information. When I read in the binary data from the file, I can use the count() function to grab the number of bytes that makes up that portion of the file data. Given the basic information to retrieve the file data, you can start to conceptualize the end solution.
Stitching WAV files together
When a user visits a particular page, you want
to provide the user with an audio file stitched together from multiple
audio files. In order for this to happen, one of the input parameters
needs to be an array of file paths. You must loop through these files,
load the file information into the FILESTRUCT structures, and create a new
FILESTRUCT structure that contains the information on the concatenated
files. Then, using that FILESTRUCT structure, set the MIME type on the
response and pump out the binary data.
I'll create a class to handle the file stitching operations. This is mostly because I use a class module in my Visual Basic solution to provide this functionality. The class will only contain one public method: StitchFiles. This method will accept a FILESTRUCT reference and an array of files:
class CStitcher {
function
StitchFiles(&$fsFile, &$sFiles)
{
...
}
}
This method contains the guts of the stitching process. Next week's newsletter will delve deeper into implementation. Also, I'll provide the code to deliver the file and some HTML code to play the audio.
Phillip Perkins is a contractor with Ajilon Consulting. His experience ranges from machine control and client server to corporate intranet applications.