Click here to Skip to main content
Click here to Skip to main content

Sound Generator – How to create alien sounds using mathematic formulas

By , 20 Jun 2004
 

Sample Image

Introduction

This article demonstrates how to generate a quasi-infinite number of sounds using math formulas. Also, a sample program is provided to allow you to do experimentation by your own. Have great times!

Background

A sound is basically a sine wave traveling in the air. It is characterized by a power and a frequency. The power determines the sound loudness and the frequency determines its pitch. If you are not familiar with this phenomenon, I propose you to start the sample program right now!

Sound frequency

First, try some variant of “sin(x*t)”, beginning with x equal to 20 and increasing it progressively by step of 20. In that expression, “x” represents the frequency in Hertz and “t” is the time variable, varying from 0 to 2pi rad in one second. Maybe with x = 20 you will hear nothing. If this is the case, don’t get mad, it’s just that your speakers can’t handle a so low frequency. (By the way, this is a good method to test your speakers’ quality.) For reference, humans generally can hear sine waves between 30Hz to 20000Hz.

Sampling rate

In the digital world, sampling rate is the number of sound snapshots (called samples) that are used to generate a sine wave in the analog world. Hence, higher is the sampling rate, better will be the sound fidelity. There is a fundamental theorem called the Nyquist theorem that states that the sampling rate must be at least twice the highest analog frequency in order to accurately represent the original sound. In other words, if you want to hear a 1000Hz sine wave, the sampling rate must be at least 2000Hz. Knowing that humans can hear sine waves up to 22000Hz, it’s the reason behind the CD quality record sampling rate of 44100Hz (but of course, this not optimal; professional equipments use sampling rate of 48000Hz and higher). Sound samples are represented with bits, typically 8 or 16 bits per sample. That value determines the minimum and the maximum value that a sound sample can take. If you bust these limits, you will hear distorted sounds (here are those alien sounds!). You can experiment this by adding a second sine wave to the preceding test and putting the volume all way up.

Adding two sounds

At first, it may be confusing, but you add sounds simply by using the “+” operator! No fancy mathematics here. If you want to play two sine waves simultaneously, you just add their sin values like this: resultingSound(t) = sin(x*t) + sin(y*t), where x and y are sound frequencies.

Basic sine shape and other sound-generating functions

The general sine wave shape is “amplitude*sin(frequency*(t+delay))”. Math formulas are evaluated using the math parser shown in this article. So, basic functions like sin, cos, tan, min, and max come with the sample application, but it is also very easy to define your own functions (read the above article for more details).

The Sample Program

The sample program is a little lab that allows you to test various sound shapes using math formulas. To be more convenient, you can break your sounds in up to three independent sound shapes instead of putting them all together in a very large formula. This also means that you can “mix” as many sounds as you want by using the “+” operator and scaling them to avoid busting the amplitude limits.

Again for the sake of usability, you can use variables to lighten your sound shape formulas. Variables are named x, y, and z. You can use another special “system” variable named “t”. This variable is the time counter in radians.

In the bottom of the sample application screen is shown the resulting wave. You can see the effect of each sound shape individually by activating and deactivating them. You can also check if the resulting sound is too loud and thus generating distortion.

One last thing: the compute time indicator. Evaluating thousands of expressions per second can be very demanding for your computer. If your computer is too slow, the sound will not be played correctly. In this case, the time indicator will display a “Warning” message. You can diminish the number of evaluations per second by decreasing the sampling rate or reducing your math formula's complexity.

A look at the code

There are three fundamental things here: playing sounds, evaluating math formulas, and mixing sounds.

To play sound, I use DirectSound. The mechanism is simple: create a sound buffer and a few notification events that will tell you when it is time to compute the next thousand sound samples and update the sound buffer. This code is executed in the CSoundGeneratorDlg::OnInitDialog() method.

Next, when you need to compute the sound samples, the first thing to do is to evaluate variable values that will be used by sound shape formulas. After that, you evaluate sound shape formulas and add their values by scaling them according to the corresponding volume. Finally, you increment the time variable t and redo the variables and sound shapes evaluation until enough sound samples will have been computed. The following code snippet shows how this is implemented:

