| 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)	{ | 
| chris@160 | 159 	/* HEAVY */ | 
| giuliomoro@480 | 160 	hvMidiHashes[kmmNoteOn] = hv_stringToHash("__hv_notein"); | 
| giuliomoro@487 | 161 //	hvMidiHashes[kmmNoteOff] = hv_stringToHash("noteoff"); // this is handled differently, see the render function | 
| giuliomoro@480 | 162 	hvMidiHashes[kmmControlChange] = hv_stringToHash("__hv_ctlin"); | 
| giuliomoro@487 | 163 	// Note that the ones below are not defined by Heavy, but they are here for (wishing) forward-compatibility | 
| giuliomoro@487 | 164 	// You need to receive from the corresponding symbol in Pd and unpack the message, e.g.: | 
| giuliomoro@487 | 165 	//[r __hv_pgmin] | 
| giuliomoro@487 | 166 	//| | 
| giuliomoro@487 | 167 	//[unpack f f] | 
| giuliomoro@487 | 168 	//|   | | 
| giuliomoro@487 | 169 	//|   [print pgmin_channel] | 
| giuliomoro@487 | 170 	//[print pgmin_number] | 
| giuliomoro@487 | 171 	hvMidiHashes[kmmProgramChange] = hv_stringToHash("__hv_pgmin"); | 
| giuliomoro@487 | 172 	hvMidiHashes[kmmPolyphonicKeyPressure] = hv_stringToHash("__hv_polytouchin"); | 
| giuliomoro@496 | 173 	hvMidiHashes[kmmChannelPressure] = hv_stringToHash("__hv_touchin"); | 
| giuliomoro@487 | 174 	hvMidiHashes[kmmPitchBend] = hv_stringToHash("__hv_bendin"); | 
| chris@160 | 175 | 
| giuliomoro@492 | 176 	gHeavyContext = hv_bela_new(context->audioSampleRate); | 
| chris@160 | 177 | 
| chris@160 | 178 	gHvInputChannels = hv_getNumInputChannels(gHeavyContext); | 
| chris@160 | 179 	gHvOutputChannels = hv_getNumOutputChannels(gHeavyContext); | 
| chris@160 | 180 | 
| giuliomoro@482 | 181 	gScopeChannelsInUse = gHvOutputChannels > gFirstScopeChannel ? | 
| giuliomoro@482 | 182 			gHvOutputChannels - gFirstScopeChannel : 0; | 
| giuliomoro@482 | 183 	gDigitalSigInChannelsInUse = gHvInputChannels > gFirstDigitalChannel ? | 
| giuliomoro@482 | 184 			gHvInputChannels - gFirstDigitalChannel : 0; | 
| giuliomoro@482 | 185 	gDigitalSigOutChannelsInUse = gHvOutputChannels > gFirstDigitalChannel ? | 
| giuliomoro@482 | 186 			gHvOutputChannels - gFirstDigitalChannel - gScopeChannelsInUse: 0; | 
| giuliomoro@482 | 187 | 
| giuliomoro@482 | 188 	printf("Starting Heavy context with %d input channels and %d output channels\n", | 
| chris@160 | 189 			  gHvInputChannels, gHvOutputChannels); | 
| giuliomoro@482 | 190 	printf("Channels in use:\n"); | 
| giuliomoro@482 | 191 	printf("Digital in : %u, Digital out: %u\n", gDigitalSigInChannelsInUse, gDigitalSigOutChannelsInUse); | 
| giuliomoro@482 | 192 	printf("Scope out: %u\n", gScopeChannelsInUse); | 
| chris@160 | 193 | 
| chris@160 | 194 	if(gHvInputChannels != 0) { | 
| chris@160 | 195 		gHvInputBuffers = (float *)calloc(gHvInputChannels * context->audioFrames,sizeof(float)); | 
| chris@160 | 196 	} | 
| chris@160 | 197 	if(gHvOutputChannels != 0) { | 
| chris@160 | 198 		gHvOutputBuffers = (float *)calloc(gHvOutputChannels * context->audioFrames,sizeof(float)); | 
| chris@160 | 199 	} | 
| chris@160 | 200 | 
| chris@160 | 201 	gInverseSampleRate = 1.0 / context->audioSampleRate; | 
| chris@160 | 202 | 
| chris@160 | 203 	// Set heavy print hook | 
| giuliomoro@480 | 204 	hv_setPrintHook(gHeavyContext, printHook); | 
| chris@160 | 205 	// Set heavy send hook | 
| chris@160 | 206 	hv_setSendHook(gHeavyContext, sendHook); | 
| chris@160 | 207 | 
| giuliomoro@480 | 208 	// TODO: change these hardcoded port values and actually change them in the Midi class | 
| giuliomoro@198 | 209 	midi.readFrom(0); | 
| giuliomoro@198 | 210 	midi.writeTo(0); | 
| giuliomoro@198 | 211 	midi.enableParser(true); | 
| giuliomoro@480 | 212 | 
| giuliomoro@482 | 213 	if(gScopeChannelsInUse > 0){ | 
| giuliomoro@482 | 214 		// block below copy/pasted from libpd, except | 
| giuliomoro@482 | 215 		scope.setup(gScopeChannelsInUse, context->audioSampleRate); | 
| giuliomoro@482 | 216 		gScopeOut = new float[gScopeChannelsInUse]; | 
| giuliomoro@482 | 217 	} | 
| giuliomoro@480 | 218 	// Bela digital | 
| giuliomoro@480 | 219 	dcm.setCallback(sendDigitalMessage); | 
| giuliomoro@480 | 220 	if(context->digitalChannels > 0){ | 
| giuliomoro@480 | 221 		for(unsigned int ch = 0; ch < context->digitalChannels; ++ch){ | 
| giuliomoro@480 | 222 			dcm.setCallbackArgument(ch, hvDigitalInHashes[ch]); | 
| giuliomoro@480 | 223 		} | 
| giuliomoro@480 | 224 	} | 
| giuliomoro@480 | 225 	// unlike libpd, no need here to bind the bela_digitalOut.. receivers | 
| giuliomoro@480 | 226 | 
| chris@160 | 227 	return true; | 
| chris@160 | 228 } | 
| chris@160 | 229 | 
| chris@160 | 230 | 
| giuliomoro@329 | 231 void render(BelaContext *context, void *userData) | 
| chris@160 | 232 { | 
| giuliomoro@480 | 233 	{ | 
| giuliomoro@480 | 234 		int num; | 
| giuliomoro@480 | 235 		while((num = midi.getParser()->numAvailableMessages()) > 0){ | 
| giuliomoro@480 | 236 			static MidiChannelMessage message; | 
| giuliomoro@480 | 237 			message = midi.getParser()->getNextChannelMessage(); | 
| giuliomoro@480 | 238 			switch(message.getType()){ | 
| giuliomoro@480 | 239 			case kmmNoteOn: { | 
| giuliomoro@487 | 240 				//message.prettyPrint(); | 
| giuliomoro@487 | 241 				int noteNumber = message.getDataByte(0); | 
| giuliomoro@487 | 242 				int velocity = message.getDataByte(1); | 
| giuliomoro@487 | 243 				int channel = message.getChannel(); | 
| giuliomoro@480 | 244 				// rt_printf("message: noteNumber: %f, velocity: %f, channel: %f\n", noteNumber, velocity, channel); | 
| giuliomoro@487 | 245 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff", | 
| giuliomoro@496 | 246 						(float)noteNumber, (float)velocity, (float)channel+1); | 
| giuliomoro@480 | 247 				break; | 
| giuliomoro@480 | 248 			} | 
| giuliomoro@487 | 249 			case kmmNoteOff: { | 
| giuliomoro@480 | 250 				/* PureData does not seem to handle noteoff messages as per the MIDI specs, | 
| giuliomoro@480 | 251 				 * so that the noteoff velocity is ignored. Here we convert them to noteon | 
| giuliomoro@480 | 252 				 * with a velocity of 0. | 
| giuliomoro@480 | 253 				 */ | 
| giuliomoro@487 | 254 				int noteNumber = message.getDataByte(0); | 
| giuliomoro@480 | 255 				// int velocity = message.getDataByte(1); // would be ignored by Pd | 
| giuliomoro@487 | 256 				int channel = message.getChannel(); | 
| giuliomoro@480 | 257 				// note we are sending the below to hvHashes[kmmNoteOn] !! | 
| giuliomoro@487 | 258 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmNoteOn], 0, "fff", | 
| giuliomoro@496 | 259 						(float)noteNumber, (float)0, (float)channel+1); | 
| giuliomoro@480 | 260 				break; | 
| giuliomoro@480 | 261 			} | 
| giuliomoro@480 | 262 			case kmmControlChange: { | 
| giuliomoro@480 | 263 				int channel = message.getChannel(); | 
| giuliomoro@480 | 264 				int controller = message.getDataByte(0); | 
| giuliomoro@480 | 265 				int value = message.getDataByte(1); | 
| giuliomoro@480 | 266 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmControlChange], 0, "fff", | 
| giuliomoro@496 | 267 						(float)value, (float)controller, (float)channel+1); | 
| giuliomoro@480 | 268 				break; | 
| giuliomoro@480 | 269 			} | 
| giuliomoro@480 | 270 			case kmmProgramChange: { | 
| giuliomoro@480 | 271 				int channel = message.getChannel(); | 
| giuliomoro@480 | 272 				int program = message.getDataByte(0); | 
| giuliomoro@480 | 273 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmProgramChange], 0, "ff", | 
| giuliomoro@496 | 274 						(float)program, (float)channel+1); | 
| giuliomoro@480 | 275 				break; | 
| giuliomoro@480 | 276 			} | 
| giuliomoro@480 | 277 			case kmmPolyphonicKeyPressure: { | 
| giuliomoro@487 | 278 				//TODO: untested, I do not have anything with polyTouch... who does, anyhow? | 
| giuliomoro@480 | 279 				int channel = message.getChannel(); | 
| giuliomoro@480 | 280 				int pitch = message.getDataByte(0); | 
| giuliomoro@480 | 281 				int value = message.getDataByte(1); | 
| giuliomoro@480 | 282 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmPolyphonicKeyPressure], 0, "fff", | 
| giuliomoro@496 | 283 						(float)channel+1, (float)pitch, (float)value); | 
| giuliomoro@480 | 284 				break; | 
| giuliomoro@480 | 285 			} | 
| giuliomoro@480 | 286 			case kmmChannelPressure: | 
| giuliomoro@480 | 287 			{ | 
| giuliomoro@480 | 288 				int channel = message.getChannel(); | 
| giuliomoro@480 | 289 				int value = message.getDataByte(0); | 
| giuliomoro@480 | 290 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmChannelPressure], 0, "ff", | 
| giuliomoro@496 | 291 						(float)value, (float)channel+1); | 
| giuliomoro@480 | 292 				break; | 
| giuliomoro@480 | 293 			} | 
| giuliomoro@480 | 294 			case kmmPitchBend: | 
| giuliomoro@480 | 295 			{ | 
| giuliomoro@480 | 296 				int channel = message.getChannel(); | 
| giuliomoro@498 | 297 				int value = ((message.getDataByte(1) << 7) | message.getDataByte(0)); | 
| giuliomoro@480 | 298 				hv_vscheduleMessageForReceiver(gHeavyContext, hvMidiHashes[kmmPitchBend], 0, "ff", | 
| giuliomoro@508 | 299 						(float)value, (float)channel+1); | 
| giuliomoro@480 | 300 				break; | 
| giuliomoro@480 | 301 			} | 
| giuliomoro@480 | 302 			case kmmNone: | 
| giuliomoro@480 | 303 			case kmmAny: | 
| giuliomoro@480 | 304 				break; | 
| giuliomoro@480 | 305 			} | 
| giuliomoro@480 | 306 		} | 
| giuliomoro@480 | 307 	} | 
| chris@160 | 308 | 
| chris@160 | 309 	// De-interleave the data | 
| chris@160 | 310 	if(gHvInputBuffers != NULL) { | 
| giuliomoro@480 | 311 		for(unsigned int n = 0; n < context->audioFrames; n++) { | 
| giuliomoro@480 | 312 			for(unsigned int ch = 0; ch < gHvInputChannels; ch++) { | 
| chris@160 | 313 				if(ch >= context->audioChannels+context->analogChannels) { | 
| chris@160 | 314 					// THESE ARE PARAMETER INPUT 'CHANNELS' USED FOR ROUTING | 
| chris@160 | 315 					// 'sensor' outputs from routing channels of dac~ are passed through here | 
| chris@160 | 316 					break; | 
| chris@160 | 317 				} else { | 
| chris@160 | 318 					// If more than 2 ADC inputs are used in the pd patch, route the analog inputs | 
| chris@160 | 319 					// i.e. ADC3->analogIn0 etc. (first two are always audio inputs) | 
| chris@160 | 320 					if(ch >= context->audioChannels)	{ | 
| chris@160 | 321 						int m = n/2; | 
| chris@160 | 322 						float mIn = context->analogIn[m*context->analogChannels + (ch-context->audioChannels)]; | 
| chris@160 | 323 						gHvInputBuffers[ch * context->audioFrames + n] = mIn; | 
| chris@160 | 324 					} else { | 
| chris@160 | 325 						gHvInputBuffers[ch * context->audioFrames + n] = context->audioIn[n * context->audioChannels + ch]; | 
| chris@160 | 326 					} | 
| chris@160 | 327 				} | 
| chris@160 | 328 			} | 
| chris@160 | 329 		} | 
| chris@160 | 330 	} | 
| chris@160 | 331 | 
| giuliomoro@480 | 332 	// Bela digital in | 
| giuliomoro@480 | 333 	// note: in multiple places below we assume that the number of digital frames is same as number of audio | 
| giuliomoro@482 | 334 	// Bela digital in at message-rate | 
| giuliomoro@480 | 335 	dcm.processInput(context->digital, context->digitalFrames); | 
| giuliomoro@480 | 336 | 
| giuliomoro@480 | 337 	// Bela digital in at signal-rate | 
| giuliomoro@482 | 338 	if(gDigitalSigInChannelsInUse > 0) | 
| giuliomoro@482 | 339 	{ | 
| giuliomoro@482 | 340 		unsigned int j, k; | 
| giuliomoro@482 | 341 		float *p0, *p1; | 
| giuliomoro@482 | 342 		const unsigned int gLibpdBlockSize = context->audioFrames; | 
| giuliomoro@482 | 343 		const unsigned int  audioFrameBase = 0; | 
| giuliomoro@482 | 344 		float* gInBuf = gHvInputBuffers; | 
| giuliomoro@482 | 345 		// block below copy/pasted from libpd, except | 
| giuliomoro@482 | 346 		// 16 has been replaced with gDigitalSigInChannelsInUse | 
| giuliomoro@482 | 347 		for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | 
| giuliomoro@482 | 348 			unsigned int digitalFrame = audioFrameBase + j; | 
| giuliomoro@482 | 349 			for (k = 0, p1 = p0 + gLibpdBlockSize * gFirstDigitalChannel; | 
| giuliomoro@482 | 350 					k < gDigitalSigInChannelsInUse; ++k, p1 += gLibpdBlockSize) { | 
| giuliomoro@482 | 351 				if(dcm.isSignalRate(k) && dcm.isInput(k)){ // only process input channels that are handled at signal rate | 
| giuliomoro@482 | 352 					*p1 = digitalRead(context, digitalFrame, k); | 
| giuliomoro@482 | 353 				} | 
| giuliomoro@482 | 354 			} | 
| giuliomoro@482 | 355 		} | 
| giuliomoro@482 | 356 	} | 
| giuliomoro@482 | 357 | 
| giuliomoro@480 | 358 | 
| chris@160 | 359 	// replacement for bang~ object | 
| giuliomoro@492 | 360 	//hv_vscheduleMessageForReceiver(gHeavyContext, "bela_bang", 0.0f, "b"); | 
| chris@160 | 361 | 
| giuliomoro@492 | 362 	hv_bela_process_inline(gHeavyContext, gHvInputBuffers, gHvOutputBuffers, context->audioFrames); | 
| chris@160 | 363 | 
| giuliomoro@480 | 364 	// Bela digital out | 
| giuliomoro@482 | 365 	// Bela digital out at signal-rate | 
| giuliomoro@482 | 366 	if(gDigitalSigOutChannelsInUse > 0) | 
| giuliomoro@482 | 367 	{ | 
| giuliomoro@482 | 368 			unsigned int j, k; | 
| giuliomoro@482 | 369 			float *p0, *p1; | 
| giuliomoro@482 | 370 			const unsigned int gLibpdBlockSize = context->audioFrames; | 
| giuliomoro@482 | 371 			const unsigned int  audioFrameBase = 0; | 
| giuliomoro@482 | 372 			float* gOutBuf = gHvOutputBuffers; | 
| giuliomoro@482 | 373 			// block below copy/pasted from libpd, except | 
| giuliomoro@482 | 374 			// context->digitalChannels has been replaced with gDigitalSigOutChannelsInUse | 
| giuliomoro@482 | 375 			for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) { | 
| giuliomoro@482 | 376 				unsigned int digitalFrame = (audioFrameBase + j); | 
| giuliomoro@482 | 377 				for (k = 0, p1 = p0  + gLibpdBlockSize * gFirstDigitalChannel; | 
| giuliomoro@482 | 378 						k < gDigitalSigOutChannelsInUse; k++, p1 += gLibpdBlockSize) { | 
| giuliomoro@482 | 379 					if(dcm.isSignalRate(k) && dcm.isOutput(k)){ // only process output channels that are handled at signal rate | 
| giuliomoro@482 | 380 						digitalWriteOnce(context, digitalFrame, k, *p1 > 0.5); | 
| giuliomoro@482 | 381 					} | 
| giuliomoro@482 | 382 				} | 
| giuliomoro@482 | 383 			} | 
| giuliomoro@482 | 384 	} | 
| giuliomoro@482 | 385 	// Bela digital out at message-rate | 
| giuliomoro@482 | 386 	dcm.processOutput(context->digital, context->digitalFrames); | 
| giuliomoro@480 | 387 | 
| giuliomoro@482 | 388 	// Bela scope | 
| giuliomoro@482 | 389 	if(gScopeChannelsInUse > 0) | 
| giuliomoro@482 | 390 	{ | 
| giuliomoro@482 | 391 		unsigned int j, k; | 
| giuliomoro@482 | 392 		float *p0, *p1; | 
| giuliomoro@482 | 393 		const unsigned int gLibpdBlockSize = context->audioFrames; | 
| giuliomoro@482 | 394 		float* gOutBuf = gHvOutputBuffers; | 
| giuliomoro@482 | 395 | 
| giuliomoro@482 | 396 		// block below copy/pasted from libpd | 
| giuliomoro@482 | 397 		for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) { | 
| giuliomoro@482 | 398 			for (k = 0, p1 = p0  + gLibpdBlockSize * gFirstScopeChannel; k < gScopeChannelsInUse; k++, p1 += gLibpdBlockSize) { | 
| giuliomoro@482 | 399 				gScopeOut[k] = *p1; | 
| giuliomoro@482 | 400 			} | 
| giuliomoro@486 | 401 			scope.log(gScopeOut); | 
| giuliomoro@482 | 402 		} | 
| giuliomoro@482 | 403 	} | 
| giuliomoro@480 | 404 | 
| chris@160 | 405 	// Interleave the output data | 
| chris@160 | 406 	if(gHvOutputBuffers != NULL) { | 
| giuliomoro@480 | 407 		for(unsigned int n = 0; n < context->audioFrames; n++) { | 
| chris@160 | 408 | 
| giuliomoro@480 | 409 			for(unsigned int ch = 0; ch < gHvOutputChannels; ch++) { | 
| chris@160 | 410 				if(ch >= context->audioChannels+context->analogChannels) { | 
| chris@160 | 411 					// THESE ARE SENSOR OUTPUT 'CHANNELS' USED FOR ROUTING | 
| chris@160 | 412 					// they are the content of the 'sensor output' dac~ channels | 
| chris@160 | 413 				} else { | 
| chris@160 | 414 					if(ch >= context->audioChannels)	{ | 
| chris@160 | 415 						int m = n/2; | 
| chris@160 | 416 						context->analogOut[m * context->analogFrames + (ch-context->audioChannels)] = constrain(gHvOutputBuffers[ch*context->audioFrames + n],0.0,1.0); | 
| chris@160 | 417 					} else { | 
| chris@160 | 418 						context->audioOut[n * context->audioChannels + ch] = gHvOutputBuffers[ch * context->audioFrames + n]; | 
| chris@160 | 419 					} | 
| chris@160 | 420 				} | 
| chris@160 | 421 			} | 
| chris@160 | 422 		} | 
| chris@160 | 423 	} | 
| chris@160 | 424 | 
| chris@160 | 425 } | 
| chris@160 | 426 | 
| chris@160 | 427 | 
| giuliomoro@329 | 428 void cleanup(BelaContext *context, void *userData) | 
| chris@160 | 429 { | 
| chris@160 | 430 | 
| giuliomoro@492 | 431 	hv_bela_free(gHeavyContext); | 
| chris@160 | 432 	if(gHvInputBuffers != NULL) | 
| chris@160 | 433 		free(gHvInputBuffers); | 
| chris@160 | 434 	if(gHvOutputBuffers != NULL) | 
| chris@160 | 435 		free(gHvOutputBuffers); | 
| giuliomoro@482 | 436 	delete[] gScopeOut; | 
| chris@160 | 437 } |