views:

374

answers:

2

Hi. I am trying to upsample an 8000hz, 16-bit wav file to 11025hz in AS3. At this point, I am not concerned about applying the low-pass filters that I know I will eventually need.

I've been referencing this wiki page.

Here is what I've done so far:

  1. Calculated the least common multiple to be 3528000
  2. Calculated L to be 441
  3. Calculated M to be 320
  4. Added 440 zeroes between samples
  5. Written every 320th sample to a new byte array

However, when I go to play the new wav, it is indistinguishable noise. Here is my code:

const sourceRate:uint = 8000;
const targetRate:uint = 11025;
var lcm:uint = lcm(targetRate, sourceRate); // = 3528000
var l:uint = lcm / sourceRate; // = 441
var m:uint = lcm / targetRate; // = 320

// upsample by factor of l
var upsampleData:ByteArray = new ByteArray();
upsampleData.endian = Endian.LITTLE_ENDIAN;

// originalWavData is a ByteArray of the source wav data
// fill is a ByteArray that contains 440 zeroes, written using writeShort(0x0)

while(originalWavData.bytesAvailable > 1) {
    upsampleData.writeBytes(fill);
    upsampleData.writeShort(originalWavData.readShort());
}

// downsample by factor of m
var downsampleData:ByteArray = new ByteArray();
downsampleData.endian = Endian.LITTLE_ENDIAN;

upsampleData.position = 0;

for(var k:uint=0; k<upsampleData.length; k++) {
    upsampleData.position = k * m;
    if(upsampleData.bytesAvailable < 2) break;
    downsampleData.writeShort(upsampleData.readShort());
}

Can anyone show me what I am doing wrong in my code? This is my first question post, so if I've forgotten something, or need to provide more information, please let me know.

Thanks!

Update:

I dumbed down Aric's answer and am now successfully upsampling using the following code:

/**
 * Generates a ByteArray containing numSamples of
 * data using linear interpolation between points
 * y0 and y1.
 */
function interpolate(y0:int, y1:int, numSamples:uint):ByteArray {
    var b:ByteArray = new ByteArray();
    b.endian = Endian.LITTLE_ENDIAN;
    var m:Number = Math.round((y1-y0)/numSamples);
    for(var i:uint=0; i<numSamples; i++) {
        var n:int = m * i + y0;
        b.writeShort(n);
    }
    b.position = 0;
    return 0;
}

// upsample by factor of l
var n1:int = 0;
while(originalWavData.bytesAvailable > 1) {
    var sample:int = originalWavData.readShort();
    upsampleData.writeBytes(interpolate(n1, sample, (l-1)));
    n1 = sample;
}

// downsample by factor of m
while(upsampleData.bytesAvailable > 1) {
    downsampleData.writeShort(upsampleData.readShort());
    upsampleData.position += ((m-1)*2);
}

A couple things to note about this solution: I am upsampling an audio captcha, so sound quality isn't terribly important. Also, the first samples are just silence, so I didn't need to calculate a value to the left of the first sample. That is why n1 is equal to 0 initially. Also, instead of averaging the generated samples together in my downsample, I just grabbed every Mth sample and it sounded just fine for my purposes.

I am sure there are 1000 better ways to do this, but for what I needed, it works. Thanks again to Aric for his answer.

+3  A: 

Is there a particular reason why you want to upsample? Upsampling won't give you better audio, any more than recording a cell phone conversation onto a CD will give you "CD-Quality" audio.

If you really do want to upsample, then as Lasse mentioned, don't just insert zeros. You need to interpolate out what the samples would have been by, as Wikipedia points out, upsampling to 3528000 Hz and then downsampling back to 11025 Hz.

For the upsampling, one way to do it is by using a line algorithm between each point. Let's say the first three samples in the 8 kHz recording are: [15, 25, 33].

To upsample those three samples to 3528000 Hz, you'll need to output (441 * 3 = 1323) samples. The 220th sample should be 15, the 661st should be 25, and the 1102nd sample should be 33. Draw a straight line between the points and you'll get a decent upsample.

Once you do that, you need to sample your 1323 samples down to about 4 samples. The first one should be the average of samples 160-480, the second the average of 481-800, the third the average of 801-1160, and the fourth the average of 1161-1480. Of course, you're missing samples 1324-1480, which you could have generated with sample #4 in the original data.

That should at least get you close. But before you do that, ask yourself if you really need to upsample. Unless you're using some weird software that only takes certain formats, you're better off just giving it the data you have.

Aric TenEyck
Thank you very much for your answer. I do understand that upsampling won't increase the quality. This is what I am doing: I am generating an audio captcha in the mid-tier using FreeTTS. The voice that I am using is an 8khz voice, and that's why I have an 8khz wav file. Flash won't play wav files natively, so I am having to generate a SWF at runtime and embed the wav file in the generated SWF. Flash only supports sample rates that are multiples of 5512.5 hz, so I decided to upsample the audio client side (save our app server some clock cycles). I got it working using your answer. Thanks again!
RJ Regenold
A: 

Hello,

If I get audio data from streaming server continuously(sound format:8k rate,16bit size,1 channel), is this fine for my case. (Because the Sound.souce/SampleDataEvent only supports 44.1 k rate)

Patch dev