Sound, Notes, and Sampling Rate

Started by
3 comments, last by TerranFury 22 years, 5 months ago
I''ve been experimenting with sound, outputting *.raw sound files. Currently, I''m outputting 44100 Khz 32-bit big-endian PCM files. I figured I''d try to output harmonic tones. Searching online, I came across This small page that seemed to have what I was looking for. However, I''m having a problem. It seems clear that I need to multiply my frequency by a conversion factor, because the formula returns the frequency of the sound in hertz. Therefore, I figured I would need to multiply the frequency by 44100/1000; the 44100 to compensate for the sampling rate and the 1000 to convert from hertz to Khz. I only get very high-pitched sounds. I tried using arbitrary conversion factors, and found that both .4 and .5 brought the sound into approximately the right range. Oddly enough, .44100 resulted in a high-pitch squeal. All I want to do is output an arbitrary note! Can anybody help? In case something else in my code is the problem, I''m posting all of the code:
  
#include <fstream.h>
#include <math.h>
#include <stdlib.h>



long round(double x)
{
	if((x - (int)x) >= 0.5)
	{
		return (long)(x + 1);
	}
	else
	{
		return (long)(x);
	}
}

/*

Old byte-swap function.  My ASM one should do the same thing.

  void byteSwapLong(long * ptr)
{
	char * crPtr = (char *)((void *)ptr);

	char b = crPtr[3];
	crPtr[3] = crPtr[0];
	crPtr[0] = b;

	b = crPtr[2];
	crPtr[2] = crPtr[1];
	crPtr[1] = b;
}
*/

void byteSwapLong(long * ptr)
{
	void * p = (void *)ptr;
	_asm
	{
		mov ebx, p
		mov ah, [ebx]
		xchg ah, [ebx+3]
		mov [ebx], ah
		mov ah, [ebx+1]
		xchg ah, [ebx+2]
		mov [ebx+1], ah
	}
}

int main()
{
		ofstream fileOut("rawsnd.raw", ios::binary | ios::out);
		double amp  = 32;
		double freq = 1.0/30.0;
		const double convfact = 44100 / 1000; //hz


		for(double i = 0; i < 2 * 1024 * 1024; i+=1.0)
		{
			
			freq = (440.0 * pow(pow(2.0, 1.0/12.0),0)) * convfact;

			long x =  round(sin(i * freq) * amp );
			
			
			byteSwapLong(&x);

			void * ptr = (long *)(&x);

                        //I''m aware that byte-by-byte output is slooow.

			fileOut.write((char *)ptr,sizeof(long));
		}
		
		fileOut.close();

		return 0;
}
[/sound]  
Advertisement
I don''t really understand your calculation. i is 1/44100 of a second if your sample rate is 44.1khz and you write one sample per iteration. freq seems to be 19404. Each iteration you are taking the sin of a value that changed by 19404 from the last iteration which is roughly 3000 pi. 2pi is a cycle so it is just an anomally of sampling that you get any sound. My best guess is that you are actually generating a sound in the range of 1/pi hz which I believe is well below the range of human hearing.

What I would suggest is to use t instead of i since you are using a float anyway and use a step of 1.0/samplerate. That makes t your relative time in seconds. That is less confusing. Then use sin(t*2*pi*freq) where freq is simply the frequency in hertz. So if freq was 1hz you would be taking the sin of a value that varies from 0 to 2*pi as t varies from 0 to 1, i.e. 1 cycle of the sine wave. If it was 2hz it would vary from 0 to 4*pi, i.e. 2 cycles of the sine wave. I don''t know much about sound samples, but I thought they were always positive. If they are then you need ((sin(t*2*pi*freq)+1)/2)*amp to get it in the range of 0 to amp. Since 2*pi*freq doesn''t change during the loop you could move that into the loop increment. So you might calculate a step and limit variable before the loop where step=2*pi*freq/rate and limit=dur/step where dur is how many seconds you want the sound to run for.
Keys to success: Ability, ambition and opportunity.
You should use the bswap instruction in assembler.
I forgot to take into account the period of sine itself! (Boy I feel dumb now). Thanks for pointing that out. You were right; I was only getting waves in my data because my frequency was a little greater than a multiple of the sample rate. (You know; the reason propellers in movies go forwards, then backwards, then forwards....) Anyway, thanks a lot LilBudyWizer for pointing that out.

Also thanks Anon for the assembly tip. I wasn''t aware of that instruction, but using it halved the length of my byte-swapper routine.
Also, LilBuddyWizer, you said, "I don''t know much about sound samples, but I thought they were always positive." That depends on the format. The format can either be signed or unsigned. If it''s signed, then the number must be in two''s compliment format. If it''s unsigned, then the sample is interpreted by the program as x - 2^(bits_per_sample - 1); in other words, it uses the offset format for signed integers.

By default, *.WAV files are unsigned if they''re 8-bit and signed if they''re 16-bit.

I use GoldWave to open my RAW sounds, so I tell it to use a 32-bit signed big-endian format. Big-endian is usually used for sounds.

Hope that clarifies things.

This topic is closed to new replies.

Advertisement