-<?xml version="1.0" encoding="utf-8"?>
-<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="250" minHeight="300" width="366" height="337" backgroundColor="#D9DFEF">
- <fx:Script>
- <![CDATA[
- // UI and event includes
- import mx.events.SliderEvent;
- import mx.controls.sliderClasses.Slider;
- import spark.components.CheckBox;
- import spark.components.Button;
- import flashx.textLayout.formats.Float;
- import mx.controls.Label;
- import spark.components.HGroup;
- import spark.components.HSlider;
-
- // random flash includes I needed along the way
- import flash.display.*;
- import flash.events.*;
- import flash.media.*;
- import flash.net.*;
-
- // Alchemy includes
- import cmodule.faustalchemy.CLibInit;
-
- private var sound:Sound = new Sound();
-
- private var inputSound:Sound;
- private var inputSoundOn:Boolean = false;
- private var inputSoundBuffer:ByteArray = new ByteArray();
- private var inputSoundPosition:Number = 0;
-
- private var samples:ByteArray = new ByteArray();
- private var tick_:Number = 0;
- private var f_:int = 440;
-
- // C++ API
- private var faust_loader:CLibInit;
- private var faust:Object; // api methods are exposed as properties of this obejct
- private var faustOK:Boolean = false; // true if library loaded.
-
- public function initFaust():void {
- faust_loader = new CLibInit();
- faust = faust_loader.init();
- // Initialize UI controls
- // This changed at the last minute and may not be up-to-date in the writeup.
- // Instead of calling back into actionscript, faust_init returns an
- // array of Objects that allows us to set up the UI as .dsp files want.
- var uistuff:Array = faust.api_init();
- for (var i:int = 0; i < uistuff.length; ++i) {
- var item:Object = uistuff[i];
- var type:Number = item['type'];
- var id:Number = item['id'];
- var label:String = item['label'];
- var min:Number = item['min'];
- var max:Number = item['max'];
- var init:Number = item['init'];
- var step:Number = item['step'];
- if (type == 0) {
- ui_addButton(label, id);
- }
- if (type == 1) {
- ui_addCheckButton(label, id);
- }
- if (type == 2) {
- ui_addHorizontalSlider(label, id, init, min, max, step);
- }
- }
-
- faustOK = true;
- }
-
- // This function gets called by Flash when it needs more audio data.
- public function soundCallback(event:SampleDataEvent):void {
- // sample length can be anywhere from 2048 to 8192.
- if (!faustOK) return;
-
- // We need to pass mp3 input to Faust if
- // If we're not processing input, save cycles.
- if (inputSoundOn) {
- if (inputSoundPosition + 8192 > (inputSound.length*44100/1000)) {
- // ran out of buffer
- inputSoundPosition = 0;
- }
-
- inputSoundBuffer.position = 0;
- inputSound.extract(inputSoundBuffer, 8192, inputSoundPosition);
- inputSoundBuffer.position = 0;
- faust.api_tick(8192, 1, inputSoundBuffer, event.data);
- inputSoundPosition += 8192;
-
- // uncomment this section to debug in case calling C++ isn't working:
- // just copy the mp3 data directly to the output buffer.
- /*
- inputSound.extract(event.data, 8192, inputSoundPosition);
- inputSoundPosition += (8192);
- */
- } else {
- faust.api_tick(8192, 0, inputSoundBuffer, event.data);
- }
- }
-
- // "Start" button callback.
- protected function button1_clickHandler(event:MouseEvent):void
- {
- initFaust();
- dbgtext.text = "Playback started.";
- sound.addEventListener(SampleDataEvent.SAMPLE_DATA, soundCallback);
- sound.play();
- }
-
- public function onControlChange(id:int, val:Number):void {
- if (!faustOK) return;
- faust.api_onControlChange(id, val);
- }
-
- // UI Hooks
- public function ui_addButton(label:String, id:int):void {
- var b:Button = new Button();
- b.label = label;
- b.addEventListener("click", function(evt:Event):void {
- onControlChange(id, 1.0);
- });
- addElement(b);
- }
-
- public function ui_addToggleButton(label:String, id:int):void {
- // toggle/check buttons seem to be identical.
- return ui_addCheckButton(label, id);
- }
-
- public function ui_addCheckButton(label:String, id:int):void {
- var cb:CheckBox = new CheckBox();
- cb.label = label;
- cb.addEventListener("change",
- function(evt:SliderEvent):void {
- var checked_val:int = cb.selected ? 1.0 : 0.0;
- onControlChange(id, checked_val);
- });
- }
-
-
- public function ui_addVerticalSlider(label:String, id:int, init:Number, min:Number, max:Number, step:Number):void {
- // Just call HSlider since we have a vertical cascade of controls.
- return ui_addHorizontalSlider(label, id, init, min, max, step);
- }
-
- public function ui_addHorizontalSlider(label:String, id:int, init:Number, min:Number, max:Number, step:Number):void {
- var compGroup:HGroup = new HGroup();
- // Text label
- var compLabel:Label = new Label();
- compLabel.text = label;
- // Actual control
- var compSlider:spark.components.HSlider = new spark.components.HSlider();
- compSlider.minimum = min;
- compSlider.maximum = max;
- compSlider.liveDragging = true; // update faust params while dragging the slider
- compSlider.value = init;
- compSlider.valueInterval = step;
- compSlider.width = 200;
-
- compSlider.addEventListener("change",
- function(evt:Event):void {
- onControlChange(id, compSlider.value);
- });
- // wire everything up
- compGroup.addElement(compLabel);
- compGroup.addElement(compSlider);
- addElement(compGroup);
- }
-
- public function ui_addNumEntry(label:String, id:int, init:Number, min:Number, max:Number, step:Number):void {
- // TODO: A proper text box requires validation.
- // Also, slider controls could probably benefit from numeric input too...
- // Create a unified control for {VSlider, HSlider, NumEntry}
- return ui_addHorizontalSlider(label, id, init, min, max, step);
- }
-
-
- // Layout management
- // These are "nice to have" functions but aren't critical.
- // It doesn't look like they're widely used.
- // TODO: if using Flex, these should be easy enough to implement.
- public function ui_openFrameBox(label:String):void { }
- public function ui_openTabBox(label:String):void { }
- public function ui_openHorizontalBox(label:String):void { }
- public function ui_openVerticalBox(label:String):void { }
- public function ui_closeBox():void { }
- // We don't need a run() notification since we call into Faust code directly.
- public function ui_run():void { }
-
-
- // "Load input sound" button callback
- protected function button2_clickHandler(evt:MouseEvent):void {
- inputSoundOn = false;
- if (!inputSound)
- inputSound = new Sound();
- var urlReq:URLRequest = new URLRequest(mp3path.text);
- inputSound.load(urlReq);
- inputSound.addEventListener(Event.COMPLETE,
- function(evt:Event):void {
- dbgtext.text = "sound finished loading.";
- inputSoundOn = true;
- inputSoundPosition = 0;
- });
- }
-
-
- //"Stop input sound" button callback
- protected function button3_clickHandler(event:MouseEvent):void
- {
- dbgtext.text = "sound stopped.";
- inputSoundOn = false;
- inputSoundPosition = 0;
- }
-
- ]]>
-
- </fx:Script>
- <s:Button label="Start" click="button1_clickHandler(event)"/>
- <s:TextArea width="322" height="19" text="Click Start to begin." id="dbgtext"/>
- <s:TextInput text="http://ccrma.stanford.edu/~travissk/faustflash/helterskelter.mp3" id="mp3path" width="359"/>
- <s:HGroup width="237" height="28">
- <s:Button label="Load Sound" click="button2_clickHandler(event)"/>
- <s:Button label="Stop Sound" click="button3_clickHandler(event)"/>
- </s:HGroup>
-
- <s:layout>
- <s:VerticalLayout/>
- </s:layout>
-</s:Application>