chris@164
|
1 /*
|
chris@164
|
2 * AIR-HARP
|
chris@164
|
3 * Physically modelled strings using waveguide junctions and mass-spring-dampers
|
chris@164
|
4 *
|
chris@164
|
5 * render.cpp
|
chris@164
|
6 *
|
chris@164
|
7 * Christian Heinrichs 04/2015
|
chris@164
|
8 *
|
chris@164
|
9 */
|
chris@164
|
10
|
chris@164
|
11
|
chris@164
|
12 #include "MassSpringDamper.h"
|
chris@164
|
13 #include "String.h"
|
chris@164
|
14 #include "Plectrum.h"
|
chris@164
|
15
|
giuliomoro@301
|
16 #include <Bela.h>
|
chris@164
|
17 #include <cmath>
|
chris@164
|
18 #include <stdio.h>
|
chris@164
|
19 #include <cstdlib>
|
chris@164
|
20 #include <rtdk.h>
|
chris@164
|
21
|
chris@164
|
22 #define ACCEL_BUF_SIZE 8
|
chris@164
|
23 #define NUMBER_OF_STRINGS 9
|
chris@164
|
24
|
chris@164
|
25 // PENTATONIC SCALE
|
chris@164
|
26 float gMidinotes[NUMBER_OF_STRINGS] = {40,45,50,55,57,60,62,64,67};
|
chris@164
|
27
|
chris@164
|
28 float gInverseSampleRate;
|
chris@164
|
29
|
chris@164
|
30 float out_gain = 5.0;
|
chris@164
|
31
|
chris@164
|
32 int accelPin_x = 0;
|
chris@164
|
33 int accelPin_y = 1;
|
chris@164
|
34 int accelPin_z = 2;
|
chris@164
|
35
|
chris@164
|
36 MassSpringDamper msd = MassSpringDamper(1,0.1,10);// (10,0.001,10);
|
chris@164
|
37 String strings[NUMBER_OF_STRINGS];
|
chris@164
|
38 Plectrum plectrums[NUMBER_OF_STRINGS];
|
chris@164
|
39
|
chris@164
|
40 float gPlectrumDisplacement = 0;
|
chris@164
|
41
|
chris@164
|
42 float gAccel_x[ACCEL_BUF_SIZE] = {0};
|
chris@164
|
43 int gAccelReadPtr = 0;
|
chris@164
|
44
|
chris@164
|
45 // DC BLOCK BUTTERWORTH
|
chris@164
|
46
|
chris@164
|
47 // Coefficients for 100hz cut-off
|
chris@164
|
48 float a0_l = 0.9899759179893742;
|
chris@164
|
49 float a1_l = -1.9799518359787485;
|
chris@164
|
50 float a2_l = 0.9899759179893742;
|
chris@164
|
51 float a3_l = -1.979851353142371;
|
chris@164
|
52 float a4_l = 0.9800523188151258;
|
chris@164
|
53
|
chris@164
|
54 float a0_r = a0_l;
|
chris@164
|
55 float a1_r = a1_l;
|
chris@164
|
56 float a2_r = a2_l;
|
chris@164
|
57 float a3_r = a3_l;
|
chris@164
|
58 float a4_r = a4_l;
|
chris@164
|
59
|
chris@164
|
60 float x1_l = 0;
|
chris@164
|
61 float x2_l = 0;
|
chris@164
|
62 float y1_l = 0;
|
chris@164
|
63 float y2_l = 0;
|
chris@164
|
64
|
chris@164
|
65 float x1_r = 0;
|
chris@164
|
66 float x2_r = 0;
|
chris@164
|
67 float y1_r = 0;
|
chris@164
|
68 float y2_r = 0;
|
chris@164
|
69
|
chris@164
|
70
|
giuliomoro@301
|
71 bool setup(BelaContext *context, void *userData)
|
chris@164
|
72 {
|
chris@164
|
73
|
chris@164
|
74 gInverseSampleRate = 1.0 / context->audioSampleRate;
|
chris@164
|
75
|
chris@164
|
76 // initialise strings & plectrums
|
chris@164
|
77 for(int i=0;i<NUMBER_OF_STRINGS;i++) {
|
chris@164
|
78
|
chris@164
|
79 plectrums[i] = Plectrum();
|
chris@164
|
80 plectrums[i].setup(250,0.25,0.05);
|
chris@164
|
81
|
chris@164
|
82 strings[i] = String();
|
chris@164
|
83 strings[i].setMidinote(gMidinotes[i]);
|
chris@164
|
84
|
chris@164
|
85 float spacing = 2.0 / (NUMBER_OF_STRINGS+1);
|
chris@164
|
86
|
chris@164
|
87 strings[i].setGlobalPosition( -1 + spacing*(i+1) );
|
chris@164
|
88
|
chris@164
|
89 rt_printf("STRING %d // midinote: %f position: %f\n",i,gMidinotes[i],( -1 + spacing*(i+1) ));
|
chris@164
|
90
|
chris@164
|
91 }
|
chris@164
|
92
|
chris@164
|
93 return true;
|
chris@164
|
94 }
|
chris@164
|
95
|
giuliomoro@301
|
96 void render(BelaContext *context, void *userData)
|
chris@164
|
97 {
|
chris@164
|
98
|
chris@164
|
99 float lastAccel = 0;
|
chris@164
|
100
|
chris@164
|
101 for(int n = 0; n < context->audioFrames; n++) {
|
chris@164
|
102
|
chris@164
|
103 /*
|
chris@164
|
104 *
|
chris@164
|
105 * ACCELEROMETER DATA
|
chris@164
|
106 *
|
chris@164
|
107 */
|
chris@164
|
108
|
chris@164
|
109 // Read accelerometer data from analog input
|
chris@164
|
110 float accel_x = 0;
|
chris@164
|
111 if(n%2) {
|
chris@164
|
112 accel_x = (float)context->analogIn[(n/2)*8+accelPin_x] * 2 - 1; // 15800 - 28300 - 41500
|
chris@164
|
113 lastAccel = accel_x;
|
chris@164
|
114 } else {
|
chris@164
|
115 // grab previous value if !n%2
|
chris@164
|
116 accel_x = lastAccel;
|
chris@164
|
117 }
|
chris@164
|
118
|
chris@164
|
119 // Dead-zone avoids noise when box is lying horizontally on a surface
|
chris@164
|
120
|
chris@164
|
121 float accelDeadZone = 0.1;
|
chris@164
|
122
|
chris@164
|
123 if(accel_x <= accelDeadZone && accel_x >= -accelDeadZone)
|
chris@164
|
124 accel_x = 0;
|
chris@164
|
125
|
chris@164
|
126 // Perform smoothing (moving average) on acceleration value
|
chris@164
|
127 if(++gAccelReadPtr >= ACCEL_BUF_SIZE)
|
chris@164
|
128 gAccelReadPtr = 0;
|
chris@164
|
129 gAccel_x[gAccelReadPtr] = accel_x;
|
chris@164
|
130 float gravity = 0;
|
chris@164
|
131 for(int i=0;i<ACCEL_BUF_SIZE;i++) {
|
chris@164
|
132 gravity = gAccel_x[(gAccelReadPtr-i+ACCEL_BUF_SIZE)%ACCEL_BUF_SIZE];
|
chris@164
|
133 }
|
chris@164
|
134 gravity /= ACCEL_BUF_SIZE;
|
chris@164
|
135
|
chris@164
|
136 /*
|
chris@164
|
137 *
|
chris@164
|
138 * PHYSICS SIMULATION
|
chris@164
|
139 *
|
chris@164
|
140 */
|
chris@164
|
141
|
chris@164
|
142 // The horizontal force (which can be gravity if box is tipped on its side)
|
chris@164
|
143 // is used as the input to a Mass-Spring-Damper model
|
chris@164
|
144 // Plectrum displacement (i.e. when interacting with string) is included
|
chris@164
|
145 float massPosition = (float)msd.update(gravity - gPlectrumDisplacement);
|
chris@164
|
146
|
chris@164
|
147 float out_l = 0;
|
chris@164
|
148 float out_r = 0;
|
chris@164
|
149 // Use this parameter to quickly adjust output gain
|
chris@164
|
150 float gain = 0.0015; // 0.0015 is a good value or 12 strings
|
chris@164
|
151 gPlectrumDisplacement = 0;
|
chris@164
|
152
|
chris@164
|
153 for(int s=0;s<NUMBER_OF_STRINGS;s++) {
|
chris@164
|
154
|
chris@164
|
155 float stringPosition = strings[s].getGlobalPosition();
|
chris@164
|
156
|
chris@164
|
157 float plectrumForce = plectrums[s].update(massPosition, stringPosition);
|
chris@164
|
158 gPlectrumDisplacement += strings[s].getPlectrumDisplacement();
|
chris@164
|
159
|
chris@164
|
160 // calculate panning based on string position (-1->left / 1->right)
|
chris@164
|
161 float panRight = map(stringPosition,1,-1,0.1,1);
|
chris@164
|
162 float panLeft = map(stringPosition,-1,1,0.1,1);
|
chris@164
|
163 panRight *= panRight;
|
chris@164
|
164 panLeft *= panLeft;
|
chris@164
|
165
|
chris@164
|
166 float out = strings[s].update(plectrumForce)*gain;
|
chris@164
|
167
|
chris@164
|
168 out_l += out*panLeft;
|
chris@164
|
169 out_r += out*panRight;
|
chris@164
|
170
|
chris@164
|
171 }
|
chris@164
|
172
|
chris@164
|
173 // APPLY DC-BLOCK FILTER TO OUTPUTS
|
chris@164
|
174
|
chris@164
|
175 // LEFT CHANNEL
|
chris@164
|
176 float temp_in = out_l;
|
chris@164
|
177 /* compute result */
|
chris@164
|
178 out_l = a0_l * out_l + a1_l * x1_l + a2_l * x2_l - a3_l * y1_l - a4_l * y2_l;
|
chris@164
|
179 /* shift x1 to x2, sample to x1 */
|
chris@164
|
180 x2_l = x1_l;
|
chris@164
|
181 x1_l = temp_in;
|
chris@164
|
182 /* shift y1 to y2, result to y1 */
|
chris@164
|
183 y2_l = y1_l;
|
chris@164
|
184 y1_l = out_l;
|
chris@164
|
185
|
chris@164
|
186 // RIGHT CHANNEL
|
chris@164
|
187 temp_in = out_r;
|
chris@164
|
188 /* compute result */
|
chris@164
|
189 out_r = a0_r * out_r + a1_r * x1_r + a2_r * x2_r - a3_r * y1_r - a4_r * y2_r;
|
chris@164
|
190 /* shift x1 to x2, sample to x1 */
|
chris@164
|
191 x2_r = x1_r;
|
chris@164
|
192 x1_r = temp_in;
|
chris@164
|
193 /* shift y1 to y2, result to y1 */
|
chris@164
|
194 y2_r = y1_r;
|
chris@164
|
195 y1_r = out_r;
|
chris@164
|
196
|
chris@164
|
197 context->audioOut[n * context->audioChannels + 1] = out_l * out_gain;
|
chris@164
|
198 context->audioOut[n * context->audioChannels + 0] = out_r * out_gain;
|
chris@164
|
199
|
chris@164
|
200 }
|
chris@164
|
201
|
chris@164
|
202 }
|
chris@164
|
203
|
chris@164
|
204
|
chris@164
|
205 // cleanup_render() is called once at the end, after the audio has stopped.
|
chris@164
|
206 // Release any resources that were allocated in initialise_render().
|
chris@164
|
207
|
giuliomoro@301
|
208 void cleanup(BelaContext *context, void *userData)
|
chris@164
|
209 {
|
chris@164
|
210
|
chris@164
|
211 }
|