Create an audio stitching tool in PHP

One way to provide visually impaired users with the ability to use OCR-defeating passcodes is to stitch WAV files together. In this article, I demonstrate how to deconstruct a group of WAV files and reconstruct one large WAV file by compiling the group. (Note: I used ASP and Visual Basic to build this solution.)

One way to provide visually impaired users with the ability to use OCR-defeating passcodes is to stitch WAV files together. In this article, I demonstrate how to deconstruct a group of WAV files and reconstruct one large WAV file by compiling the group. (Note: I used ASP and Visual Basic to build this solution.)

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.