2 // Faust -> Alchemy -> ActionScript C++ Architecture File
5 // math.h is needed for many faust examples, so include it.
6 // otherwise we have to hand-edit c++.
12 #define max(x,y) (((x)>(y)) ? (x) : (y))
13 #define min(x,y) (((x)<(y)) ? (x) : (y))
15 // abs is now predefined
16 //template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; }
18 inline int lsr (int x
, int n
) { return int(((unsigned int)x
) >> n
); }
21 /******************************************************************************
22 *******************************************************************************
26 *******************************************************************************
27 *******************************************************************************/
29 //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); }
30 inline void *aligned_calloc(size_t nmemb
, size_t size
) { return (void*)((size_t)(calloc((nmemb
*size
)+15,sizeof(char)))+15 & ~15); }
34 /******************************************************************************
35 *******************************************************************************
37 ABSTRACT USER INTERFACE
39 *******************************************************************************
40 *******************************************************************************/
47 UI() : fStopped(false) {}
50 virtual void addButton(char* label
, float* zone
) = 0;
51 virtual void addToggleButton(char* label
, float* zone
) = 0;
52 virtual void addCheckButton(char* label
, float* zone
) = 0;
53 virtual void addVerticalSlider(char* label
, float* zone
, float init
, float min
, float max
, float step
) = 0;
54 virtual void addHorizontalSlider(char* label
, float* zone
, float init
, float min
, float max
, float step
) = 0;
55 virtual void addNumEntry(char* label
, float* zone
, float init
, float min
, float max
, float step
) = 0;
57 virtual void openFrameBox(char* label
) = 0;
58 virtual void openTabBox(char* label
) = 0;
59 virtual void openHorizontalBox(char* label
) = 0;
60 virtual void openVerticalBox(char* label
) = 0;
61 virtual void closeBox() = 0;
63 virtual void run() = 0;
65 void stop() { fStopped
= true; }
66 bool stopped() { return fStopped
; }
70 ////// Implementation of UI
72 // Faust UI hookup is straightforward
73 // We subclass from UI and then these methods will get called by compiled
74 // Faust which assembles controls. We handle all UI as needed, and when
75 // things changed we need to set *zone=(some value).
76 // This is a little more complicated when jumping between alchemy/script.
77 // So rather than deal with marshalling pointers, just cheat and use an int:pointer map.
78 // NOTE: I should have used an STL map but that wasn't working back when I used gcc.
79 // I'll investigate since that would clean up the code quite a bit.
81 // upper bound on number of controls
82 #define MAX_CONTROLS 25
83 // map of unique ui control id to a float* where faust reads the corresponding value.
84 static float* uidToZone
[MAX_CONTROLS
];
85 // Counter that assigns control IDs.
86 static int uiMap_id
= 0;
88 // I wasn't able to properly thunk the UI actionscrpit methods to C.
89 // Since we know the complete UI at the time of creation, a collection of
90 // ui creation info is passed back from faust_init, and actionscript
91 // can read it and create the UI.
92 // This is a little messy and was done last-minute.
93 const int TYPE_BUTTON
= 0;
94 const int TYPE_TOGGLE
= 1;
95 const int TYPE_SLIDER
= 2;
96 // max length for a label - more than 50 chars will get cut.
101 char label
[LABEL_LEN
+1];
107 uiElemInfo uielems
[MAX_CONTROLS
];
108 static int uielems_size
= 0;
110 // todo: stdio.h has strncpy, I just got paranoid about extra includes making the code bigger :)
111 void strcopy(char *src
, char *dst
) {
112 dst
[LABEL_LEN
] = '\0';
113 for (int i
= 0; i
< LABEL_LEN
; ++i
) {
114 if (0 == (dst
[i
] = src
[i
])) return;
118 void BuildUIArray(AS3_Val
&array
) {
119 for (int i
= 0; i
< uielems_size
; ++i
) {
120 AS3_Val result
= AS3_Object( "type:AS3ValType,id:AS3ValType,label:AS3ValType,min:AS3ValType,max:AS3ValType,init:AS3ValType,step:AS3ValType",
121 AS3_Int(uielems
[i
].type
),
122 AS3_Int(uielems
[i
].id
),
123 AS3_String(uielems
[i
].label
),
124 AS3_Number(uielems
[i
].min
),
125 AS3_Number(uielems
[i
].max
),
126 AS3_Number(uielems
[i
].init
),
127 AS3_Number(uielems
[i
].step
)
129 AS3_Set(array
, AS3_Int(i
), result
);
130 // decrease refcount? todo: this may leak memory...
131 //AS3_Release(result);
135 class ActionScriptUI
: public UI
{
141 virtual ~ActionScriptUI() {
144 // Pass in a zone, get back a unique ID.
145 int registerControl(float *zone
) {
148 uidToZone
[uiMap_id
] = zone
;
152 // Called from Flash when any control is updated.
153 // Results will take effect on the next dsp callback
154 // since everything runs in the same thread.
155 void updateControl(int id
, float value
) {
156 *(uidToZone
[id
]) = value
;
160 virtual void addButton(char* label
, float* zone
) {
161 int id
= registerControl(zone
);
162 uielems
[uielems_size
].type
= TYPE_BUTTON
;
163 uielems
[uielems_size
].id
= id
;
164 strcopy(label
, uielems
[uielems_size
].label
);
165 uielems
[uielems_size
].min
= 0;
166 uielems
[uielems_size
].max
= 0;
167 uielems
[uielems_size
].init
= 0;
168 uielems
[uielems_size
].step
= 0;
171 virtual void addToggleButton(char* label
, float* zone
) {
172 int id
= registerControl(zone
);
173 uielems
[uielems_size
].type
= TYPE_TOGGLE
;
174 uielems
[uielems_size
].id
= id
;
175 strcopy(label
, uielems
[uielems_size
].label
);
176 uielems
[uielems_size
].min
= 0;
177 uielems
[uielems_size
].max
= 0;
178 uielems
[uielems_size
].init
= 0;
179 uielems
[uielems_size
].step
= 0;
182 virtual void addCheckButton(char* label
, float* zone
) {
183 return addToggleButton(label
, zone
);
185 virtual void addVerticalSlider(char* label
, float* zone
, float init
, float min
, float max
, float step
) {
186 return addHorizontalSlider(label
, zone
, init
, min
, max
, step
);
188 virtual void addHorizontalSlider(char* label
, float* zone
, float init
, float min
, float max
, float step
) {
189 int id
= registerControl(zone
);
190 uielems
[uielems_size
].type
= TYPE_SLIDER
;
191 uielems
[uielems_size
].id
= id
;
192 strcopy(label
, uielems
[uielems_size
].label
);
193 uielems
[uielems_size
].min
= min
;
194 uielems
[uielems_size
].max
= max
;
195 uielems
[uielems_size
].init
= init
;
196 uielems
[uielems_size
].step
= step
;
199 virtual void addNumEntry(char* label
, float* zone
, float init
, float min
, float max
, float step
) {
200 return addHorizontalSlider(label
, zone
, init
, min
, max
, step
);
203 // Not implemented yet - these only affect UI layout and aren't critical.
204 // See actionscript comments for details.
205 virtual void openFrameBox(char* label
) {}
206 virtual void openTabBox(char* label
) {}
207 virtual void openHorizontalBox(char* label
) {}
208 virtual void openVerticalBox(char* label
) {}
209 virtual void closeBox() {}
210 virtual void run() { }
213 /******************************************************************************
214 *******************************************************************************
218 *******************************************************************************
219 *******************************************************************************/
223 //----------------------------------------------------------------
224 // abstract definition of a signal processor
225 //----------------------------------------------------------------
234 virtual int getNumInputs() = 0;
235 virtual int getNumOutputs() = 0;
236 virtual void buildUserInterface(UI
* interface
) = 0;
237 virtual void init(int samplingRate
) = 0;
238 virtual void compute(int len
, float** inputs
, float** outputs
) = 0;
242 //----------------------------------------------------------------------------
243 // FAUST generated signal processor
244 //----------------------------------------------------------------------------
256 // mydsp will be defined by faust in 'includeclass'
258 ui_
= new ActionScriptUI();
259 dsp_
->buildUserInterface(ui_
);
260 dsp_
->init(44100); // 44.1k, 2 channels, @ 32-bit is hardcoded into flash player 10.
264 if (dsp_
) delete dsp_
;
269 public: // we're all friends here
277 // Alchemy wrapper interface code
278 static AS3_Val
api_init(void *thisPtr
, AS3_Val args
) {
280 AS3_Val array
= AS3_Array("");
285 static AS3_Val
api_shutdown(void *thisPtr
, AS3_Val args
) {
291 // args = int id, float value
292 static AS3_Val
api_onControlChange(void *thisPtr
, AS3_Val args
) {
293 if (!faust
) return AS3_Int(0);
295 // Marshall the arguments in.
298 AS3_ArrayValue(args
, "IntType, AS3ValType", &id
, &controlVal
);
299 double control_double
= AS3_NumberValue(controlVal
);
301 // loss of precision is ok.
302 float value
= (float)control_double
;
304 // Call the actual update function
305 faust
->ui_
->updateControl(id
, value
);
309 #define MAX_FLASH_BUFFER 8192
310 // output buffers - L/R channels separate
311 static float bufferL
[MAX_FLASH_BUFFER
];
312 static float bufferR
[MAX_FLASH_BUFFER
];
313 // output buffer - construct interleaved output
314 static float bufferSum
[2*MAX_FLASH_BUFFER
];
316 // input buffers - L/R separate
317 static float inputL
[MAX_FLASH_BUFFER
];
318 static float inputR
[MAX_FLASH_BUFFER
];
319 // input buffer scratch space - interleaved
320 static float bufferInSum
[2*MAX_FLASH_BUFFER
];
322 // This is the most 'interesting' function of the file - it takes in flash sound buffers
323 // and sends them through Faust DSP code.
324 // args = int nsamples, float* buffer (byte[] in flash)
325 static AS3_Val
api_tick(void *thisPtr
, AS3_Val args
) {
326 if (!faust
) return AS3_Int(0);
328 // Marshall arguments in.
333 AS3_ArrayValue(args
, "IntType, IntType, AS3ValType, AS3ValType", &nsamples
, &use_input
, &input
, &buffer
);
335 float* outputs
[2] = {bufferL
, bufferR
};
336 float* inputs
[2] = {inputL
, inputR
};
338 //AS3_ByteArray_seek(input, 0, 0);
339 // we need (#samples * sizeof(float) * 2 channels) bytes.
340 AS3_ByteArray_readBytes((char*)bufferInSum
, input
, nsamples
* 4 * 2);
341 char *src
= (char*)bufferInSum
;
342 char *dl
= (char*)inputL
, *dr
= (char*)inputR
;
343 for (int i
= 0; i
< nsamples
; ++i
) {
360 faust
->dsp_
->compute(nsamples
, inputs
, outputs
);
363 // Post-process: interleave arrays.
364 // Faust outputs to two separate arrays (which are probably contiguous in memory - see above)
365 // Flash's sound callback needs this as LRLRLRLR...
366 // For added fun, LLVM internal float seems to be the opposite endianness
367 // as what Flash uses, so we have to do this byte-by-byte.
368 char *copyL
= (char*)bufferL
;
369 char *copyR
= (char*)bufferR
;
370 char *tape_head
= (char*)bufferSum
;
371 for (int i
= 0; i
< nsamples
; ++i
) {
372 *tape_head
++ = copyL
[3];
373 *tape_head
++ = copyL
[2];
374 *tape_head
++ = copyL
[1];
375 *tape_head
++ = copyL
[0];
376 *tape_head
++ = copyR
[3];
377 *tape_head
++ = copyR
[2];
378 *tape_head
++ = copyR
[1];
379 *tape_head
++ = copyR
[0];
383 AS3_ByteArray_writeBytes(buffer
, bufferSum
, 4 * nsamples
* 2);
389 //Alchemy entry point
390 // Here we are responsible for contructing an API object to pass back to Flash.
391 // This must contain pointers to all functions which may be called.
394 //define the methods exposed to ActionScript
395 //typed as an ActionScript Function instance
396 AS3_Val methodInit
= AS3_Function( NULL
, api_init
);
397 AS3_Val methodShutdown
= AS3_Function( NULL
, api_shutdown
);
398 AS3_Val methodOnControlChange
= AS3_Function( NULL
, api_onControlChange
);
399 AS3_Val methodTick
= AS3_Function( NULL
, api_tick
);
401 // construct an API lookup table with references to all functions
402 // In flash we'll instantiate one of these and call methods on it
403 // e.g. faust.api_tick().
404 AS3_Val result
= AS3_Object(
405 "api_init:AS3ValType, api_shutdown:AS3ValType, api_onControlChange:AS3ValType, api_tick:AS3ValType",
406 methodInit
, methodShutdown
, methodOnControlChange
, methodTick
);
408 AS3_Release(methodInit
);
409 AS3_Release(methodShutdown
);
410 AS3_Release(methodOnControlChange
);
411 AS3_Release(methodTick
);
413 // notify Flash of our functions and run -- this function never returns.