SoundTouch AS3 is a port of the SoundTouch audio processing library. It allows realtime processing of audio in Flash 10 and includes filters that perform time compression/expansion and rate transposition. In tandem, these filters can perform pitch-shifting.


// `SimplePlayer` is an example of how to use the SoundTouch AS3 library to play
// a `Sound` object. It implements play, pause and seek, while allowing the
// SoundTouch filter parameters to be manipulated during playback.

//### License
// The SoundTouch AS3 library, like the original SoundTouch C++ library, is
// released under the LGPL.
* SoundTouch AS3 audio processing library
* Copyright (c) Ryan Berdeen
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

package {
    import com.ryanberdeen.soundtouch.SimpleFilter;
    import com.ryanberdeen.soundtouch.SoundTouch;


    //### Events
    // SimplePlayer dispatches two events: `play` and `pause`. There is no
    // equivalent to the `progress` event; use a timer if you need to monitor
    // the position.
    /** Dispatched when the sound is played. */
    [Event(type="", name="play")]

    /** Dispatched whent the sound is paused. */
    [Event(type="", name="pause")]

    * Example of an audio player using Soundtouch. It supports manipulating the
    * tempo, rate, and pitch during playback.
    public class SimplePlayer extends EventDispatcher {
        //### Private variables

        // The `outputPosition` when the current `soundChannel` started playing.
        // This value is used to calculate the `outputPosition`, as the
        // `position` of the `soundChannel` only gives us the position since the
        // most recent invocation of `play()`.
        private var channelOffset:Number;

        // The `outputPosition` and `position` when a filter parameter was
        // changed. These two values are used to calculate the `position` in the
        // source `sound`.
        private var filterChangedOutputPosition:Number;
        private var filterChangedPosition:Number;

        // The `SoundTouch` object is a wrapper around two others: a `Stretch`
        // and a `RateTransposer`. It takes care of wiring them all together in
        // the correct order. If you only need to change the tempo, you can use
        // a `Stretch` object by itself. If you only need to change the rate,
        // you could use `RateTransposer`, but in that case, SoundTouch AS3 is
        // probably much more than you need.
        private var soundTouch:SoundTouch;

        // The `SimpleFilter` handles the input and output buffering to use
        // `SoundTouch` with a `sound` as input.
        private var sound:Sound;
        private var filter:SimpleFilter;

        // Flash's source of audio. The `soundChannel` will control the
        // `outputSound`, which `extract`s data from the `filter`.
        private var outputSound:Sound;
        private var soundChannel:SoundChannel;

        public function SimplePlayer(sound:Sound):void {
            this.sound = sound;

            soundTouch = new SoundTouch();
            filter = new SimpleFilter(sound, soundTouch);

            outputSound = new Sound();
                SampleDataEvent.SAMPLE_DATA, filter.handleSampleData);

            channelOffset = 0;
            filterChangedOutputPosition = 0;
            filterChangedPosition = 0;

        //### Parameters

        // Expose the interesting `SoundTouch` parameters, and keep track of the
        // `position` and `outputPosition` when they change.
        /** The rate of the underlying SoundTouch object. */
        public function get rate():Number {
            return soundTouch.rate;

        /** @private */
        public function set rate(rate:Number):void {
            soundTouch.rate = rate;

        /** The tempo of the underlying SoundTouch object. */
        public function get tempo():Number {
            return soundTouch.tempo;

        /** @private */
        public function set tempo(tempo:Number):void {
            soundTouch.tempo = tempo;

        /** The pitchOctaves of the underlying SoundTouch object. */
        public function set pitchOctaves(pitchOctaves:Number):void {
            soundTouch.pitchOctaves = pitchOctaves;

        private function beforeUpdateFilter():void {
            filterChangedPosition = position;
            filterChangedOutputPosition = outputPosition;

        //### Position
        // We expose positions in terms of the source `sound`.

        // The current position is calculated based on the `position` and
        // `outputPosition` when the filter was last changed. Since it last
        // changed, we've played `outputPosition - filterChangedOutputPosition`
        // milliseconds of audio. This was played at a rate of
        // `soundTouch.tempo * soundTouch.rate` compared to the source--if we
        // played one second of audio at with a `rate` of `2`, we moved two
        // seconds through the source. If we had previously been at `position`
        // `5`, we are now at `7`.
        * The position of the source, in milliseconds. The value returned is an
        * approximation which can become less accurate if the filter parameters
        * are changed.
        public function get position():Number {
            var outputDelta:Number =
                outputPosition - filterChangedOutputPosition;
            var positionDelta:Number =
                outputDelta * soundTouch.tempo * soundTouch.rate;
            return filterChangedPosition + positionDelta;

        // Seeks to a given position in the source. This will also eliminate any
        // error that has accumulated in the calculated `position`.
        /** @private */
        public function set position(sourcePosition:Number):void {
            var resume:Boolean = playing;
            filter.sourcePosition = sourcePosition * 44.1;
            filterChangedPosition = sourcePosition;
            filterChangedOutputPosition = outputPosition;
            if (resume) {

        /** The number of milliseconds of audio played. */
        private function get outputPosition():Number {
            return channelOffset +
                (soundChannel != null ? soundChannel.position : 0);

        //### Control

        // Are we playing audio? If we are, the `soundChannel` will not be null.
        // Otherwise, we haven't started, or `pause()` has cleared it.
        /** Indicates whether the player is currently playing. */
        public function get playing():Boolean {
            return soundChannel != null;

        // Start playback, but only if we aren't currently playing.
        /** Starts playback. */
        public function play():void {
            if (!playing) {
                soundChannel =;
                    Event.SOUND_COMPLETE, soundCompleteHandler);
                dispatchEvent(new Event('play'));

        // When we reach the end, clean up and let everyone know.
        private function soundCompleteHandler(e:Event):void {

        // Pause playback, but only if we are currently playing.
        /** Pauses playback. */
        public function pause():void {
            if (playing) {
                // When a `SoundChannel` is stopped, all unplayed data in its
                // buffer is lost. Without correction, this would lead to a few
                // milliseconds of audio skipped every time the player was
                // paused.
                // `SimpleFilter` buffers the last few milliseconds of audio,
                // and lets us seek back to the exact position we were at when
                // we paused.
                filter.position = outputPosition * 44.1;

                // Stop the `soundChannel`, and keep track of how long we've
                // played so far.
                channelOffset += soundChannel.position;
                soundChannel = null;
                dispatchEvent(new Event('pause'));

        /** Toggles between play and pause. */
        public function togglePlayPause():void {
            !playing ? play() : pause();
Sound . URL.