annotate examples/08-PureData/customRender/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 - LIBPD
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 <DigitalChannelManager.h>
chris@552 35 #include <cmath>
chris@552 36 #include <I2c_Codec.h>
chris@552 37 #include <PRU.h>
chris@552 38 #include <stdio.h>
chris@552 39 #include <libpd/z_libpd.h>
chris@552 40 #include <libpd/s_stuff.h>
chris@552 41 #include <UdpServer.h>
chris@552 42 #include <Midi.h>
chris@552 43 #include <Scope.h>
chris@552 44
chris@552 45 /*
chris@552 46 * MODIFICATION
chris@552 47 * ------------
chris@552 48 * Global variables for tremolo effect applied to libpd output
chris@552 49 */
chris@552 50
chris@552 51 float gTremoloRate = 4.0;
chris@552 52 float gPhase;
chris@552 53 float gInverseSampleRate;
chris@552 54
chris@552 55 /*********/
chris@552 56
chris@552 57 // if you are 100% sure of what value was used to compile libpd/puredata, then
chris@552 58 // you could #define gBufLength instead of getting it at runtime. It has proved to give some 0.3%
chris@552 59 // performance boost when it is 8 (thanks to vectorize optimizations I guess).
chris@552 60 int gBufLength;
chris@552 61
chris@552 62 float* gInBuf;
chris@552 63 float* gOutBuf;
chris@552 64
chris@552 65 void pdnoteon(int ch, int pitch, int vel) {
chris@552 66 printf("noteon: %d %d %d\n", ch, pitch, vel);
chris@552 67 }
chris@552 68
chris@552 69 void Bela_printHook(const char *recv){
chris@552 70 rt_printf("%s", recv);
chris@552 71 }
chris@552 72 #define PARSE_MIDI
chris@552 73 static Midi midi;
chris@552 74 static DigitalChannelManager dcm;
chris@552 75
chris@552 76 void sendDigitalMessage(bool state, unsigned int delay, void* receiverName){
chris@552 77 libpd_float((char*)receiverName, (float)state);
chris@552 78 // rt_printf("%s: %d\n", (char*)receiverName, state);
chris@552 79 }
chris@552 80
chris@552 81 #define LIBPD_DIGITAL_OFFSET 11 // digitals are preceded by 2 audio and 8 analogs (even if using a different number of analogs)
chris@552 82
chris@552 83 void Bela_messageHook(const char *source, const char *symbol, int argc, t_atom *argv){
chris@552 84 if(strcmp(source, "bela_setDigital") == 0){
chris@552 85 // symbol is the direction, argv[0] is the channel, argv[1] (optional)
chris@552 86 // is signal("sig" or "~") or message("message", default) rate
chris@552 87 bool isMessageRate = true; // defaults to message rate
chris@552 88 bool direction = 0; // initialize it just to avoid the compiler's warning
chris@552 89 bool disable = false;
chris@552 90 if(strcmp(symbol, "in") == 0){
chris@552 91 direction = INPUT;
chris@552 92 } else if(strcmp(symbol, "out") == 0){
chris@552 93 direction = OUTPUT;
chris@552 94 } else if(strcmp(symbol, "disable") == 0){
chris@552 95 disable = true;
chris@552 96 } else {
chris@552 97 return;
chris@552 98 }
chris@552 99 if(argc == 0){
chris@552 100 return;
chris@552 101 } else if (libpd_is_float(&argv[0]) == false){
chris@552 102 return;
chris@552 103 }
chris@552 104 int channel = libpd_get_float(&argv[0]) - LIBPD_DIGITAL_OFFSET;
chris@552 105 if(disable == true){
chris@552 106 dcm.unmanage(channel);
chris@552 107 return;
chris@552 108 }
chris@552 109 if(argc >= 2){
chris@552 110 t_atom* a = &argv[1];
chris@552 111 if(libpd_is_symbol(a)){
chris@552 112 char *s = libpd_get_symbol(a);
chris@552 113 if(strcmp(s, "~") == 0 || strncmp(s, "sig", 3) == 0){
chris@552 114 isMessageRate = false;
chris@552 115 }
chris@552 116 }
chris@552 117 }
chris@552 118 dcm.manage(channel, direction, isMessageRate);
chris@552 119 }
chris@552 120 }
chris@552 121
chris@552 122 void Bela_floatHook(const char *source, float value){
chris@552 123
chris@552 124 /*
chris@552 125 * MODIFICATION
chris@552 126 * ------------
chris@552 127 * Parse float sent to receiver 'tremoloRate' and assign it to a global variable
chris@552 128 * N.B. When using libpd receiver names need to be registered (see setup() function below)
chris@552 129 */
chris@552 130 if(strncmp(source, "tremoloRate", 11) == 0){
chris@552 131 gTremoloRate = value;
chris@552 132 }
chris@552 133
chris@552 134 /*********/
chris@552 135
chris@552 136 // let's make this as optimized as possible for built-in digital Out parsing
chris@552 137 // the built-in digital receivers are of the form "bela_digitalOutXX" where XX is between 11 and 26
chris@552 138 static int prefixLength = 15; // strlen("bela_digitalOut")
chris@552 139 if(strncmp(source, "bela_digitalOut", prefixLength)==0){
chris@552 140 if(source[prefixLength] != 0){ //the two ifs are used instead of if(strlen(source) >= prefixLength+2)
chris@552 141 if(source[prefixLength + 1] != 0){
chris@552 142 // quickly convert the suffix to integer, assuming they are numbers, avoiding to call atoi
chris@552 143 int receiver = ((source[prefixLength] - 48) * 10);
chris@552 144 receiver += (source[prefixLength+1] - 48);
chris@552 145 unsigned int channel = receiver - 11; // go back to the actual Bela digital channel number
chris@552 146 if(channel < 16){ //16 is the hardcoded value for the number of digital channels
chris@552 147 dcm.setValue(channel, value);
chris@552 148 }
chris@552 149 }
chris@552 150 }
chris@552 151 }
chris@552 152 }
chris@552 153
chris@552 154 char receiverNames[16][21]={
chris@552 155 {"bela_digitalIn11"},{"bela_digitalIn12"},{"bela_digitalIn13"},{"bela_digitalIn14"},{"bela_digitalIn15"},
chris@552 156 {"bela_digitalIn16"},{"bela_digitalIn17"},{"bela_digitalIn18"},{"bela_digitalIn19"},{"bela_digitalIn20"},
chris@552 157 {"bela_digitalIn21"},{"bela_digitalIn22"},{"bela_digitalIn23"},{"bela_digitalIn24"},{"bela_digitalIn25"},
chris@552 158 {"bela_digitalIn26"}
chris@552 159 };
chris@552 160
chris@552 161 static unsigned int gAnalogChannelsInUse;
chris@552 162 static unsigned int gLibpdBlockSize;
chris@552 163 // 2 audio + (up to)8 analog + (up to) 16 digital + 4 scope outputs
chris@552 164 static const unsigned int gChannelsInUse = 30;
chris@552 165 //static const unsigned int gFirstAudioChannel = 0;
chris@552 166 static const unsigned int gFirstAnalogChannel = 2;
chris@552 167 static const unsigned int gFirstDigitalChannel = 10;
chris@552 168 static const unsigned int gFirstScopeChannel = 26;
chris@552 169
chris@552 170 Scope scope;
chris@552 171 unsigned int gScopeChannelsInUse = 4;
chris@552 172 float* gScopeOut;
chris@552 173
chris@552 174 bool setup(BelaContext *context, void *userData)
chris@552 175 {
chris@552 176
chris@552 177 /*
chris@552 178 * MODIFICATION
chris@552 179 * ------------
chris@552 180 * Initialise variables for tremolo effect
chris@552 181 */
chris@552 182
chris@552 183 gInverseSampleRate = 1.0 / context->audioSampleRate;
chris@552 184 gPhase = 0.0;
chris@552 185
chris@552 186 /*********/
chris@552 187
chris@552 188 scope.setup(gScopeChannelsInUse, context->audioSampleRate);
chris@552 189 gScopeOut = new float[gScopeChannelsInUse];
chris@552 190
chris@552 191 // Check first of all if file exists. Will actually open it later.
chris@552 192 char file[] = "_main.pd";
chris@552 193 char folder[] = "./";
chris@552 194 unsigned int strSize = strlen(file) + strlen(folder) + 1;
chris@552 195 char* str = (char*)malloc(sizeof(char) * strSize);
chris@552 196 snprintf(str, strSize, "%s%s", folder, file);
chris@552 197 if(access(str, F_OK) == -1 ) {
chris@552 198 printf("Error file %s/%s not found. The %s file should be your main patch.\n", folder, file, file);
chris@552 199 return false;
chris@552 200 }
chris@552 201 if(context->analogInChannels != context->analogOutChannels ||
chris@552 202 context->audioInChannels != context->audioOutChannels){
chris@552 203 printf("This project requires the number of inputs and the number of outputs to be the same\n");
chris@552 204 return false;
chris@552 205 }
chris@552 206 // analog setup
chris@552 207 gAnalogChannelsInUse = context->analogInChannels;
chris@552 208
chris@552 209 // digital setup
chris@552 210 dcm.setCallback(sendDigitalMessage);
chris@552 211 if(context->digitalChannels > 0){
chris@552 212 for(unsigned int ch = 0; ch < context->digitalChannels; ++ch){
chris@552 213 dcm.setCallbackArgument(ch, receiverNames[ch]);
chris@552 214 }
chris@552 215 }
chris@552 216
chris@552 217 midi.readFrom(0);
chris@552 218 midi.writeTo(0);
chris@552 219 #ifdef PARSE_MIDI
chris@552 220 midi.enableParser(true);
chris@552 221 #else
chris@552 222 midi.enableParser(false);
chris@552 223 #endif /* PARSE_MIDI */
chris@552 224 // udpServer.bindToPort(1234);
chris@552 225
chris@552 226 gLibpdBlockSize = libpd_blocksize();
chris@552 227 // check that we are not running with a blocksize smaller than gLibPdBlockSize
chris@552 228 // We could still make it work, but the load would be executed unevenly between calls to render
chris@552 229 if(context->audioFrames < gLibpdBlockSize){
chris@552 230 fprintf(stderr, "Error: minimum block size must be %d\n", gLibpdBlockSize);
chris@552 231 return false;
chris@552 232 }
chris@552 233 // set hooks before calling libpd_init
chris@552 234 libpd_set_printhook(Bela_printHook);
chris@552 235 libpd_set_floathook(Bela_floatHook);
chris@552 236 libpd_set_messagehook(Bela_messageHook);
chris@552 237 libpd_set_noteonhook(pdnoteon);
chris@552 238 //TODO: add hooks for other midi events and generate MIDI output appropriately
chris@552 239 libpd_init();
chris@552 240 //TODO: ideally, we would analyse the ASCII of the patch file and find out which in/outs to use
chris@552 241 libpd_init_audio(gChannelsInUse, gChannelsInUse, context->audioSampleRate);
chris@552 242 gInBuf = libpd_get_sys_soundin();
chris@552 243 gOutBuf = libpd_get_sys_soundout();
chris@552 244
chris@552 245 libpd_start_message(1); // one entry in list
chris@552 246 libpd_add_float(1.0f);
chris@552 247 libpd_finish_message("pd", "dsp");
chris@552 248
chris@552 249 gBufLength = max(gLibpdBlockSize, context->audioFrames);
chris@552 250
chris@552 251
chris@552 252 // bind your receivers here
chris@552 253 libpd_bind("bela_digitalOut11");
chris@552 254 libpd_bind("bela_digitalOut12");
chris@552 255 libpd_bind("bela_digitalOut13");
chris@552 256 libpd_bind("bela_digitalOut14");
chris@552 257 libpd_bind("bela_digitalOut15");
chris@552 258 libpd_bind("bela_digitalOut16");
chris@552 259 libpd_bind("bela_digitalOut17");
chris@552 260 libpd_bind("bela_digitalOut18");
chris@552 261 libpd_bind("bela_digitalOut19");
chris@552 262 libpd_bind("bela_digitalOut20");
chris@552 263 libpd_bind("bela_digitalOut21");
chris@552 264 libpd_bind("bela_digitalOut22");
chris@552 265 libpd_bind("bela_digitalOut23");
chris@552 266 libpd_bind("bela_digitalOut24");
chris@552 267 libpd_bind("bela_digitalOut25");
chris@552 268 libpd_bind("bela_digitalOut26");
chris@552 269 libpd_bind("bela_setDigital");
chris@552 270 /*
chris@552 271 * MODIFICATION
chris@552 272 * ------------
chris@552 273 * Bind an additional receiver for the tremoloRate parameter
chris@552 274 */
chris@552 275 libpd_bind("tremoloRate");
chris@552 276 /*********/
chris@552 277
chris@552 278 // open patch [; pd open file folder(
chris@552 279 void* patch = libpd_openfile(file, folder);
chris@552 280 if(patch == NULL){
chris@552 281 printf("Error: file %s/%s is corrupted.\n", folder, file);
chris@552 282 return false;
chris@552 283 }
chris@552 284 return true;
chris@552 285 }
chris@552 286
chris@552 287 // render() is called regularly at the highest priority by the audio engine.
chris@552 288 // Input and output are given from the audio hardware and the other
chris@552 289 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
chris@552 290 // will be 0.
chris@552 291
chris@552 292 void render(BelaContext *context, void *userData)
chris@552 293 {
chris@552 294 int num;
chris@552 295 // the safest thread-safe option to handle MIDI input is to process the MIDI buffer
chris@552 296 // from the audio thread.
chris@552 297 #ifdef PARSE_MIDI
chris@552 298 while((num = midi.getParser()->numAvailableMessages()) > 0){
chris@552 299 static MidiChannelMessage message;
chris@552 300 message = midi.getParser()->getNextChannelMessage();
chris@552 301 //message.prettyPrint(); // use this to print beautified message (channel, data bytes)
chris@552 302 switch(message.getType()){
chris@552 303 case kmmNoteOn:
chris@552 304 {
chris@552 305 int noteNumber = message.getDataByte(0);
chris@552 306 int velocity = message.getDataByte(1);
chris@552 307 int channel = message.getChannel();
chris@552 308 libpd_noteon(channel, noteNumber, velocity);
chris@552 309 break;
chris@552 310 }
chris@552 311 case kmmNoteOff:
chris@552 312 {
chris@552 313 /* PureData does not seem to handle noteoff messages as per the MIDI specs,
chris@552 314 * so that the noteoff velocity is ignored. Here we convert them to noteon
chris@552 315 * with a velocity of 0.
chris@552 316 */
chris@552 317 int noteNumber = message.getDataByte(0);
chris@552 318 // int velocity = message.getDataByte(1); // would be ignored by Pd
chris@552 319 int channel = message.getChannel();
chris@552 320 libpd_noteon(channel, noteNumber, 0);
chris@552 321 break;
chris@552 322 }
chris@552 323 case kmmControlChange:
chris@552 324 {
chris@552 325 int channel = message.getChannel();
chris@552 326 int controller = message.getDataByte(0);
chris@552 327 int value = message.getDataByte(1);
chris@552 328 libpd_controlchange(channel, controller, value);
chris@552 329 break;
chris@552 330 }
chris@552 331 case kmmProgramChange:
chris@552 332 {
chris@552 333 int channel = message.getChannel();
chris@552 334 int program = message.getDataByte(0);
chris@552 335 libpd_programchange(channel, program);
chris@552 336 break;
chris@552 337 }
chris@552 338 case kmmPolyphonicKeyPressure:
chris@552 339 {
chris@552 340 int channel = message.getChannel();
chris@552 341 int pitch = message.getDataByte(0);
chris@552 342 int value = message.getDataByte(1);
chris@552 343 libpd_polyaftertouch(channel, pitch, 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 libpd_aftertouch(channel, value);
chris@552 351 break;
chris@552 352 }
chris@552 353 case kmmPitchBend:
chris@552 354 {
chris@552 355 int channel = message.getChannel();
chris@552 356 int value = ((message.getDataByte(1) << 7)| message.getDataByte(0)) - 8192;
chris@552 357 libpd_pitchbend(channel, value);
chris@552 358 break;
chris@552 359 }
chris@552 360 case kmmNone:
chris@552 361 case kmmAny:
chris@552 362 break;
chris@552 363 }
chris@552 364 }
chris@552 365 #else
chris@552 366 int input;
chris@552 367 while((input = midi.getInput()) >= 0){
chris@552 368 libpd_midibyte(0, input);
chris@552 369 }
chris@552 370 #endif /* PARSE_MIDI */
chris@552 371
chris@552 372 static unsigned int numberOfPdBlocksToProcess = gBufLength / gLibpdBlockSize;
chris@552 373
chris@552 374 for(unsigned int tick = 0; tick < numberOfPdBlocksToProcess; ++tick){
chris@552 375 unsigned int audioFrameBase = gLibpdBlockSize * tick;
chris@552 376 unsigned int j;
chris@552 377 unsigned int k;
chris@552 378 float* p0;
chris@552 379 float* p1;
chris@552 380 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 381 for (k = 0, p1 = p0; k < context->audioInChannels; k++, p1 += gLibpdBlockSize) {
chris@552 382 *p1 = audioRead(context, audioFrameBase + j, k);
chris@552 383 }
chris@552 384 }
chris@552 385 // then analogs
chris@552 386 // this loop resamples by ZOH, as needed, using m
chris@552 387 if(context->analogInChannels == 8 ){ //hold the value for two frames
chris@552 388 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 389 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstAnalogChannel; k < gAnalogChannelsInUse; ++k, p1 += gLibpdBlockSize) {
chris@552 390 unsigned int analogFrame = (audioFrameBase + j) / 2;
chris@552 391 *p1 = analogRead(context, analogFrame, k);
chris@552 392 }
chris@552 393 }
chris@552 394 } else if(context->analogInChannels == 4){ //write every frame
chris@552 395 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 396 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstAnalogChannel; k < gAnalogChannelsInUse; ++k, p1 += gLibpdBlockSize) {
chris@552 397 unsigned int analogFrame = audioFrameBase + j;
chris@552 398 *p1 = analogRead(context, analogFrame, k);
chris@552 399 }
chris@552 400 }
chris@552 401 } else if(context->analogInChannels == 2){ //drop every other frame
chris@552 402 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 403 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstAnalogChannel; k < gAnalogChannelsInUse; ++k, p1 += gLibpdBlockSize) {
chris@552 404 unsigned int analogFrame = (audioFrameBase + j) * 2;
chris@552 405 *p1 = analogRead(context, analogFrame, k);
chris@552 406 }
chris@552 407 }
chris@552 408 }
chris@552 409
chris@552 410 // Bela digital input
chris@552 411 // note: in multiple places below we assume that the number of digitals is same as number of audio
chris@552 412 // digital in at message-rate
chris@552 413 dcm.processInput(&context->digital[audioFrameBase], gLibpdBlockSize);
chris@552 414
chris@552 415 // digital in at signal-rate
chris@552 416 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 417 unsigned int digitalFrame = audioFrameBase + j;
chris@552 418 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstDigitalChannel;
chris@552 419 k < 16; ++k, p1 += gLibpdBlockSize) {
chris@552 420 if(dcm.isSignalRate(k) && dcm.isInput(k)){ // only process input channels that are handled at signal rate
chris@552 421 *p1 = digitalRead(context, digitalFrame, k);
chris@552 422 }
chris@552 423 }
chris@552 424 }
chris@552 425
chris@552 426 libpd_process_sys(); // process the block
chris@552 427
chris@552 428 //digital out
chris@552 429 // digital out at signal-rate
chris@552 430 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) {
chris@552 431 unsigned int digitalFrame = (audioFrameBase + j);
chris@552 432 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstDigitalChannel;
chris@552 433 k < context->digitalChannels; k++, p1 += gLibpdBlockSize) {
chris@552 434 if(dcm.isSignalRate(k) && dcm.isOutput(k)){ // only process output channels that are handled at signal rate
chris@552 435 digitalWriteOnce(context, digitalFrame, k, *p1 > 0.5);
chris@552 436 }
chris@552 437 }
chris@552 438 }
chris@552 439
chris@552 440 // digital out at message-rate
chris@552 441 dcm.processOutput(&context->digital[audioFrameBase], gLibpdBlockSize);
chris@552 442
chris@552 443 //audio
chris@552 444 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 445
chris@552 446 /*
chris@552 447 * MODIFICATION
chris@552 448 * ------------
chris@552 449 * Processing for tremolo effect while writing libpd output to Bela output buffer
chris@552 450 */
chris@552 451
chris@552 452 // Generate a sinewave with frequency set by gTremoloRate
chris@552 453 // and amplitude from -0.5 to 0.5
chris@552 454 float lfo = sinf(gPhase) * 0.5;
chris@552 455 // Keep track and wrap the phase of the sinewave
chris@552 456 gPhase += 2.0 * M_PI * gTremoloRate * gInverseSampleRate;
chris@552 457 if(gPhase > 2.0 * M_PI)
chris@552 458 gPhase -= 2.0 * M_PI;
chris@552 459
chris@552 460 /*********/
chris@555 461
chris@552 462 for (k = 0, p1 = p0; k < context->audioOutChannels; k++, p1 += gLibpdBlockSize) {
chris@552 463 audioWrite(context, audioFrameBase + j, k, *p1 * lfo); // MODIFICATION (* lfo)
chris@552 464 }
chris@552 465 }
chris@552 466
chris@552 467 //scope
chris@552 468 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) {
chris@552 469 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstScopeChannel; k < gScopeChannelsInUse; k++, p1 += gLibpdBlockSize) {
chris@552 470 gScopeOut[k] = *p1;
chris@552 471 }
chris@552 472 scope.log(gScopeOut[0], gScopeOut[1], gScopeOut[2], gScopeOut[3]);
chris@552 473 }
chris@552 474
chris@552 475
chris@552 476 //analog
chris@552 477 if(context->analogOutChannels == 8){
chris@552 478 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; j += 2, p0 += 2) { //write every two frames
chris@552 479 unsigned int analogFrame = (audioFrameBase + j) / 2;
chris@552 480 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstAnalogChannel; k < gAnalogChannelsInUse; k++, p1 += gLibpdBlockSize) {
chris@552 481 analogWriteOnce(context, analogFrame, k, *p1);
chris@552 482 }
chris@552 483 }
chris@552 484 } else if(context->analogOutChannels == 4){ //write every frame
chris@552 485 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) {
chris@552 486 unsigned int analogFrame = (audioFrameBase + j);
chris@552 487 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstAnalogChannel; k < gAnalogChannelsInUse; k++, p1 += gLibpdBlockSize) {
chris@552 488 analogWriteOnce(context, analogFrame, k, *p1);
chris@552 489 }
chris@552 490 }
chris@552 491 } else if(context->analogOutChannels == 2){ //write every frame twice
chris@552 492 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; j++, p0++) {
chris@552 493 for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstAnalogChannel; k < gAnalogChannelsInUse; k++, p1 += gLibpdBlockSize) {
chris@552 494 int analogFrame = audioFrameBase * 2 + j * 2;
chris@552 495 analogWriteOnce(context, analogFrame, k, *p1);
chris@552 496 analogWriteOnce(context, analogFrame + 1, k, *p1);
chris@552 497 }
chris@552 498 }
chris@552 499 }
chris@552 500 }
chris@552 501 }
chris@552 502
chris@552 503 // cleanup() is called once at the end, after the audio has stopped.
chris@552 504 // Release any resources that were allocated in setup().
chris@552 505
chris@552 506 void cleanup(BelaContext *context, void *userData)
chris@552 507 {
chris@552 508 delete [] gScopeOut;
chris@552 509 }