view examples/10-Instruments/airharp/render.cpp @ 556:ce391098f321 prerelease tip

THIS PROJECT HAS MOVED TO https://github.com/BelaPlatform/bela
author Giulio Moro <giuliomoro@yahoo.it>
date Sat, 25 Jun 2016 20:21:00 +0100
parents 8f8809c77dda
children
line wrap: on
line source
/*
 ____  _____ _        _    
| __ )| ____| |      / \   
|  _ \|  _| | |     / _ \  
| |_) | |___| |___ / ___ \ 
|____/|_____|_____/_/   \_\

The platform for ultra-low latency audio and sensor processing

http://bela.io

A project of the Augmented Instruments Laboratory within the
Centre for Digital Music at Queen Mary University of London.
http://www.eecs.qmul.ac.uk/~andrewm

(c) 2016 Augmented Instruments Laboratory: Andrew McPherson,
	Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack,
	Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved.

The Bela software is distributed under the GNU Lesser General Public License
(LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt
*/


/*
 * AIR-HARP
 * Physically modelled strings using waveguide junctions and mass-spring-dampers
 * controllable using an accelerometer
 *
 * 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 high-pass centre frequency
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->audioOutChannels + 1] = out_l * out_gain;
		context->audioOut[n * context->audioOutChannels + 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)
{

}