views:

297

answers:

2

I have a waveform 64 samples long. If the sampling rate is 44100 hz, how can I play(loop) this waveform so that it plays arbitrary frequencies?

frequency = samplerate / waveform duration in samples

Therefore the frequency should be 689hz(44100/64). If I wanted it to be say, 65.41hz(C-2), I would have to do this:

65.41 = 44100 / x

Solving for x yields aprox. 674.208. So I need to figure out what speed to play the waveform at to get this frequency. So we can solve this equation:

64 * x = 674.208

and get about 10.5. So the waveform needs to be played at 10.5% of its original speed.

Here is my code:

double smp_index = 0;
double freq = .105;

void callback(void *data, Uint8 *buf, int len){
    int i;
    s8 *out;
    out = (s8*) buf;
    if(smp_index < waveform_length){
        for(i = 0; i < len; i ++){
            out[i] = smpdata[(int)smp_index];
            smp_index +=freq;
            if(smp_index >= waveform_length)
                smp_index = 0;
        }
    }
}

So the resulting audio should be about the note C-2, but its more of a D-2. Is the cast

(int)smp_index

causing the problem? I couldn't see any other way to accomplish this...

+1  A: 

The cast (int)smp_index is not causing the problem. It simply stretches the wave - this is quality loss (maybe you should have your wave data longer than 64 samples) but cannot possibly change the frequency. Most likely, the problem is that:

        if(smp_index > realLength)
            smp_index = 0;

should be:

        if(smp_index >= realLength)
            smp_index -= realLength;

I also have some other notes for you:

frequency = samplerate / waveform duration in samples

Um, if by "waveform duration" you mean the period of a wave, then yes. I.e. if your 64-sample waveform is a sine wave of period 64, then yes. If it's 32 or 16 then things will be different. If it's something that doesn't divide 64 (like 48 or 30) then your waveform is not periodic in the first place.

Now:

u32 waveform_length;
out = (s8*) buf;
if(smp_index < waveform_length){

What's the value of waveform_length? Looks uninitialised to me...

Artelius
I already took care of replacing > with >=.There were some other typos in the code, I have fixed those.waveform_length length is initialized elsewhere in the code, it is read from a file, and i know it is right because i compared my value of waveform_length with another program that read in the file.
freedrull
This wave is not exactly a sine, but yes, I do mean its period.
freedrull
what about `smp_index -= realLength`? That was the main part...
Artelius
that's a very good thing to do, but no it did not solve the problem either...
freedrull
+1  A: 

Actually, the main problem is not in your code, but in your reasoning.

So we can solve this equation:

64 * x = 674.208

and get about 10.5.

So far so good. (Actually 674.208 should be 674.246 but that's because you rounded 65.41 to 4 significant figures earlier on.)

So the waveform needs to be played at 10.5% of its original speed.

No! The waveform must be slowed down by a factor of 10.5. Which means it must be played at 1/10.5 = 0.095 or 9.5% of its original speed.

Artelius
awesome! This was exactly the problem. Just in time, I was almost to the point where I forgot what I was doing originally.
freedrull
Excellent. Thanks for the response!
Artelius
I've run into another problem. What if the waveform is not periodic? :{
freedrull
Well you would need to repopulate your smpdata[] buffer somehow (in a circular fashion), or use a double buffer technique, so while one buffer is playing a second buffer is being filled, and then they are swapped.
Artelius