Mercurial > hg > beaglert
comparison scripts/hvresources/render.cpp @ 510:85ba865d3845 prerelease
moved hvresources/heavy_render.cpp to hvresources/render.cpp
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Wed, 22 Jun 2016 03:28:26 +0100 |
parents | scripts/hvresources/heavy_render.cpp@a85851bb2e10 |
children | c301cc07ae11 |
comparison
equal
deleted
inserted
replaced
509:3bb63b848960 | 510:85ba865d3845 |
---|---|
1 /* | |
2 * render.cpp | |
3 * | |
4 * Template render.cpp file for on-board heavy compiling | |
5 * | |
6 * N.B. this is currently *not* compatible with foleyDesigner source files! | |
7 * | |
8 * Created on: November 5, 2015 | |
9 * | |
10 * Christian Heinrichs | |
11 * | |
12 */ | |
13 | |
14 #include <Bela.h> | |
15 #include <Midi.h> | |
16 #include <Scope.h> | |
17 #include <cmath> | |
18 #include <Heavy_bela.h> | |
19 #include <string.h> | |
20 #include <stdlib.h> | |
21 #include <string.h> | |
22 #include <DigitalChannelManager.h> | |
23 | |
24 /* | |
25 * HEAVY CONTEXT & BUFFERS | |
26 */ | |
27 | |
28 Hv_bela *gHeavyContext; | |
29 float *gHvInputBuffers = NULL, *gHvOutputBuffers = NULL; | |
30 unsigned int gHvInputChannels = 0, gHvOutputChannels = 0; | |
31 | |
32 float gInverseSampleRate; | |
33 | |
34 /* | |
35 * HEAVY FUNCTIONS | |
36 */ | |
37 | |
38 // TODO: rename this | |
39 #define LIBPD_DIGITAL_OFFSET 11 // digitals are preceded by 2 audio and 8 analogs (even if using a different number of analogs) | |
40 | |
41 void printHook(double timestampSecs, const char *printLabel, const char *msgString, void *userData) { | |
42 rt_printf("Message from Heavy patch: [@ %.3f] %s: %s\n", timestampSecs, printLabel, msgString); | |
43 } | |
44 | |
45 | |
46 // digitals | |
47 static DigitalChannelManager dcm; | |
48 | |
49 void sendDigitalMessage(bool state, unsigned int delay, void* receiverName){ | |
50 hv_sendFloatToReceiver(gHeavyContext, hv_stringToHash((char*)receiverName), (float)state); | |
51 // rt_printf("%s: %d\n", (char*)receiverName, state); | |
52 } | |
53 | |
54 // TODO: turn them into hv hashes and adjust sendDigitalMessage accordingly | |
55 char hvDigitalInHashes[16][21]={ | |
56 {"bela_digitalIn11"},{"bela_digitalIn12"},{"bela_digitalIn13"},{"bela_digitalIn14"},{"bela_digitalIn15"}, | |
57 {"bela_digitalIn16"},{"bela_digitalIn17"},{"bela_digitalIn18"},{"bela_digitalIn19"},{"bela_digitalIn20"}, | |
58 {"bela_digitalIn21"},{"bela_digitalIn22"},{"bela_digitalIn23"},{"bela_digitalIn24"},{"bela_digitalIn25"}, | |
59 {"bela_digitalIn26"} | |
60 }; | |
61 | |
62 static void sendHook( | |
63 double timestamp, // in milliseconds | |
64 const char *receiverName, | |
65 const HvMessage *const m, | |
66 void *userData) { | |
67 | |
68 // Bela digital | |
69 | |
70 // Bela digital run-time messages | |
71 | |
72 // TODO: this first block is almost an exact copy of libpd's code, should we add this to the class? | |
73 // let's make this as optimized as possible for built-in digital Out parsing | |
74 // the built-in digital receivers are of the form "bela_digitalOutXX" where XX is between 11 and 26 | |
75 static int prefixLength = 15; // strlen("bela_digitalOut") | |
76 if(strncmp(receiverName, "bela_digitalOut", prefixLength)==0){ | |
77 if(receiverName[prefixLength] != 0){ //the two ifs are used instead of if(strlen(source) >= prefixLength+2) | |
78 if(receiverName[prefixLength + 1] != 0){ | |
79 // quickly convert the suffix to integer, assuming they are numbers, avoiding to call atoi | |
80 int receiver = ((receiverName[prefixLength] - 48) * 10); | |
81 receiver += (receiverName[prefixLength+1] - 48); | |
82 unsigned int channel = receiver - LIBPD_DIGITAL_OFFSET; // go back to the actual Bela digital channel number | |
83 bool value = hv_msg_getFloat(m, 0); | |
84 if(channel < 16){ //16 is the hardcoded value for the number of digital channels | |
85 dcm.setValue(channel, value); | |
86 } | |
87 } | |
88 } | |
89 } | |
90 | |
91 // Bela digital initialization messages | |
92 if(strcmp(receiverName, "bela_setDigital") == 0){ | |
93 // Third argument (optional) can be ~ or sig for signal-rate, message-rate otherwise. | |
94 // [in 14 ~( | |
95 // | | |
96 // [s bela_setDigital] | |
97 // is signal("sig" or "~") or message("message", default) rate | |
98 bool isMessageRate = true; // defaults to message rate | |
99 bool direction = 0; // initialize it just to avoid the compiler's warning | |
100 bool disable = false; | |
101 int numArgs = hv_msg_getNumElements(m); | |
102 if(numArgs < 2 || numArgs > 3 || !hv_msg_isSymbol(m, 0) || !hv_msg_isFloat(m, 1)) | |
103 return; | |
104 if(numArgs == 3 && !hv_msg_isSymbol(m,2)) | |
105 return; | |
106 char * symbol = hv_msg_getSymbol(m, 0); | |
107 | |
108 if(strcmp(symbol, "in") == 0){ | |
109 direction = INPUT; | |
110 } else if(strcmp(symbol, "out") == 0){ | |
111 direction = OUTPUT; | |
112 } else if(strcmp(symbol, "disable") == 0){ | |
113 disable = true; | |
114 } else { | |
115 return; | |
116 } | |
117 int channel = hv_msg_getFloat(m, 1) - LIBPD_DIGITAL_OFFSET; | |
118 if(disable == true){ | |
119 dcm.unmanage(channel); | |
120 return; | |
121 } | |
122 if(numArgs >= 3){ | |
123 char* s = hv_msg_getSymbol(m, 2); | |
124 if(strcmp(s, "~") == 0 || strncmp(s, "sig", 3) == 0){ | |
125 isMessageRate = false; | |
126 } | |
127 } | |
128 dcm.manage(channel, direction, isMessageRate); | |
129 } | |
130 } | |
131 | |
132 | |
133 /* | |
134 * SETUP, RENDER LOOP & CLEANUP | |
135 */ | |
136 | |
137 // leaving this here, trying to come up with a coherent interface with libpd. | |
138 // commenting them out so the compiler does not warn | |
139 // 2 audio + (up to)8 analog + (up to) 16 digital + 4 scope outputs | |
140 //static const unsigned int gChannelsInUse = 30; | |
141 //static unsigned int gAnalogChannelsInUse = 8; // hard-coded for the moment, TODO: get it at run-time from hv_context | |
142 //static const unsigned int gFirstAudioChannel = 0; | |
143 //static const unsigned int gFirstAnalogChannel = 2; | |
144 static const unsigned int gFirstDigitalChannel = 10; | |
145 static const unsigned int gFirstScopeChannel = 26; | |
146 static unsigned int gDigitalSigInChannelsInUse; | |
147 static unsigned int gDigitalSigOutChannelsInUse; | |
148 | |
149 // Bela Midi | |
150 Midi midi; | |
151 unsigned int hvMidiHashes[7]; | |
152 // Bela Scope | |
153 Scope scope; | |
154 unsigned int gScopeChannelsInUse; | |
155 float* gScopeOut; | |
156 | |
157 | |
158 bool setup(BelaContext *context, void *userData) { | |
159 /* HEAVY */ | |
160 hvMidiHashes[kmmNoteOn] = hv_stringToHash("__hv_notein"); | |
161 // hvMidiHashes[kmmNoteOff] = hv_stringToHash("noteoff"); // this is handled differently, see the render function | |
162 hvMidiHashes[kmmControlChange] = hv_stringToHash("__hv_ctlin"); | |
163 // Note that the ones below are not defined by Heavy, but they are here for (wishing) forward-compatibility | |
164 // You need to receive from the corresponding symbol in Pd and unpack the message, e.g.: | |
165 //[r __hv_pgmin] | |
166 //| | |
167 //[unpack f f] | |
168 //| | | |
169 //| [print pgmin_channel] | |
170 //[print pgmin_number] | |
171 hvMidiHashes[kmmProgramChange] = hv_stringToHash("__hv_pgmin"); | |
172 hvMidiHashes[kmmPolyphonicKeyPressure] = hv_stringToHash("__hv_polytouchin"); | |
173 hvMidiHashes[kmmChannelPressure] = hv_stringToHash("__hv_touchin"); | |
174 hvMidiHashes[kmmPitchBend] = hv_stringToHash("__hv_bendin"); | |
175 | |
176 gHeavyContext = hv_bela_new(context->audioSampleRate); | |
177 | |
178 gHvInputChannels = hv_getNumInputChannels(gHeavyContext); | |
179 gHvOutputChannels = hv_getNumOutputChannels(gHeavyContext); | |
180 | |
181 gScopeChannelsInUse = gHvOutputChannels > gFirstScopeChannel ? | |
182 gHvOutputChannels - gFirstScopeChannel : 0; | |
183 gDigitalSigInChannelsInUse = gHvInputChannels > gFirstDigitalChannel ? | |
184 gHvInputChannels - gFirstDigitalChannel : 0; | |
185 gDigitalSigOutChannelsInUse = gHvOutputChannels > gFirstDigitalChannel ? | |
186 gHvOutputChannels - gFirstDigitalChannel - gScopeChannelsInUse: 0; | |
187 | |
188 printf("Starting Heavy context with %d input channels and %d output channels\n", | |
189 gHvInputChannels, gHvOutputChannels); | |
190 printf("Channels in use:\n"); | |
191 printf("Digital in : %u, Digital out: %u\n", gDigitalSigInChannelsInUse, gDigitalSigOutChannelsInUse); | |
192 printf("Scope out: %u\n", gScopeChannelsInUse); | |
193 | |
194 if(gHvInputChannels != 0) { | |
195 gHvInputBuffers = (float *)calloc(gHvInputChannels * context->audioFrames,sizeof(float)); | |
196 } | |
197 if(gHvOutputChannels != 0) { | |
198 gHvOutputBuffers = (float *)calloc(gHvOutputChannels * context->audioFrames,sizeof(float)); | |
199 } | |
200 | |
201 gInverseSampleRate = 1.0 / context->audioSampleRate; | |
202 | |
203 // Set heavy print hook | |
204 hv_setPrintHook(gHeavyContext, printHook); | |
205 // Set heavy send hook | |
206 hv_setSendHook(gHeavyContext, sendHook); | |
207 | |
208 // TODO: change these hardcoded port values and actually change them in the Midi class | |
209 midi.readFrom(0); | |
210 midi.writeTo(0); | |
211 midi.enableParser(true); | |
212 | |
213 if(gScopeChannelsInUse > 0){ | |
214 // block below copy/pasted from libpd, except | |
215 scope.setup(gScopeChannelsInUse, context->audioSampleRate); | |
216 gScopeOut = new float[gScopeChannelsInUse]; | |
217 } | |
218 // Bela digital | |
219 dcm.setCallback(sendDigitalMessage); | |
220 if(context->digitalChannels > 0){ | |
221 for(unsigned int ch = 0; ch < context->digitalChannels; ++ch){ | |
222 dcm.setCallbackArgument(ch, hvDigitalInHashes[ch]); | |
223 } | |
224 } | |
225 // unlike libpd, no need here to bind the bela_digitalOut.. receivers | |
226 | |
227 return true; | |
228 } | |
229 | |
230 | |
231 void render(BelaContext *context, void *userData) | |
232 { | |
233 { | |
234 int num; | |
235 while((num = midi.getParser()->numAvailableMessages()) > 0){ | |
236 static MidiChannelMessage message; | |
237 message = midi.getParser()->getNextChannelMessage(); | |
238 switch(message.getType()){ | |
239 case kmmNoteOn: { | |
240 //message.prettyPrint(); | |
241 int noteNumber = message.getDataByte(0); | |
242 int velocity = message.getDataByte(1); | |
243 int channel = message.getChannel(); | |
244 // rt_printf("message: noteNumber: %f, velocity: %f, channel: %f\n", noteNumber, velocity, channel); | |
245 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff", | |
246 (float)noteNumber, (float)velocity, (float)channel+1); | |
247 break; | |
248 } | |
249 case kmmNoteOff: { | |
250 /* PureData does not seem to handle noteoff messages as per the MIDI specs, | |
251 * so that the noteoff velocity is ignored. Here we convert them to noteon | |
252 * with a velocity of 0. | |
253 */ | |
254 int noteNumber = message.getDataByte(0); | |
255 // int velocity = message.getDataByte(1); // would be ignored by Pd | |
256 int channel = message.getChannel(); | |
257 // note we are sending the below to hvHashes[kmmNoteOn] !! | |
258 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff", | |
259 (float)noteNumber, (float)0, (float)channel+1); | |
260 break; | |
261 } | |
262 case kmmControlChange: { | |
263 int channel = message.getChannel(); | |
264 int controller = message.getDataByte(0); | |
265 int value = message.getDataByte(1); | |
266 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmControlChange], 0, "fff", | |
267 (float)value, (float)controller, (float)channel+1); | |
268 break; | |
269 } | |
270 case kmmProgramChange: { | |
271 int channel = message.getChannel(); | |
272 int program = message.getDataByte(0); | |
273 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmProgramChange], 0, "ff", | |
274 (float)program, (float)channel+1); | |
275 break; | |
276 } | |
277 case kmmPolyphonicKeyPressure: { | |
278 //TODO: untested, I do not have anything with polyTouch... who does, anyhow? | |
279 int channel = message.getChannel(); | |
280 int pitch = message.getDataByte(0); | |
281 int value = message.getDataByte(1); | |
282 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmPolyphonicKeyPressure], 0, "fff", | |
283 (float)channel+1, (float)pitch, (float)value); | |
284 break; | |
285 } | |
286 case kmmChannelPressure: | |
287 { | |
288 int channel = message.getChannel(); | |
289 int value = message.getDataByte(0); | |
290 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmChannelPressure], 0, "ff", | |
291 (float)value, (float)channel+1); | |
292 break; | |
293 } | |
294 case kmmPitchBend: | |
295 { | |
296 int channel = message.getChannel(); | |
297 int value = ((message.getDataByte(1) << 7) | message.getDataByte(0)); | |
298 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmPitchBend], 0, "ff", | |
299 (float)value, (float)channel+1); | |
300 break; | |
301 } | |
302 case kmmNone: | |
303 case kmmAny: | |
304 break; | |
305 } | |
306 } | |
307 } | |
308 | |
309 // De-interleave the data | |
310 if(gHvInputBuffers != NULL) { | |
311 for(unsigned int n = 0; n < context->audioFrames; n++) { | |
312 for(unsigned int ch = 0; ch < gHvInputChannels; ch++) { | |
313 if(ch >= context->audioChannels+context->analogChannels) { | |
314 // THESE ARE PARAMETER INPUT 'CHANNELS' USED FOR ROUTING | |
315 // 'sensor' outputs from routing channels of dac~ are passed through here | |
316 break; | |
317 } else { | |
318 // If more than 2 ADC inputs are used in the pd patch, route the analog inputs | |
319 // i.e. ADC3->analogIn0 etc. (first two are always audio inputs) | |
320 if(ch >= context->audioChannels) { | |
321 int m = n/2; | |
322 float mIn = context->analogIn[m*context->analogChannels + (ch-context->audioChannels)]; | |
323 gHvInputBuffers[ch * context->audioFrames + n] = mIn; | |
324 } else { | |
325 gHvInputBuffers[ch * context->audioFrames + n] = context->audioIn[n * context->audioChannels + ch]; | |
326 } | |
327 } | |
328 } | |
329 } | |
330 } | |
331 | |
332 // Bela digital in | |
333 // note: in multiple places below we assume that the number of digital frames is same as number of audio | |
334 // Bela digital in at message-rate | |
335 dcm.processInput(context->digital, context->digitalFrames); | |
336 | |
337 // Bela digital in at signal-rate | |
338 if(gDigitalSigInChannelsInUse > 0) | |
339 { | |
340 unsigned int j, k; | |
341 float *p0, *p1; | |
342 const unsigned int gLibpdBlockSize = context->audioFrames; | |
343 const unsigned int audioFrameBase = 0; | |
344 float* gInBuf = gHvInputBuffers; | |
345 // block below copy/pasted from libpd, except | |
346 // 16 has been replaced with gDigitalSigInChannelsInUse | |
347 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | |
348 unsigned int digitalFrame = audioFrameBase + j; | |
349 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstDigitalChannel; | |
350 k < gDigitalSigInChannelsInUse; ++k, p1 += gLibpdBlockSize) { | |
351 if(dcm.isSignalRate(k) && dcm.isInput(k)){ // only process input channels that are handled at signal rate | |
352 *p1 = digitalRead(context, digitalFrame, k); | |
353 } | |
354 } | |
355 } | |
356 } | |
357 | |
358 | |
359 // replacement for bang~ object | |
360 //hv_vscheduleMessageForReceiver(gHeavyContext, "bela_bang", 0.0f, "b"); | |
361 | |
362 hv_bela_process_inline(gHeavyContext, gHvInputBuffers, gHvOutputBuffers, context->audioFrames); | |
363 | |
364 // Bela digital out | |
365 // Bela digital out at signal-rate | |
366 if(gDigitalSigOutChannelsInUse > 0) | |
367 { | |
368 unsigned int j, k; | |
369 float *p0, *p1; | |
370 const unsigned int gLibpdBlockSize = context->audioFrames; | |
371 const unsigned int audioFrameBase = 0; | |
372 float* gOutBuf = gHvOutputBuffers; | |
373 // block below copy/pasted from libpd, except | |
374 // context->digitalChannels has been replaced with gDigitalSigOutChannelsInUse | |
375 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) { | |
376 unsigned int digitalFrame = (audioFrameBase + j); | |
377 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstDigitalChannel; | |
378 k < gDigitalSigOutChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
379 if(dcm.isSignalRate(k) && dcm.isOutput(k)){ // only process output channels that are handled at signal rate | |
380 digitalWriteOnce(context, digitalFrame, k, *p1 > 0.5); | |
381 } | |
382 } | |
383 } | |
384 } | |
385 // Bela digital out at message-rate | |
386 dcm.processOutput(context->digital, context->digitalFrames); | |
387 | |
388 // Bela scope | |
389 if(gScopeChannelsInUse > 0) | |
390 { | |
391 unsigned int j, k; | |
392 float *p0, *p1; | |
393 const unsigned int gLibpdBlockSize = context->audioFrames; | |
394 float* gOutBuf = gHvOutputBuffers; | |
395 | |
396 // block below copy/pasted from libpd | |
397 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) { | |
398 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstScopeChannel; k < gScopeChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
399 gScopeOut[k] = *p1; | |
400 } | |
401 scope.log(gScopeOut); | |
402 } | |
403 } | |
404 | |
405 // Interleave the output data | |
406 if(gHvOutputBuffers != NULL) { | |
407 for(unsigned int n = 0; n < context->audioFrames; n++) { | |
408 | |
409 for(unsigned int ch = 0; ch < gHvOutputChannels; ch++) { | |
410 if(ch >= context->audioChannels+context->analogChannels) { | |
411 // THESE ARE SENSOR OUTPUT 'CHANNELS' USED FOR ROUTING | |
412 // they are the content of the 'sensor output' dac~ channels | |
413 } else { | |
414 if(ch >= context->audioChannels) { | |
415 int m = n/2; | |
416 context->analogOut[m * context->analogFrames + (ch-context->audioChannels)] = constrain(gHvOutputBuffers[ch*context->audioFrames + n],0.0,1.0); | |
417 } else { | |
418 context->audioOut[n * context->audioChannels + ch] = gHvOutputBuffers[ch * context->audioFrames + n]; | |
419 } | |
420 } | |
421 } | |
422 } | |
423 } | |
424 | |
425 } | |
426 | |
427 | |
428 void cleanup(BelaContext *context, void *userData) | |
429 { | |
430 | |
431 hv_bela_free(gHeavyContext); | |
432 if(gHvInputBuffers != NULL) | |
433 free(gHvInputBuffers); | |
434 if(gHvOutputBuffers != NULL) | |
435 free(gHvOutputBuffers); | |
436 delete[] gScopeOut; | |
437 } |