An floppy drive audio generator using dsp on live audio

Dependencies:   Terminal asyncADC mbed-dsp mbed

main2.cpp

Committer:
Condo2k4
Date:
2017-05-24
Revision:
1:02553973d9cf
Parent:
0:84c336a81482

File content as of revision 1:02553973d9cf:

#include "mbed.h"
#include <algorithm>
#include "asyncADC.h"
#include "arm_math.h"
#include "arm_common_tables.h"
#include "moppy.h"

#define AUDIO_PIN       (A0)
 
Serial pc(USBTX, USBRX, 115200);
DigitalOut red(LED_RED), green(LED_GREEN), blue(LED_BLUE);
Timer timer;
InterruptIn filterBtn(SW2);
bool useFilter = false;

#define LED_ON      (0)
#define LED_OFF     (1)

#define FFT_SIZE 1024
#include "fft.h"

#define TARGET_SAMPLE_RATE_HZ  19200 // Sample rate in Hertz
float achievedSampleRate;

#define BIN_TO_FREQ(bin)  ((bin*(long)achievedSampleRate)/FFT_SIZE)

#define PC_BUFFER_SIZE (FFT_SIZE+30)

//BUFFERS

uint16_t adcBuffer[FFT_SIZE*2];
int spectrumBufferIndex = 0, spectrumBufferLength = 0;
char spectrumBuffer[PC_BUFFER_SIZE];
float fftBuffer[FFT_SIZE*2];
float magnitudes[FFT_SIZE];

// INTERRUPTS

volatile int sectionCounter = 0;
volatile bool sectionFilled = false, toggleFilter;
volatile uint32_t sectionStart, sectionEnd;

void callback(uint32_t start, uint32_t end) {
    sectionFilled = true;
    sectionCounter++;
    sectionStart = start;
    sectionEnd = end;
}

void press() {
    toggleFilter=true;
}

// UTILS

uint32_t ceilToPowerOf2(uint32_t i) {
    i--;
    i|=(i>>1);
    i|=(i>>2);
    i|=(i>>4);
    i|=(i>>8);
    i|=(i>>16);
    return i+1;
}

void trySwp(int16_t *indices, float *magnitudes, int a, int b) {
    if(magnitudes[indices[a]]>magnitudes[indices[b]]) {
        int x = indices[a];
        indices[a] = indices[b];
        indices[b] = x;
    }
}

void sort(int16_t *indices, float *magnitudes, int size) {
    indices[0] = 0;
    for(int i=1; i<size; i++) {
        indices[i]=i;
        for(int j=i; j-->0;) {
            trySwp(indices, magnitudes, j, j+1);
        }
    }
}

bool magComparator(int const & a, int const & b) {
    return magnitudes[a] > magnitudes[b];
}

// POST PROCESSING

float mqe(float* fft) {
    float t0_real, t0_imag, t1_real, t1_imag; //temp values
    
    t0_real = fft[0]-fft[4];
    t0_imag = fft[1]-fft[5];
    
    t1_real = fft[2]*2.0f - (fft[0] + fft[4]);
    t1_imag = fft[3]*2.0f - (fft[1] + fft[5]);
    
    return (t0_real*t1_real + t0_imag*t1_imag)/(t1_real*t1_real + t1_imag*t1_imag);
}

void modifiedQuadraticEstimator() {
    //Post process
    float prev = mqe(&fftBuffer[2]);
    for(int i=4; i<FFT_SIZE/2; i+=2) {
        //Eric Jacobsen's Modified Quadratic Estimator
        float curr = mqe(&fftBuffer[i]);
        fftBuffer[i-2]+=prev;
        prev = curr;
    }
    fftBuffer[FFT_SIZE/2]+=prev;
}

float getMag(int index) {
    return (index<1 && index>=FFT_SIZE/2) ? 0.0f : magnitudes[index];
}

