Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.cpp@7:c84086dce9ac, 2016-07-30 (annotated)
- Committer:
- dallegre
- Date:
- Sat Jul 30 21:34:28 2016 +0000
- Revision:
- 7:c84086dce9ac
- Parent:
- 6:df635006ee1c
- Child:
- 8:ef4dca8695d1
got it! still sounds a bit low fi;
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| dallegre | 0:c12e968e5b60 | 1 | //delay dsp for stm32f401re nucleo board with openmusiclabs audio codec shield. Need to jumpter the i2c pins from the |
| dallegre | 0:c12e968e5b60 | 2 | //top right arduino headers on the nucleo board to the bottom left 2 pins on the codec shield. Bottom left i2c pins are |
| dallegre | 0:c12e968e5b60 | 3 | //not mbed enabled on the nucleo boards. |
| dallegre | 0:c12e968e5b60 | 4 | //Also need to install a 11.2896MHz crystal on nucleo board and connect the appropriate jumpers as explained in the nucleo |
| dallegre | 0:c12e968e5b60 | 5 | //user manual. Two 20pf capacitors are used with the crystal. The series resistors going from the crystal to the stm32 chip |
| dallegre | 0:c12e968e5b60 | 6 | //are just shorts. |
| dallegre | 0:c12e968e5b60 | 7 | //Preceed audio path with a 1/47 voltage divider and follow with a 47X inverting op amp to bring up to eurorack levels. Use DC |
| dallegre | 0:c12e968e5b60 | 8 | //blocking cap after the op amp. |
| dallegre | 0:c12e968e5b60 | 9 | //Note that this is setup for the left input only and drives both outputs. It is easy to modify the code for stereo effects |
| dallegre | 0:c12e968e5b60 | 10 | //and both inputs. |
| dallegre | 0:c12e968e5b60 | 11 | |
| dallegre | 0:c12e968e5b60 | 12 | //June 2015.. |
| dallegre | 0:c12e968e5b60 | 13 | //Added interpolation so it acts more like a bbd or tape delay. It is easy to switch between cosine and linear interpolation. |
| dallegre | 0:c12e968e5b60 | 14 | //Cubic interpolation might be a possibility if I can figure out how it works. |
| dallegre | 0:c12e968e5b60 | 15 | //Added a nice lowpass filter class. Might want to put the delay in a class like this for reverb work. Need to make an oscillator next. |
| dallegre | 0:c12e968e5b60 | 16 | |
| dallegre | 7:c84086dce9ac | 17 | #define DELAYLEN 2900 //24,000 is about as far as you can go in the ram. |
| dallegre | 7:c84086dce9ac | 18 | #define SAMPLINGFREQ 44 //roughly the smapling frequency in KHz. options are 22,29,44,88 for 22khz,29khz,etc.. |
| dallegre | 0:c12e968e5b60 | 19 | |
| dallegre | 0:c12e968e5b60 | 20 | #include "mbed.h" |
| dallegre | 1:a4ce08417c60 | 21 | #include "dsp.h" |
| dallegre | 1:a4ce08417c60 | 22 | #include "allpass.h" |
| dallegre | 0:c12e968e5b60 | 23 | #include "codec.h" |
| dallegre | 2:1b50325e256f | 24 | #include "osc.h" |
| dallegre | 0:c12e968e5b60 | 25 | |
| dallegre | 0:c12e968e5b60 | 26 | SPI spi(SPI_MOSI,SPI_MISO,SPI_SCK); |
| dallegre | 0:c12e968e5b60 | 27 | Ticker timer; |
| dallegre | 0:c12e968e5b60 | 28 | Ticker timer2; |
| dallegre | 0:c12e968e5b60 | 29 | AnalogIn analog_value(A1); //delay time pot |
| dallegre | 0:c12e968e5b60 | 30 | AnalogIn analog_value2(A0); //feedback amount pot |
| dallegre | 0:c12e968e5b60 | 31 | DigitalOut cs(D10); |
| dallegre | 0:c12e968e5b60 | 32 | |
| dallegre | 0:c12e968e5b60 | 33 | int left_out = 0, right_out = 0, left_out_u = 0, right_out_u = 0, left_in = 0, right_in = 0, |
| dallegre | 0:c12e968e5b60 | 34 | index1 = 0, index2 = (DELAYLEN*0.99), index3 = (DELAYLEN*0.99); |
| dallegre | 0:c12e968e5b60 | 35 | uint16_t left_in_u = 0, meas = 0, measprev = 0; |
| dallegre | 0:c12e968e5b60 | 36 | float left_in_f = 0, feedback = 0, feedback2 = 0, feedback3 = 0, offset = 10.0, |
| dallegre | 7:c84086dce9ac | 37 | meas2f = 0.5, damping = 0, damping2 = 0, damping3 = 0, modL = 0, modL2 = 0, modL3 = 0, modL4, modR = 0, modR2 = 0, modR3 = 0, modR4, |
| dallegre | 3:f8bc3ac22ffd | 38 | left_out_f = 0, right_out_f = 0; |
| dallegre | 7:c84086dce9ac | 39 | float left_out_2 = 0, right_out_2 = 0, left_out_3 = 0, right_out_3 = 0, left_out_4 = 0, right_out_4 = 0, loopfb = 0, loopfb2 = 0; |
| dallegre | 0:c12e968e5b60 | 40 | float audioFeedbackRight,audioFeedbackLeft; |
| dallegre | 0:c12e968e5b60 | 41 | |
| dallegre | 0:c12e968e5b60 | 42 | float LPdampingFreq = 11000, HPdampingFreq = 100; //some parameters you might want to adjust. |
| dallegre | 0:c12e968e5b60 | 43 | int LPdampingPoles = 2, HPdampingPoles = 0; //I don't think the highpass is quite working. |
| dallegre | 0:c12e968e5b60 | 44 | |
| dallegre | 0:c12e968e5b60 | 45 | OnePoleLp feedbackFilter; |
| dallegre | 0:c12e968e5b60 | 46 | OnePoleLp lengthFilter; |
| dallegre | 7:c84086dce9ac | 47 | OnePoleLp dampingFilter; |
| dallegre | 1:a4ce08417c60 | 48 | AllPass delayLeft; |
| dallegre | 3:f8bc3ac22ffd | 49 | AllPass delayLeft2; |
| dallegre | 3:f8bc3ac22ffd | 50 | AllPass delayLeft3; |
| dallegre | 7:c84086dce9ac | 51 | AllPass delayLeft4; |
| dallegre | 1:a4ce08417c60 | 52 | AllPass delayRight; |
| dallegre | 2:1b50325e256f | 53 | AllPass delayRight2; |
| dallegre | 3:f8bc3ac22ffd | 54 | AllPass delayRight3; |
| dallegre | 7:c84086dce9ac | 55 | AllPass delayRight4; |
| dallegre | 3:f8bc3ac22ffd | 56 | gsOsc OscillatorL; |
| dallegre | 3:f8bc3ac22ffd | 57 | gsOsc OscillatorL2; |
| dallegre | 3:f8bc3ac22ffd | 58 | gsOsc OscillatorL3; |
| dallegre | 7:c84086dce9ac | 59 | gsOsc OscillatorL4; |
| dallegre | 3:f8bc3ac22ffd | 60 | gsOsc OscillatorR; |
| dallegre | 3:f8bc3ac22ffd | 61 | gsOsc OscillatorR2; |
| dallegre | 3:f8bc3ac22ffd | 62 | gsOsc OscillatorR3; |
| dallegre | 7:c84086dce9ac | 63 | gsOsc OscillatorR4; |
| dallegre | 0:c12e968e5b60 | 64 | |
| dallegre | 0:c12e968e5b60 | 65 | void I2S_send(void){ |
| dallegre | 0:c12e968e5b60 | 66 | cs.write(0); //0 for left output |
| dallegre | 0:c12e968e5b60 | 67 | left_in = spi.write(left_out); |
| dallegre | 0:c12e968e5b60 | 68 | cs.write(1); //1 for right output |
| dallegre | 0:c12e968e5b60 | 69 | right_in = spi.write(right_out); |
| dallegre | 0:c12e968e5b60 | 70 | |
| dallegre | 0:c12e968e5b60 | 71 | left_in_f = intToFloat(left_in); |
| dallegre | 0:c12e968e5b60 | 72 | |
| dallegre | 0:c12e968e5b60 | 73 | /////////////////////////////////////////dsp part////////////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 74 | |
| dallegre | 0:c12e968e5b60 | 75 | //process control signals |
| dallegre | 0:c12e968e5b60 | 76 | feedback2 = feedbackFilter.process(feedback); |
| dallegre | 0:c12e968e5b60 | 77 | feedback3 = feedbackFilter.process(feedback2); |
| dallegre | 0:c12e968e5b60 | 78 | //update delay times with every audio sample if you're going to filter them. |
| dallegre | 7:c84086dce9ac | 79 | damping2 = lengthFilter.process(damping); |
| dallegre | 7:c84086dce9ac | 80 | damping3 = lengthFilter.process(damping2); |
| dallegre | 7:c84086dce9ac | 81 | damping3 *= (5000); //make about 10k the max cutoff for damping |
| dallegre | 7:c84086dce9ac | 82 | |
| dallegre | 7:c84086dce9ac | 83 | |
| dallegre | 7:c84086dce9ac | 84 | dampingFilter.setFc(damping3); |
| dallegre | 0:c12e968e5b60 | 85 | |
| dallegre | 2:1b50325e256f | 86 | //process audio. probably calculate these coefficients elsewhere so you don't do the multiply all the time. |
| dallegre | 2:1b50325e256f | 87 | //left delay mod |
| dallegre | 7:c84086dce9ac | 88 | modL = DELAYLEN*.74 + OscillatorL.process(.53,DELAYLEN/66,1); |
| dallegre | 7:c84086dce9ac | 89 | modL2 = DELAYLEN*.43 + OscillatorL2.process(.03,DELAYLEN/65,1); |
| dallegre | 7:c84086dce9ac | 90 | modL3 = DELAYLEN*.33 + OscillatorL3.process(.02,DELAYLEN/78,1); |
| dallegre | 7:c84086dce9ac | 91 | modL3 = DELAYLEN*.37 + OscillatorL3.process(.023,DELAYLEN/78,0); |
| dallegre | 2:1b50325e256f | 92 | //right delay mod |
| dallegre | 7:c84086dce9ac | 93 | modR = DELAYLEN*.75 + OscillatorR.process(.51,DELAYLEN/69,0); |
| dallegre | 7:c84086dce9ac | 94 | modR2 = DELAYLEN*.49 + OscillatorR2.process(.03,DELAYLEN/67,0); |
| dallegre | 7:c84086dce9ac | 95 | modR3 = DELAYLEN*.33 + OscillatorR3.process(.02,DELAYLEN/78,1); |
| dallegre | 7:c84086dce9ac | 96 | modR4 = DELAYLEN*.37 + OscillatorR4.process(.023,DELAYLEN/78,1); |
| dallegre | 7:c84086dce9ac | 97 | if(modL > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 98 | modL = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 99 | if(modL < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 100 | modL = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 101 | if(modL2 > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 102 | modL2 = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 103 | if(modL2 < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 104 | modL2 = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 105 | if(modL3 > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 106 | modL3 = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 107 | if(modL3 < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 108 | modL3 = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 109 | if(modL4 > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 110 | modL4 = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 111 | if(modL4 < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 112 | modL4 = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 113 | if(modR > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 114 | modR = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 115 | if(modR < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 116 | modR = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 117 | if(modR2 > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 118 | modR2 = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 119 | if(modR2 < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 120 | modR2 = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 121 | if(modR3 > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 122 | modR3 = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 123 | if(modR3 < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 124 | modR3 = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 125 | if(modR4 > DELAYLEN*.9) |
| dallegre | 7:c84086dce9ac | 126 | modR4 = DELAYLEN*.9; |
| dallegre | 7:c84086dce9ac | 127 | if(modR4 < DELAYLEN*.05) |
| dallegre | 7:c84086dce9ac | 128 | modR4 = DELAYLEN*.05; |
| dallegre | 7:c84086dce9ac | 129 | left_out_2 = delayLeft2.process(satAdd(left_in_f,loopfb),.3,modL); |
| dallegre | 7:c84086dce9ac | 130 | left_out_3 = delayLeft3.process(left_out_2,.4,modL2); |
| dallegre | 7:c84086dce9ac | 131 | left_out_4 = delayLeft4.process(left_out_3,.4,modL3); |
| dallegre | 7:c84086dce9ac | 132 | left_out_f = delayLeft.process(left_out_4,.4,modL4); |
| dallegre | 7:c84086dce9ac | 133 | right_out_2 = delayRight2.process(satAdd(left_in_f,loopfb2),.3,modR); |
| dallegre | 7:c84086dce9ac | 134 | right_out_3 = delayRight3.process(right_out_2,.4,modR2); |
| dallegre | 7:c84086dce9ac | 135 | right_out_4 = delayRight4.process(right_out_3,.4,modR3); |
| dallegre | 7:c84086dce9ac | 136 | right_out_f = delayRight.process(right_out_4,.4,modR4); |
| dallegre | 7:c84086dce9ac | 137 | //loopfb = dampingFilter.process(left_out_f); |
| dallegre | 7:c84086dce9ac | 138 | //loopfb2 = dampingFilter.process(right_out_f); |
| dallegre | 7:c84086dce9ac | 139 | |
| dallegre | 7:c84086dce9ac | 140 | //loopfb *= feedback3; |
| dallegre | 7:c84086dce9ac | 141 | //loopfb2 *= feedback3; |
| dallegre | 7:c84086dce9ac | 142 | loopfb2 = left_out_f*feedback3; //swizzle the feedbacks left to right |
| dallegre | 7:c84086dce9ac | 143 | loopfb = right_out_f*feedback3; |
| dallegre | 7:c84086dce9ac | 144 | //loopfb = dampingFilter.process(loopfb); |
| dallegre | 7:c84086dce9ac | 145 | //loopfb2 = dampingFilter.process(loopfb2); |
| dallegre | 7:c84086dce9ac | 146 | //left_out_u = satAdd(left_in_f,left_out_f*damping3/200); |
| dallegre | 7:c84086dce9ac | 147 | //right_out_u = satAdd(left_in_f,right_out_f*damping3/200); |
| dallegre | 6:df635006ee1c | 148 | left_out_u = left_out_f; |
| dallegre | 6:df635006ee1c | 149 | right_out_u = right_out_f; |
| dallegre | 0:c12e968e5b60 | 150 | |
| dallegre | 0:c12e968e5b60 | 151 | /////////////////////////////////////////end of dsp part///////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 152 | |
| dallegre | 0:c12e968e5b60 | 153 | left_out = unsignedToSigned(left_out_u); |
| dallegre | 0:c12e968e5b60 | 154 | right_out = unsignedToSigned(right_out_u); |
| dallegre | 0:c12e968e5b60 | 155 | |
| dallegre | 0:c12e968e5b60 | 156 | } |
| dallegre | 0:c12e968e5b60 | 157 | |
| dallegre | 0:c12e968e5b60 | 158 | void analog_read(void){ |
| dallegre | 0:c12e968e5b60 | 159 | //this is the stuff for the feedback amount pot |
| dallegre | 0:c12e968e5b60 | 160 | meas2f = analog_value2.read_u16(); //converts unint16 value to float |
| dallegre | 0:c12e968e5b60 | 161 | feedback = meas2f/4100; //now you have a value between 0 and 1 that will control the feedback amount. |
| dallegre | 7:c84086dce9ac | 162 | feedback *= feedback; |
| dallegre | 0:c12e968e5b60 | 163 | |
| dallegre | 0:c12e968e5b60 | 164 | //this is the stuff for the delay time pot |
| dallegre | 0:c12e968e5b60 | 165 | meas = 4096 - analog_value.read_u16(); //values go from 1 to 4095 |
| dallegre | 0:c12e968e5b60 | 166 | //if(abs(meas - measprev) > 5){ |
| dallegre | 0:c12e968e5b60 | 167 | measprev = meas; |
| dallegre | 7:c84086dce9ac | 168 | damping = meas; |
| dallegre | 7:c84086dce9ac | 169 | damping /= 4200; //this number took some tweaking. |
| dallegre | 7:c84086dce9ac | 170 | damping *= damping; |
| dallegre | 0:c12e968e5b60 | 171 | //} |
| dallegre | 0:c12e968e5b60 | 172 | } |
| dallegre | 0:c12e968e5b60 | 173 | |
| dallegre | 0:c12e968e5b60 | 174 | int main(){ |
| dallegre | 0:c12e968e5b60 | 175 | |
| dallegre | 0:c12e968e5b60 | 176 | //set up baud rate for 11.2MHz crystal |
| dallegre | 0:c12e968e5b60 | 177 | pc.baud(.7*9600*2); |
| dallegre | 0:c12e968e5b60 | 178 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 179 | |
| dallegre | 0:c12e968e5b60 | 180 | //do I2C transfter to initialize codec |
| dallegre | 0:c12e968e5b60 | 181 | codecInit(); |
| dallegre | 0:c12e968e5b60 | 182 | |
| dallegre | 0:c12e968e5b60 | 183 | //initialize fixed filters |
| dallegre | 0:c12e968e5b60 | 184 | double feedbackCutoff = 10.0/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 185 | feedbackFilter.setFc(feedbackCutoff); |
| dallegre | 0:c12e968e5b60 | 186 | double lengthCutoff = 3.0/(SAMPLINGFREQ*1000); |
| dallegre | 0:c12e968e5b60 | 187 | lengthFilter.setFc(lengthCutoff); |
| dallegre | 0:c12e968e5b60 | 188 | |
| dallegre | 0:c12e968e5b60 | 189 | //set up I2S |
| dallegre | 0:c12e968e5b60 | 190 | spi.frequency(8e6*2); //8MHz spi. This is fast enough for 88KHz 16 bit sampling. |
| dallegre | 0:c12e968e5b60 | 191 | spi.format(16,0); //16 bit. clock polarity 0, phase 0. |
| dallegre | 0:c12e968e5b60 | 192 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 193 | |
| dallegre | 0:c12e968e5b60 | 194 | //audio rate timers. 29 and 44 are not very good. Might want to use an external clock for the Nucleo. |
| dallegre | 0:c12e968e5b60 | 195 | if(SAMPLINGFREQ == 22) |
| dallegre | 0:c12e968e5b60 | 196 | timer.attach(&I2S_send,32e-6); //22KHz sampling. 12.2008MHz/512 = 21.876KHz. 1/(21.876KHz(8/11.2008)) = 64e-6 Sec and / by 2 because the codec divides by to before sending the osc out. |
| dallegre | 0:c12e968e5b60 | 197 | if(SAMPLINGFREQ == 29) |
| dallegre | 0:c12e968e5b60 | 198 | timer.attach(&I2S_send,24e-6); //29.168KHz sampling. 11.2008MHz/384 = 19.168KHz. 1/(29.168KHz(8/11.2008)) = 48.0012e-6 Sec and / by 2 because the codec divides by to before sending the osc out. |
| dallegre | 0:c12e968e5b60 | 199 | if(SAMPLINGFREQ == 44) |
| dallegre | 0:c12e968e5b60 | 200 | timer.attach(&I2S_send,16.0e-6); //44.1KHz sampling. almost exactly 32e-6 and then divide by 2. Doesn't sound right yet. |
| dallegre | 0:c12e968e5b60 | 201 | if(SAMPLINGFREQ == 88) |
| dallegre | 0:c12e968e5b60 | 202 | timer.attach(&I2S_send,8e-6); //88KHz sampling. Need to calculate. |
| dallegre | 0:c12e968e5b60 | 203 | |
| dallegre | 0:c12e968e5b60 | 204 | //control signal timer |
| dallegre | 0:c12e968e5b60 | 205 | timer2.attach(&analog_read,25e-3); |
| dallegre | 0:c12e968e5b60 | 206 | |
| dallegre | 0:c12e968e5b60 | 207 | //go! |
| dallegre | 0:c12e968e5b60 | 208 | while(1){} |
| dallegre | 0:c12e968e5b60 | 209 | } |