X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/c7f552fd8888da2f0d8cfb228fe0f28d3df3a12c..b4b6f2ea75b9f0f3ca918f5b84016610bf7a4d4f:/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2flash_v0.1/Step_1_faust_architecture_file/alchemy-as.cpp diff --git a/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2flash_v0.1/Step_1_faust_architecture_file/alchemy-as.cpp b/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2flash_v0.1/Step_1_faust_architecture_file/alchemy-as.cpp new file mode 100644 index 0000000..33e2d07 --- /dev/null +++ b/interpretor/preprocessor/faust-0.9.47mr3/tools/faust2flash_v0.1/Step_1_faust_architecture_file/alchemy-as.cpp @@ -0,0 +1,418 @@ + +// 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 T abs (T a) { return (a> 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); } + +<> + +/****************************************************************************** +******************************************************************************* + + 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 +//---------------------------------------------------------------------------- + + +<> + + + + +/// 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; +} +