views:

158

answers:

1

Please see the class I have created at http://textsnip.com/see/WAVinAS3 for parsing a WAVE file in ActionScript 3.0.

This class is correctly pulling apart info from the file header & fmt chunks, isolating the data chunk, and creating a new ByteArray to store the data chunk. It takes in an uncompressed WAVE file with a format tag of 1. The WAVE file is embedded into my SWF with the following Flex embed tag:

[Embed(source="some_sound.wav", mimeType="application/octet-stream")]
public var sound_class:Class;
public var wave:WaveFile = new WaveFile(new sound_class());

After the data chunk is separated, the class attempts to make a Sound object that can stream the samples from the data chunk. I'm having issues with the streaming process, probably because I'm not good at math and don't really know what's happening with the bits/bytes, etc.

Here are the two documents I'm using as a reference for the WAVE file format: http://www.lightlink.com/tjweber/StripWav/Canon.html https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

Right now, the file IS playing back! In real time, even! But...the sound is really distorted. What's going on?

+1  A: 

The problem is in the onSampleData handler.

In your wav file, the amplitudes are stored as signed shorts, that is 16 bit integers. You are reading them as 32 bit signed floats. Integers and floats are represented differently in binary, so that will never work right.

Now, the player expects floats. Why did they use floats? Don't know for sure, but one good reason is that it allows the player to accept a normalized value for each sample. That way you don't have to care or know what bitdept the player is using: the max value is 1, and the min value is -1, and that's it.

So, your problem is you have to convert your signed short to a normalized signed float. A short takes 16 bits, so it can store 2 ^ 16 (or 65,536) different values. Since it's signed and the sign takes up one bit, the max value will be 2 ^ 15. So, you know your input is the range -32,768 ... 32,767.

The sample value is normalized and must be in the range -1 ... 1, on the other hand.

So, you have to normalize your input. It's quite easy. Just take the read value and divide it by the max value, and you have your input amplitude converted to the range -1 ... 1.

Something like this:

    private function onSampleData(evt:SampleDataEvent):void 
    { 
        var amplitude:int = 0;
        var maxAmplitude:int = 1 << (bitsPerSample - 1); // or Math.pow(2, bitsPerSample - 1);
        var sample:Number = 0; 
        var actualSamples:int = 8192;
        var samplesPerChannel:int = actualSamples / channels;

        for ( var c:int = 0; c < samplesPerChannel ; c++ ) { 
            var i:int = 0;
            while(i < channels && data.bytesAvailable >= 2) {
                amplitude = data.readShort();
                sample = amplitude / maxAmplitude;
                evt.data.writeFloat(sample); 
                i++;
            }
        } 
    }  

A couple of things to note:

  1. maxAmplitude could (and probably should) be calculated when you read the bitdepth. I'm doing it in the method just so you can see it in the pasted code.

  2. Although maxAmplitude is calculated based on the read bitdepth and thus will be correct for any bitdepth, I'm reading shorts in the loop, so if your wav file happens to use a different bitdepth, this function will not work correctly. You could add a switch and read the necessary ammount of data (i.e., readInt if bitdepth is 32). However, 16 bits is such a widely used standard, that I doubt this is practically needed.

  3. This function will work for stereo wavs. If you want it to work for mono, re write it to write the same sample twice. That is, for each read, you do two writes (your input is mono, but the player expects 2 samples).

  4. I removed the EOF catch, as you can know if you have enough data to read from your buffer checking bytesAvailable. Reaching the end of stream is not exceptional in any way, IMO, so I'd rather control that case without an exception handler, but this is just a personal preference.

Juan Pablo Califano
PERFECT! I /knew/ the problem was with my lack of math skills. I more or less plugged this right into my class and it worked like a charm. Very well written and thorough response. Thank you!
Jeremy White
Sure. Glad it helped.
Juan Pablo Califano