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