#define FILTER_CUTOFF       (10000.0f)
void localMaximaRad10() {
    float p10, p9, p8, p7, p6, p5, p4, p3, p2, p1, c, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10;
    c = getMag(1);
    n1 = getMag(2);
    n2 = getMag(3);
    n3 = getMag(4);
    n4 = getMag(5);
    n5 = getMag(6);
    n6 = getMag(7);
    n7 = getMag(8);
    n8 = getMag(9);
    n9 = getMag(10);
    n10 = getMag(11);
    
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    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);
    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;
    
    for(int i=11; i<FFT_SIZE/2; i++) {
        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);
        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;
    }
}
void localMaximaRad6() {
    float p6, p5, p4, p3, p2, p1, c, n1, n2, n3, n4, n5, n6;
    c = getMag(1);
    n1 = getMag(2);
    n2 = getMag(3);
    n3 = getMag(4);
    n4 = getMag(5);
    n5 = getMag(6);
    n6 = getMag(7);
    
    magnitudes[1] = (c>=FILTER_CUTOFF && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0;
    p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(5);
    magnitudes[2] = (c>=FILTER_CUTOFF && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0;
    p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(6);
    magnitudes[3] = (c>=FILTER_CUTOFF && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3 && c>=n4 && c>=n5 && c>=n6) ? sqrt(c) : 0;
    p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(7);
    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;
    p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=n4; n4=n5; n5=n6; n6=getMag(8);
    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;
    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);
    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;
    
    for(int i=7; i<FFT_SIZE/2; i++) {
        p6=p5; p5=p4; p4=p3; p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=getMag(i+6);
        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;
    }
}
void localMaximaRad3() {
    float p3, p2, p1, c, n1, n2, n3;
    c = getMag(1);
    n1 = getMag(2);
    n2 = getMag(3);
    n3 = getMag(4);
    
    magnitudes[1] = (c>=FILTER_CUTOFF && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0;
    p1=c; c=n1; n1=n2; n2=n3; n3=getMag(5);
    magnitudes[2] = (c>=FILTER_CUTOFF && c>p1 && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0;
    p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=getMag(6);
    magnitudes[3] = (c>=FILTER_CUTOFF && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0;
    
    for(int i=4; i<FFT_SIZE/2; i++) {
        p3=p2; p2=p1; p1=c; c=n1; n1=n2; n2=n3; n3=getMag(i+3);
        magnitudes[i] = (c>=FILTER_CUTOFF && c>p3 && c>p2 && c>p1 && c>=n1 && c>=n2 && c>=n3) ? sqrt(c) : 0;
    }
}

// MAIN

int main()
{
    filterBtn.fall(press);
    red   = LED_OFF;
    green = LED_OFF;
    blue  = LED_OFF;
    
    Moppy moppy(D1, D0, 38400); // tx, rx, baud
    
    achievedSampleRate = approximateFrequency((float)TARGET_SAMPLE_RATE_HZ);
    switch(asyncAnalogToCircularBuffer(AUDIO_PIN, adcBuffer, 2*FFT_SIZE, TARGET_SAMPLE_RATE_HZ, callback)) {
        case E_ASYNC_ADC_ACTIVE:
            error("AsyncADC already in use");
        case E_DMA_IN_USE:
            error("DMA already in use");
        case E_INVALID_BUFFER_SIZE:
            error("Invalid destination buffer size");
    }
    
    //fixed header
    spectrumBuffer[0]=255;
    spectrumBuffer[1]=(FFT_SIZE/2-1)/255;
    spectrumBuffer[2]=(FFT_SIZE/2-1)%255;
    
    timer.start();
    
    while(1) {
        if(toggleFilter) {
            useFilter = !useFilter;
            toggleFilter = false;
        }
        if(sectionFilled) {
            if(useFilter)
                green = LED_ON;
            else
                blue = LED_ON;
            
            bool buildSpectrumBuffer = false;
            
            if(spectrumBufferIndex==spectrumBufferLength) {
                buildSpectrumBuffer = true;
                spectrumBufferIndex = 0;
            }
            
            timer.reset();
            
            //convert to floating point
            for(int i=sectionStart, j=0; i<=sectionEnd; i++, j+=2) {
                fftBuffer[j] = (float)adcBuffer[i];
                fftBuffer[j+1] = 0.0f;
            }
                
            // Calculate FFT if a full sample is available.
            // Run FFT on sample data.
            arm_cfft_f32(FFT_BUFFER, fftBuffer, 0, 1);
            // Calculate magnitude of complex numbers output by the FFT.
            
            //Convert FFT complex vectors to magnitudes
            arm_cmplx_mag_f32(fftBuffer, magnitudes, FFT_SIZE);
            
            //Post Process / Filtering
            if(useFilter) {
                localMaximaRad10();
            } else {
                for(int i=FFT_SIZE/2; i-->0;) {
                    magnitudes[i]=magnitudes[i]<FILTER_CUTOFF ? 0.0f : sqrt(magnitudes[i]); //trivial amplitude filtering and normalise
                }
            }
            
            if(buildSpectrumBuffer) {
                spectrumBufferLength = 3;
                //data
                for(int i=1; i<(FFT_SIZE/2); i++) {
                    int m = ((int)magnitudes[i]+2)>>2;
                    spectrumBuffer[spectrumBufferLength++] = m>=254?254:m;
                }
            }
            
            for(int i=moppy.getFloppyDrives(); i-->0;) {
                int peak = 1;
                for(int j=FFT_SIZE/2; j-->2;) {
                    if(magnitudes[j]>magnitudes[peak])
                        peak=j;
                }
                if(magnitudes[peak]<30.0f) {
                    moppy.setFrequency(i, 0);
                    while(i-->0)
                        moppy.setFrequency(i, 0);
                    break;
                }
                moppy.setFrequency(i, BIN_TO_FREQ(peak));
                magnitudes[peak]*=0.5f;
                if(buildSpectrumBuffer) {
                    spectrumBuffer[spectrumBufferLength++] = peak;
                }
            }
            moppy.flush();
            
            if(buildSpectrumBuffer) {
                spectrumBuffer[spectrumBufferLength++] = 0; //mark end of playing
                //process time
                long usedTime = timer.read_us();
                long availTime = 1000000*FFT_SIZE/achievedSampleRate;
                long percent = usedTime*10000/availTime; //fixed point to 0.01%
                spectrumBuffer[spectrumBufferLength++] = percent/255;
                spectrumBuffer[spectrumBufferLength++] = percent%255;
            }
            
            if(useFilter)
                green = LED_OFF;
            else
                blue = LED_OFF;
            
            sectionFilled = false;
        }
        
        if(spectrumBufferIndex<spectrumBufferLength) {
            pc.putc(spectrumBuffer[spectrumBufferIndex++]);
        } else {
            sleep();
        }
    }
}