7
votes

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

2

2 Answers

2
votes

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.

1
votes

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