chris@160: /*
chris@160:  * render.cpp
chris@160:  *
chris@160:  *  Template render.cpp file for on-board heavy compiling
chris@160:  *
chris@160:  *  N.B. this is currently *not* compatible with foleyDesigner source files!
chris@160:  *
chris@160:  *  Created on: November 5, 2015
chris@160:  *
chris@160:  *  Christian Heinrichs
chris@160:  *
chris@160:  */
chris@160: 
giuliomoro@329: #include <Bela.h>
giuliomoro@198: #include <Midi.h>
chris@160: #include <cmath>
chris@160: #include "Heavy_bbb.h"
chris@190: #include <string.h>
chris@190: #include <stdlib.h>
chris@190: #include <string.h>
chris@160: /*
chris@160:  *	HEAVY CONTEXT & BUFFERS
chris@160:  */
chris@160: 
chris@160: Hv_bbb *gHeavyContext;
chris@160: float *gHvInputBuffers = NULL, *gHvOutputBuffers = NULL;
chris@160: int gHvInputChannels = 0, gHvOutputChannels = 0;
chris@160: 
chris@160: float gInverseSampleRate;
chris@160: 
chris@160: /*
chris@160:  *	HEAVY FUNCTIONS
chris@160:  */
chris@160: 
chris@160: void printHook(double timestampSecs, const char *printLabel, const char *msgString, void *userData) {
chris@160:   printf("Message from Heavy patch: [@ %.3f] %s: %s\n", timestampSecs, printLabel, msgString);
chris@160: }
chris@160: 
chris@160: static void sendHook(
chris@160:     double timestamp, // in milliseconds
chris@160:     const char *receiverName,
chris@160:     const HvMessage *const m,
chris@160:     void *userData) {
chris@160: 
chris@160:   // only react to messages sent to receivers named "hello"
chris@160:   if (!strncmp(receiverName, "hello", 5)) {
chris@160:   }
chris@160: 
chris@160: }
chris@160: 
chris@160: /*
chris@166:  * SETUP, RENDER LOOP & CLEANUP
chris@160:  */
chris@160: 
giuliomoro@198: Midi midi;
giuliomoro@329: bool setup(BelaContext *context, void *userData)	{
chris@160: 
chris@160: 	/* HEAVY */
chris@160: 
chris@160: 	gHeavyContext = hv_bbb_new(context->audioSampleRate);
chris@160: 
chris@160: 	gHvInputChannels = hv_getNumInputChannels(gHeavyContext);
chris@160: 	gHvOutputChannels = hv_getNumOutputChannels(gHeavyContext);
chris@160: 
chris@160: 	rt_printf("Starting Heavy context with %d input channels and %d output channels\n",
chris@160: 			  gHvInputChannels, gHvOutputChannels);
chris@160: 
chris@160: 	if(gHvInputChannels != 0) {
chris@160: 		gHvInputBuffers = (float *)calloc(gHvInputChannels * context->audioFrames,sizeof(float));
chris@160: 	}
chris@160: 	if(gHvOutputChannels != 0) {
chris@160: 		gHvOutputBuffers = (float *)calloc(gHvOutputChannels * context->audioFrames,sizeof(float));
chris@160: 	}
chris@160: 
chris@160: 	gInverseSampleRate = 1.0 / context->audioSampleRate;
chris@160: 
chris@160: 	// Set heavy print hook
chris@160: 	hv_setPrintHook(gHeavyContext, &printHook);
chris@160: 	// Set heavy send hook
chris@160: 	hv_setSendHook(gHeavyContext, sendHook);
chris@160: 
giuliomoro@198: 	midi.readFrom(0);
giuliomoro@198: 	midi.writeTo(0);
giuliomoro@198: 	midi.enableParser(true);
chris@160: 	return true;
chris@160: }
chris@160: 
chris@160: 
giuliomoro@329: void render(BelaContext *context, void *userData)
chris@160: {
chris@160: 
chris@160: 	// De-interleave the data
chris@160: 	if(gHvInputBuffers != NULL) {
chris@160: 		for(int n = 0; n < context->audioFrames; n++) {
chris@160: 			for(int ch = 0; ch < gHvInputChannels; ch++) {
chris@160: 				if(ch >= context->audioChannels+context->analogChannels) {
chris@160: 					// THESE ARE PARAMETER INPUT 'CHANNELS' USED FOR ROUTING
chris@160: 					// 'sensor' outputs from routing channels of dac~ are passed through here
chris@160: 					break;
chris@160: 				} else {
chris@160: 					// If more than 2 ADC inputs are used in the pd patch, route the analog inputs
chris@160: 					// i.e. ADC3->analogIn0 etc. (first two are always audio inputs)
chris@160: 					if(ch >= context->audioChannels)	{
chris@160: 						int m = n/2;
chris@160: 						float mIn = context->analogIn[m*context->analogChannels + (ch-context->audioChannels)];
chris@160: 						gHvInputBuffers[ch * context->audioFrames + n] = mIn;
chris@160: 					} else {
chris@160: 						gHvInputBuffers[ch * context->audioFrames + n] = context->audioIn[n * context->audioChannels + ch];
chris@160: 					}
chris@160: 				}
chris@160: 			}
chris@160: 		}
chris@160: 	}
chris@160: 
chris@160: 	// replacement for bang~ object
chris@160: 	//hv_vscheduleMessageForReceiver(gHeavyContext, "bbb_bang", 0.0f, "b");
giuliomoro@198: 	{
giuliomoro@198: 		int num;
giuliomoro@289: 		unsigned int hvHashes[3];
giuliomoro@289: 		hvHashes[0] = hv_stringToHash("bela_notein");
giuliomoro@289: 		hvHashes[1] = hv_stringToHash("bela_ctlin");
giuliomoro@289: 		hvHashes[2] = hv_stringToHash("bela_pgmin"); 
giuliomoro@198: 		while((num = midi.getParser()->numAvailableMessages()) > 0){
giuliomoro@198: 			static MidiChannelMessage message;
giuliomoro@198: 			message = midi.getParser()->getNextChannelMessage();
giuliomoro@198: 			switch(message.getType()){
giuliomoro@198: 			case kmmNoteOn: {
giuliomoro@198: //					message.prettyPrint();
giuliomoro@198: 					float noteNumber = message.getDataByte(0);
giuliomoro@198: 					float velocity = message.getDataByte(1);
giuliomoro@198: 					float channel = message.getChannel();
giuliomoro@198: //					rt_printf("message: noteNumber: %f, velocity: %f, channel: %f\n", noteNumber, velocity, channel);
giuliomoro@289: 					hv_vscheduleMessageForReceiver(gHeavyContext, hvHashes[0], 0, "fff", noteNumber, velocity, channel);
giuliomoro@198: 				}
giuliomoro@198: 				break;
giuliomoro@198: 			case kmmControlChange: {
giuliomoro@289: 				hv_vscheduleMessageForReceiver(gHeavyContext, hvHashes[1], 0, "fff",
giuliomoro@198: 						(float)message.getDataByte(1), (float)message.getDataByte(0), (float)message.getChannel());
giuliomoro@198: 				}
giuliomoro@198: 				break;
giuliomoro@198: 			case kmmProgramChange:
giuliomoro@289: 				hv_vscheduleMessageForReceiver(gHeavyContext, hvHashes[2], 0, "ff",
giuliomoro@198: 						(float)message.getDataByte(0), (float)message.getChannel());
giuliomoro@198: 				break;
giuliomoro@198: 			}
giuliomoro@198: 		}
giuliomoro@198: 	}
giuliomoro@198: //	hv_sendFloatToReceiver(gHeavyContext, "notein", 1.123f);
giuliomoro@198: 
chris@160: 
chris@160: 	hv_bbb_process_inline(gHeavyContext, gHvInputBuffers, gHvOutputBuffers, context->audioFrames);
chris@160: 
chris@160: 	// Interleave the output data
chris@160: 	if(gHvOutputBuffers != NULL) {
chris@160: 		for(int n = 0; n < context->audioFrames; n++) {
chris@160: 
chris@160: 			for(int ch = 0; ch < gHvOutputChannels; ch++) {
chris@160: 				if(ch >= context->audioChannels+context->analogChannels) {
chris@160: 					// THESE ARE SENSOR OUTPUT 'CHANNELS' USED FOR ROUTING
chris@160: 					// they are the content of the 'sensor output' dac~ channels
chris@160: 				} else {
chris@160: 					if(ch >= context->audioChannels)	{
chris@160: 						int m = n/2;
chris@160: 						context->analogOut[m * context->analogFrames + (ch-context->audioChannels)] = constrain(gHvOutputBuffers[ch*context->audioFrames + n],0.0,1.0);
chris@160: 					} else {
chris@160: 						context->audioOut[n * context->audioChannels + ch] = gHvOutputBuffers[ch * context->audioFrames + n];
chris@160: 					}
chris@160: 				}
chris@160: 			}
chris@160: 		}
chris@160: 	}
chris@160: 
chris@160: }
chris@160: 
chris@160: 
giuliomoro@329: void cleanup(BelaContext *context, void *userData)
chris@160: {
chris@160: 
chris@160: 	hv_bbb_free(gHeavyContext);
chris@160: 	if(gHvInputBuffers != NULL)
chris@160: 		free(gHvInputBuffers);
chris@160: 	if(gHvOutputBuffers != NULL)
chris@160: 		free(gHvOutputBuffers);
chris@160: 
chris@160: }