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 }