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@9:f42863126573, 2018-10-08 (annotated)
- Committer:
- dallegre
- Date:
- Mon Oct 08 16:11:21 2018 +0000
- Revision:
- 9:f42863126573
- Parent:
- 8:ef4dca8695d1
Publishing.
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 | 9:f42863126573 | 214 | spi.frequency(4e6*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 | 9:f42863126573 | 229 | timer2.attach(&analog_read,50e-3); |
| dallegre | 0:c12e968e5b60 | 230 | |
| dallegre | 0:c12e968e5b60 | 231 | //go! |
| dallegre | 0:c12e968e5b60 | 232 | while(1){} |
| dallegre | 0:c12e968e5b60 | 233 | } |