Refactoring of rec process "~" in faustexp.ml.
[Faustine.git] / interpretor / faust-0.9.47mr3 / architecture / alchemy-as.cpp
1 /************************************************************************
2
3 IMPORTANT NOTE : this file contains two clearly delimited sections :
4 the ARCHITECTURE section (in two parts) and the USER section. Each section
5 is governed by its own copyright and license. Please check individually
6 each section for license and copyright information.
7 *************************************************************************/
8
9 /*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/
10
11 /************************************************************************
12 FAUST Architecture File
13 Copyright (C) 2010-2011 Travis Skare
14 ---------------------------------------------------------------------
15 This Architecture section is free software; you can redistribute it
16 and/or modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 3 of
18 the License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; If not, see <http://www.gnu.org/licenses/>.
27
28 EXCEPTION : As a special exception, you may create a larger work that
29 contains this FAUST architecture section and distribute
30 that work under terms of your choice, so long as that this FAUST
31 architecture section is not modified.
32
33
34 ************************************************************************
35 ************************************************************************/
36
37 // Faust -> Alchemy -> ActionScript C++ Architecture File
38
39 #include "AS3.h"
40 // math.h is needed for many faust examples, so include it.
41 // otherwise we have to hand-edit c++.
42 #include "math.h"
43
44
45 #ifdef __GNUC__
46
47 #define max(x,y) (((x)>(y)) ? (x) : (y))
48 #define min(x,y) (((x)<(y)) ? (x) : (y))
49
50 // abs is now predefined
51 //template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; }
52
53 inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); }
54 #else
55 #endif
56 /******************************************************************************
57 *******************************************************************************
58
59 VECTOR INTRINSICS
60
61 *******************************************************************************
62 *******************************************************************************/
63
64 //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); }
65 inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); }
66
67 <<includeIntrinsic>>
68
69 /******************************************************************************
70 *******************************************************************************
71
72 ABSTRACT USER INTERFACE
73
74 *******************************************************************************
75 *******************************************************************************/
76
77 class UI
78 {
79
80 public:
81 bool fStopped;
82 UI() : fStopped(false) {}
83 virtual ~UI() {}
84
85 virtual void addButton(char* label, float* zone) = 0;
86 virtual void addToggleButton(char* label, float* zone) = 0;
87 virtual void addCheckButton(char* label, float* zone) = 0;
88 virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
89 virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
90 virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) = 0;
91
92 virtual void openFrameBox(char* label) = 0;
93 virtual void openTabBox(char* label) = 0;
94 virtual void openHorizontalBox(char* label) = 0;
95 virtual void openVerticalBox(char* label) = 0;
96 virtual void closeBox() = 0;
97
98 virtual void run() = 0;
99
100 void stop() { fStopped = true; }
101 bool stopped() { return fStopped; }
102 };
103
104
105 ////// Implementation of UI
106
107 // Faust UI hookup is straightforward
108 // We subclass from UI and then these methods will get called by compiled
109 // Faust which assembles controls. We handle all UI as needed, and when
110 // things changed we need to set *zone=(some value).
111 // This is a little more complicated when jumping between alchemy/script.
112 // So rather than deal with marshalling pointers, just cheat and use an int:pointer map.
113 // NOTE: I should have used an STL map but that wasn't working back when I used gcc.
114 // I'll investigate since that would clean up the code quite a bit.
115
116 // upper bound on number of controls
117 #define MAX_CONTROLS 25
118 // map of unique ui control id to a float* where faust reads the corresponding value.
119 static float* uidToZone[MAX_CONTROLS];
120 // Counter that assigns control IDs.
121 static int uiMap_id = 0;
122
123 // I wasn't able to properly thunk the UI actionscrpit methods to C.
124 // Since we know the complete UI at the time of creation, a collection of
125 // ui creation info is passed back from faust_init, and actionscript
126 // can read it and create the UI.
127 // This is a little messy and was done last-minute.
128 const int TYPE_BUTTON = 0;
129 const int TYPE_TOGGLE = 1;
130 const int TYPE_SLIDER = 2;
131 // max length for a label - more than 50 chars will get cut.
132 #define LABEL_LEN 50
133 struct uiElemInfo {
134 int type;
135 int id;
136 char label[LABEL_LEN+1];
137 float min;
138 float max;
139 float init;
140 float step;
141 };
142 uiElemInfo uielems[MAX_CONTROLS];
143 static int uielems_size = 0;
144
145 // todo: stdio.h has strncpy, I just got paranoid about extra includes making the code bigger :)
146 void strcopy(char *src, char *dst) {
147 dst[LABEL_LEN] = '\0';
148 for (int i = 0; i < LABEL_LEN; ++i) {
149 if (0 == (dst[i] = src[i])) return;
150 }
151 }
152
153 void BuildUIArray(AS3_Val &array) {
154 for (int i = 0; i < uielems_size; ++i) {
155 AS3_Val result = AS3_Object( "type:AS3ValType,id:AS3ValType,label:AS3ValType,min:AS3ValType,max:AS3ValType,init:AS3ValType,step:AS3ValType",
156 AS3_Int(uielems[i].type),
157 AS3_Int(uielems[i].id),
158 AS3_String(uielems[i].label),
159 AS3_Number(uielems[i].min),
160 AS3_Number(uielems[i].max),
161 AS3_Number(uielems[i].init),
162 AS3_Number(uielems[i].step)
163 );
164 AS3_Set(array, AS3_Int(i), result);
165 // decrease refcount? todo: this may leak memory...
166 //AS3_Release(result);
167 }
168 }
169
170 class ActionScriptUI : public UI {
171 public:
172 ActionScriptUI() {
173 fStopped = false;
174 }
175
176 virtual ~ActionScriptUI() {
177 }
178
179 // Pass in a zone, get back a unique ID.
180 int registerControl(float *zone) {
181 if (!zone) return 0;
182 uiMap_id++;
183 uidToZone[uiMap_id] = zone;
184 return uiMap_id;
185 }
186
187 // Called from Flash when any control is updated.
188 // Results will take effect on the next dsp callback
189 // since everything runs in the same thread.
190 void updateControl(int id, float value) {
191 *(uidToZone[id]) = value;
192 }
193
194
195 virtual void addButton(char* label, float* zone) {
196 int id = registerControl(zone);
197 uielems[uielems_size].type = TYPE_BUTTON;
198 uielems[uielems_size].id = id;
199 strcopy(label, uielems[uielems_size].label);
200 uielems[uielems_size].min = 0;
201 uielems[uielems_size].max = 0;
202 uielems[uielems_size].init = 0;
203 uielems[uielems_size].step = 0;
204 uielems_size++;
205 }
206 virtual void addToggleButton(char* label, float* zone) {
207 int id = registerControl(zone);
208 uielems[uielems_size].type = TYPE_TOGGLE;
209 uielems[uielems_size].id = id;
210 strcopy(label, uielems[uielems_size].label);
211 uielems[uielems_size].min = 0;
212 uielems[uielems_size].max = 0;
213 uielems[uielems_size].init = 0;
214 uielems[uielems_size].step = 0;
215 uielems_size++;
216 }
217 virtual void addCheckButton(char* label, float* zone) {
218 return addToggleButton(label, zone);
219 }
220 virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) {
221 return addHorizontalSlider(label, zone, init, min, max, step);
222 }
223 virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) {
224 int id = registerControl(zone);
225 uielems[uielems_size].type = TYPE_SLIDER;
226 uielems[uielems_size].id = id;
227 strcopy(label, uielems[uielems_size].label);
228 uielems[uielems_size].min = min;
229 uielems[uielems_size].max = max;
230 uielems[uielems_size].init = init;
231 uielems[uielems_size].step = step;
232 uielems_size++;
233 }
234 virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) {
235 return addHorizontalSlider(label, zone, init, min, max, step);
236 }
237
238 // Not implemented yet - these only affect UI layout and aren't critical.
239 // See actionscript comments for details.
240 virtual void openFrameBox(char* label) {}
241 virtual void openTabBox(char* label) {}
242 virtual void openHorizontalBox(char* label) {}
243 virtual void openVerticalBox(char* label) {}
244 virtual void closeBox() {}
245 virtual void run() { }
246 };
247
248 /******************************************************************************
249 *******************************************************************************
250
251 FAUST DSP
252
253 *******************************************************************************
254 *******************************************************************************/
255
256
257
258 //----------------------------------------------------------------
259 // abstract definition of a signal processor
260 //----------------------------------------------------------------
261
262 class dsp {
263 protected:
264 int fSamplingFreq;
265 public:
266 dsp() {}
267 virtual ~dsp() {}
268
269 virtual int getNumInputs() = 0;
270 virtual int getNumOutputs() = 0;
271 virtual void buildUserInterface(UI* interface) = 0;
272 virtual void init(int samplingRate) = 0;
273 virtual void compute(int len, float** inputs, float** outputs) = 0;
274 };
275
276
277 //----------------------------------------------------------------------------
278 // FAUST generated signal processor
279 //----------------------------------------------------------------------------
280
281
282
283 /********************END ARCHITECTURE SECTION (part 1/2)****************/
284
285 /**************************BEGIN USER SECTION **************************/
286
287 <<includeclass>>
288
289 /***************************END USER SECTION ***************************/
290
291 /*******************BEGIN ARCHITECTURE SECTION (part 2/2)***************/
292
293
294
295
296 /// Alchemy DSP
297 class Faust {
298 public:
299 Faust() {
300 // mydsp will be defined by faust in 'includeclass'
301 dsp_ = new mydsp();
302 ui_ = new ActionScriptUI();
303 dsp_->buildUserInterface(ui_);
304 dsp_->init(44100); // 44.1k, 2 channels, @ 32-bit is hardcoded into flash player 10.
305 }
306
307 ~Faust() {
308 if (dsp_) delete dsp_;
309 if (ui_) delete ui_;
310 }
311
312 //private:
313 public: // we're all friends here
314 mydsp *dsp_;
315 ActionScriptUI *ui_;
316 };
317
318
319 Faust *faust = NULL;
320
321 // Alchemy wrapper interface code
322 static AS3_Val api_init(void *thisPtr, AS3_Val args) {
323 faust = new Faust();
324 AS3_Val array = AS3_Array("");
325 BuildUIArray(array);
326 return array;
327 }
328
329 static AS3_Val api_shutdown(void *thisPtr, AS3_Val args) {
330 if (faust)
331 delete faust;
332 return AS3_Int(0);
333 }
334
335 // args = int id, float value
336 static AS3_Val api_onControlChange(void *thisPtr, AS3_Val args) {
337 if (!faust) return AS3_Int(0);
338
339 // Marshall the arguments in.
340 int id = 0;
341 AS3_Val controlVal;
342 AS3_ArrayValue(args, "IntType, AS3ValType", &id, &controlVal);
343 double control_double = AS3_NumberValue(controlVal);
344
345 // loss of precision is ok.
346 float value = (float)control_double;
347
348 // Call the actual update function
349 faust->ui_->updateControl(id, value);
350 return AS3_Int(0);
351 }
352
353 #define MAX_FLASH_BUFFER 8192
354 // output buffers - L/R channels separate
355 static float bufferL[MAX_FLASH_BUFFER];
356 static float bufferR[MAX_FLASH_BUFFER];
357 // output buffer - construct interleaved output
358 static float bufferSum[2*MAX_FLASH_BUFFER];
359
360 // input buffers - L/R separate
361 static float inputL[MAX_FLASH_BUFFER];
362 static float inputR[MAX_FLASH_BUFFER];
363 // input buffer scratch space - interleaved
364 static float bufferInSum[2*MAX_FLASH_BUFFER];
365
366 // This is the most 'interesting' function of the file - it takes in flash sound buffers
367 // and sends them through Faust DSP code.
368 // args = int nsamples, float* buffer (byte[] in flash)
369 static AS3_Val api_tick(void *thisPtr, AS3_Val args) {
370 if (!faust) return AS3_Int(0);
371
372 // Marshall arguments in.
373 int nsamples = 0;
374 int use_input = 0;
375 AS3_Val buffer;
376 AS3_Val input;
377 AS3_ArrayValue(args, "IntType, IntType, AS3ValType, AS3ValType", &nsamples, &use_input, &input, &buffer);
378
379 float* outputs[2] = {bufferL, bufferR};
380 float* inputs[2] = {inputL, inputR};
381 if (use_input) {
382 //AS3_ByteArray_seek(input, 0, 0);
383 // we need (#samples * sizeof(float) * 2 channels) bytes.
384 AS3_ByteArray_readBytes((char*)bufferInSum, input, nsamples * 4 * 2);
385 char *src = (char*)bufferInSum;
386 char *dl = (char*)inputL, *dr = (char*)inputR;
387 for (int i = 0; i < nsamples; ++i) {
388 // fix endianness
389 dl[3] = src[0];
390 dl[2] = src[1];
391 dl[1] = src[2];
392 dl[0] = src[3];
393 dr[3] = src[4];
394 dr[2] = src[5];
395 dr[1] = src[6];
396 dr[0] = src[7];
397 dl += 4;
398 dr += 4;
399 src += 8;
400 }
401 }
402
403 // magic!
404 faust->dsp_->compute(nsamples, inputs, outputs);
405
406
407 // Post-process: interleave arrays.
408 // Faust outputs to two separate arrays (which are probably contiguous in memory - see above)
409 // Flash's sound callback needs this as LRLRLRLR...
410 // For added fun, LLVM internal float seems to be the opposite endianness
411 // as what Flash uses, so we have to do this byte-by-byte.
412 char *copyL = (char*)bufferL;
413 char *copyR = (char*)bufferR;
414 char *tape_head = (char*)bufferSum;
415 for (int i = 0; i < nsamples; ++i) {
416 *tape_head++ = copyL[3];
417 *tape_head++ = copyL[2];
418 *tape_head++ = copyL[1];
419 *tape_head++ = copyL[0];
420 *tape_head++ = copyR[3];
421 *tape_head++ = copyR[2];
422 *tape_head++ = copyR[1];
423 *tape_head++ = copyR[0];
424 copyL+=4;
425 copyR+=4;
426 }
427 AS3_ByteArray_writeBytes(buffer, bufferSum, 4 * nsamples * 2);
428
429 return AS3_Int(0);
430 }
431
432
433 //Alchemy entry point
434 // Here we are responsible for contructing an API object to pass back to Flash.
435 // This must contain pointers to all functions which may be called.
436 int main()
437 {
438 //define the methods exposed to ActionScript
439 //typed as an ActionScript Function instance
440 AS3_Val methodInit = AS3_Function( NULL, api_init );
441 AS3_Val methodShutdown = AS3_Function( NULL, api_shutdown );
442 AS3_Val methodOnControlChange = AS3_Function( NULL, api_onControlChange );
443 AS3_Val methodTick = AS3_Function( NULL, api_tick );
444
445 // construct an API lookup table with references to all functions
446 // In flash we'll instantiate one of these and call methods on it
447 // e.g. faust.api_tick().
448 AS3_Val result = AS3_Object(
449 "api_init:AS3ValType, api_shutdown:AS3ValType, api_onControlChange:AS3ValType, api_tick:AS3ValType",
450 methodInit, methodShutdown, methodOnControlChange, methodTick);
451
452 AS3_Release(methodInit);
453 AS3_Release(methodShutdown);
454 AS3_Release(methodOnControlChange);
455 AS3_Release(methodTick);
456
457 // notify Flash of our functions and run -- this function never returns.
458 AS3_LibInit(result);
459
460 return 0;
461 }
462 /********************END ARCHITECTURE SECTION (part 2/2)****************/
463