annotate scripts/hvresources/render.cpp @ 541:c301cc07ae11 prerelease

updated heavy to new API
author Giulio Moro <giuliomoro@yahoo.it>
date Fri, 24 Jun 2016 02:29:05 +0100
parents 85ba865d3845
children
rev   line source
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 }