AS3 Sound Synthesis IV – Tone Class

In order to make the code so far a little more reusable, I moved it over into its own class, called Tone. I also implemented some optimizations and other little tricks. The most important is that instead of calculating the next batch of samples along with the envelope on every SAMPLE_DATA event, I precalculate all the samples within the envelope right up front, storing it in a Vector of Numbers. Here’s the class:

[as3]package
{
import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.Event;

public class Tone
{
protected const RATE:Number = 44100;
protected var _position:int = 0;
protected var _sound:Sound;
protected var _numSamples:int = 2048;
protected var _samples:Vector.;
protected var _isPlaying:Boolean = false;

protected var _frequency:Number;

public function Tone(frequency:Number)
{
_frequency = frequency;
_sound = new Sound();
_sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
_samples = new Vector.();
createSamples();
}

protected function createSamples():void
{
var amp:Number = 1.0;
var i:int = 0;
var mult:Number = frequency / RATE * Math.PI * 2;
while(amp > 0.01)
{
_samples[i] = Math.sin(i * mult) * amp;
amp *= 0.9998;
i++;
}
_samples.length = i;
}

public function play():void
{
if(!_isPlaying)
{
_position = 0;
_sound.play();
_isPlaying = true;
}
}

protected function onSampleData(event:SampleDataEvent):void
{
for (var i:int = 0; i < _numSamples; i++) { if(_position >= _samples.length)
{
_isPlaying = false;
return;
}
event.data.writeFloat(_samples[_position]);
event.data.writeFloat(_samples[_position]);
_position++;
}
}

public function set frequency(value:Number):void
{
_frequency = value;
createSamples();
}
public function get frequency():Number
{
return _frequency;
}
}
}[/as3]

Note that in the constructor I call createSamples(). This creates the Vector with all samples needed for the duration of the note, including the amplitude of the pseudo-envelope. In the frequency setter, the samples are re-created. The result is that in the onSampleData handler method, I just fill up the byte array with the next so many values out of the _samples vector, stopping when I reach the end of that Vector.

Note also that the amplitude is decreased per sample, rather than per SAMPLE_DATA event, thus it needs to be reduced by a much smaller amount each time. This should also give a smoother envelope, though I’m not sure how noticeable it is.

Here’s a brief bit of code that shows it in action:

[as3]import flash.events.MouseEvent;

var tone:Tone = new Tone(800);
stage.addEventListener(MouseEvent.CLICK, onClick);
function onClick(event:MouseEvent):void
{
tone.frequency = 300 + mouseY;
tone.play();
}[/as3]

It creates a tone. Whenever you click on the stage, it calculates a new frequency for the tone based on the y position of the mouse and plays the tone. Simple enough.

I don’t consider this class anywhere near “complete”. Just a beginning evolution in something. I’d like to add support for more flexible and/or complex envelopes, a stop method, and some other parameters to change the sound. But even so, this is relatively useful as is, IMHO.

This entry was posted in ActionScript, Flash. Bookmark the permalink.

