Produce waves with certain freq

Started by
18 comments, last by Dospro 18 years, 9 months ago
Hi. im trying to solve one problem with sound emulation. Well, in the API(SDL, allegro, etc) y init the sound at 8 bits, 44100hertz(which means 44100 samples in one second, right?). Well, the problem is that im generating the wave based on an array. I mean, i have an array of x number of samples(8, 16, or 32), and i have to produce the wave from this list of samples. Well, the problem here is that that this wave have another freq, so how can i combine the 44100 freq with the wave freq and produce the sample in this freq? Well, remembering my physics class i think that to do this is multiplying 44100*(1/freq)*(1/number_of_samples). This formula should give me how much do i have to repeat each sample of the array. I still dont know if this is fully correct, if not, correct me please. Ok, when i usethis i encountered one problem. If the array has 32 samples, the freq gets low(like an octave). If i sue 8 samples in th array, i get more less correct freq but still low to what it must be. I was using a bad mehtod to implementing this, in sdl, i put all the translation and sound generation routines in the sound generating thread sdl creates. This coused some pst pst in the sound but i get this error with sdl and allegro. Also, i was trying to do the same with fmod but i cant it make produce waves like i do with sdl and allegro, based on an array. How can i solve all this?
Advertisement
When you say the wave in the array has another frequency, I think you actually mean it's sampled at a different rate, yes? If you know the rate then you should be able to resample it to 44.1KHz with interpolation, and just repeat it over and over. For example if it's at 22.05KHz, then you read one sample from the array, then one interpolated sample half-way between the current one and the next one, then the next one from the array, etc. It's a bit more complex if you don't have such an easy conversion ratio though.
no no no.
What i meant was this:

I have the main frequency(44100 or maybe 48000).
And i have the wave frequeny(always change) which is the tone of the sound wave.
And finally i have the array, which has in this case 8 samples which must be repeated certain number of times depending on the other 2 frequencies.

Another problem i encountered with my code was that when i use 22050 instead, i get low tones instead of low quality. I need to know how to calculate the freq to produce a wave with its own freq at a rate of 44100.
I still don't understand what you mean. When you say 8 samples, do you really mean a sample as in 1 piece of data, or a sample as in a wave file? To produce a wave at any given frequency you would just use the sin() function, where the period is 44100 / tone frequency.

If you get low tones instead of low quality then you are probably halving the frequency of the tone you generate, instead of just generating half as many samples for it. If you halve the sampling frequency (eg. from 44100 -> 22050) then you also need to halve the number of samples you use in any single oscillation.
mmm. With one sample i mean 8 bits data. The array has 8 samples so its 8 bytes.
And i dont understand what you are trying to say about the sin thing.

Try to draw(literaly) this stuff out.
A pure tone is a sine wave, so you use the sin function to generate the values. The number of values you generate is inversely proportional to the frequency of the tone you want and proportional to the sampling frequency. The values you pass to sin() range from 0 to 2*PI across that range (if my quick calculations are correct), and you repeat that for the duration of the tone you want to generate.

I don't know what purpose your 8 bytes serve, and unfortunately I can't give a better explanation without more information. Perhaps that array is supposed to replace the sin wave but then we'd need to know what sort of period it has, etc.
No, you are going to something more complex.
Ok lets try to squeme.
The aPI(lets say allegro) is inited with a buffer of 1024 bytes and a freq of 44100.
Ok, know i have an array which is my wave(lets say it is a quadrangular wave)

|_|-|_|-|_|

(An odd representation of the square wave.

the array you look like this:

unsigned char wave[]={0,0,0,0,255,255,255,255};//This is a quadrangular wave.

Ok, the sound engine which im emulating, has some registers which give me the tone of this wave, they give me a frequency. If this frequency is low the tone should be low, if it is high the tone must be high.

Know, i need to fill the 1024 buffer allegro gives me with the correct data.
How many times must i repeat each sample?
How to calculate it?

I used the formula 44100*(1/tone_freq)*(1/8_bytes_array) which is 44100/tone_freq/8. This works "fine" give me tones that are similar to what i need but the freqs get low if i use a bigger array, and using the 8 bytes array i get a lower frequency than i want.
So i suppose that i must have an error in caluclation the final freq.
Ok, thinking about it, your formula is fine so I assume you are just not using it properly when it comes to copying the values across if the frequency is low. Either that or the fact that you're using a square wave causes aliasing at the end although that would generally make it sound more high pitched. Are you making sure you don't lose the fractional parts when you perform the calculation?
Well, that a little bit odd.
When working in some other code of this emulation, i had the problem with this. making calculations like (1024*15.7)/(2048-(13000.xxxx/2048);
The result was kept in a float vriable but i dont know why it never gave me the fractional part, i used instead >> and << which improved a little more(becouse the division of 2048/1300000 always gives me 0, that really produce me headaches, but with the binary operands i didnt resolve it, i just improved it a lot, so the values are not always 0.

Now, in this calcualtion, i use a double variable because i use a buffer of 2048 and it must be filled with the correct values.
Ill put some explained code of what did i do.

void build_sound(void)
{
float f1, f2, f3;
int i;
int l, h;
f1=1/(131072/(2048-ch1.freq));//This is the formula implementation
f2=1/(131072/(2048-ch2.freq));//where chx.freq is the wave tone
f3=1/(65536/ (2048-ch3.freq));
memset(buffer, 0, SOUND_BUFFER*2);
i=Ram[0xFF22][0]&7;
//More emulation
//.
//.
for(i=0; i<SOUND_BUFFER; i++)//This is the loop which fills the buffer
{
l=get_pattern_sample(f1, &ch1.c1, ch1.pattern, 32, &ch1.c2)/15)>>4;
//get_pattern_sample should give me the sample depending on the //frequency
//Maybe the error is there

buffer[i<<1]=l<<1;
buffer[(i<<1)|1]=l<<1;//We produce the stereo sound
}
memcpy(spointer, buffer, (SOUND_BUFFER*2));//We fill the sound buffer
}

Ok, this is very simplyfied function of what i do, its much more complex, but almost all of this is the main work.
Ill put the get_pattern_sample code so you can see the geart of the formula calculating.

unsigned char get_pattern_sample(double freq, double *counter, unsigned char *pattern, int size, int *buf_pp)
{//This will return the correct sample to be read
double r_size=(freq/size)*SOUND_FREQ;//caclulate how many times we must //repat the same sample
//SOUND_FREQ is the 44100 constant
//tha pattern argument is a pointer to the array containing the quadrangular wave
//And the counters are external float and int variables to count how many times we have repeated a sample.
if ((*counter)<=r_size)
{
(*counter)+=1;
return pattern[(*buf_pp)];//Return the sample
}
else // we must go to the next sample
{
*buf_pp+=1;
if ((*buf_pp)>=size)
(*buf_pp)=0;//from the beggining
(*counter)-=r_size;
return pattern[(*buf_pp)];
}
}

As you cn see the counters are outside the function, thi is because the same function is called for different channels.
I still dont ytust this routine, because well, its the one which gives me the tone, its the heart of the frequency

What do you think?

This topic is closed to new replies.

Advertisement