This post will show you how to generate sine waves for specific frequencies using the AS3 Sound object. It assumes you have read, or are familiar with the data in Part I of this series.
Basics of Sound
Sound itself is essentially a change in the pressure of the air. Extremely simple layman’s terms here. Air is composed of various molecules. They are not uniformly smoothly distributed. There can be areas where they are under more pressure and packed more tightly together, and other areas where they are more spaced out. When something like a guitar string vibrates, it moves quickly back and forth at a specific speed. When it moves in one direction, it pushes the molecules of air closer to some other molecules in the same direction. The creates a dense pocket of air. Then the string moves back in the opposite direction, creating a bit of a vacuum. Not a real vacuum, but an area where there are less molecules. It then moves back again, creating another dense pocket.
These areas of dense and undense air move out across the room and eventually hit your ears. The dense air pushes your eardrum in, and the less dense pocket causes it to move out. The result is your eardrum starts vibrating at roughly the same frequency as the guitar string. This causes some bones to vibrate, which stimulate nerves at the same frequency, which send signals to your brain, saying “C Sharp”.
When you record sound, you use a microphone as a sort of electronic ear. It has some kind of diaphragm or other moving part that vibrates and creates and electrical signal which is recorded one way or the other. For playback, this electrical signal is regenerated and causes a speaker to vibrate at the same frequency. This pushes the air the same way the original guitar string did and you hear the same sound.
Synthesizing Sound
However, when we talk about synthesizing sound, we are doing it all from scratch. Flash, your computer’s sound card, and your headphones or speakers will handle generating the correct electrical signal and vibrating the air. But you need to do the math to figure out much and how fast to make things vibrate.
In Part I of this tutorial, we created random values which caused the speaker or headphones to vibrate at a completely chaotic pace, resulting in a radio-static-like fuzz. Creating an actual tone requires a bit more work, and hopefully some understanding of what you are doing.
Digital Sound
In analog sound, such as vinyl records or 8-track tapes (showing my age here), the sound is encoded smoothly as bumps in the groove of the record, or changes in a magnetic field on the tape. Digital sound takes discrete samples of the sound pressure at specific intervals.
Taking one of the simplest sound forms, a sine wave, here is a smooth analog version:
And here is the same wave, represented as 50 samples:
As you can see, the sampled version is not quite as accurate as the smooth wave. However, in high quality digital sound, these intervals are numerous enough that it is virtually impossible for most of the population to notice any difference. When you are synthesizing sound in Flash, you will be dealing with 44,100 samples per second. Remember that number, we’ll be doing some calculations with it.
Now, what we need to do is generate our samples with a series of values that wind up forming a sine wave like you see above. The top peak of the sine wave will be 1.0, the bottom will be –1.0 and the middle 0.0. To start simply, we’ll generate a single sine wave over the course of a full second. To keep track of where we’re at, we’ll use a variable called position. We’ll initialize it to 0 and increment it each time we create a new sample. Thus position will range from 0 to 44100 over the course of the first second of audio.
If we then divide position by 44100, we’ll get values that range from 0.0 up to 1.0 over the course of one second. And if we multiply that by 2PI, We’ll get values from 0 to 2PI, just what we need to generate a sine wave with the Math.sin function. Here’s the code so far:
[as3]import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.MouseEvent;
var position:int = 0;
var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
sound.play();
function onSampleData(event:SampleDataEvent):void
{
for(var i:int = 0; i < 2048; i++)
{
var phase:Number = position / 44100 * Math.PI * 2;
position ++;
var sample:Number = Math.sin(phase);
event.data.writeFloat(sample); // left
event.data.writeFloat(sample); // right
}
}[/as3]
If you run that file, you'll be generating a sine wave that does one full cycle each second. Of course, this, being a 1 Hz sound wave, is far too low for the human ear to hear. To get a specific frequency sound, simply multiply phase by the frequency you want to hear. Humans can hear frequencies generally in the range of 25 to 25,000 Hz. Middle A on the standard musical scale is 440 Hz. So let's try that. Change the line that calculates the sample to:
[as3]var sample:Number = Math.sin(phase * 440);[/as3]
That gives you A. You can find charts like this all over the net:
A 440
B flat 466
B 494
C 523
C sharp 554
D 587
D sharp 622
E 659
F 698
F sharp 740
G 784
A flat 831
A 880
Or, if you want to get more mathematical about it, the formula for each note, n, above or below 440 is:
440 * 2^(n / 12)
We can implement scales then by setting up an n variable, incrementing it on a timer, and using the above formula to calculate our frequency:
[as3]import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
var position:int = 0;
var n:Number = 0;
var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
sound.play();
function onSampleData(event:SampleDataEvent):void
{
for(var i:int = 0; i < 2048; i++)
{
var phase:Number = position / 44100 * Math.PI * 2;
position ++;
var sample:Number = Math.sin(phase * 440 * Math.pow(2, n / 12));
event.data.writeFloat(sample); // left
event.data.writeFloat(sample); // right
}
}
var timer:Timer = new Timer(500);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
function onTimer(event:TimerEvent):void
{
n++;
}[/as3]
Alternately, we can make a poor man's generative music composer with a little help from Math.random:
[as3]function onTimer(event:TimerEvent):void
{
n = Math.floor(Math.random() * 20 - 5);
timer.delay = 125 * (1 + Math.floor(Math.random() * 8));
}[/as3]
This generates a different note, and a different duration (from 1/8th of a second up to one full second) for each note.
Armed with this alone, you are on your way to making your own sequencer or mini piano or other type instrument. Later, I'll try to post some stuff on other wave forms, combining waves, envelopes, and other topics.
Keith,
Thank you, good work and great explanation.
I agree Igorn there is very little info on the net about this subject, and this is exactly what I was looking for.
Excellent work, thanks again.
Joel
Great tutorial…. one point of contention regarding the statement:
“However, in high quality digital sound, these intervals are numerous enough that it is virtually impossible for most of the population to notice any difference.”
This is not exactly how sampling works…it’s not a ‘close enough’ estimation through numerous finite sample points. It’s an interpolated reconstruction using some basic maths.
See: http://en.wikipedia.org/wiki/Nyquist–Shannon_sampling_theorem
Also…
To:TheDarkIn1978
If you transition to the new frequency at the zero crossing point of the wave form (0 in this case) the popping will be eliminated.
Actually, the human ear can’t hear frequencies @ 25000 Hz! The general audible spectrum, for humans, is 20 Hz to 20000 Hz, but for most people this is actually a smaller spectrum. Myself for one reaches my upper limit around 16-17 kHz!
This is due to general wear and tear of the ear drum. Wear those ear plugs peeps!
Great post though! Thanks a lot!
Random inclusion of an unnecessary import?
import flashx.textLayout.elements.InlineGraphicElement;
thanx for these posts. helpful and well explained, theres indeed very little posts about this subject across the web!
Jolyon, yes. Fixed. Damn auto-imports! I meant “int”!
i’m heart broken. while experimenting with your code, i realize that quick changes of the frequency with SampleDataEvent produce really nasty popping sounds.
hear it for yourself: add a mouseMove event listener to the stage to assign xCoord:int and yCoord:int variables the value of the mouse event’s stageX and stageY property, while passing those values to the sample loop:
var sampleLeft:Number = Math.sin(phase * xCoord);
var sampleRight:Number = Math.cos(phase * yCoord);
evt.data.writeFloat(sampleLeft);
evt.data.writeFloat(sampleRight);
i’m not very hopeful that this problem has a solution, but if it does and you know about it please let me know. i believe this problem to be a major limitation.
how can v add this sine wave for an audio / mp3 playing on swf
Thanks for this Keith, really nice example…
@TheDarkIn1978 – the reason why you get a popping sound is that the waveform is being ‘disrupted’ when you change the frequency. So instead of a smooth sine wave, you’re getting a number of different frequency sine waves ‘bolted’ together. So, picture a sine wave going up and down, up and down, then as you change frequency, the new value make the wave suddenly revert to being ‘up’ (such a rubbish explanation!), which breaks the wave and produces a glitch.
In the end it’s all mathematics and the result you get is a direct consequence of what you put in.
Cheers,
Michael
Oh man , this is awesome …. Been looking for this for a looooong time …… This is so gonna help me build my shruthi box to completion 🙂
good!!
great job keith,
i wonder; can we get a human voice from a spectrum, how can i analize the spectrum? i mean i want to make a lip sync project. so i can get the spectrum of microphone but i can get the rest of it? i don’t know what to do next? i mean how can i understand the “A” from the spectrum? do you have any idea? can you help me about it?
thanks,