comparison scripts/hvresources/heavy_render.cpp @ 480:4ff80956c27a prerelease

heavy supports digitals at message rate
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 21 Jun 2016 05:44:21 +0100
parents 6462d0cc8906
children 4d5edf7ee953
comparison
equal deleted inserted replaced
479:bfc60429cca6 480:4ff80956c27a
16 #include <cmath> 16 #include <cmath>
17 #include "Heavy_bbb.h" 17 #include "Heavy_bbb.h"
18 #include <string.h> 18 #include <string.h>
19 #include <stdlib.h> 19 #include <stdlib.h>
20 #include <string.h> 20 #include <string.h>
21 #include <DigitalChannelManager.h>
22
21 /* 23 /*
22 * HEAVY CONTEXT & BUFFERS 24 * HEAVY CONTEXT & BUFFERS
23 */ 25 */
24 26
25 Hv_bbb *gHeavyContext; 27 Hv_bbb *gHeavyContext;
26 float *gHvInputBuffers = NULL, *gHvOutputBuffers = NULL; 28 float *gHvInputBuffers = NULL, *gHvOutputBuffers = NULL;
27 int gHvInputChannels = 0, gHvOutputChannels = 0; 29 unsigned int gHvInputChannels = 0, gHvOutputChannels = 0;
28 30
29 float gInverseSampleRate; 31 float gInverseSampleRate;
30 32
31 /* 33 /*
32 * HEAVY FUNCTIONS 34 * HEAVY FUNCTIONS
33 */ 35 */
34 36
37 // TODO: rename this
38 #define LIBPD_DIGITAL_OFFSET 11 // digitals are preceded by 2 audio and 8 analogs (even if using a different number of analogs)
39
35 void printHook(double timestampSecs, const char *printLabel, const char *msgString, void *userData) { 40 void printHook(double timestampSecs, const char *printLabel, const char *msgString, void *userData) {
36 printf("Message from Heavy patch: [@ %.3f] %s: %s\n", timestampSecs, printLabel, msgString); 41 rt_printf("Message from Heavy patch: [@ %.3f] %s: %s\n", timestampSecs, printLabel, msgString);
37 } 42 }
43
44
45 // digitals
46 static DigitalChannelManager dcm;
47
48 void sendDigitalMessage(bool state, unsigned int delay, void* receiverName){
49 hv_sendFloatToReceiver(gHeavyContext, hv_stringToHash((char*)receiverName), (float)state);
50 // rt_printf("%s: %d\n", (char*)receiverName, state);
51 }
52
53 // TODO: turn them into hv hashes and adjust sendDigitalMessage accordingly
54 char hvDigitalInHashes[16][21]={
55 {"bela_digitalIn11"},{"bela_digitalIn12"},{"bela_digitalIn13"},{"bela_digitalIn14"},{"bela_digitalIn15"},
56 {"bela_digitalIn16"},{"bela_digitalIn17"},{"bela_digitalIn18"},{"bela_digitalIn19"},{"bela_digitalIn20"},
57 {"bela_digitalIn21"},{"bela_digitalIn22"},{"bela_digitalIn23"},{"bela_digitalIn24"},{"bela_digitalIn25"},
58 {"bela_digitalIn26"}
59 };
38 60
39 static void sendHook( 61 static void sendHook(
40 double timestamp, // in milliseconds 62 double timestamp, // in milliseconds
41 const char *receiverName, 63 const char *receiverName,
42 const HvMessage *const m, 64 const HvMessage *const m,
43 void *userData) { 65 void *userData) {
44 66
45 // only react to messages sent to receivers named "hello" 67 // Bela digital
46 if (!strncmp(receiverName, "hello", 5)) { 68
47 } 69 // Bela digital run-time messages
48 70
49 } 71 // TODO: this first block is almost an exact copy of libpd's code, should we add this to the class?
72 // let's make this as optimized as possible for built-in digital Out parsing
73 // the built-in digital receivers are of the form "bela_digitalOutXX" where XX is between 11 and 26
74 static int prefixLength = 15; // strlen("bela_digitalOut")
75 if(strncmp(receiverName, "bela_digitalOut", prefixLength)==0){
76 if(receiverName[prefixLength] != 0){ //the two ifs are used instead of if(strlen(source) >= prefixLength+2)
77 if(receiverName[prefixLength + 1] != 0){
78 // quickly convert the suffix to integer, assuming they are numbers, avoiding to call atoi
79 int receiver = ((receiverName[prefixLength] - 48) * 10);
80 receiver += (receiverName[prefixLength+1] - 48);
81 unsigned int channel = receiver - LIBPD_DIGITAL_OFFSET; // go back to the actual Bela digital channel number
82 bool value = hv_msg_getFloat(m, 0);
83 if(channel < 16){ //16 is the hardcoded value for the number of digital channels
84 dcm.setValue(channel, value);
85 }
86 }
87 }
88 }
89
90 // Bela digital initialization messages
91 if(strcmp(receiverName, "bela_setDigital") == 0){
92 // Third argument (optional) can be ~ or sig for signal-rate, message-rate otherwise.
93 // [in 14 ~(
94 // |
95 // [s bela_setDigital]
96 // is signal("sig" or "~") or message("message", default) rate
97 bool isMessageRate = true; // defaults to message rate
98 bool direction = 0; // initialize it just to avoid the compiler's warning
99 bool disable = false;
100 int numArgs = hv_msg_getNumElements(m);
101 if(numArgs < 2 || numArgs > 3 || !hv_msg_isSymbol(m, 0) || !hv_msg_isFloat(m, 1))
102 return;
103 if(numArgs == 3 && !hv_msg_isSymbol(m,2))
104 return;
105 char * symbol = hv_msg_getSymbol(m, 0);
106
107 if(strcmp(symbol, "in") == 0){
108 direction = INPUT;
109 } else if(strcmp(symbol, "out") == 0){
110 direction = OUTPUT;
111 } else if(strcmp(symbol, "disable") == 0){
112 disable = true;
113 } else {
114 return;
115 }
116 int channel = hv_msg_getFloat(m, 1) - LIBPD_DIGITAL_OFFSET;
117 if(disable == true){
118 dcm.unmanage(channel);
119 return;
120 }
121 if(numArgs >= 3){
122 char* s = hv_msg_getSymbol(m, 2);
123 if(strcmp(s, "~") == 0 || strncmp(s, "sig", 3) == 0){
124 isMessageRate = false;
125 }
126 }
127 dcm.manage(channel, direction, isMessageRate);
128 }
129 }
130
50 131
51 /* 132 /*
52 * SETUP, RENDER LOOP & CLEANUP 133 * SETUP, RENDER LOOP & CLEANUP
53 */ 134 */
54 135
136
137
138 // Midi
55 Midi midi; 139 Midi midi;
140 unsigned int hvMidiHashes[7];
141
142
56 bool setup(BelaContext *context, void *userData) { 143 bool setup(BelaContext *context, void *userData) {
57 144
145 printf("top o setup\n");
58 /* HEAVY */ 146 /* HEAVY */
59 147 hvMidiHashes[kmmNoteOn] = hv_stringToHash("__hv_notein");
148 hvMidiHashes[kmmNoteOff] = hv_stringToHash("noteoff"); // this is handled differently, see the render function
149 hvMidiHashes[kmmControlChange] = hv_stringToHash("__hv_ctlin");
150 hvMidiHashes[kmmProgramChange] = hv_stringToHash("pgmin");
151 hvMidiHashes[kmmPolyphonicKeyPressure] = hv_stringToHash("polytouchin");
152 hvMidiHashes[kmmChannelPressure] = hv_stringToHash("touchin");
153 hvMidiHashes[kmmPitchBend] = hv_stringToHash("bendin");
154
155 printf("after midi o setup\n");
60 gHeavyContext = hv_bbb_new(context->audioSampleRate); 156 gHeavyContext = hv_bbb_new(context->audioSampleRate);
61 157
158 printf("aftet new o setup\n");
62 gHvInputChannels = hv_getNumInputChannels(gHeavyContext); 159 gHvInputChannels = hv_getNumInputChannels(gHeavyContext);
63 gHvOutputChannels = hv_getNumOutputChannels(gHeavyContext); 160 gHvOutputChannels = hv_getNumOutputChannels(gHeavyContext);
64 161
65 rt_printf("Starting Heavy context with %d input channels and %d output channels\n", 162 rt_printf("Starting Heavy context with %d input channels and %d output channels\n",
66 gHvInputChannels, gHvOutputChannels); 163 gHvInputChannels, gHvOutputChannels);
69 gHvInputBuffers = (float *)calloc(gHvInputChannels * context->audioFrames,sizeof(float)); 166 gHvInputBuffers = (float *)calloc(gHvInputChannels * context->audioFrames,sizeof(float));
70 } 167 }
71 if(gHvOutputChannels != 0) { 168 if(gHvOutputChannels != 0) {
72 gHvOutputBuffers = (float *)calloc(gHvOutputChannels * context->audioFrames,sizeof(float)); 169 gHvOutputBuffers = (float *)calloc(gHvOutputChannels * context->audioFrames,sizeof(float));
73 } 170 }
171 printf("mid o setup\n");
74 172
75 gInverseSampleRate = 1.0 / context->audioSampleRate; 173 gInverseSampleRate = 1.0 / context->audioSampleRate;
76 174
77 // Set heavy print hook 175 // Set heavy print hook
78 hv_setPrintHook(gHeavyContext, &printHook); 176 hv_setPrintHook(gHeavyContext, printHook);
79 // Set heavy send hook 177 // Set heavy send hook
80 hv_setSendHook(gHeavyContext, sendHook); 178 hv_setSendHook(gHeavyContext, sendHook);
81 179
180 // TODO: change these hardcoded port values and actually change them in the Midi class
82 midi.readFrom(0); 181 midi.readFrom(0);
83 midi.writeTo(0); 182 midi.writeTo(0);
84 midi.enableParser(true); 183 midi.enableParser(true);
184
185 // Bela digital
186 dcm.setCallback(sendDigitalMessage);
187 if(context->digitalChannels > 0){
188 for(unsigned int ch = 0; ch < context->digitalChannels; ++ch){
189 dcm.setCallbackArgument(ch, hvDigitalInHashes[ch]);
190 }
191 }
192 // unlike libpd, no need here to bind the bela_digitalOut.. receivers
193
194 printf("end o setup\n");
85 return true; 195 return true;
86 } 196 }
87 197
88 198
89 void render(BelaContext *context, void *userData) 199 void render(BelaContext *context, void *userData)
90 { 200 {
201 {
202 int num;
203 while((num = midi.getParser()->numAvailableMessages()) > 0){
204 static MidiChannelMessage message;
205 message = midi.getParser()->getNextChannelMessage();
206 switch(message.getType()){
207 case kmmNoteOn: {
208 // message.prettyPrint();
209 float noteNumber = message.getDataByte(0);
210 float velocity = message.getDataByte(1);
211 float channel = message.getChannel();
212 // rt_printf("message: noteNumber: %f, velocity: %f, channel: %f\n", noteNumber, velocity, channel);
213 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff", noteNumber, velocity, channel);
214 break;
215 }
216 case kmmNoteOff:
217 {
218 /* PureData does not seem to handle noteoff messages as per the MIDI specs,
219 * so that the noteoff velocity is ignored. Here we convert them to noteon
220 * with a velocity of 0.
221 */
222 float noteNumber = message.getDataByte(0);
223 // int velocity = message.getDataByte(1); // would be ignored by Pd
224 float channel = message.getChannel();
225 // note we are sending the below to hvHashes[kmmNoteOn] !!
226 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff", noteNumber, 0, channel);
227 break;
228 }
229 case kmmControlChange: {
230 int channel = message.getChannel();
231 int controller = message.getDataByte(0);
232 int value = message.getDataByte(1);
233 //TODO: maybe the order of the arguments is wrong here?
234 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmControlChange], 0, "fff",
235 value, controller, channel);
236 break;
237 }
238 case kmmProgramChange: {
239 int channel = message.getChannel();
240 int program = message.getDataByte(0);
241 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmProgramChange], 0, "ff",
242 program, channel);
243 //TODO: maybe the order of the arguments is wrong here?
244 break;
245 }
246 case kmmPolyphonicKeyPressure: {
247 int channel = message.getChannel();
248 int pitch = message.getDataByte(0);
249 int value = message.getDataByte(1);
250 //TODO: maybe the order of the arguments is wrong here?
251 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmPolyphonicKeyPressure], 0, "fff",
252 channel, pitch, value);
253 break;
254 }
255 case kmmChannelPressure:
256 {
257 int channel = message.getChannel();
258 int value = message.getDataByte(0);
259 //TODO: maybe the order of the arguments is wrong here?
260 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmChannelPressure], 0, "ff",
261 channel, value);
262 break;
263 }
264 case kmmPitchBend:
265 {
266 int channel = message.getChannel();
267 int value = ((message.getDataByte(1) << 7) | message.getDataByte(0)) + 8192;
268 //TODO: is the value range correct?
269 //TODO: maybe the order of the arguments is wrong here?
270 hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmPitchBend], 0, "ff",
271 channel, value);
272 break;
273 }
274 case kmmNone:
275 case kmmAny:
276 break;
277 }
278 }
279 }
91 280
92 // De-interleave the data 281 // De-interleave the data
93 if(gHvInputBuffers != NULL) { 282 if(gHvInputBuffers != NULL) {
94 for(int n = 0; n < context->audioFrames; n++) { 283 for(unsigned int n = 0; n < context->audioFrames; n++) {
95 for(int ch = 0; ch < gHvInputChannels; ch++) { 284 for(unsigned int ch = 0; ch < gHvInputChannels; ch++) {
96 if(ch >= context->audioChannels+context->analogChannels) { 285 if(ch >= context->audioChannels+context->analogChannels) {
97 // THESE ARE PARAMETER INPUT 'CHANNELS' USED FOR ROUTING 286 // THESE ARE PARAMETER INPUT 'CHANNELS' USED FOR ROUTING
98 // 'sensor' outputs from routing channels of dac~ are passed through here 287 // 'sensor' outputs from routing channels of dac~ are passed through here
99 break; 288 break;
100 } else { 289 } else {
110 } 299 }
111 } 300 }
112 } 301 }
113 } 302 }
114 303
304 // Bela digital in
305 // note: in multiple places below we assume that the number of digital frames is same as number of audio
306 // digital in at message-rate
307 dcm.processInput(context->digital, context->digitalFrames);
308
309 // Bela digital in at signal-rate
310 // TODO: not really straightforward to implement as Heavy determines the number of channels in use at compile time
311 // on the basis of the number of adc~ / dac~ of the patch ... Maybe we should always include
312 // a dummy [adc~ 27] [dac~ 27] to make sure all channels are always allocated and then leave them all unprocessed ?
313
115 // replacement for bang~ object 314 // replacement for bang~ object
116 //hv_vscheduleMessageForReceiver(gHeavyContext, "bbb_bang", 0.0f, "b"); 315 //hv_vscheduleMessageForReceiver(gHeavyContext, "bbb_bang", 0.0f, "b");
117 {
118 int num;
119 unsigned int hvHashes[3];
120 hvHashes[0] = hv_stringToHash("bela_notein");
121 hvHashes[1] = hv_stringToHash("bela_ctlin");
122 hvHashes[2] = hv_stringToHash("bela_pgmin");
123 while((num = midi.getParser()->numAvailableMessages()) > 0){
124 static MidiChannelMessage message;
125 message = midi.getParser()->getNextChannelMessage();
126 switch(message.getType()){
127 case kmmNoteOn: {
128 // message.prettyPrint();
129 float noteNumber = message.getDataByte(0);
130 float velocity = message.getDataByte(1);
131 float channel = message.getChannel();
132 // rt_printf("message: noteNumber: %f, velocity: %f, channel: %f\n", noteNumber, velocity, channel);
133 hv_vscheduleMessageForReceiver(gHeavyContext, hvHashes[0], 0, "fff", noteNumber, velocity, channel);
134 }
135 break;
136 case kmmControlChange: {
137 hv_vscheduleMessageForReceiver(gHeavyContext, hvHashes[1], 0, "fff",
138 (float)message.getDataByte(1), (float)message.getDataByte(0), (float)message.getChannel());
139 }
140 break;
141 case kmmProgramChange:
142 hv_vscheduleMessageForReceiver(gHeavyContext, hvHashes[2], 0, "ff",
143 (float)message.getDataByte(0), (float)message.getChannel());
144 break;
145 }
146 }
147 }
148 // hv_sendFloatToReceiver(gHeavyContext, "notein", 1.123f);
149
150 316
151 hv_bbb_process_inline(gHeavyContext, gHvInputBuffers, gHvOutputBuffers, context->audioFrames); 317 hv_bbb_process_inline(gHeavyContext, gHvInputBuffers, gHvOutputBuffers, context->audioFrames);
318
319 // Bela digital out
320 // digital out at signal-rate
321 // TODO: see note above.
322
323 // digital out at message-rate
324 dcm.processOutput(context->digital, context->digitalFrames);
152 325
153 // Interleave the output data 326 // Interleave the output data
154 if(gHvOutputBuffers != NULL) { 327 if(gHvOutputBuffers != NULL) {
155 for(int n = 0; n < context->audioFrames; n++) { 328 for(unsigned int n = 0; n < context->audioFrames; n++) {
156 329
157 for(int ch = 0; ch < gHvOutputChannels; ch++) { 330 for(unsigned int ch = 0; ch < gHvOutputChannels; ch++) {
158 if(ch >= context->audioChannels+context->analogChannels) { 331 if(ch >= context->audioChannels+context->analogChannels) {
159 // THESE ARE SENSOR OUTPUT 'CHANNELS' USED FOR ROUTING 332 // THESE ARE SENSOR OUTPUT 'CHANNELS' USED FOR ROUTING
160 // they are the content of the 'sensor output' dac~ channels 333 // they are the content of the 'sensor output' dac~ channels
161 } else { 334 } else {
162 if(ch >= context->audioChannels) { 335 if(ch >= context->audioChannels) {