Mercurial > hg > beaglert
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) { |