for( int s=0; s < m_soundPlayerEventNbSamples; s++ )
{
    sample = 0;

    for( int t=0; t < m_nbVars; t++ )
    {
        m_pUserVar[t] = m_pVarExpr[t].evaluate();
    }

    for( t=0; t < m_nbShapes; t++ )
    {
        if( m_pShapeActive[t] )
        {
            // volume scaling
            sample += m_pShapeExpr[t].evaluate() * m_pShapeVolume[t];
        }
    }

    // rescaling to the sample range
    m_pSamples[s] = sample* m_sampleScaleVal;

    m_t+=m_tStep;
}

There are some details to be aware of. First, sine function value ranges from –1 to 1, so it must be rescaled to cover the entire sample range that is, in 16 bits, –2^15 to 2^15. With 8 bits sound samples, this scale value would be different (i.e., –2^7 to 2^7). Another thing to note is how the time is computed. Since the sin(x) function takes radian value as input, the time variable must grow by exactly 2*pi in one second in order to generate a 1Hz sine wave. If the sin(x) function input value would have been in degrees instead of radians, then the time variable would grow by 360 (degrees) each second. The variable m_step is the time increment per sample.

Performance issue

Because generating high quality sound in real-time is very CPU intensive, performance is a major issue. I ran the code profiler and found that the most demanding computation was the math expression evaluations. So, I optimized the math parser and succeeded to save some precious milliseconds. Maybe you will notice that the math parser uses a recursive algorithm to evaluate expression, and will wonder if this is a good idea since performance is an issue. I asked me the same question, and I tried a non-recursive algorithm. The result was that while this solution ran faster in debug mode (twice as fast), in release mode, the recursive solution ran faster (a couple of milliseconds faster). So, I concluded that the compiler could do a lot of optimizations with a recursive algorithm and hence, I put it in the final software version.

The morale of this tale is that before doing performance optimization, always use a profiler to find where are the performance bottlenecks, and always use a profiler to validate that your changes actually increased performance.

History

June 17 - First draft.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Mathieu Jacques
Web Developer
Canada Canada
Member
Software Engineer working at a fun and smart startup company

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: One Simple(?) questionmemberokchan7930 Nov '06 - 18:04 
I think this program is best because there are no abrupt changes between adjacent buffer parts.
 
I should learn this technique Roll eyes | :rolleyes:
 

 
Thank you for your kind reply Smile | :)
 

GeneralGreat stuffmemberabites21 Sep '06 - 3:44 
Great. Ohhh..I have so much to learn
Generalfine tuningmemberandré_k17 Mar '06 - 6:53 
"Cher Mathieu", first of all, congratulations. It works wonderful, a "matlab like" formulas analyzer... is amazing, you are a "Chef" !...
 
Very good work, you have my 5/5.
 
My little problem: trying to tune a real instrument, on my new PC, the frequencyes are wrong !! Took the old one, with an independent soundcard : it works !!
 
The new one has a motherboard with a lot of integrated functions and perhaps the sound function is implemented with less precision as on the old one ?!?
 
How would you make a fine tuning of the real emitted frequency between the 2 PC's ?!?
 

-- modified at 13:01 Friday 17th March, 2006
GeneralRe: fine tuningmemberMathieu Jacques20 Mar '06 - 3:07 
Hi,
 
Thank you for your feedback.
 
I'm not a sound engineer but to fine tune real emitted frequency between 2 computers I would use a microphone to record the emitted sound and compare both sound frequency spectrums. Maybe you can use a third computer to record and analyse the sound... I've done an application to record and analyse the frequency spectrum, I can send it to you if needed.
 
Regards,
Mathieu

GeneralNoise Cancelationmembertobylang14 Feb '06 - 17:51 
Neat program I am just learning a bit about noise cancelation. Are there two formulas you know of that I could use to show cancelation in your application?
QuestionSimple wave generator?memberbruto10 Jan '05 - 7:23 
Hi, I'm looking for something simple ( I hope so...):
I need to generate a sine or square ( if possible any kind of shape...) wave with my sound card,and to modify its frequency while it is playing. It seems to be a simpler application of your SoundGenerator, no?
 
