annotate examples/08-PureData/customRender/heavy/render.cpp @ 555:5ef33a8c9702 prerelease

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