diff examples/10-Instruments/airharp/render.cpp @ 464:8fcfbfb32aa0 prerelease

Examples reorder with subdirectories. Added header to each project. Moved Doxygen to bottom of render.cpp.
author Robert Jack <robert.h.jack@gmail.com>
date Mon, 20 Jun 2016 16:20:38 +0100
parents
children 8f8809c77dda
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/10-Instruments/airharp/render.cpp	Mon Jun 20 16:20:38 2016 +0100
@@ -0,0 +1,211 @@
+/*
+ * AIR-HARP
+ * Physically modelled strings using waveguide junctions and mass-spring-dampers
+ *
+ * render.cpp
+ *
+ * Christian Heinrichs 04/2015
+ *
+ */
+
+
+#include "MassSpringDamper.h"
+#include "String.h"
+#include "Plectrum.h"
+
+#include <Bela.h>
+#include <cmath>
+#include <stdio.h>
+#include <cstdlib>
+#include <rtdk.h>
+
+#define ACCEL_BUF_SIZE 8
+#define NUMBER_OF_STRINGS 9
+
+// PENTATONIC SCALE
+float gMidinotes[NUMBER_OF_STRINGS] = {40,45,50,55,57,60,62,64,67};
+
+float gInverseSampleRate;
+
+float out_gain = 5.0;
+
+int accelPin_x = 0;
+int accelPin_y = 1;
+int accelPin_z = 2;
+
+MassSpringDamper msd = MassSpringDamper(1,0.1,10);// (10,0.001,10);
+String strings[NUMBER_OF_STRINGS];
+Plectrum plectrums[NUMBER_OF_STRINGS];
+
+float gPlectrumDisplacement = 0;
+
+float gAccel_x[ACCEL_BUF_SIZE] = {0};
+int gAccelReadPtr = 0;
+
+// DC BLOCK BUTTERWORTH
+
+// Coefficients for 100hz cut-off
+float a0_l = 0.9899759179893742;
+float a1_l = -1.9799518359787485;
+float a2_l = 0.9899759179893742;
+float a3_l = -1.979851353142371;
+float a4_l = 0.9800523188151258;
+
+float a0_r = a0_l;
+float a1_r = a1_l;
+float a2_r = a2_l;
+float a3_r = a3_l;
+float a4_r = a4_l;
+
+float x1_l = 0;
+float x2_l = 0;
+float y1_l = 0;
+float y2_l = 0;
+
+float x1_r = 0;
+float x2_r = 0;
+float y1_r = 0;
+float y2_r = 0;
+
+
+bool setup(BelaContext *context, void *userData)
+{
+
+	gInverseSampleRate = 1.0 / context->audioSampleRate;
+
+	// initialise strings & plectrums
+	for(int i=0;i<NUMBER_OF_STRINGS;i++)	{
+
+		plectrums[i] = Plectrum();
+		plectrums[i].setup(250,0.25,0.05);
+
+		strings[i] = String();
+		strings[i].setMidinote(gMidinotes[i]);
+
+		float spacing = 2.0 / (NUMBER_OF_STRINGS+1);
+
+		strings[i].setGlobalPosition( -1 + spacing*(i+1) );
+
+		rt_printf("STRING %d // midinote: %f position: %f\n",i,gMidinotes[i],( -1 + spacing*(i+1) ));
+
+	}
+
+	return true;
+}
+
+void render(BelaContext *context, void *userData)
+{
+
+	float lastAccel = 0;
+
+	for(int n = 0; n < context->audioFrames; n++) {
+
+		/*
+		 *
+		 * ACCELEROMETER DATA
+		 *
+		 */
+
+		// Read accelerometer data from analog input
+		float accel_x = 0;
+		if(n%2)	{
+			accel_x = (float)context->analogIn[(n/2)*8+accelPin_x] * 2 - 1;	// 15800 - 28300 - 41500
+			lastAccel = accel_x;
+		} else {
+			// grab previous value if !n%2
+			accel_x = lastAccel;
+		}
+
+		// Dead-zone avoids noise when box is lying horizontally on a surface
+
+		float accelDeadZone = 0.1;
+
+		if(accel_x <= accelDeadZone && accel_x >= -accelDeadZone)
+			accel_x = 0;
+
+		// Perform smoothing (moving average) on acceleration value
+		if(++gAccelReadPtr >= ACCEL_BUF_SIZE)
+			gAccelReadPtr = 0;
+		gAccel_x[gAccelReadPtr] = accel_x;
+		float gravity = 0;
+		for(int i=0;i<ACCEL_BUF_SIZE;i++)	{
+			gravity = gAccel_x[(gAccelReadPtr-i+ACCEL_BUF_SIZE)%ACCEL_BUF_SIZE];
+		}
+		gravity /= ACCEL_BUF_SIZE;
+
+		/*
+		 *
+		 * PHYSICS SIMULATION
+		 *
+		 */
+
+		// The horizontal force (which can be gravity if box is tipped on its side)
+		// is used as the input to a Mass-Spring-Damper model
+		// Plectrum displacement (i.e. when interacting with string) is included
+		float massPosition = (float)msd.update(gravity - gPlectrumDisplacement);
+
+		float out_l = 0;
+		float out_r = 0;
+		// Use this parameter to quickly adjust output gain
+		float gain = 0.0015;	// 0.0015 is a good value or 12 strings
+		gPlectrumDisplacement = 0;
+
+		for(int s=0;s<NUMBER_OF_STRINGS;s++)	{
+
+			float stringPosition = strings[s].getGlobalPosition();
+
+			float plectrumForce = plectrums[s].update(massPosition, stringPosition);
+			gPlectrumDisplacement += strings[s].getPlectrumDisplacement();
+
+			// calculate panning based on string position (-1->left / 1->right)
+			float panRight = map(stringPosition,1,-1,0.1,1);
+			float panLeft = map(stringPosition,-1,1,0.1,1);
+			panRight *= panRight;
+			panLeft *= panLeft;
+
+			float out = strings[s].update(plectrumForce)*gain;
+
+			out_l += out*panLeft;
+			out_r += out*panRight;
+
+		}
+
+		// APPLY DC-BLOCK FILTER TO OUTPUTS
+
+		// LEFT CHANNEL
+		float temp_in = out_l;
+		/* compute result */
+    	out_l = a0_l * out_l + a1_l * x1_l + a2_l * x2_l - a3_l * y1_l - a4_l * y2_l;
+    	/* shift x1 to x2, sample to x1 */
+    	x2_l = x1_l;
+    	x1_l = temp_in;
+    	/* shift y1 to y2, result to y1 */
+    	y2_l = y1_l;
+   	 	y1_l = out_l;
+
+   	 	// RIGHT CHANNEL
+		temp_in = out_r;
+		/* compute result */
+    	out_r = a0_r * out_r + a1_r * x1_r + a2_r * x2_r - a3_r * y1_r - a4_r * y2_r;
+    	/* shift x1 to x2, sample to x1 */
+    	x2_r = x1_r;
+    	x1_r = temp_in;
+    	/* shift y1 to y2, result to y1 */
+    	y2_r = y1_r;
+   	 	y1_r = out_r;
+
+		context->audioOut[n * context->audioChannels + 1] = out_l * out_gain;
+		context->audioOut[n * context->audioChannels + 0] = out_r * out_gain;
+
+	}
+
+}
+
+
+// cleanup_render() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in initialise_render().
+
+void cleanup(BelaContext *context, void *userData)
+{
+
+}