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@1:a4ce08417c60, 2016-07-30 (annotated)
- Committer:
- dallegre
- Date:
- Sat Jul 30 02:21:09 2016 +0000
- Revision:
- 1:a4ce08417c60
- Parent:
- 0:c12e968e5b60
- Child:
- 2:1b50325e256f
made it into an allpass delay program
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 | 0:c12e968e5b60 | 17 | #define DELAYLEN 11500 //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 | 0:c12e968e5b60 | 24 | |
| dallegre | 0:c12e968e5b60 | 25 | SPI spi(SPI_MOSI,SPI_MISO,SPI_SCK); |
| dallegre | 0:c12e968e5b60 | 26 | Ticker timer; |
| dallegre | 0:c12e968e5b60 | 27 | Ticker timer2; |
| dallegre | 0:c12e968e5b60 | 28 | AnalogIn analog_value(A1); //delay time pot |
| dallegre | 0:c12e968e5b60 | 29 | AnalogIn analog_value2(A0); //feedback amount pot |
| dallegre | 0:c12e968e5b60 | 30 | DigitalOut cs(D10); |
| dallegre | 0:c12e968e5b60 | 31 | |
| dallegre | 0:c12e968e5b60 | 32 | int left_out = 0, right_out = 0, left_out_u = 0, right_out_u = 0, left_in = 0, right_in = 0, |
| dallegre | 0:c12e968e5b60 | 33 | index1 = 0, index2 = (DELAYLEN*0.99), index3 = (DELAYLEN*0.99); |
| dallegre | 0:c12e968e5b60 | 34 | uint16_t left_in_u = 0, meas = 0, measprev = 0; |
| dallegre | 0:c12e968e5b60 | 35 | float left_in_f = 0, feedback = 0, feedback2 = 0, feedback3 = 0, offset = 10.0, |
| dallegre | 0:c12e968e5b60 | 36 | meas2f = 0.5, measf = 0, measf2 = 0, measf3 = 0, left_out_f = 0, right_out_f = 0; |
| dallegre | 0:c12e968e5b60 | 37 | float audioFeedbackRight,audioFeedbackLeft; |
| dallegre | 0:c12e968e5b60 | 38 | |
| dallegre | 0:c12e968e5b60 | 39 | float LPdampingFreq = 11000, HPdampingFreq = 100; //some parameters you might want to adjust. |
| dallegre | 0:c12e968e5b60 | 40 | int LPdampingPoles = 2, HPdampingPoles = 0; //I don't think the highpass is quite working. |
| dallegre | 0:c12e968e5b60 | 41 | |
| dallegre | 0:c12e968e5b60 | 42 | OnePoleLp audioFilter; //might use this as a mor thorough DC offset removal tool. Not yet implemented. |
| dallegre | 0:c12e968e5b60 | 43 | OnePoleHp audioFilterHP; |
| dallegre | 0:c12e968e5b60 | 44 | OnePoleLp feedbackFilter; |
| dallegre | 0:c12e968e5b60 | 45 | OnePoleLp lengthFilter; |
| dallegre | 1:a4ce08417c60 | 46 | AllPass delayLeft; |
| dallegre | 1:a4ce08417c60 | 47 | AllPass delayRight; |
| dallegre | 0:c12e968e5b60 | 48 | |
| dallegre | 0:c12e968e5b60 | 49 | void I2S_send(void){ |
| dallegre | 0:c12e968e5b60 | 50 | cs.write(0); //0 for left output |
| dallegre | 0:c12e968e5b60 | 51 | left_in = spi.write(left_out); |
| dallegre | 0:c12e968e5b60 | 52 | cs.write(1); //1 for right output |
| dallegre | 0:c12e968e5b60 | 53 | right_in = spi.write(right_out); |
| dallegre | 0:c12e968e5b60 | 54 | |
| dallegre | 0:c12e968e5b60 | 55 | left_in_f = intToFloat(left_in); |
| dallegre | 0:c12e968e5b60 | 56 | |
| dallegre | 0:c12e968e5b60 | 57 | /////////////////////////////////////////dsp part////////////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 58 | |
| dallegre | 0:c12e968e5b60 | 59 | //process control signals |
| dallegre | 0:c12e968e5b60 | 60 | feedback2 = feedbackFilter.process(feedback); |
| dallegre | 0:c12e968e5b60 | 61 | feedback3 = feedbackFilter.process(feedback2); |
| dallegre | 0:c12e968e5b60 | 62 | //update delay times with every audio sample if you're going to filter them. |
| dallegre | 0:c12e968e5b60 | 63 | measf2 = lengthFilter.process(measf); |
| dallegre | 0:c12e968e5b60 | 64 | measf3 = lengthFilter.process(measf2); |
| dallegre | 0:c12e968e5b60 | 65 | measf3 *= (DELAYLEN); //convert to delay index normalization |
| dallegre | 0:c12e968e5b60 | 66 | |
| dallegre | 0:c12e968e5b60 | 67 | //process audio |
| dallegre | 1:a4ce08417c60 | 68 | left_out_f = delayLeft.process(left_in_f,feedback3,measf3); |
| dallegre | 1:a4ce08417c60 | 69 | right_out_f = delayRight.process(left_in_f,feedback3,measf3+offset); //add an offset in just for shits. |
| dallegre | 0:c12e968e5b60 | 70 | left_out_u = satAdd(left_in_f,left_out_f); |
| dallegre | 0:c12e968e5b60 | 71 | right_out_u = satAdd(left_in_f,right_out_f); |
| dallegre | 0:c12e968e5b60 | 72 | |
| dallegre | 0:c12e968e5b60 | 73 | /////////////////////////////////////////end of dsp part///////////////////////////////////////////////////////////////// |
| dallegre | 0:c12e968e5b60 | 74 | |
| dallegre | 0:c12e968e5b60 | 75 | left_out = unsignedToSigned(left_out_u); |
| dallegre | 0:c12e968e5b60 | 76 | right_out = unsignedToSigned(right_out_u); |
| dallegre | 0:c12e968e5b60 | 77 | |
| dallegre | 0:c12e968e5b60 | 78 | } |
| dallegre | 0:c12e968e5b60 | 79 | |
| dallegre | 0:c12e968e5b60 | 80 | void analog_read(void){ |
| dallegre | 0:c12e968e5b60 | 81 | //this is the stuff for the feedback amount pot |
| dallegre | 0:c12e968e5b60 | 82 | meas2f = analog_value2.read_u16(); //converts unint16 value to float |
| dallegre | 0:c12e968e5b60 | 83 | feedback = meas2f/4100; //now you have a value between 0 and 1 that will control the feedback amount. |
| dallegre | 0:c12e968e5b60 | 84 | |
| dallegre | 0:c12e968e5b60 | 85 | //this is the stuff for the delay time pot |
| dallegre | 0:c12e968e5b60 | 86 | meas = 4096 - analog_value.read_u16(); //values go from 1 to 4095 |
| dallegre | 0:c12e968e5b60 | 87 | //if(abs(meas - measprev) > 5){ |
| dallegre | 0:c12e968e5b60 | 88 | measprev = meas; |
| dallegre | 0:c12e968e5b60 | 89 | measf = meas; |
| dallegre | 0:c12e968e5b60 | 90 | measf /= 4200; //this number took some tweaking. |
| dallegre | 0:c12e968e5b60 | 91 | measf *= measf; |
| dallegre | 0:c12e968e5b60 | 92 | //} |
| dallegre | 0:c12e968e5b60 | 93 | } |
| dallegre | 0:c12e968e5b60 | 94 | |
| dallegre | 0:c12e968e5b60 | 95 | int main(){ |
| dallegre | 0:c12e968e5b60 | 96 | |
| dallegre | 0:c12e968e5b60 | 97 | //set up baud rate for 11.2MHz crystal |
| dallegre | 0:c12e968e5b60 | 98 | pc.baud(.7*9600*2); |
| dallegre | 0:c12e968e5b60 | 99 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 100 | |
| dallegre | 0:c12e968e5b60 | 101 | //do I2C transfter to initialize codec |
| dallegre | 0:c12e968e5b60 | 102 | codecInit(); |
| dallegre | 0:c12e968e5b60 | 103 | |
| dallegre | 0:c12e968e5b60 | 104 | //initialize fixed filters |
| dallegre | 0:c12e968e5b60 | 105 | double feedbackCutoff = 10.0/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 106 | feedbackFilter.setFc(feedbackCutoff); |
| dallegre | 0:c12e968e5b60 | 107 | double lengthCutoff = 3.0/(SAMPLINGFREQ*1000); |
| dallegre | 0:c12e968e5b60 | 108 | lengthFilter.setFc(lengthCutoff); |
| dallegre | 0:c12e968e5b60 | 109 | |
| dallegre | 0:c12e968e5b60 | 110 | //initialize audio filter |
| dallegre | 0:c12e968e5b60 | 111 | double audioCutoff = LPdampingFreq/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 112 | audioFilter.setFc(audioCutoff); |
| dallegre | 0:c12e968e5b60 | 113 | audioCutoff = HPdampingFreq/(SAMPLINGFREQ*1000); //adjust these cutoffs according to taste. |
| dallegre | 0:c12e968e5b60 | 114 | audioFilterHP.setFc(audioCutoff); |
| dallegre | 0:c12e968e5b60 | 115 | |
| dallegre | 0:c12e968e5b60 | 116 | //set up I2S |
| dallegre | 0:c12e968e5b60 | 117 | spi.frequency(8e6*2); //8MHz spi. This is fast enough for 88KHz 16 bit sampling. |
| dallegre | 0:c12e968e5b60 | 118 | spi.format(16,0); //16 bit. clock polarity 0, phase 0. |
| dallegre | 0:c12e968e5b60 | 119 | wait(.1); |
| dallegre | 0:c12e968e5b60 | 120 | |
| dallegre | 0:c12e968e5b60 | 121 | //audio rate timers. 29 and 44 are not very good. Might want to use an external clock for the Nucleo. |
| dallegre | 0:c12e968e5b60 | 122 | if(SAMPLINGFREQ == 22) |
| dallegre | 0:c12e968e5b60 | 123 | 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 | 124 | if(SAMPLINGFREQ == 29) |
| dallegre | 0:c12e968e5b60 | 125 | 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 | 126 | if(SAMPLINGFREQ == 44) |
| dallegre | 0:c12e968e5b60 | 127 | 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 | 128 | if(SAMPLINGFREQ == 88) |
| dallegre | 0:c12e968e5b60 | 129 | timer.attach(&I2S_send,8e-6); //88KHz sampling. Need to calculate. |
| dallegre | 0:c12e968e5b60 | 130 | |
| dallegre | 0:c12e968e5b60 | 131 | //control signal timer |
| dallegre | 0:c12e968e5b60 | 132 | timer2.attach(&analog_read,25e-3); |
| dallegre | 0:c12e968e5b60 | 133 | |
| dallegre | 0:c12e968e5b60 | 134 | //go! |
| dallegre | 0:c12e968e5b60 | 135 | while(1){} |
| dallegre | 0:c12e968e5b60 | 136 | } |