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