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@6:df635006ee1c, 2016-07-30 (annotated)
- Committer:
- dallegre
- Date:
- Sat Jul 30 20:02:42 2016 +0000
- Revision:
- 6:df635006ee1c
- Parent:
- 5:265c9540466a
- Child:
- 7:c84086dce9ac
pretty good sounding..
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 | 2:1b50325e256f | 17 | #define DELAYLEN 3900 //24,000 is about as far as you can go in the ram. |
| dallegre | 6:df635006ee1c | 18 | #define SAMPLINGFREQ 29 //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 | 3:f8bc3ac22ffd | 37 | meas2f = 0.5, measf = 0, measf2 = 0, measf3 = 0, measf3L = 0, measf3L2 = 0, measf3L3 = 0, measf3R = 0, measf3R2 = 0, measf3R3 = 0, |
| dallegre | 3:f8bc3ac22ffd | 38 | left_out_f = 0, right_out_f = 0; |
| dallegre | 3:f8bc3ac22ffd | 39 | float left_out_2 = 0, right_out_2 = 0, left_out_3 = 0, right_out_3 = 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 audioFilter; //might use this as a mor thorough DC offset removal tool. Not yet implemented. |
| dallegre | 0:c12e968e5b60 | 46 | OnePoleHp audioFilterHP; |
| dallegre | 0:c12e968e5b60 | 47 | OnePoleLp feedbackFilter; |
| dallegre | 0:c12e968e5b60 | 48 | OnePoleLp lengthFilter; |
| dallegre | 1:a4ce08417c60 | 49 | AllPass delayLeft; |
| dallegre | 3:f8bc3ac22ffd | 50 | AllPass delayLeft2; |
| dallegre | 3:f8bc3ac22ffd | 51 | AllPass delayLeft3; |
| dallegre | 1:a4ce08417c60 | 52 | AllPass delayRight; |
| dallegre | 2:1b50325e256f | 53 | AllPass delayRight2; |
| dallegre | 3:f8bc3ac22ffd | 54 | AllPass delayRight3; |
| dallegre | 3:f8bc3ac22ffd | 55 | gsOsc OscillatorL; |
| dallegre | 3:f8bc3ac22ffd | 56 | gsOsc OscillatorL2; |
| dallegre | 3:f8bc3ac22ffd | 57 | gsOsc OscillatorL3; |
| dallegre | 3:f8bc3ac22ffd | 58 | gsOsc OscillatorR; |
| dallegre | 3:f8bc3ac22ffd | 59 | gsOsc OscillatorR2; |
| dallegre | 3:f8bc3ac22ffd | 60 | gsOsc OscillatorR3; |
| dallegre | 0:c12e968e5b60 | 61 | |
| dallegre | 0:c12e968e5b60 | 62 | void I2S_send(void){ |
| dallegre | 0:c12e968e5b60 | 63 | cs.write(0); //0 for left output |
| dallegre | 0:c12e968e5b60 | 64 | left_in = spi.write(left_out); |
| dallegre | 0:c12e968e5b60 | 65 | cs.write(1); //1 for right output |
| dallegre | 0:c12e968e5b60 | 66 | right_in = spi.write(right_out); |
| dallegre | 0:c12e968e5b60 | 67 | |
| dallegre | 0:c12e968e5b60 | 68 | left_in_f = intToFloat(left_in); |
| dallegre | 0:c12e968e5b60 | 69 | |
| dallegre | 0:c12e968e5b60 | 70 | /////////////////////////////////////////dsp part////////////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 71 | |
| dallegre | 0:c12e968e5b60 | 72 | //process control signals |
| dallegre | 0:c12e968e5b60 | 73 | feedback2 = feedbackFilter.process(feedback); |
| dallegre | 0:c12e968e5b60 | 74 | feedback3 = feedbackFilter.process(feedback2); |
| dallegre | 0:c12e968e5b60 | 75 | //update delay times with every audio sample if you're going to filter them. |
| dallegre | 0:c12e968e5b60 | 76 | measf2 = lengthFilter.process(measf); |
| dallegre | 0:c12e968e5b60 | 77 | measf3 = lengthFilter.process(measf2); |
| dallegre | 0:c12e968e5b60 | 78 | measf3 *= (DELAYLEN); //convert to delay index normalization |
| dallegre | 0:c12e968e5b60 | 79 | |
| dallegre | 2:1b50325e256f | 80 | //process audio. probably calculate these coefficients elsewhere so you don't do the multiply all the time. |
| dallegre | 2:1b50325e256f | 81 | //left delay mod |
| dallegre | 6:df635006ee1c | 82 | measf3L = DELAYLEN*.74 + OscillatorL.process(.23,DELAYLEN/76,1); |
| dallegre | 6:df635006ee1c | 83 | measf3L2 = DELAYLEN*.43 + OscillatorL2.process(.05,DELAYLEN/75,1); |
| dallegre | 6:df635006ee1c | 84 | measf3L3 = DELAYLEN*.23 + OscillatorL3.process(.03,DELAYLEN/78,1); |
| dallegre | 2:1b50325e256f | 85 | //right delay mod |
| dallegre | 6:df635006ee1c | 86 | measf3R = DELAYLEN*.75 + OscillatorR.process(.21,DELAYLEN/79,0); |
| dallegre | 6:df635006ee1c | 87 | measf3R2 = DELAYLEN*.49 + OscillatorR2.process(.05,DELAYLEN/77,0); |
| dallegre | 6:df635006ee1c | 88 | measf3R3 = DELAYLEN*.23 + OscillatorR3.process(.02,DELAYLEN/78,1); |
| dallegre | 3:f8bc3ac22ffd | 89 | if(measf3L > DELAYLEN*.9) |
| dallegre | 3:f8bc3ac22ffd | 90 | measf3L = DELAYLEN*.9; |
| dallegre | 3:f8bc3ac22ffd | 91 | if(measf3L < DELAYLEN*.05) |
| dallegre | 3:f8bc3ac22ffd | 92 | measf3L = DELAYLEN*.05; |
| dallegre | 3:f8bc3ac22ffd | 93 | if(measf3L2 > DELAYLEN*.9) |
| dallegre | 3:f8bc3ac22ffd | 94 | measf3L2 = DELAYLEN*.9; |
| dallegre | 3:f8bc3ac22ffd | 95 | if(measf3L2 < DELAYLEN*.05) |
| dallegre | 3:f8bc3ac22ffd | 96 | measf3L2 = DELAYLEN*.05; |
| dallegre | 3:f8bc3ac22ffd | 97 | if(measf3L3 > DELAYLEN*.9) |
| dallegre | 3:f8bc3ac22ffd | 98 | measf3L3 = DELAYLEN*.9; |
| dallegre | 3:f8bc3ac22ffd | 99 | if(measf3L3 < DELAYLEN*.05) |
| dallegre | 3:f8bc3ac22ffd | 100 | measf3L3 = DELAYLEN*.05; |
| dallegre | 3:f8bc3ac22ffd | 101 | if(measf3R > DELAYLEN*.9) |
| dallegre | 3:f8bc3ac22ffd | 102 | measf3R = DELAYLEN*.9; |
| dallegre | 3:f8bc3ac22ffd | 103 | if(measf3R < DELAYLEN*.05) |
| dallegre | 3:f8bc3ac22ffd | 104 | measf3R = DELAYLEN*.05; |
| dallegre | 3:f8bc3ac22ffd | 105 | if(measf3R2 > DELAYLEN*.9) |
| dallegre | 3:f8bc3ac22ffd | 106 | measf3R2 = DELAYLEN*.9; |
| dallegre | 3:f8bc3ac22ffd | 107 | if(measf3R2 < DELAYLEN*.05) |
| dallegre | 3:f8bc3ac22ffd | 108 | measf3R2 = DELAYLEN*.05; |
| dallegre | 3:f8bc3ac22ffd | 109 | if(measf3R3 > DELAYLEN*.9) |
| dallegre | 3:f8bc3ac22ffd | 110 | measf3R3 = DELAYLEN*.9; |
| dallegre | 3:f8bc3ac22ffd | 111 | if(measf3R3 < DELAYLEN*.05) |
| dallegre | 3:f8bc3ac22ffd | 112 | measf3R3 = DELAYLEN*.05; |
| dallegre | 4:efa5e07fd268 | 113 | left_out_2 = delayLeft2.process(satSubtract(left_in_f,loopfb),.3,measf3L); |
| dallegre | 4:efa5e07fd268 | 114 | left_out_3 = delayLeft3.process(left_out_2,.4,measf3L2); |
| dallegre | 5:265c9540466a | 115 | left_out_f = delayLeft.process(left_out_3,.4,measf3L3); |
| dallegre | 4:efa5e07fd268 | 116 | right_out_2 = delayRight2.process(satSubtract(left_in_f,loopfb2),.3,measf3R); |
| dallegre | 4:efa5e07fd268 | 117 | right_out_3 = delayRight3.process(right_out_2,.4,measf3R2); |
| dallegre | 5:265c9540466a | 118 | right_out_f = delayRight.process(right_out_3,.4,measf3R3); |
| dallegre | 3:f8bc3ac22ffd | 119 | loopfb = left_out_f*feedback3; |
| dallegre | 3:f8bc3ac22ffd | 120 | loopfb2 = right_out_f*feedback3; |
| dallegre | 6:df635006ee1c | 121 | //left_out_u = satAdd(left_in_f,left_out_f*measf3/200); |
| dallegre | 6:df635006ee1c | 122 | //right_out_u = satAdd(left_in_f,right_out_f*measf3/200); |
| dallegre | 6:df635006ee1c | 123 | left_out_u = left_out_f; |
| dallegre | 6:df635006ee1c | 124 | right_out_u = right_out_f; |
| dallegre | 0:c12e968e5b60 | 125 | |
| dallegre | 0:c12e968e5b60 | 126 | /////////////////////////////////////////end of dsp part///////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 127 | |
| dallegre | 0:c12e968e5b60 | 128 | left_out = unsignedToSigned(left_out_u); |
| dallegre | 0:c12e968e5b60 | 129 | right_out = unsignedToSigned(right_out_u); |
| dallegre | 0:c12e968e5b60 | 130 | |
| dallegre | 0:c12e968e5b60 | 131 | } |
| dallegre | 0:c12e968e5b60 | 132 | |
| dallegre | 0:c12e968e5b60 | 133 | void analog_read(void){ |
| dallegre | 0:c12e968e5b60 | 134 | //this is the stuff for the feedback amount pot |
| dallegre | 0:c12e968e5b60 | 135 | meas2f = analog_value2.read_u16(); //converts unint16 value to float |
| dallegre | 0:c12e968e5b60 | 136 | feedback = meas2f/4100; //now you have a value between 0 and 1 that will control the feedback amount. |
| dallegre | 0:c12e968e5b60 | 137 | |
| dallegre | 0:c12e968e5b60 | 138 | //this is the stuff for the delay time pot |
| dallegre | 0:c12e968e5b60 | 139 | meas = 4096 - analog_value.read_u16(); //values go from 1 to 4095 |
| dallegre | 0:c12e968e5b60 | 140 | //if(abs(meas - measprev) > 5){ |
| dallegre | 0:c12e968e5b60 | 141 | measprev = meas; |
| dallegre | 0:c12e968e5b60 | 142 | measf = meas; |
| dallegre | 0:c12e968e5b60 | 143 | measf /= 4200; //this number took some tweaking. |
| dallegre | 0:c12e968e5b60 | 144 | measf *= measf; |
| dallegre | 0:c12e968e5b60 | 145 | //} |
| dallegre | 0:c12e968e5b60 | 146 | } |
| dallegre | 0:c12e968e5b60 | 147 | |
| dallegre | 0:c12e968e5b60 | 148 | int main(){ |
| dallegre | 0:c12e968e5b60 | 149 | |
| dallegre | 0:c12e968e5b60 | 150 | //set up baud rate for 11.2MHz crystal |
| dallegre | 0:c12e968e5b60 | 151 | pc.baud(.7*9600*2); |
| dallegre | 0:c12e968e5b60 | 152 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 153 | |
| dallegre | 0:c12e968e5b60 | 154 | //do I2C transfter to initialize codec |
| dallegre | 0:c12e968e5b60 | 155 | codecInit(); |
| dallegre | 0:c12e968e5b60 | 156 | |
| dallegre | 0:c12e968e5b60 | 157 | //initialize fixed filters |
| dallegre | 0:c12e968e5b60 | 158 | double feedbackCutoff = 10.0/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 159 | feedbackFilter.setFc(feedbackCutoff); |
| dallegre | 0:c12e968e5b60 | 160 | double lengthCutoff = 3.0/(SAMPLINGFREQ*1000); |
| dallegre | 0:c12e968e5b60 | 161 | lengthFilter.setFc(lengthCutoff); |
| dallegre | 0:c12e968e5b60 | 162 | |
| dallegre | 0:c12e968e5b60 | 163 | //initialize audio filter |
| dallegre | 0:c12e968e5b60 | 164 | double audioCutoff = LPdampingFreq/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 165 | audioFilter.setFc(audioCutoff); |
| dallegre | 0:c12e968e5b60 | 166 | audioCutoff = HPdampingFreq/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 167 | audioFilterHP.setFc(audioCutoff); |
| dallegre | 0:c12e968e5b60 | 168 | |
| dallegre | 0:c12e968e5b60 | 169 | //set up I2S |
| dallegre | 0:c12e968e5b60 | 170 | spi.frequency(8e6*2); //8MHz spi. This is fast enough for 88KHz 16 bit sampling. |
| dallegre | 0:c12e968e5b60 | 171 | spi.format(16,0); //16 bit. clock polarity 0, phase 0. |
| dallegre | 0:c12e968e5b60 | 172 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 173 | |
| dallegre | 0:c12e968e5b60 | 174 | //audio rate timers. 29 and 44 are not very good. Might want to use an external clock for the Nucleo. |
| dallegre | 0:c12e968e5b60 | 175 | if(SAMPLINGFREQ == 22) |
| dallegre | 0:c12e968e5b60 | 176 | 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 | 177 | if(SAMPLINGFREQ == 29) |
| dallegre | 0:c12e968e5b60 | 178 | 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 | 179 | if(SAMPLINGFREQ == 44) |
| dallegre | 0:c12e968e5b60 | 180 | 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 | 181 | if(SAMPLINGFREQ == 88) |
| dallegre | 0:c12e968e5b60 | 182 | timer.attach(&I2S_send,8e-6); //88KHz sampling. Need to calculate. |
| dallegre | 0:c12e968e5b60 | 183 | |
| dallegre | 0:c12e968e5b60 | 184 | //control signal timer |
| dallegre | 0:c12e968e5b60 | 185 | timer2.attach(&analog_read,25e-3); |
| dallegre | 0:c12e968e5b60 | 186 | |
| dallegre | 0:c12e968e5b60 | 187 | //go! |
| dallegre | 0:c12e968e5b60 | 188 | while(1){} |
| dallegre | 0:c12e968e5b60 | 189 | } |