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@8:ef4dca8695d1, 2016-07-30 (annotated)
- Committer:
- dallegre
- Date:
- Sat Jul 30 23:57:45 2016 +0000
- Revision:
- 8:ef4dca8695d1
- Parent:
- 7:c84086dce9ac
- Child:
- 9:f42863126573
got it just about fast enough to run at 22khz. see if you can make it more efficient or speed up the processor by configuration.
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 | 8:ef4dca8695d1 | 18 | #define SAMPLINGFREQ 22 //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 | 8:ef4dca8695d1 | 38 | modLos = 0, modL2os = 0, modL3os = 0, modL4os = 0, modRos = 0, modR2os = 0, modR3os = 0, modR4os = 0, |
| dallegre | 8:ef4dca8695d1 | 39 | upperLim, lowerLim, |
| dallegre | 3:f8bc3ac22ffd | 40 | left_out_f = 0, right_out_f = 0; |
| dallegre | 7:c84086dce9ac | 41 | 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 | 42 | float audioFeedbackRight,audioFeedbackLeft; |
| dallegre | 0:c12e968e5b60 | 43 | |
| dallegre | 0:c12e968e5b60 | 44 | float LPdampingFreq = 11000, HPdampingFreq = 100; //some parameters you might want to adjust. |
| dallegre | 0:c12e968e5b60 | 45 | int LPdampingPoles = 2, HPdampingPoles = 0; //I don't think the highpass is quite working. |
| dallegre | 0:c12e968e5b60 | 46 | |
| dallegre | 0:c12e968e5b60 | 47 | OnePoleLp feedbackFilter; |
| dallegre | 0:c12e968e5b60 | 48 | OnePoleLp lengthFilter; |
| dallegre | 7:c84086dce9ac | 49 | OnePoleLp dampingFilter; |
| dallegre | 1:a4ce08417c60 | 50 | AllPass delayLeft; |
| dallegre | 3:f8bc3ac22ffd | 51 | AllPass delayLeft2; |
| dallegre | 3:f8bc3ac22ffd | 52 | AllPass delayLeft3; |
| dallegre | 7:c84086dce9ac | 53 | AllPass delayLeft4; |
| dallegre | 1:a4ce08417c60 | 54 | AllPass delayRight; |
| dallegre | 2:1b50325e256f | 55 | AllPass delayRight2; |
| dallegre | 3:f8bc3ac22ffd | 56 | AllPass delayRight3; |
| dallegre | 7:c84086dce9ac | 57 | AllPass delayRight4; |
| dallegre | 3:f8bc3ac22ffd | 58 | gsOsc OscillatorL; |
| dallegre | 3:f8bc3ac22ffd | 59 | gsOsc OscillatorL2; |
| dallegre | 3:f8bc3ac22ffd | 60 | gsOsc OscillatorL3; |
| dallegre | 7:c84086dce9ac | 61 | gsOsc OscillatorL4; |
| dallegre | 3:f8bc3ac22ffd | 62 | gsOsc OscillatorR; |
| dallegre | 3:f8bc3ac22ffd | 63 | gsOsc OscillatorR2; |
| dallegre | 3:f8bc3ac22ffd | 64 | gsOsc OscillatorR3; |
| dallegre | 7:c84086dce9ac | 65 | gsOsc OscillatorR4; |
| dallegre | 0:c12e968e5b60 | 66 | |
| dallegre | 0:c12e968e5b60 | 67 | void I2S_send(void){ |
| dallegre | 0:c12e968e5b60 | 68 | cs.write(0); //0 for left output |
| dallegre | 0:c12e968e5b60 | 69 | left_in = spi.write(left_out); |
| dallegre | 0:c12e968e5b60 | 70 | cs.write(1); //1 for right output |
| dallegre | 0:c12e968e5b60 | 71 | right_in = spi.write(right_out); |
| dallegre | 0:c12e968e5b60 | 72 | |
| dallegre | 0:c12e968e5b60 | 73 | left_in_f = intToFloat(left_in); |
| dallegre | 0:c12e968e5b60 | 74 | |
| dallegre | 0:c12e968e5b60 | 75 | /////////////////////////////////////////dsp part////////////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 76 | |
| dallegre | 8:ef4dca8695d1 | 77 | ///* |
| dallegre | 0:c12e968e5b60 | 78 | //process control signals |
| dallegre | 0:c12e968e5b60 | 79 | feedback2 = feedbackFilter.process(feedback); |
| dallegre | 8:ef4dca8695d1 | 80 | //maybe move this to the dac sampling interrupt. seems like there was a reason it's here though. |
| dallegre | 8:ef4dca8695d1 | 81 | //damping2 = lengthFilter.process(damping); |
| dallegre | 8:ef4dca8695d1 | 82 | //damping3 = lengthFilter.process(damping2); |
| dallegre | 8:ef4dca8695d1 | 83 | //damping3 *= (5000); //make about 10k the max cutoff for damping |
| dallegre | 8:ef4dca8695d1 | 84 | |
| dallegre | 8:ef4dca8695d1 | 85 | //dampingFilter.setFc(damping3/(SAMPLINGFREQ*1000)); |
| dallegre | 0:c12e968e5b60 | 86 | |
| dallegre | 2:1b50325e256f | 87 | //process audio. probably calculate these coefficients elsewhere so you don't do the multiply all the time. |
| dallegre | 2:1b50325e256f | 88 | //left delay mod |
| dallegre | 8:ef4dca8695d1 | 89 | modL = modLos + OscillatorL.process(1); |
| dallegre | 8:ef4dca8695d1 | 90 | modL2 = modL2os + OscillatorL2.process(1); |
| dallegre | 8:ef4dca8695d1 | 91 | modL3 = modL3os + OscillatorL3.process(1); |
| dallegre | 8:ef4dca8695d1 | 92 | modL4 = modL4os + OscillatorL3.process(0); |
| dallegre | 2:1b50325e256f | 93 | //right delay mod |
| dallegre | 8:ef4dca8695d1 | 94 | modR = modRos + OscillatorR.process(0); |
| dallegre | 8:ef4dca8695d1 | 95 | modR2 = modR2os + OscillatorR2.process(0); |
| dallegre | 8:ef4dca8695d1 | 96 | modR3 = modR3os + OscillatorR3.process(1); |
| dallegre | 8:ef4dca8695d1 | 97 | modR4 = modR4os + OscillatorR4.process(1); |
| dallegre | 8:ef4dca8695d1 | 98 | |
| dallegre | 8:ef4dca8695d1 | 99 | if(modL > upperLim) |
| dallegre | 8:ef4dca8695d1 | 100 | modL = upperLim; |
| dallegre | 8:ef4dca8695d1 | 101 | if(modL < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 102 | modL = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 103 | if(modL2 > upperLim) |
| dallegre | 8:ef4dca8695d1 | 104 | modL2 = upperLim; |
| dallegre | 8:ef4dca8695d1 | 105 | if(modL2 < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 106 | modL2 = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 107 | if(modL3 > upperLim) |
| dallegre | 8:ef4dca8695d1 | 108 | modL3 = upperLim; |
| dallegre | 8:ef4dca8695d1 | 109 | if(modL3 < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 110 | modL3 = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 111 | if(modL4 > upperLim) |
| dallegre | 8:ef4dca8695d1 | 112 | modL4 = upperLim; |
| dallegre | 8:ef4dca8695d1 | 113 | if(modL4 < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 114 | modL4 = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 115 | if(modR > upperLim) |
| dallegre | 8:ef4dca8695d1 | 116 | modR = upperLim; |
| dallegre | 8:ef4dca8695d1 | 117 | if(modR < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 118 | modR = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 119 | if(modR2 > upperLim) |
| dallegre | 8:ef4dca8695d1 | 120 | modR2 = upperLim; |
| dallegre | 8:ef4dca8695d1 | 121 | if(modR2 < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 122 | modR2 = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 123 | if(modR3 > upperLim) |
| dallegre | 8:ef4dca8695d1 | 124 | modR3 = upperLim; |
| dallegre | 8:ef4dca8695d1 | 125 | if(modR3 < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 126 | modR3 = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 127 | if(modR4 > upperLim) |
| dallegre | 8:ef4dca8695d1 | 128 | modR4 = upperLim; |
| dallegre | 8:ef4dca8695d1 | 129 | if(modR4 < lowerLim) |
| dallegre | 8:ef4dca8695d1 | 130 | modR4 = lowerLim; |
| dallegre | 8:ef4dca8695d1 | 131 | |
| dallegre | 7:c84086dce9ac | 132 | left_out_2 = delayLeft2.process(satAdd(left_in_f,loopfb),.3,modL); |
| dallegre | 7:c84086dce9ac | 133 | left_out_3 = delayLeft3.process(left_out_2,.4,modL2); |
| dallegre | 7:c84086dce9ac | 134 | left_out_4 = delayLeft4.process(left_out_3,.4,modL3); |
| dallegre | 7:c84086dce9ac | 135 | left_out_f = delayLeft.process(left_out_4,.4,modL4); |
| dallegre | 7:c84086dce9ac | 136 | right_out_2 = delayRight2.process(satAdd(left_in_f,loopfb2),.3,modR); |
| dallegre | 7:c84086dce9ac | 137 | right_out_3 = delayRight3.process(right_out_2,.4,modR2); |
| dallegre | 7:c84086dce9ac | 138 | right_out_4 = delayRight4.process(right_out_3,.4,modR3); |
| dallegre | 7:c84086dce9ac | 139 | right_out_f = delayRight.process(right_out_4,.4,modR4); |
| dallegre | 7:c84086dce9ac | 140 | |
| dallegre | 8:ef4dca8695d1 | 141 | loopfb2 = left_out_f*feedback2; //swizzle the feedbacks left to right |
| dallegre | 8:ef4dca8695d1 | 142 | loopfb = right_out_f*feedback2; |
| dallegre | 8:ef4dca8695d1 | 143 | |
| dallegre | 8:ef4dca8695d1 | 144 | //apply damping |
| dallegre | 7:c84086dce9ac | 145 | //loopfb = dampingFilter.process(loopfb); |
| dallegre | 7:c84086dce9ac | 146 | //loopfb2 = dampingFilter.process(loopfb2); |
| dallegre | 8:ef4dca8695d1 | 147 | //*/ |
| dallegre | 8:ef4dca8695d1 | 148 | //left_out_f = left_in_f; |
| dallegre | 8:ef4dca8695d1 | 149 | //right_out_f = left_in_f; |
| dallegre | 8:ef4dca8695d1 | 150 | |
| dallegre | 6:df635006ee1c | 151 | left_out_u = left_out_f; |
| dallegre | 6:df635006ee1c | 152 | right_out_u = right_out_f; |
| dallegre | 0:c12e968e5b60 | 153 | |
| dallegre | 0:c12e968e5b60 | 154 | /////////////////////////////////////////end of dsp part///////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 155 | |
| dallegre | 0:c12e968e5b60 | 156 | left_out = unsignedToSigned(left_out_u); |
| dallegre | 0:c12e968e5b60 | 157 | right_out = unsignedToSigned(right_out_u); |
| dallegre | 0:c12e968e5b60 | 158 | |
| dallegre | 0:c12e968e5b60 | 159 | } |
| dallegre | 0:c12e968e5b60 | 160 | |
| dallegre | 0:c12e968e5b60 | 161 | void analog_read(void){ |
| dallegre | 0:c12e968e5b60 | 162 | //this is the stuff for the feedback amount pot |
| dallegre | 0:c12e968e5b60 | 163 | meas2f = analog_value2.read_u16(); //converts unint16 value to float |
| dallegre | 0:c12e968e5b60 | 164 | feedback = meas2f/4100; //now you have a value between 0 and 1 that will control the feedback amount. |
| dallegre | 7:c84086dce9ac | 165 | feedback *= feedback; |
| dallegre | 0:c12e968e5b60 | 166 | |
| dallegre | 0:c12e968e5b60 | 167 | //this is the stuff for the delay time pot |
| dallegre | 0:c12e968e5b60 | 168 | meas = 4096 - analog_value.read_u16(); //values go from 1 to 4095 |
| dallegre | 0:c12e968e5b60 | 169 | //if(abs(meas - measprev) > 5){ |
| dallegre | 0:c12e968e5b60 | 170 | measprev = meas; |
| dallegre | 7:c84086dce9ac | 171 | damping = meas; |
| dallegre | 7:c84086dce9ac | 172 | damping /= 4200; //this number took some tweaking. |
| dallegre | 7:c84086dce9ac | 173 | damping *= damping; |
| dallegre | 0:c12e968e5b60 | 174 | //} |
| dallegre | 0:c12e968e5b60 | 175 | } |
| dallegre | 0:c12e968e5b60 | 176 | |
| dallegre | 0:c12e968e5b60 | 177 | int main(){ |
| dallegre | 0:c12e968e5b60 | 178 | |
| dallegre | 0:c12e968e5b60 | 179 | //set up baud rate for 11.2MHz crystal |
| dallegre | 0:c12e968e5b60 | 180 | pc.baud(.7*9600*2); |
| dallegre | 0:c12e968e5b60 | 181 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 182 | |
| dallegre | 0:c12e968e5b60 | 183 | //do I2C transfter to initialize codec |
| dallegre | 0:c12e968e5b60 | 184 | codecInit(); |
| dallegre | 0:c12e968e5b60 | 185 | |
| dallegre | 0:c12e968e5b60 | 186 | //initialize fixed filters |
| dallegre | 0:c12e968e5b60 | 187 | double feedbackCutoff = 10.0/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 188 | feedbackFilter.setFc(feedbackCutoff); |
| dallegre | 0:c12e968e5b60 | 189 | double lengthCutoff = 3.0/(SAMPLINGFREQ*1000); |
| dallegre | 0:c12e968e5b60 | 190 | lengthFilter.setFc(lengthCutoff); |
| dallegre | 8:ef4dca8695d1 | 191 | |
| dallegre | 8:ef4dca8695d1 | 192 | //initize oscillators and mod offsets |
| dallegre | 8:ef4dca8695d1 | 193 | modLos = DELAYLEN*.74; |
| dallegre | 8:ef4dca8695d1 | 194 | modL2os = DELAYLEN*.43; |
| dallegre | 8:ef4dca8695d1 | 195 | modL3os = DELAYLEN*.33; |
| dallegre | 8:ef4dca8695d1 | 196 | modL4os = DELAYLEN*.37; |
| dallegre | 8:ef4dca8695d1 | 197 | OscillatorL.setF(.53,DELAYLEN/66); |
| dallegre | 8:ef4dca8695d1 | 198 | OscillatorL2.setF(.03,DELAYLEN/65); |
| dallegre | 8:ef4dca8695d1 | 199 | OscillatorL3.setF(.02,DELAYLEN/78); |
| dallegre | 8:ef4dca8695d1 | 200 | OscillatorL4.setF(.023,DELAYLEN/78); |
| dallegre | 8:ef4dca8695d1 | 201 | //right delay mod |
| dallegre | 8:ef4dca8695d1 | 202 | modRos = DELAYLEN*.75; |
| dallegre | 8:ef4dca8695d1 | 203 | modR2os = DELAYLEN*.49; |
| dallegre | 8:ef4dca8695d1 | 204 | modR3os = DELAYLEN*.33; |
| dallegre | 8:ef4dca8695d1 | 205 | modR4os = DELAYLEN*.37; |
| dallegre | 8:ef4dca8695d1 | 206 | OscillatorR.setF(.51,DELAYLEN/69); |
| dallegre | 8:ef4dca8695d1 | 207 | OscillatorR2.setF(.03,DELAYLEN/67); |
| dallegre | 8:ef4dca8695d1 | 208 | OscillatorR3.setF(.02,DELAYLEN/78); |
| dallegre | 8:ef4dca8695d1 | 209 | OscillatorR4.setF(.023,DELAYLEN/78); |
| dallegre | 8:ef4dca8695d1 | 210 | upperLim = .9*DELAYLEN; |
| dallegre | 8:ef4dca8695d1 | 211 | lowerLim = .05*DELAYLEN; |
| dallegre | 0:c12e968e5b60 | 212 | |
| dallegre | 0:c12e968e5b60 | 213 | //set up I2S |
| dallegre | 0:c12e968e5b60 | 214 | spi.frequency(8e6*2); //8MHz spi. This is fast enough for 88KHz 16 bit sampling. |
| dallegre | 0:c12e968e5b60 | 215 | spi.format(16,0); //16 bit. clock polarity 0, phase 0. |
| dallegre | 0:c12e968e5b60 | 216 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 217 | |
| dallegre | 0:c12e968e5b60 | 218 | //audio rate timers. 29 and 44 are not very good. Might want to use an external clock for the Nucleo. |
| dallegre | 0:c12e968e5b60 | 219 | if(SAMPLINGFREQ == 22) |
| dallegre | 0:c12e968e5b60 | 220 | 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 | 221 | if(SAMPLINGFREQ == 29) |
| dallegre | 0:c12e968e5b60 | 222 | 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 | 223 | if(SAMPLINGFREQ == 44) |
| dallegre | 0:c12e968e5b60 | 224 | 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 | 225 | if(SAMPLINGFREQ == 88) |
| dallegre | 0:c12e968e5b60 | 226 | timer.attach(&I2S_send,8e-6); //88KHz sampling. Need to calculate. |
| dallegre | 0:c12e968e5b60 | 227 | |
| dallegre | 0:c12e968e5b60 | 228 | //control signal timer |
| dallegre | 0:c12e968e5b60 | 229 | timer2.attach(&analog_read,25e-3); |
| dallegre | 0:c12e968e5b60 | 230 | |
| dallegre | 0:c12e968e5b60 | 231 | //go! |
| dallegre | 0:c12e968e5b60 | 232 | while(1){} |
| dallegre | 0:c12e968e5b60 | 233 | } |