Do you have any advice?
 
Thanks in advance
Claudio.
AnswerRe: Simple wave generator?memberMathieu Jacques10 Jan '05 - 7:50 
Hi,
 
In your case you don’t need run-time math formula evaluations. You can write directly your math formulas (for square or sine shapes) in your program. To play a sound and modify it in real-time, I think DirectSound works pretty well. So you can re-use all the DirectSound stuff from the demo application. Is this too much complicated? Using DirectSound you don’t have to care about the soundcard type you have.
 
Regards,
Mathieu

GeneralRe: Simple wave generator?memberScot McDermid7 May '09 - 2:14 
I had a terrible time trying to get a simple sine wave generator to work properly. Finally I figured it out.
 
(But DirectSound is no longer the up to date way of doing this. You are supposed to use Xact, or XNA, or XAudio2 or something like that.)
 
But for DirectSound this works:
 
If using Visual Studio, add a reference to the Microsoft.DirectX.DirectSound under the .NET tab.

using Microsoft.DirectX.DirectSound;
using DS = Microsoft.DirectX.DirectSound;
 
private void button1_Click(object sender, EventArgs e)
{
DS.Device sounddevice;
 
sounddevice = new DS.Device();
sounddevice.SetCooperativeLevel(this, CooperativeLevel.Normal);
 
WaveFormat format = new WaveFormat();
format.BitsPerSample = 16; // DirectSound uses 8 bit or 16 bit signed integers
format.Channels = 2; // left and right channels.
format.BlockAlign = (short)(format.Channels * format.BitsPerSample /8);

format.FormatTag = WaveFormatTag.Pcm;
format.SamplesPerSecond = 44100; //sampling frequency of your data;
format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign;
 
//* tone parameters
int duration = 3; // three seconds
double frequency = 220.0F;
int amplitude = System.Int16.MaxValue / 4;
double phase = Math.PI / 3;
 
// buffer description
BufferDescription desc = new BufferDescription(format);
desc.DeferLocation = true;
desc.BufferBytes = (int)duration * format.AverageBytesPerSecond;
 
SecondaryBuffer secondaryBuffer = new SecondaryBuffer(desc, sounddevice);
 
int NoOfSamples = format.SamplesPerSecond * duration;
System.Int16[] buffer = new Int16[(int)(NoOfSamples * format.Channels)];
 
double factor = 2.0 * Math.PI * frequency / format.SamplesPerSecond;
for (int sample_number = 0; sample_number < NoOfSamples; sample_number++)
{
// left channel
buffer[sample_number * 2] = (System.Int16)Math.Round(amplitude * Math.Sin(sample_number * factor));
// right channel
buffer[sample_number * 2 + 1] = (System.Int16)Math.Round(amplitude * Math.Sin(sample_number * factor + phase));
}


//load audio samples to secondary buffer
secondaryBuffer.Write(0, buffer, LockFlag.EntireBuffer);
 
//play audio buffer
secondaryBuffer.Play(0, BufferPlayFlags.Default);
}

GeneralAudio SpeedmemberNguyen Van Thuan14 Sep '04 - 16:16 
Hi all!
I have a wave file, now I want to increase or decrease it how to do?
Can you show me the algorism or formula to do above promlems
If you can do it please reply to me.
Thanks you very much!
Regard
AnswerRe: Audio Speedmemberrichard sancenot5 Oct '06 - 4:30 
Changing audio speed is easy
 
If you want to double the playback speed, just read twice the number of samples you should.
For example, if you have 22,000 samples per seconds, just play 44,000 instead.
Of course you can directly modify the audio buffer with FFT algorithms but that's a bit hard.
 
If you use DirectSound then you can call the SetFrequency(DWORD) function from DIRECTSOUNDBUFFER and that's all folks Smile | :)
 
Tout programme dont la fiabilité dépend de l'homme n'est pas fiable

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 21 Jun 2004
Article Copyright 2004 by Mathieu Jacques
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid