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:
- Calculated the least common multiple to be 3528000
- Calculated L to be 441
- Calculated M to be 320
- Added 440 zeroes between samples
- 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.