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