Created
January 19, 2010 05:13
-
-
Save also/280686 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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