Skip to content

Instantly share code, notes, and snippets.

@also
Created January 19, 2010 05:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save also/280686 to your computer and use it in GitHub Desktop.
Save also/280686 to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel title="SoundTouch Demo" width="100%" paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10">
<mx:HBox paddingBottom="10">
<mx:Button id="browseButton" label="Open an MP3…" click="chooseFile();"/>
</mx:HBox>
<mx:VBox width="100%">
<mx:Label text="Tempo"/>
<mx:HSlider id="tempoSlider" minimum="0.01" maximum="1.99" value="1" snapInterval="0.01" liveDragging="false" change="updateFilter();" width="100%"/>
<mx:Label text="Rate"/>
<mx:HSlider id="rateSlider" minimum="0.01" maximum="1.99" value="1" snapInterval="0.01" liveDragging="false" change="updateFilter();" width="100%"/>
<mx:Label text="Pitch"/>
<mx:HSlider id="pitchSlider" minimum="-1" maximum="1" value="0" snapInterval="0.01" liveDragging="false" change="updateFilter();" width="100%"/>
<mx:Label text="Position"/>
<mx:HSlider id="positionSlider" minimum="0" maximum="1" value="0" snapInterval="0.001" liveDragging="false" thumbPress="seeking = true;" thumbRelease="updatePosition(); seeking = false;" width="100%"/>
</mx:VBox>
<mx:Button id="playButton" label="Play" enabled="false" click="togglePlayPause();"/>
<mx:Label text="Tempo: " id="tempoLabel"/>
<mx:Label text="Rate: " id="rateLabel"/>
</mx:Panel>
<mx:Script><![CDATA[
import com.ryanberdeen.soundtouch.SimpleFilter;
import com.ryanberdeen.soundtouch.SoundTouch;
import com.ryanberdeen.soundtouch.FifoSampleBuffer;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.media.Sound;
import flash.net.FileReference;
import flash.utils.Timer;
import org.audiofx.mp3.MP3FileReferenceLoader;
import org.audiofx.mp3.MP3SoundEvent;
private var fileReference:FileReference;
private var mp3Loader:MP3FileReferenceLoader;
private var sound:Sound;
private var positionUpdateTimer:Timer;
private var channelOffset:Number;
private var filterChangedOutputPosition:Number;
private var calculatedOffset:Number;
private var filterChangedCalculatedPosition:Number;
private var outputSound:Sound;
private var soundChannel:SoundChannel;
private var soundTouch:SoundTouch;
private var filter:SimpleFilter;
private var playing:Boolean;
private var seeking:Boolean;
private function chooseFile():void {
fileReference = new FileReference();
fileReference.addEventListener(Event.SELECT, handleFileSelected);
fileReference.browse([new FileFilter("MP3 Files", "*.mp3")]);
positionUpdateTimer = new Timer(10);
positionUpdateTimer.addEventListener(TimerEvent.TIMER, handlePositionUpdateTimer);
}
private function handleFileSelected(e:Event):void {
resetPlayer();
mp3Loader = new MP3FileReferenceLoader();
mp3Loader.addEventListener(Event.COMPLETE, handleSoundLoadComplete);
mp3Loader.getSound(fileReference);
}
private function handleSoundLoadComplete(e:MP3SoundEvent):void {
mp3Loader.removeEventListener(Event.COMPLETE, handleSoundLoadComplete);
sound = e.sound;
preparePlayer();
}
private function preparePlayer():void {
resetPlayer();
outputSound = new Sound();
soundTouch = new SoundTouch();
updateFilter();
filter = new SimpleFilter(sound, soundTouch);
outputSound.addEventListener(SampleDataEvent.SAMPLE_DATA, filter.handleSampleData);
playButton.enabled = true;
playButton.label = "Play";
playing = false;
}
private function updateFilter():void {
if (soundChannel) {
filterChangedCalculatedPosition = calculatePosition;
filterChangedOutputPosition = outputPosition;
}
if (soundTouch != null) {
soundTouch.tempo = tempoSlider.value;
soundTouch.rate = rateSlider.value;
soundTouch.pitchOctaves = pitchSlider.value;
tempoLabel.text = "Tempo: " + soundTouch.tempo;
rateLabel.text = "Rate: " + soundTouch.rate;
}
}
private function updatePosition():void {
var resume:Boolean = playing;
if (playing) {
pause();
}
filter.sourcePosition = sound.length * positionSlider.value * 44.1;
filterChangedOutputPosition = outputPosition;
filterChangedCalculatedPosition = filter.sourcePosition / 44.1;
if (resume) {
play();
}
}
private function handlePositionUpdateTimer(e:TimerEvent):void {
if (!seeking) {
positionSlider.value = calculatePosition / sound.length;
}
}
/** The number of milliseconds of audio played. */
private function get outputPosition():Number {
return channelOffset + (soundChannel != null ? soundChannel.position : 0);
}
/** The approximate position of the source, in milliseconds. */
private function get calculatePosition():Number {
var outputDelta:Number = outputPosition - filterChangedOutputPosition;
var calculatedDelta:Number = outputDelta * soundTouch.tempo * soundTouch.rate;
return filterChangedCalculatedPosition + calculatedDelta;
}
private function resetPlayer():void {
if (playing) {
pause();
}
soundChannel = null;
channelOffset = 0;
calculatedOffset = 0;
filterChangedOutputPosition = 0;
filterChangedCalculatedPosition = 0;
}
private function soundCompleteHandler(e:Event):void {
preparePlayer();
}
private function togglePlayPause():void {
if (!playing) {
play();
}
else {
pause();
}
}
private function play():void {
soundChannel = outputSound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
positionUpdateTimer.start();
playing = true;
playButton.label = "Pause";
playing = true;
}
private function pause():void {
if (playing) {
filter.position = outputPosition * 44.1;
soundChannel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
channelOffset += soundChannel.position;
soundChannel.stop();
soundChannel = null;
playing = false;
}
playButton.label = "Play";
playing = false;
positionUpdateTimer.stop();
}
]]></mx:Script>
</mx:Application>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment