An floppy drive audio generator using dsp on live audio
Dependencies: Terminal asyncADC mbed-dsp mbed
main2.cpp
00001 #include "mbed.h" 00002 #include <algorithm> 00003 #include "asyncADC.h" 00004 #include "arm_math.h" 00005 #include "arm_common_tables.h" 00006 #include "moppy.h" 00007 00008 #define AUDIO_PIN (A0) 00009 00010 Serial pc(USBTX, USBRX, 115200); 00011 DigitalOut red(LED_RED), green(LED_GREEN), blue(LED_BLUE); 00012 Timer timer; 00013 InterruptIn filterBtn(SW2); 00014 bool useFilter = false; 00015 00016 #define LED_ON (0) 00017 #define LED_OFF (1) 00018 00019 #define FFT_SIZE 1024 00020 #include "fft.h" 00021 00022 #define TARGET_SAMPLE_RATE_HZ 19200 // Sample rate in Hertz 00023 float achievedSampleRate; 00024 00025 #define BIN_TO_FREQ(bin) ((bin*(long)achievedSampleRate)/FFT_SIZE) 00026 00027 #define PC_BUFFER_SIZE (FFT_SIZE+30) 00028 00029 //BUFFERS 00030 00031 uint16_t adcBuffer[FFT_SIZE*2]; 00032 int spectrumBufferIndex = 0, spectrumBufferLength = 0; 00033 char spectrumBuffer[PC_BUFFER_SIZE]; 00034 float fftBuffer[FFT_SIZE*2]; 00035 float magnitudes[FFT_SIZE]; 00036 00037 // INTERRUPTS 00038 00039 volatile int sectionCounter = 0; 00040 volatile bool sectionFilled = false, toggleFilter; 00041 volatile uint32_t sectionStart, sectionEnd; 00042 00043 void callback(uint32_t start, uint32_t end) { 00044 sectionFilled = true; 00045 sectionCounter++; 00046 sectionStart = start; 00047 sectionEnd = end; 00048 } 00049 00050 void press() { 00051 toggleFilter=true; 00052 } 00053 00054 // UTILS 00055 00056 uint32_t ceilToPowerOf2(uint32_t i) { 00057 i--; 00058 i|=(i>>1); 00059 i|=(i>>2); 00060 i|=(i>>4); 00061 i|=(i>>8); 00062 i|=(i>>16); 00063 return i+1; 00064 } 00065 00066 void trySwp(int16_t *indices, float *magnitudes, int a, int b) { 00067 if(magnitudes[indices[a]]>magnitudes[indices[b]]) { 00068 int x = indices[a]; 00069 indices[a] = indices[b]; 00070 indices[b] = x; 00071 } 00072 } 00073 00074 void sort(int16_t *indices, float *magnitudes, int size) { 00075 indices[0] = 0; 00076 for(int i=1; i<size; i++) { 00077 indices[i]=i; 00078 for(int j=i; j-->0;) { 00079 trySwp(indices, magnitudes, j, j+1); 00080 } 00081 } 00082 } 00083 00084 bool magComparator(int const & a, int const & b) { 00085 return magnitudes[a] > magnitudes[b]; 00086 } 00087 00088 // POST PROCESSING 00089 00090 float mqe(float* fft) { 00091 float t0_real, t0_imag, t1_real, t1_imag; //temp values 00092 00093 t0_real = fft[0]-fft[4]; 00094 t0_imag = fft[1]-fft[5]; 00095 00096 t1_real = fft[2]*2.0f - (fft[0] + fft[4]); 00097 t1_imag = fft[3]*2.0f - (fft[1] + fft[5]); 00098 00099 return (t0_real*t1_real + t0_imag*t1_imag)/(t1_real*t1_real + t1_imag*t1_imag); 00100 } 00101 00102 void modifiedQuadraticEstimator() { 00103 //Post process 00104 float prev = mqe(&fftBuffer[2]); 00105 for(int i=4; i<FFT_SIZE/2; i+=2) { 00106 //Eric Jacobsen's Modified Quadratic Estimator 00107 float curr = mqe(&fftBuffer[i]); 00108 fftBuffer[i-2]+=prev; 00109 prev = curr; 00110 } 00111 fftBuffer[FFT_SIZE/2]+=prev; 00112 } 00113 00114 float getMag(int index) { 00115 return (index<1 && index>=FFT_SIZE/2) ? 0.0f : magnitudes[index]; 00116 } 00117 00118 #define FILTER_CUTOFF (10000.0f) 00119 void localMaximaRad10() { 00120 float p10, p9, p8, p7, p6, p5, p4, p3, p2, p1, c, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10; 00121 c = getMag(1); 00122 n1 = getMag(2); 00123 n2 = getMag(3); 00124 n3 = getMag(4); 00125 n4 = getMag(5); 00126 n5 = getMag(6); 00127 n6 = getMag(7); 00128 n7 = getMag(8); 00129 n8 = getMag(9); 00130 n9 = getMag(10); 00131 n10 = getMag(11); 00132 00133 magnitudes[1] = (c>=FILTER_CUTOFF && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00134 p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(12); 00135 magnitudes[2] = (c>=FILTER_CUTOFF && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00136 p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(13); 00137 magnitudes[3] = (c>=FILTER_CUTOFF && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00138 p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(14); 00139 magnitudes[4] = (c>=FILTER_CUTOFF && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00140 p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(15); 00141 magnitudes[5] = (c>=FILTER_CUTOFF && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00142 p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(16); 00143 magnitudes[6] = (c>=FILTER_CUTOFF && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00144 p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(17); 00145 magnitudes[7] = (c>=FILTER_CUTOFF && c>p6 && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00146 p7=p6; p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(18); 00147 magnitudes[8] = (c>=FILTER_CUTOFF && c>p7 && c>p6 && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00148 p8=p7; p7=p6; p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(19); 00149 magnitudes[9] = (c>=FILTER_CUTOFF && c>p8 && c>p7 && c>p6 && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00150 p9=p8; p8=p7; p7=p6; p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(20); 00151 magnitudes[10]= (c>=FILTER_CUTOFF && c>p9 && c>p8 && c>p7 && c>p6 && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00152 00153 for(int i=11; i<FFT_SIZE/2; i++) { 00154 p10=p9; p9=p8; p8=p7; p7=p6; p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=n7; n7=n8; n8=n9; n9=n10; n10=getMag(i+10); 00155 magnitudes[i] = (c>=FILTER_CUTOFF && c>p10 && c>p9 && c>p8 && c>p7 && c>p6 && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6 && c>=n7 && c>=n8 && c>=n9 && c>=n10) ? sqrt(c) : 0; 00156 } 00157 } 00158 void localMaximaRad6() { 00159 float p6, p5, p4, p3, p2, p1, c, n1, n2, n3, n4, n5, n6; 00160 c = getMag(1); 00161 n1 = getMag(2); 00162 n2 = getMag(3); 00163 n3 = getMag(4); 00164 n4 = getMag(5); 00165 n5 = getMag(6); 00166 n6 = getMag(7); 00167 00168 magnitudes[1] = (c>=FILTER_CUTOFF && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00169 p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(5); 00170 magnitudes[2] = (c>=FILTER_CUTOFF && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00171 p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(6); 00172 magnitudes[3] = (c>=FILTER_CUTOFF && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00173 p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(7); 00174 magnitudes[4] = (c>=FILTER_CUTOFF && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00175 p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(8); 00176 magnitudes[5] = (c>=FILTER_CUTOFF && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00177 p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(9); 00178 magnitudes[6] = (c>=FILTER_CUTOFF && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00179 00180 for(int i=7; i<FFT_SIZE/2; i++) { 00181 p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=getMag(i+6); 00182 magnitudes[i] = (c>=FILTER_CUTOFF && c>p6 && c>p5 && c>p4 && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0; 00183 } 00184 } 00185 void localMaximaRad3() { 00186 float p3, p2, p1, c, n1, n2, n3; 00187 c = getMag(1); 00188 n1 = getMag(2); 00189 n2 = getMag(3); 00190 n3 = getMag(4); 00191 00192 magnitudes[1] = (c>=FILTER_CUTOFF && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0; 00193 p1=c; c=n1; n1=n2; n2=n3; n3=getMag(5); 00194 magnitudes[2] = (c>=FILTER_CUTOFF && c>p1 && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0; 00195 p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=getMag(6); 00196 magnitudes[3] = (c>=FILTER_CUTOFF && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0; 00197 00198 for(int i=4; i<FFT_SIZE/2; i++) { 00199 p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=getMag(i+3); 00200 magnitudes[i] = (c>=FILTER_CUTOFF && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0; 00201 } 00202 } 00203 00204 // MAIN 00205 00206 int main() 00207 { 00208 filterBtn.fall(press); 00209 red = LED_OFF; 00210 green = LED_OFF; 00211 blue = LED_OFF; 00212 00213 Moppy moppy(D1, D0, 38400); // tx, rx, baud 00214 00215 achievedSampleRate = approximateFrequency((float)TARGET_SAMPLE_RATE_HZ); 00216 switch(asyncAnalogToCircularBuffer(AUDIO_PIN, adcBuffer, 2*FFT_SIZE, TARGET_SAMPLE_RATE_HZ, callback)) { 00217 case E_ASYNC_ADC_ACTIVE: 00218 error("AsyncADC already in use"); 00219 case E_DMA_IN_USE: 00220 error("DMA already in use"); 00221 case E_INVALID_BUFFER_SIZE: 00222 error("Invalid destination buffer size"); 00223 } 00224 00225 //fixed header 00226 spectrumBuffer[0]=255; 00227 spectrumBuffer[1]=(FFT_SIZE/2-1)/255; 00228 spectrumBuffer[2]=(FFT_SIZE/2-1)%255; 00229 00230 timer.start(); 00231 00232 while(1) { 00233 if(toggleFilter) { 00234 useFilter = !useFilter; 00235 toggleFilter = false; 00236 } 00237 if(sectionFilled) { 00238 if(useFilter) 00239 green = LED_ON; 00240 else 00241 blue = LED_ON; 00242 00243 bool buildSpectrumBuffer = false; 00244 00245 if(spectrumBufferIndex==spectrumBufferLength) { 00246 buildSpectrumBuffer = true; 00247 spectrumBufferIndex = 0; 00248 } 00249 00250 timer.reset(); 00251 00252 //convert to floating point 00253 for(int i=sectionStart, j=0; i<=sectionEnd; i++, j+=2) { 00254 fftBuffer[j] = (float)adcBuffer[i]; 00255 fftBuffer[j+1] = 0.0f; 00256 } 00257 00258 // Calculate FFT if a full sample is available. 00259 // Run FFT on sample data. 00260 arm_cfft_f32(FFT_BUFFER, fftBuffer, 0, 1); 00261 // Calculate magnitude of complex numbers output by the FFT. 00262 00263 //Convert FFT complex vectors to magnitudes 00264 arm_cmplx_mag_f32(fftBuffer, magnitudes, FFT_SIZE); 00265 00266 //Post Process / Filtering 00267 if(useFilter) { 00268 localMaximaRad10(); 00269 } else { 00270 for(int i=FFT_SIZE/2; i-->0;) { 00271 magnitudes[i]=magnitudes[i]<FILTER_CUTOFF ? 0.0f : sqrt(magnitudes[i]); //trivial amplitude filtering and normalise 00272 } 00273 } 00274 00275 if(buildSpectrumBuffer) { 00276 spectrumBufferLength = 3; 00277 //data 00278 for(int i=1; i<(FFT_SIZE/2); i++) { 00279 int m = ((int)magnitudes[i]+2)>>2; 00280 spectrumBuffer[spectrumBufferLength++] = m>=254?254:m; 00281 } 00282 } 00283 00284 for(int i=moppy.getFloppyDrives(); i-->0;) { 00285 int peak = 1; 00286 for(int j=FFT_SIZE/2; j-->2;) { 00287 if(magnitudes[j]>magnitudes[peak]) 00288 peak=j; 00289 } 00290 if(magnitudes[peak]<30.0f) { 00291 moppy.setFrequency(i, 0); 00292 while(i-->0) 00293 moppy.setFrequency(i, 0); 00294 break; 00295 } 00296 moppy.setFrequency(i, BIN_TO_FREQ(peak)); 00297 magnitudes[peak]*=0.5f; 00298 if(buildSpectrumBuffer) { 00299 spectrumBuffer[spectrumBufferLength++] = peak; 00300 } 00301 } 00302 moppy.flush(); 00303 00304 if(buildSpectrumBuffer) { 00305 spectrumBuffer[spectrumBufferLength++] = 0; //mark end of playing 00306 //process time 00307 long usedTime = timer.read_us(); 00308 long availTime = 1000000*FFT_SIZE/achievedSampleRate; 00309 long percent = usedTime*10000/availTime; //fixed point to 0.01% 00310 spectrumBuffer[spectrumBufferLength++] = percent/255; 00311 spectrumBuffer[spectrumBufferLength++] = percent%255; 00312 } 00313 00314 if(useFilter) 00315 green = LED_OFF; 00316 else 00317 blue = LED_OFF; 00318 00319 sectionFilled = false; 00320 } 00321 00322 if(spectrumBufferIndex<spectrumBufferLength) { 00323 pc.putc(spectrumBuffer[spectrumBufferIndex++]); 00324 } else { 00325 sleep(); 00326 } 00327 } 00328 }
Generated on Thu Jul 21 2022 02:01:04 by 1.7.2