Digital Sound Synthesizer for lab 4

Dependencies:   LSM9DS1_Library_cal PinDetect mbed-rtos mbed

main.cpp

Committer:
nsloth
Date:
2016-03-15
Revision:
0:0d977b83a68d

File content as of revision 0:0d977b83a68d:

#include "mbed.h"
#include "rtos.h"
#include "mpr121.h"
#include "PinDetect.h"
#include "LSM9DS1.h"

// Sound waveform synthesizer
// The program precomputes 128 data points on one wave cycle 
// for triangular, sawtooth, and sine waves. These are stored 
// and outout to the PWM to vary the PWM duty cycle. A very high 
// PWM frequency is used and the frequency of the output soundwave 
// is changed by changing the sampling rate. The program uses the 
// IMU to shift frequencies, the keypad to choose center frequencies, 
// the RGB LED to visually display pitch, and a pushbutton to switch between wave modes.

#define PI 3.141592653589793238462

InterruptIn irq(p11);
I2C i2c1(p9,p10);
Mpr121 touchpad(&i2c1, Mpr121::ADD_VSS); 
PwmOut PWM(p26);
Serial pc(USBTX,USBRX);
LSM9DS1 IMU(p28, p27, 0xD6, 0x3C);
Mutex stdio_mutex; 

PwmOut RGBLED_r(p21);
PwmOut RGBLED_g(p22);
PwmOut RGBLED_b(p23);

PinDetect pb1(p17);
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

//Global variables used by interrupt routines and threads
volatile int i=0;
volatile int j=0;
float sineInit[128];
float sawInit[128];
float triInit[128];
volatile float baseFreq = 440;
float maxDeltaFreq = 200;
float maxDeltaXAxis = 15;
volatile float freq = 900;
volatile float deltaFreq = 0;
volatile float deltaXAxis = 0;
volatile float deltaXAxisOld = 0;          
float frequencyScales[12] = {261.626, 293.665, 329.628, 349.228, 391.995, 440.0,
                             493.883, 523.251, 587.33, 659.255, 783.991, 880};

enum Waveforms {sine=0,sawTooth,triangle};
Waveforms wave = sine;

//Samples IMU every .5 seconds and adjusts frequencies accordingly
void sample_IMU(void const *args)
{
    while(1) {
        if(IMU.accelAvailable()) {
            stdio_mutex.lock();
            deltaXAxisOld = deltaXAxis;
            deltaXAxis = (0.0024*IMU.readAccel(X_AXIS)); //Measures difference of IMU from base value of 0
            stdio_mutex.unlock();
        }
        Thread::wait(500);
    }
}

//Plays the current waveform at the current frequency
void playSineWave() {
    while(j<150) {  //Controls how many cycles. Plays about 3 full cycles
        switch(wave) {
            case sine:
                PWM = (sineInit[i]);
            break;
            case sawTooth:
                PWM = sawInit[i];
            break;
            case triangle:
                PWM = triInit[i];
            break;
            default:
                led2 = 0;
                led3 = 0;
                led4 = 0;
            break;
        }
        //(1.0 + sin((float(i)*freq*6.28318530717959/(128.0))))/2.0);
        // increment pointer and wrap around back to 0 at 128
        if(i>60) {
            j++;
        }
        i = (i+1) & 0x07F;
        wait_us(1000000.0/((freq+deltaFreq)*128));
    }
    
    //At the end of 3 cycles, reset counters
    i=0;
    j=0;
    
    int value=touchpad.read(0x00);
    value +=touchpad.read(0x01)<<8;
    if (value == 0) {
        //Reset values if keypad is not being touched
        PWM = 0;
        deltaFreq = 0;
        deltaXAxis = 0;
        RGBLED_r = 0;
        RGBLED_g = 0;
        RGBLED_b = 0;
    } else {
        //If the keypad has a nonzero value, update the frequency and play more cycles
        stdio_mutex.lock();
        float diff = (abs(deltaXAxis - deltaXAxisOld) > 2) ? deltaXAxis : deltaXAxisOld; //Makes the delta frequency resistant to noise in the IMU
        stdio_mutex.unlock();
        
        //Calculates delta frequency from center frequency based off of IMU accelerometer
        if(abs(diff) > maxDeltaXAxis)
            deltaFreq = maxDeltaFreq;
        else {
            deltaFreq = diff/maxDeltaXAxis*maxDeltaFreq;
        }
        
        RGBLED_r = abs(freq-deltaFreq-220)/880;
        RGBLED_g = abs(freq-220)/880;
        RGBLED_b = abs(freq+deltaFreq-220)/880;;
        playSineWave();
    }
}

//Called when the touchpad is tapped
void fallInterrupt() {
    int key_code=0;
    int n=0;
    int value=touchpad.read(0x00);
    value +=touchpad.read(0x01)<<8;
    for (n=0; n<12; n++) {
        if (((value>>n)&0x01)==1) {
            key_code=n+1;
        }
    }
    //sets frequency based on the tapped value
    freq = frequencyScales[key_code-1];
    if (value != 0) {
        playSineWave();
    }
}

//Switches the current waveform and leds
void pb1_released_callback(void){
    if (wave == triangle) {
        led2 = 1;
        led3 = 0;
        led4 = 0;
        wave = sine;
    } else if (wave == sine) {
        led2 = 0;
        led3 = 1;
        led4 = 0;
        wave = sawTooth;
    } else {
        led2 = 0;
        led3 = 0;
        led4 = 1;
        wave = triangle;
    }
}

int main()
{
    pc.printf("Program starting.");
    PWM.period(1.0/200000.0);
    led2 = 1;
    led3 = 0;
    led4 = 0;
    
    // Precompute 128 sample points on one wave cycle for three wave forms
    // used for continuous wave output later
    for(int k=0; k<128; k++) {
        sineInit[k] = 1.0*((1.0 + sin((float(k)/128.0*2*PI)))/2.0);
        sawInit[k] = (1.0 + ((-2.0*1.0/PI)*atan(cos(float(k)*PI/128.0)/sin(float(k)*PI/128.0))))/2.0;
        triInit[k] = (1.0 + ((2.0*1.0/PI)*asin(sin(2.0*PI*float(k)/128.0))))/2.0;
        // scale the waves from 0.0 to 1.0 - as needed for AnalogOut arg 
    }
    
    //Configure the IMU
    IMU.begin();
    if (!IMU.begin()) {
        pc.printf("Failed to communicate with LSM9DS1.\n");
    }
    IMU.calibrate(1);
    
    //Configure the Push button
    pb1.mode(PullUp);
    wait(0.01);
    pb1.attach_deasserted(&pb1_released_callback);
    pb1.setSampleFrequency();
    
    //Set up the I2C Touch keypad interrupt
    irq.fall(&fallInterrupt);
    irq.mode(PullUp);
    
    //And the IMU Sample thread
    Thread t1(sample_IMU);
    
    while(1) {}
}