1 #include <string>
2 #include <map>
3 #include <iostream>
4 #include <sstream>
5 #include <math.h>
6 #include <list>
7 
8 #define MEMORY_READER
9 #include "faust/gui/SoundUI.h"
10 
11 #include "faust/dsp/llvm-dsp.h"
12 #include "faust/dsp/one-sample-dsp.h"
13 #include "faust/gui/GUI.h"
14 #include "faust/dsp/poly-dsp.h"
15 #include "faust/audio/channels.h"
16 #include "faust/gui/DecoratorUI.h"
17 #include "faust/gui/FUI.h"
18 #include "faust/gui/MidiUI.h"
19 #include "faust/midi/midi.h"
20 #include "faust/misc.h"
21 
22 using std::max;
23 using std::min;
24 
25 #define kFrames 64
26 
27 using namespace std;
28 
29 std::list<GUI*> GUI::fGuiList;
30 ztimedmap GUI::gTimedZoneMap;
31 
32 //----------------------------------------------------------------------------
33 // Test MemoryReader
34 //----------------------------------------------------------------------------
35 
36 struct TestMemoryReader : public MemoryReader {
37 
checkFileTestMemoryReader38     virtual bool checkFile(const std::string& path_name)
39     {
40         return true;
41     }
42 
getParamsFileTestMemoryReader43     virtual void getParamsFile(const std::string& path_name, int& channels, int& length)
44     {
45         channels = SOUND_CHAN;
46         length = SOUND_LENGTH;
47     }
48 
readFileTestMemoryReader49     virtual void readFile(Soundfile* soundfile, const std::string& path_name, int part, int& offset, int max_chan)
50     {
51         soundfile->fLength[part] = SOUND_LENGTH;
52         soundfile->fSR[part] = SOUND_SR;
53         soundfile->fOffset[part] = offset;
54 
55         // Audio frames have to be written for each chan
56        if (soundfile->fIsDouble) {
57             for (int sample = 0; sample < SOUND_LENGTH; sample++) {
58                 for (int chan = 0; chan < SOUND_CHAN; chan++) {
59                     static_cast<double**>(soundfile->fBuffers)[chan][offset + sample] = std::sin(part + (2 * M_PI * double(sample)/SOUND_LENGTH));
60                 }
61             }
62         } else {
63             for (int sample = 0; sample < SOUND_LENGTH; sample++) {
64                 for (int chan = 0; chan < SOUND_CHAN; chan++) {
65                     static_cast<float**>(soundfile->fBuffers)[chan][offset + sample] = std::sin(part + (2 * M_PI * float(sample)/SOUND_LENGTH));
66                 }
67             }
68         }
69 
70         // Update offset
71         offset += SOUND_LENGTH;
72     }
73 
74 };
75 
76 //----------------------------------------------------------------------------
77 // DSP control UI
78 //----------------------------------------------------------------------------
79 
80 struct CheckControlUI : public GenericUI {
81 
82     map<FAUSTFLOAT*, FAUSTFLOAT> fControlZone;
83 
addButtonCheckControlUI84     virtual void addButton(const char* label, FAUSTFLOAT* zone)
85     {
86         addItem(zone, FAUSTFLOAT(0));
87     }
addCheckButtonCheckControlUI88     virtual void addCheckButton(const char* label, FAUSTFLOAT* zone)
89     {
90         addItem(zone, FAUSTFLOAT(0));
91     }
addVerticalSliderCheckControlUI92     virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
93     {
94         addItem(zone, init);
95     }
addHorizontalSliderCheckControlUI96     virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
97     {
98         addItem(zone, init);
99     }
addNumEntryCheckControlUI100     virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
101     {
102         addItem(zone, init);
103     }
104 
addItemCheckControlUI105     void addItem(FAUSTFLOAT* zone, FAUSTFLOAT init)
106     {
107         fControlZone[zone] = init;
108     }
109 
checkDefaultsCheckControlUI110     bool checkDefaults()
111     {
112         for (const auto& it : fControlZone) {
113             if (*it.first != it.second) return false;
114         }
115         return true;
116     }
117 
initRandomCheckControlUI118     void initRandom()
119     {
120         for (const auto& it : fControlZone) {
121             *it.first = 0.123456789;
122         }
123     }
124 };
125 
126 //----------------------------------------------------------------------------
127 // Test memory manager
128 //----------------------------------------------------------------------------
129 
130 struct malloc_memory_manager : public dsp_memory_manager {
131 
allocatemalloc_memory_manager132     virtual void* allocate(size_t size)
133     {
134         void* res = malloc(size);
135         memset(res, 0, size);
136         return res;
137     }
138 
destroymalloc_memory_manager139     virtual void destroy(void* ptr)
140     {
141         free(ptr);
142     }
143 
144 };
145 
printHeader(dsp * DSP,int nbsamples)146 static void printHeader(dsp* DSP, int nbsamples)
147 {
148     // Print general informations
149     printf("number_of_inputs  : %3d\n", DSP->getNumInputs());
150     printf("number_of_outputs : %3d\n", DSP->getNumOutputs());
151     printf("number_of_frames  : %6d\n", nbsamples);
152 }
153 
normalize(FAUSTFLOAT f)154 static inline FAUSTFLOAT normalize(FAUSTFLOAT f)
155 {
156     if (std::isnan(f)) {
157         cerr << "ERROR : isnan" << std::endl;
158         throw -1;
159     } else if (std::isinf(f)) {
160         cerr << "ERROR : isinf" << std::endl;
161         throw -1;
162     }
163     return (fabs(f) < FAUSTFLOAT(0.000001) ? FAUSTFLOAT(0.0) : f);
164 }
165 
166 // To be used in static context
167 static void runPolyDSP(dsp* dsp, int& linenum, int nbsamples, int num_voices = 4)
168 {
169     mydsp_poly* DSP = new mydsp_poly(dsp, num_voices, true, false);
170 
171     // Soundfile
172     TestMemoryReader memory_reader;
173     SoundUI sound_ui("", -1, &memory_reader, (sizeof(FAUSTFLOAT) == sizeof(double)));
174     DSP->buildUserInterface(&sound_ui);
175 
176     // Get control and then 'initRandom'
177     CheckControlUI controlui;
178     DSP->buildUserInterface(&controlui);
179     controlui.initRandom();
180 
181     // init signal processor and the user interface values:
182     DSP->init(44100);
183 
184     // Check getSampleRate
185     if (DSP->getSampleRate() != 44100) {
186         cerr << "ERROR runPolyDSP in getSampleRate : " << DSP->getSampleRate() << std::endl;
187     }
188 
189     // Check default after 'init'
190     if (!controlui.checkDefaults()) {
191         cerr << "ERROR runPolyDSP in checkDefaults after 'init'" << std::endl;
192     }
193 
194     // Check default after 'instanceResetUserInterface'
195     controlui.initRandom();
196     DSP->instanceResetUserInterface();
197     if (!controlui.checkDefaults()) {
198         cerr << "ERROR runPolyDSP in checkDefaults after 'instanceResetUserInterface'" << std::endl;
199     }
200 
201     // Check default after 'instanceInit'
202     controlui.initRandom();
203     DSP->instanceInit(44100);
204     if (!controlui.checkDefaults()) {
205         cerr << "ERROR runPolyDSP in checkDefaults after 'instanceInit'" << std::endl;
206     }
207 
208     // Init again
209     DSP->init(44100);
210 
211     int nins = DSP->getNumInputs();
212     channels ichan(kFrames, nins);
213 
214     int nouts = DSP->getNumOutputs();
215     channels ochan(kFrames, nouts);
216 
217     // Test polyphony
218     for (int i = 0; i < num_voices; i++) {
219         DSP->keyOn(0, 60 + i*2, 100);
220     }
221 
222     // Compute audio frames
223     while (nbsamples > 0) {
224         int nFrames = min(kFrames, nbsamples);
225         DSP->compute(nFrames, ichan.buffers(), ochan.buffers());
226         // Print samples
227         for (int i = 0; i < nFrames; i++) {
228             printf("%6d : ", linenum++);
229             for (int c = 0; c < nouts; c++) {
230                 FAUSTFLOAT f = normalize(ochan.buffers()[c][i]);
231                 printf(" %8.6f", f);
232             }
233             printf("\n");
234         }
235         nbsamples -= nFrames;
236     }
237 
238     delete DSP;
239 }
240 
241 // To be used in dynamic context (LLVM or interp backends)
242 static void runPolyDSP1(dsp_factory* factory, int& linenum, int nbsamples, int num_voices = 4, bool is_mem_alloc = false)
243 {
244     malloc_memory_manager manager;
245     factory->setMemoryManager((is_mem_alloc) ? &manager : nullptr);
246     runPolyDSP(factory->createDSPInstance(), linenum, nbsamples, num_voices);
247 }
248 
249 // To be used in static context
250 static void runDSP(dsp* DSP, const string& file, int& linenum, int nbsamples, bool inpl = false, bool random = false)
251 {
252     char rcfilename[256];
253     string filename = file;
254     filename = filename.substr(0, filename.find ('.'));
255     snprintf(rcfilename, 255, "%src", filename.c_str());
256 
257     FUI finterface;
258     DSP->buildUserInterface(&finterface);
259 
260     // Soundfile
261     TestMemoryReader memory_reader;
262     SoundUI sound_ui("", -1, &memory_reader, (sizeof(FAUSTFLOAT) == sizeof(double)));
263     DSP->buildUserInterface(&sound_ui);
264 
265     // Get control and then 'initRandom'
266     CheckControlUI controlui;
267     DSP->buildUserInterface(&controlui);
268     controlui.initRandom();
269 
270     // MIDI control
271     midi_handler handler;
272     MidiUI midi_ui(&handler);
273     DSP->buildUserInterface(&midi_ui);
274 
275     // Init signal processor and the user interface values
276     DSP->init(44100);
277 
278     // Check getSampleRate
279     if (DSP->getSampleRate() != 44100) {
280         cerr << "ERROR runDSP in getSampleRate : " << DSP->getSampleRate() << std::endl;
281     }
282 
283     // Check default after 'init'
284     if (!controlui.checkDefaults()) {
285         cerr << "ERROR runDSP in checkDefaults after 'init'" << std::endl;
286     }
287 
288     // Check default after 'instanceResetUserInterface'
289     controlui.initRandom();
290     DSP->instanceResetUserInterface();
291     if (!controlui.checkDefaults()) {
292         cerr << "ERROR runDSP in checkDefaults after 'instanceResetUserInterface'" << std::endl;
293     }
294 
295     // Check default after 'instanceInit'
296     controlui.initRandom();
297     DSP->instanceInit(44100);
298     if (!controlui.checkDefaults()) {
299         cerr << "ERROR runDSP in checkDefaults after 'instanceInit'" << std::endl;
300     }
301 
302     // To test that instanceInit properly init a cloned DSP
303     DSP = DSP->clone();
304     DSP->instanceInit(44100);
305 
306     // Init UIs on cloned DSP
307     DSP->buildUserInterface(&finterface);
308     DSP->buildUserInterface(&sound_ui);
309     DSP->buildUserInterface(&midi_ui);
310 
311     int nins = DSP->getNumInputs();
312     int nouts = DSP->getNumOutputs();
313 
314     channels* ichan = new channels(kFrames, ((inpl) ? std::max(nins, nouts) : nins));
315     channels* ochan = (inpl) ? ichan : new channels(kFrames, nouts);
316 
317     int run = 0;
318 
319     // recall saved state
320     finterface.recallState(rcfilename);
321 
322     // Test MIDI control
323     for (int i = 0; i < 127; i++) {
324         handler.handleData2(0, midi::MidiStatus::MIDI_CONTROL_CHANGE, 0, i, 100);
325         handler.handleData2(0, midi::MidiStatus::MIDI_POLY_AFTERTOUCH, 0, i, 75);
326         handler.handleData2(0, midi::MidiStatus::MIDI_NOTE_ON, 0, i, 75);
327         handler.handleData2(0, midi::MidiStatus::MIDI_NOTE_OFF, 0, i, 75);
328         handler.handleData2(0, midi::MidiStatus::MIDI_PITCH_BEND, 0, i, 4000);
329     }
330     handler.handleData1(0, midi::MidiStatus::MIDI_PROGRAM_CHANGE, 0, 10);
331     handler.handleData1(0, midi::MidiStatus::MIDI_AFTERTOUCH, 0, 10);
332 
333     GUI::updateAllGuis();
334 
335     // print audio frames
336     int i = 0;
337     try {
338         while (nbsamples > 0) {
339             if (run == 0) {
340                 ichan->impulse();
341                 finterface.setButtons(true);
342             }
343             if (run >= 1) {
344                 ichan->zero();
345                 finterface.setButtons(false);
346             }
347             int nFrames = min(kFrames, nbsamples);
348 
349             if (random) {
350                 int randval = rand();
351                 int n1 = randval % nFrames;
352                 int n2 = nFrames - n1;
353                 DSP->compute(n1, ichan->buffers(), ochan->buffers());
354                 DSP->compute(n2, ichan->buffers(n1), ochan->buffers(n1));
355             } else {
356                 DSP->compute(nFrames, ichan->buffers(), ochan->buffers());
357             }
358 
359             run++;
360             // Print samples
361             for (i = 0; i < nFrames; i++) {
362                 printf("%6d : ", linenum++);
363                 for (int c = 0; c < nouts; c++) {
364                     FAUSTFLOAT f = normalize(ochan->buffers()[c][i]);
365                     printf(" %8.6f", f);
366                 }
367                 printf("\n");
368             }
369             nbsamples -= nFrames;
370         }
catch(...)371     } catch (...) {
372         cerr << "ERROR in '" << file << "' at line : " << i << std::endl;
373     }
374 
375     delete ichan;
376     if (ochan != ichan) delete ochan;
377     delete DSP;
378 }
379 
380 // To be used in dynamic context (LLVM or interp backends)
381 static void runDSP1(dsp_factory* factory, const string& file, int& linenum, int nbsamples, bool is_mem_alloc = false, bool inpl = false, bool random = false)
382 {
383     malloc_memory_manager manager;
384     factory->setMemoryManager((is_mem_alloc) ? &manager : nullptr);
385     runDSP(factory->createDSPInstance(), file, linenum, nbsamples, inpl, random);
386 }
387