robert@464: /* robert@464: * AIR-HARP robert@464: * Physically modelled strings using waveguide junctions and mass-spring-dampers robert@464: * robert@464: * render.cpp robert@464: * robert@464: * Christian Heinrichs 04/2015 robert@464: * robert@464: */ robert@464: robert@464: robert@464: #include "MassSpringDamper.h" robert@464: #include "String.h" robert@464: #include "Plectrum.h" robert@464: robert@464: #include robert@464: #include robert@464: #include robert@464: #include robert@464: #include robert@464: robert@464: #define ACCEL_BUF_SIZE 8 robert@464: #define NUMBER_OF_STRINGS 9 robert@464: robert@464: // PENTATONIC SCALE robert@464: float gMidinotes[NUMBER_OF_STRINGS] = {40,45,50,55,57,60,62,64,67}; robert@464: robert@464: float gInverseSampleRate; robert@464: robert@464: float out_gain = 5.0; robert@464: robert@464: int accelPin_x = 0; robert@464: int accelPin_y = 1; robert@464: int accelPin_z = 2; robert@464: robert@464: MassSpringDamper msd = MassSpringDamper(1,0.1,10);// (10,0.001,10); robert@464: String strings[NUMBER_OF_STRINGS]; robert@464: Plectrum plectrums[NUMBER_OF_STRINGS]; robert@464: robert@464: float gPlectrumDisplacement = 0; robert@464: robert@464: float gAccel_x[ACCEL_BUF_SIZE] = {0}; robert@464: int gAccelReadPtr = 0; robert@464: robert@464: // DC BLOCK BUTTERWORTH robert@464: robert@464: // Coefficients for 100hz cut-off robert@464: float a0_l = 0.9899759179893742; robert@464: float a1_l = -1.9799518359787485; robert@464: float a2_l = 0.9899759179893742; robert@464: float a3_l = -1.979851353142371; robert@464: float a4_l = 0.9800523188151258; robert@464: robert@464: float a0_r = a0_l; robert@464: float a1_r = a1_l; robert@464: float a2_r = a2_l; robert@464: float a3_r = a3_l; robert@464: float a4_r = a4_l; robert@464: robert@464: float x1_l = 0; robert@464: float x2_l = 0; robert@464: float y1_l = 0; robert@464: float y2_l = 0; robert@464: robert@464: float x1_r = 0; robert@464: float x2_r = 0; robert@464: float y1_r = 0; robert@464: float y2_r = 0; robert@464: robert@464: robert@464: bool setup(BelaContext *context, void *userData) robert@464: { robert@464: robert@464: gInverseSampleRate = 1.0 / context->audioSampleRate; robert@464: robert@464: // initialise strings & plectrums robert@464: for(int i=0;iaudioFrames; n++) { robert@464: robert@464: /* robert@464: * robert@464: * ACCELEROMETER DATA robert@464: * robert@464: */ robert@464: robert@464: // Read accelerometer data from analog input robert@464: float accel_x = 0; robert@464: if(n%2) { robert@464: accel_x = (float)context->analogIn[(n/2)*8+accelPin_x] * 2 - 1; // 15800 - 28300 - 41500 robert@464: lastAccel = accel_x; robert@464: } else { robert@464: // grab previous value if !n%2 robert@464: accel_x = lastAccel; robert@464: } robert@464: robert@464: // Dead-zone avoids noise when box is lying horizontally on a surface robert@464: robert@464: float accelDeadZone = 0.1; robert@464: robert@464: if(accel_x <= accelDeadZone && accel_x >= -accelDeadZone) robert@464: accel_x = 0; robert@464: robert@464: // Perform smoothing (moving average) on acceleration value robert@464: if(++gAccelReadPtr >= ACCEL_BUF_SIZE) robert@464: gAccelReadPtr = 0; robert@464: gAccel_x[gAccelReadPtr] = accel_x; robert@464: float gravity = 0; robert@464: for(int i=0;ileft / 1->right) robert@464: float panRight = map(stringPosition,1,-1,0.1,1); robert@464: float panLeft = map(stringPosition,-1,1,0.1,1); robert@464: panRight *= panRight; robert@464: panLeft *= panLeft; robert@464: robert@464: float out = strings[s].update(plectrumForce)*gain; robert@464: robert@464: out_l += out*panLeft; robert@464: out_r += out*panRight; robert@464: robert@464: } robert@464: robert@464: // APPLY DC-BLOCK FILTER TO OUTPUTS robert@464: robert@464: // LEFT CHANNEL robert@464: float temp_in = out_l; robert@464: /* compute result */ robert@464: out_l = a0_l * out_l + a1_l * x1_l + a2_l * x2_l - a3_l * y1_l - a4_l * y2_l; robert@464: /* shift x1 to x2, sample to x1 */ robert@464: x2_l = x1_l; robert@464: x1_l = temp_in; robert@464: /* shift y1 to y2, result to y1 */ robert@464: y2_l = y1_l; robert@464: y1_l = out_l; robert@464: robert@464: // RIGHT CHANNEL robert@464: temp_in = out_r; robert@464: /* compute result */ robert@464: out_r = a0_r * out_r + a1_r * x1_r + a2_r * x2_r - a3_r * y1_r - a4_r * y2_r; robert@464: /* shift x1 to x2, sample to x1 */ robert@464: x2_r = x1_r; robert@464: x1_r = temp_in; robert@464: /* shift y1 to y2, result to y1 */ robert@464: y2_r = y1_r; robert@464: y1_r = out_r; robert@464: robert@464: context->audioOut[n * context->audioChannels + 1] = out_l * out_gain; robert@464: context->audioOut[n * context->audioChannels + 0] = out_r * out_gain; robert@464: robert@464: } robert@464: robert@464: } robert@464: robert@464: robert@464: // cleanup_render() is called once at the end, after the audio has stopped. robert@464: // Release any resources that were allocated in initialise_render(). robert@464: robert@464: void cleanup(BelaContext *context, void *userData) robert@464: { robert@464: robert@464: }