21 Responses to AS3 Sound Synthesis IV – Tone Class

  1. boblemarin says:

    Just a idea, but I wanted your opinion about this :

    If you stored the samples in a ByteArray instead of a Vector, you could be using the readBytes / writeBytes methods in the SampleDataEvent handler, thus avoiding the cost of the loop.

    Maybe it would help optimize the stuff, wouldn’t it ?

  2. Luciano says:

    Hello there!
    I’ve been following the articles in this series and I must say that this is really great!
    Thanks
    Luciano

  3. T says:

    What a great series of posts! I couldn’t be happier for the insight, and I’ll be playing around with this for the next few weeks.

  4. kp says:

    boblemarin, yeah, I thought about that. just didn’t have a chance to test and compare the two methods. but i imagine you are totally correct that a byte array would be faster.

  5. Freddy says:

    So nice Keith!, you have no idea how many times I’ve tried to understand AS3 + audio 😀

  6. joshspoon says:

    Thanks for the tuts. I’ve been messing with this for over a year and got stuck. I asked Andre and he was kinda vague about creating an engine from scratch. Thanks

  7. This info is so helpful, thank you so much! I bet you could fill a book with this stuff. Interactive Audio!

  8. My friends are amazed when I created music using your Tone Class. In my mother tongue I just want to say “kalakura keith…..”

  9. TheDarkIn1978 says:

    This is very cool stuff. Thanks for this.

    Just a note, you have 2 syntax errors on lines 13 and 23. The code wasn’t compiling in Flash Professional CS5 because your Vector objects are of type “number” with a lower-case “n”

  10. hi keith, I made a tonewheel application similar to andrew’s using your tone class with some controls. Your making me crazy. http://www.thirstyneurons.com .

  11. hi keith, which syntax highlighter you are using?

  12. Thanks for the great tutorials! I have been having a hard time understanding all the SampleData stuff.
    For a short children’s story (http://www.eiland.cc/astronaut/ ) I wanted the user to control the pitch and volume of samples (by moving the mouse). Luckily I found Andre Michelle’s MP3Pitch.as, tried to figure it out and then changed it a little so it could loop short sound samples and control pitch and volume. For people who are interested in these changes of this class, see this post: http://allowtoinfuse.wordpress.com/2010/03/11/mp3pitch-as-with-loop-function/

  13. esimov says:

    hello Peter,
    i must confess i’m a big fan of you. I like very much your books, mostly “Making things move”. Even i was (and am) a novice Actionscript coder, without too much coding experience behind me (actually after two earlier atempts, at the beginning of this year i was starting to be very captivated by AS3), i understood perfectly the concepts and ideas behind every chapter. Do you felt at the beginning of your Flash adventure that you have the idea, but you can’t make it working, because something is missing. I’m at that point sometimes.

    Sorry if this is outside of the topic.

  14. boblemarin says:

    Hi Keith,
    I just built a simple pulse synth as an experiment and it can be fun to play width.
    There’s the source available, as well as a -basic- Audio Unit version.

    Just in case, it is there : http://minimal.be/lab/polySynth/

  15. mari says:

    Hi, I’ve been trying to use this, but when I export it in flash, it gives me 4 errors:

    Scene 1, Layer ‘Layer 1’, Frame 1, Line 7 1120: Access of undefined property mouseY.
    C:\…\Tone.as, Line 1 1180: Call to a possibly undefined method addFrameScript.

    Scene 1, Layer ‘Layer 1’, Frame 1, Line 4 1120: Access of undefined property stage.

    C:\…\Tone.as, Line 1 5000: The class ‘Tone’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

    I’ve added import flash.display.MovieClip; to the class with no luck, and unchecked automatically add instances… with no luck. Any ideas what I am doing wrong?
    thanks!

  16. alex says:

    Hi there, i am totally new to AS3 and am struggling the exact way as mari does.
    Any ideas or hints are welcom.

    thanks in advance and cheers,
    Alex

  17. Nick says:

    Great tutorials. I wish sound manipulation in ActionScript was covered more.

    I was wondering, how do you make the Tone class play tones continually in the way you’ve done here with the pre-calculating the samples? I tried this:

    var mult:Number = _frequency / RATE * Math.PI * 2;

    for(var i:int = 0; i < _numSamples; i++)
    {
    _samples[i] = Math.sin(mult * i);
    }

    But the result is a glitchy mess.

    Cheers

    Nick.

  18. visitor says:

    Expect your new article about sound~ thanks again!~

  19. djankey says:

    Hi,

    please check this code: http://wonderfl.net/c/gyTW

    Try to record more than 2-3 seconds and then listen saved mix. Mixed sound is distorted after couple of seconds of recording
    Wav encoder is working ok…
    How to fix this?

    Thanks in advance!

  20. Joe says:

    Hey,

    I wanted to drop you a line saying thanks. This series of posts is hugely inspiring and incredibly useful. I’m wondering if you’ve kept up your experiments in it. I’ve been playing with the concept and have had some success implementing a more complex envelope that allows the user control over ADSR, though I’ve yet to develop a suitable method for sustaining the note until the user releases the mouse.

    Thanks for putting this out.

Leave a Reply