33e2d07d3fb3fbfead0e73ca2d79a7d53579ffa2
[Faustine.git] /
1
2 // Faust -> Alchemy -> ActionScript C++ Architecture File
3
4 #include "AS3.h"
5 // math.h is needed for many faust examples, so include it.
6 // otherwise we have to hand-edit c++.
7 #include "math.h"
8
9
10 #ifdef __GNUC__
11
12 #define max(x,y) (((x)>(y)) ? (x) : (y))
13 #define min(x,y) (((x)<(y)) ? (x) : (y))
14
15 // abs is now predefined
16 //template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; }
17
18 inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); }
19 #else
20 #endif
21 /******************************************************************************
22 *******************************************************************************
23
24 VECTOR INTRINSICS
25
26 *******************************************************************************
27 *******************************************************************************/
28
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); }
31
32 <<includeIntrinsic>>
33
34 /******************************************************************************
35 *******************************************************************************
36
37 ABSTRACT USER INTERFACE
38
39 *******************************************************************************
40 *******************************************************************************/
41
42 class UI
43 {
44
45 public:
46 bool fStopped;
47 UI() : fStopped(false) {}
48 virtual ~UI() {}
49
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;
56
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;
62
63 virtual void run() = 0;
64
65 void stop() { fStopped = true; }
66 bool stopped() { return fStopped; }
67 };
68
69
70 ////// Implementation of UI
71
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.
80
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;
87
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.
97 #define LABEL_LEN 50
98 struct uiElemInfo {
99 int type;
100 int id;
101 char label[LABEL_LEN+1];
102 float min;
103 float max;
104 float init;
105 float step;
106 };
107 uiElemInfo uielems[MAX_CONTROLS];
108 static int uielems_size = 0;
109
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;
115 }
116 }
117
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)
128 );
129 AS3_Set(array, AS3_Int(i), result);
130 // decrease refcount? todo: this may leak memory...
131 //AS3_Release(result);
132 }
133 }
134
135 class ActionScriptUI : public UI {
136 public:
137 ActionScriptUI() {
138 fStopped = false;
139 }
140
141 virtual ~ActionScriptUI() {
142 }
143
144 // Pass in a zone, get back a unique ID.
145 int registerControl(float *zone) {
146 if (!zone) return 0;
147 uiMap_id++;
148 uidToZone[uiMap_id] = zone;
149 return uiMap_id;
150 }
151
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;
157 }
158
159
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;
169 uielems_size++;
170 }
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;
180 uielems_size++;
181 }
182 virtual void addCheckButton(char* label, float* zone) {
183 return addToggleButton(label, zone);
184 }
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);
187 }
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;
197 uielems_size++;
198 }
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);
201 }
202
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() { }
211 };
212
213 /******************************************************************************
214 *******************************************************************************
215
216 FAUST DSP
217
218 *******************************************************************************
219 *******************************************************************************/
220
221
222
223 //----------------------------------------------------------------
224 // abstract definition of a signal processor
225 //----------------------------------------------------------------
226
227 class dsp {
228 protected:
229 int fSamplingFreq;
230 public:
231 dsp() {}
232 virtual ~dsp() {}
233
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;
239 };
240
241
242 //----------------------------------------------------------------------------
243 // FAUST generated signal processor
244 //----------------------------------------------------------------------------
245
246
247 <<includeclass>>
248
249
250
251
252 /// Alchemy DSP
253 class Faust {
254 public:
255 Faust() {
256 // mydsp will be defined by faust in 'includeclass'
257 dsp_ = new mydsp();
258 ui_ = new ActionScriptUI();
259 dsp_->buildUserInterface(ui_);
260 dsp_->init(44100); // 44.1k, 2 channels, @ 32-bit is hardcoded into flash player 10.
261 }
262
263 ~Faust() {
264 if (dsp_) delete dsp_;
265 if (ui_) delete ui_;
266 }
267
268 //private:
269 public: // we're all friends here
270 mydsp *dsp_;
271 ActionScriptUI *ui_;
272 };
273
274
275 Faust *faust = NULL;
276
277 // Alchemy wrapper interface code
278 static AS3_Val api_init(void *thisPtr, AS3_Val args) {
279 faust = new Faust();
280 AS3_Val array = AS3_Array("");
281 BuildUIArray(array);
282 return array;
283 }
284
285 static AS3_Val api_shutdown(void *thisPtr, AS3_Val args) {
286 if (faust)
287 delete faust;
288 return AS3_Int(0);
289 }
290
291 // args = int id, float value
292 static AS3_Val api_onControlChange(void *thisPtr, AS3_Val args) {
293 if (!faust) return AS3_Int(0);
294
295 // Marshall the arguments in.
296 int id = 0;
297 AS3_Val controlVal;
298 AS3_ArrayValue(args, "IntType, AS3ValType", &id, &controlVal);
299 double control_double = AS3_NumberValue(controlVal);
300
301 // loss of precision is ok.
302 float value = (float)control_double;
303
304 // Call the actual update function
305 faust->ui_->updateControl(id, value);
306 return AS3_Int(0);
307 }
308
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];
315
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];
321
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);
327
328 // Marshall arguments in.
329 int nsamples = 0;
330 int use_input = 0;
331 AS3_Val buffer;
332 AS3_Val input;
333 AS3_ArrayValue(args, "IntType, IntType, AS3ValType, AS3ValType", &nsamples, &use_input, &input, &buffer);
334
335 float* outputs[2] = {bufferL, bufferR};
336 float* inputs[2] = {inputL, inputR};
337 if (use_input) {
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) {
344 // fix endianness
345 dl[3] = src[0];
346 dl[2] = src[1];
347 dl[1] = src[2];
348 dl[0] = src[3];
349 dr[3] = src[4];
350 dr[2] = src[5];
351 dr[1] = src[6];
352 dr[0] = src[7];
353 dl += 4;
354 dr += 4;
355 src += 8;
356 }
357 }
358
359 // magic!
360 faust->dsp_->compute(nsamples, inputs, outputs);
361
362
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];
380 copyL+=4;
381 copyR+=4;
382 }
383 AS3_ByteArray_writeBytes(buffer, bufferSum, 4 * nsamples * 2);
384
385 return AS3_Int(0);
386 }
387
388
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.
392 int main()
393 {
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 );
400
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);
407
408 AS3_Release(methodInit);
409 AS3_Release(methodShutdown);
410 AS3_Release(methodOnControlChange);
411 AS3_Release(methodTick);
412
413 // notify Flash of our functions and run -- this function never returns.
414 AS3_LibInit(result);
415
416 return 0;
417 }
418