play a waveform at a certain frequency in SDL callback function
Asked Answered
C

2

8

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...

Culdesac answered 2/11, 2009 at 23:31 Comment(0)
W
2

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.

Winzler answered 3/11, 2009 at 22:45 Comment(3)
awesome! This was exactly the problem. Just in time, I was almost to the point where I forgot what I was doing originally.Culdesac
I've run into another problem. What if the waveform is not periodic? :{Culdesac
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.Winzler
W
1

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...

Winzler answered 2/11, 2009 at 23:52 Comment(2)
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.Culdesac
This wave is not exactly a sine, but yes, I do mean its period.Culdesac

© 2022 - 2024 — McMap. All rights reserved.