Click here to Skip to main content
15,890,399 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi All :)

I'm working on my test sinewave generator application wich can generate, visualize and play variety of sinewave signals and signal patterns. I encountered a pretty strange problem (at least for me) when I tried to generate a sinewave with linearly descending frequency (more specifically: starting at 22Hz and descending down to 8.4Hz).
This is an example of frequency modulation where the modulating signal is just a simple linear descending pattern.
But in order to explain exactly what the problem is I have to first show you what my approach is to generate sinewave signal with constant frequency in the first place:

I use the obvious math equation:

Amplitude * sin(Time * Frequency + Phase)
(The phase is not included in the actual equation because it's always 0)

This is a simplified version of the function that generates sinewave audio signals.
C++
/* RAD() is a macro I use to translate the value given to sin() from degrees into radians as the standard sin() function requires it. This is what allows me to use linear frequency instead of angular.*/
#define RAD(a)	((a) * 0.017453292519943295769236907684886)

void CWAV_ViewerDoc::OnToolsGeneratevoice()
{
	/* Defining the initial parameters of the playable audio signal. */	
	m_waveHeader.nChannels	  = 2;	    // Channels.
	m_waveHeader.nSampPerSec  = 44100;  // Samples per second.
	m_waveHeader.nBytePerSec  = 176400; // Bytes per second (playback speed).
	m_waveHeader.nByteInSamp  = 4;	    // Bytes in sample.
	m_waveHeader.nBitsPerSamp = 16;	    // Bytes per sample.

	/* Defining playback time. (in number of samples) */
	m_nNumSamples = m_waveHeader.nSampPerSec * 60; // 1 minute playback.

	m_puiAudioData = new UINT[m_nNumSamples];

	/* Here I define the factor at which the dTime argument will be progressing through out the track in order to generate 1Hz frequency. */
	double dFactor	= 360.0 / double(m_waveHeader.nSampPerSec); // 1 Hz
	double dTime	= 0.0; // The argument...
	UINT   uiSample;

	for(UINT pos = 0; pos < m_nNumSamples; pos++)
	{
		/* As you can see, since dTime progresses is set at specific rate (for 1Hz) determined by the dFactor simply multiplying it by the required number in hertz produces exactly the desired frequency.*/
		// Amplitude = +/-32767 (as per spec of PCM standard).
		// Frequency = 22Hz
		uiSample = (SHORT)(32767.0 * sin(RAD(dTime * 22.0));
		dTime += dFactor;

		/* Here I copy the value from the lower word to the higher so it'll be the same for both channels and then put it in the buffer. */
		m_puiAudioData[pos] = uiSample << 16 | uiSample & 0x0000ffff;
	}
}


This is (for my mind) the simplest, the most straightforward and intuitive way to generate sinewave signal, it's tested and it works perfectly.

Now the problem! I'll now list the same function but with frequency descension added to the algorithm, and it's also simple and straightforward. I simply get the difference between 22 and 8.4 and devide it by the total number of samples and that gives me the descension factor at which the frequency sould descend every cycle - here it is:

C++
/*With this macro I translate the value given to sin() from degrees into radians as the standard sin() function requires it.*/
#define RAD(a)	((a) * 0.017453292519943295769236907684886)

void CWAV_ViewerDoc::OnToolsGeneratevoice()
{
	/* Defining the initial parameters of the playable audio signal. */	
	m_waveHeader.nChannels	  = 2;	    // Channels.
	m_waveHeader.nSampPerSec  = 44100;  // Samples per second.
	m_waveHeader.nBytePerSec  = 176400; // Bytes per second (playback speed).
	m_waveHeader.nByteInSamp  = 4;	    // Bytes in sample.
	m_waveHeader.nBitsPerSamp = 16;	    // Bytes per sample.

	/* Defining playback time. */
	m_nNumSamples = m_waveHeader.nSampPerSec * 60; // 1 minute playback.

	m_puiAudioData = new UINT[m_nNumSamples];

	/* Here I define the factor at which the dTime argument will be progressing in order to generate 1Hz frequency. */
	double dFactor	= 360.0 / double(m_waveHeader.nSampPerSec); // 1 Hz
	double dTime	= 0.0; // The argument...
	UINT   uiSample;

	/* Here it is the descention factor. */
	double dDescFactor = (22.0 - 8.4) / double(m_nNumSamples);
	double dDescension  = 0.0; // The descension argument...

	for(UINT pos = 0; pos < m_nNumSamples; pos++)
	{
		/* Now you can see how the constant 22.0 is substracted by the argument which is 0.0 at the beginning and gradually increases to 13.6 at the end which substracted from 22.0 gives 8.4 */
	     uiSample = (SHORT)(32767.0 * sin(RAD(dTime * (22.0 - dDescension)));
	     dTime += dFactor;
	     dDescension += dDescFactor;

		/* Here I double the sample value so it'll be the same for both channels and then put it in the buffer. */
		m_puiAudioData[pos] = uiSample << 16 | uiSample & 0x0000ffff;
	}
}


I'm sure that this example is way too simple to cause any difficulties understanding it, and I think that everybody would agree that the result must be a sinewave signal pattern with frequency descending from 22Hz down to 8.4Hz - but that's not what happens...

In reality what happens is that in the first half of the generated track the frequency descends very little (and it actually starts from freq. higher than 22Hz). In the second part of the track it starts descending at ever increassing rate (that's definitely not a linear pattern), and then roughly at 7/8-ths of the track it forms what I could call a "turnover" - it rapidly shifts the phase at 180 degrees forming 'W' letter like pattern at this point, and then the frequency actualy starts to ascend?! And it does so from that point to the end (also around the turnover the freq. is much lower than 8.4Hz).

Also in my attempts to solve the problem I found that the "turnover" is only one in this particular case. If the freq. range is wider, multiple such 'W' - 'M' turnover points will appear and the frequency between them moves up and down, while the driving pattern of all this is only a linear dissension from one positive number to another positive number.

One last thing: If you get any particular frequency from within the range and set it to be generated as constant freq. - it'll be generated perfectly as it should!

This whole thing does not make any sense! I don't nearly have any idea on how to solve this mistery...

Any help would be much appreciated!
Thank you for even reading this prolong TC! :)
Posted
Comments
cariolihome 26-Aug-11 17:57pm    
Can You specify in programming (not in mathematic or phisics) terms, what's bothering you ?
I mean, try to specify indexes (e.g. pos) and corresponding values which incorrect or not realistic
Philippe Mori 26-Aug-11 18:35pm    
I have not tried to really uinderstand you code but I think that your problem is that the wave you generate for each frequency is aligned with the start...

You cannot multiply the absolute time by the current frequency. You have to integrate the time for each time slice multiplied by the frequency factor.
For example:
double currAngle = 0;
for(...)
{
  uiSample = (SHORT)(32767.0 * sin(RAD(currAngle));
//dTime += dFactor;
  dDescension += dDescFactor;
  currAngle += dFactor * (22.0 - dDescension);
}

Regards.
 
Share this answer
 
v3
Comments
Ivan Ivanov 83 27-Aug-11 7:22am    
You are a Genius! :)
I just tested it, and it works perfectly!

Well, I missed to mention that my math skill isn't gery good ;). I'll need some time to process onto how does it work so well, but the fact is a fact - it works!

I was so puzzled by this glitch that I thought that nobody could help me :D thank god I was so wrong :D
It turns out that generating descending patterns IS possible after all :D :D
Sergey Alexandrovich Kryukov 28-Aug-11 23:36pm    
Ha-ha, nice catch, a 5.
--SA
In my opinion, you are using the wrong dFactor (it doesn't match the frequency decreasing rate -and the playback duration-). It should be
dFactor = 1.0/m_waveHeader.nSampPerSec;
 
Share this answer
 
v3
Comments
Ivan Ivanov 83 27-Aug-11 7:31am    
I appreciate your answer but I have already tasted the factor (360 / 44100) and it produces exactly 1 Hz frequency.

with (1 / 44100) factor it will produce just higher frequency but those turnovers will remain ;)

Thanks a lot anyway :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900