-
-// Faust -> Alchemy -> ActionScript C++ Architecture File
-
-#include "AS3.h"
-// math.h is needed for many faust examples, so include it.
-// otherwise we have to hand-edit c++.
-#include "math.h"
-
-
-#ifdef __GNUC__
-
-#define max(x,y) (((x)>(y)) ? (x) : (y))
-#define min(x,y) (((x)<(y)) ? (x) : (y))
-
-// abs is now predefined
-//template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; }
-
-inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); }
-#else
-#endif
-/******************************************************************************
-*******************************************************************************
-
- VECTOR INTRINSICS
-
-*******************************************************************************
-*******************************************************************************/
-
-//inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); }
-inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); }
-
-<<includeIntrinsic>>
-
-/******************************************************************************
-*******************************************************************************
-
- ABSTRACT USER INTERFACE
-
-*******************************************************************************
-*******************************************************************************/
-
-class UI
-{
-
-public:
- bool fStopped;
- UI() : fStopped(false) {}
- virtual ~UI() {}
-
- virtual void addButton(char* label, float* zone) = 0;
- virtual void addToggleButton(char* label, float* zone) = 0;
- virtual void addCheckButton(char* label, float* zone) = 0;
- virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
- virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
- virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) = 0;
-
- virtual void openFrameBox(char* label) = 0;
- virtual void openTabBox(char* label) = 0;
- virtual void openHorizontalBox(char* label) = 0;
- virtual void openVerticalBox(char* label) = 0;
- virtual void closeBox() = 0;
-
- virtual void run() = 0;
-
- void stop() { fStopped = true; }
- bool stopped() { return fStopped; }
-};
-
-
-////// Implementation of UI
-
-// Faust UI hookup is straightforward
-// We subclass from UI and then these methods will get called by compiled
-// Faust which assembles controls. We handle all UI as needed, and when
-// things changed we need to set *zone=(some value).
-// This is a little more complicated when jumping between alchemy/script.
-// So rather than deal with marshalling pointers, just cheat and use an int:pointer map.
-// NOTE: I should have used an STL map but that wasn't working back when I used gcc.
-// I'll investigate since that would clean up the code quite a bit.
-
-// upper bound on number of controls
-#define MAX_CONTROLS 25
-// map of unique ui control id to a float* where faust reads the corresponding value.
-static float* uidToZone[MAX_CONTROLS];
-// Counter that assigns control IDs.
-static int uiMap_id = 0;
-
-// I wasn't able to properly thunk the UI actionscrpit methods to C.
-// Since we know the complete UI at the time of creation, a collection of
-// ui creation info is passed back from faust_init, and actionscript
-// can read it and create the UI.
-// This is a little messy and was done last-minute.
-const int TYPE_BUTTON = 0;
-const int TYPE_TOGGLE = 1;
-const int TYPE_SLIDER = 2;
-// max length for a label - more than 50 chars will get cut.
-#define LABEL_LEN 50
-struct uiElemInfo {
- int type;
- int id;
- char label[LABEL_LEN+1];
- float min;
- float max;
- float init;
- float step;
-};
-uiElemInfo uielems[MAX_CONTROLS];
-static int uielems_size = 0;
-
-// todo: stdio.h has strncpy, I just got paranoid about extra includes making the code bigger :)
-void strcopy(char *src, char *dst) {
- dst[LABEL_LEN] = '\0';
- for (int i = 0; i < LABEL_LEN; ++i) {
- if (0 == (dst[i] = src[i])) return;
- }
-}
-
-void BuildUIArray(AS3_Val &array) {
- for (int i = 0; i < uielems_size; ++i) {
- AS3_Val result = AS3_Object( "type:AS3ValType,id:AS3ValType,label:AS3ValType,min:AS3ValType,max:AS3ValType,init:AS3ValType,step:AS3ValType",
- AS3_Int(uielems[i].type),
- AS3_Int(uielems[i].id),
- AS3_String(uielems[i].label),
- AS3_Number(uielems[i].min),
- AS3_Number(uielems[i].max),
- AS3_Number(uielems[i].init),
- AS3_Number(uielems[i].step)
- );
- AS3_Set(array, AS3_Int(i), result);
- // decrease refcount? todo: this may leak memory...
- //AS3_Release(result);
- }
-}
-
-class ActionScriptUI : public UI {
- public:
- ActionScriptUI() {
- fStopped = false;
- }
-
- virtual ~ActionScriptUI() {
- }
-
- // Pass in a zone, get back a unique ID.
- int registerControl(float *zone) {
- if (!zone) return 0;
- uiMap_id++;
- uidToZone[uiMap_id] = zone;
- return uiMap_id;
- }
-
- // Called from Flash when any control is updated.
- // Results will take effect on the next dsp callback
- // since everything runs in the same thread.
- void updateControl(int id, float value) {
- *(uidToZone[id]) = value;
- }
-
-
- virtual void addButton(char* label, float* zone) {
- int id = registerControl(zone);
- uielems[uielems_size].type = TYPE_BUTTON;
- uielems[uielems_size].id = id;
- strcopy(label, uielems[uielems_size].label);
- uielems[uielems_size].min = 0;
- uielems[uielems_size].max = 0;
- uielems[uielems_size].init = 0;
- uielems[uielems_size].step = 0;
- uielems_size++;
- }
- virtual void addToggleButton(char* label, float* zone) {
- int id = registerControl(zone);
- uielems[uielems_size].type = TYPE_TOGGLE;
- uielems[uielems_size].id = id;
- strcopy(label, uielems[uielems_size].label);
- uielems[uielems_size].min = 0;
- uielems[uielems_size].max = 0;
- uielems[uielems_size].init = 0;
- uielems[uielems_size].step = 0;
- uielems_size++;
- }
- virtual void addCheckButton(char* label, float* zone) {
- return addToggleButton(label, zone);
- }
- virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) {
- return addHorizontalSlider(label, zone, init, min, max, step);
- }
- virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) {
- int id = registerControl(zone);
- uielems[uielems_size].type = TYPE_SLIDER;
- uielems[uielems_size].id = id;
- strcopy(label, uielems[uielems_size].label);
- uielems[uielems_size].min = min;
- uielems[uielems_size].max = max;
- uielems[uielems_size].init = init;
- uielems[uielems_size].step = step;
- uielems_size++;
- }
- virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) {
- return addHorizontalSlider(label, zone, init, min, max, step);
- }
-
- // Not implemented yet - these only affect UI layout and aren't critical.
- // See actionscript comments for details.
- virtual void openFrameBox(char* label) {}
- virtual void openTabBox(char* label) {}
- virtual void openHorizontalBox(char* label) {}
- virtual void openVerticalBox(char* label) {}
- virtual void closeBox() {}
- virtual void run() { }
-};
-
-/******************************************************************************
-*******************************************************************************
-
- FAUST DSP
-
-*******************************************************************************
-*******************************************************************************/
-
-
-
-//----------------------------------------------------------------
-// abstract definition of a signal processor
-//----------------------------------------------------------------
-
-class dsp {
- protected:
- int fSamplingFreq;
- public:
- dsp() {}
- virtual ~dsp() {}
-
- virtual int getNumInputs() = 0;
- virtual int getNumOutputs() = 0;
- virtual void buildUserInterface(UI* interface) = 0;
- virtual void init(int samplingRate) = 0;
- virtual void compute(int len, float** inputs, float** outputs) = 0;
-};
-
-
-//----------------------------------------------------------------------------
-// FAUST generated signal processor
-//----------------------------------------------------------------------------
-
-
-<<includeclass>>
-
-
-
-
-/// Alchemy DSP
-class Faust {
-public:
- Faust() {
- // mydsp will be defined by faust in 'includeclass'
- dsp_ = new mydsp();
- ui_ = new ActionScriptUI();
- dsp_->buildUserInterface(ui_);
- dsp_->init(44100); // 44.1k, 2 channels, @ 32-bit is hardcoded into flash player 10.
- }
-
- ~Faust() {
- if (dsp_) delete dsp_;
- if (ui_) delete ui_;
- }
-
-//private:
-public: // we're all friends here
- mydsp *dsp_;
- ActionScriptUI *ui_;
-};
-
-
-Faust *faust = NULL;
-
-// Alchemy wrapper interface code
-static AS3_Val api_init(void *thisPtr, AS3_Val args) {
- faust = new Faust();
- AS3_Val array = AS3_Array("");
- BuildUIArray(array);
- return array;
-}
-
-static AS3_Val api_shutdown(void *thisPtr, AS3_Val args) {
- if (faust)
- delete faust;
- return AS3_Int(0);
-}
-
-// args = int id, float value
-static AS3_Val api_onControlChange(void *thisPtr, AS3_Val args) {
- if (!faust) return AS3_Int(0);
-
- // Marshall the arguments in.
- int id = 0;
- AS3_Val controlVal;
- AS3_ArrayValue(args, "IntType, AS3ValType", &id, &controlVal);
- double control_double = AS3_NumberValue(controlVal);
-
- // loss of precision is ok.
- float value = (float)control_double;
-
- // Call the actual update function
- faust->ui_->updateControl(id, value);
- return AS3_Int(0);
-}
-
-#define MAX_FLASH_BUFFER 8192
-// output buffers - L/R channels separate
-static float bufferL[MAX_FLASH_BUFFER];
-static float bufferR[MAX_FLASH_BUFFER];
-// output buffer - construct interleaved output
-static float bufferSum[2*MAX_FLASH_BUFFER];
-
-// input buffers - L/R separate
-static float inputL[MAX_FLASH_BUFFER];
-static float inputR[MAX_FLASH_BUFFER];
-// input buffer scratch space - interleaved
-static float bufferInSum[2*MAX_FLASH_BUFFER];
-
-// This is the most 'interesting' function of the file - it takes in flash sound buffers
-// and sends them through Faust DSP code.
-// args = int nsamples, float* buffer (byte[] in flash)
-static AS3_Val api_tick(void *thisPtr, AS3_Val args) {
- if (!faust) return AS3_Int(0);
-
- // Marshall arguments in.
- int nsamples = 0;
- int use_input = 0;
- AS3_Val buffer;
- AS3_Val input;
- AS3_ArrayValue(args, "IntType, IntType, AS3ValType, AS3ValType", &nsamples, &use_input, &input, &buffer);
-
- float* outputs[2] = {bufferL, bufferR};
- float* inputs[2] = {inputL, inputR};
- if (use_input) {
- //AS3_ByteArray_seek(input, 0, 0);
- // we need (#samples * sizeof(float) * 2 channels) bytes.
- AS3_ByteArray_readBytes((char*)bufferInSum, input, nsamples * 4 * 2);
- char *src = (char*)bufferInSum;
- char *dl = (char*)inputL, *dr = (char*)inputR;
- for (int i = 0; i < nsamples; ++i) {
- // fix endianness
- dl[3] = src[0];
- dl[2] = src[1];
- dl[1] = src[2];
- dl[0] = src[3];
- dr[3] = src[4];
- dr[2] = src[5];
- dr[1] = src[6];
- dr[0] = src[7];
- dl += 4;
- dr += 4;
- src += 8;
- }
- }
-
- // magic!
- faust->dsp_->compute(nsamples, inputs, outputs);
-
-
- // Post-process: interleave arrays.
- // Faust outputs to two separate arrays (which are probably contiguous in memory - see above)
- // Flash's sound callback needs this as LRLRLRLR...
- // For added fun, LLVM internal float seems to be the opposite endianness
- // as what Flash uses, so we have to do this byte-by-byte.
- char *copyL = (char*)bufferL;
- char *copyR = (char*)bufferR;
- char *tape_head = (char*)bufferSum;
- for (int i = 0; i < nsamples; ++i) {
- *tape_head++ = copyL[3];
- *tape_head++ = copyL[2];
- *tape_head++ = copyL[1];
- *tape_head++ = copyL[0];
- *tape_head++ = copyR[3];
- *tape_head++ = copyR[2];
- *tape_head++ = copyR[1];
- *tape_head++ = copyR[0];
- copyL+=4;
- copyR+=4;
- }
- AS3_ByteArray_writeBytes(buffer, bufferSum, 4 * nsamples * 2);
-
- return AS3_Int(0);
-}
-
-
-//Alchemy entry point
-// Here we are responsible for contructing an API object to pass back to Flash.
-// This must contain pointers to all functions which may be called.
-int main()
-{
- //define the methods exposed to ActionScript
- //typed as an ActionScript Function instance
- AS3_Val methodInit = AS3_Function( NULL, api_init );
- AS3_Val methodShutdown = AS3_Function( NULL, api_shutdown );
- AS3_Val methodOnControlChange = AS3_Function( NULL, api_onControlChange );
- AS3_Val methodTick = AS3_Function( NULL, api_tick );
-
- // construct an API lookup table with references to all functions
- // In flash we'll instantiate one of these and call methods on it
- // e.g. faust.api_tick().
- AS3_Val result = AS3_Object(
- "api_init:AS3ValType, api_shutdown:AS3ValType, api_onControlChange:AS3ValType, api_tick:AS3ValType",
- methodInit, methodShutdown, methodOnControlChange, methodTick);
-
- AS3_Release(methodInit);
- AS3_Release(methodShutdown);
- AS3_Release(methodOnControlChange);
- AS3_Release(methodTick);
-
- // notify Flash of our functions and run -- this function never returns.
- AS3_LibInit(result);
-
- return 0;
-}
-