Musical Theremin

Description:

Create a simple smart theremin using mbed. Our approach includes the following features:

  • Improve distance sensing sonar to play only designated pitches in a scale
  • Add second sonar sensor to control volume
  • Plot sine wave of pitch (frequency) vs amplitude (volume) in real time
  • Print pitch (Hz) and volume (0-100%)
  • Add Bluetooth to customize “smart” theremin
  • Choose between major and minor keys
  • Allows you to input your own name

Parts List:

  1. LPC1768 mbed: https://os.mbed.com/platforms/mbed-LPC1768/
  2. uLCD: https://os.mbed.com/users/4180_1/notebook/ulcd-144-g2-128-by-128-color-lcd/
  3. Potentiometer: https://os.mbed.com/components/Potentiometer/
  4. (2) HR-SR04 Sonar Sensors: https://os.mbed.com/users/4180_1/notebook/using-the-hc-sr04-sonar-sensor/
  5. Class D Audio Amp: https://os.mbed.com/users/4180_1/notebook/tpa2005d1-class-d-audio-amp/
  6. Speaker: https://os.mbed.com/users/4180_1/notebook/using-a-speaker-for-audio-output/
  7. Adafruit Bluefruit Bluetooth: https://os.mbed.com/users/4180_1/notebook/adafruit-bluefruit-le-uart-friend---bluetooth-low-/
  8. Simple breadboard
  9. External Power Supply
  10. 2 330uF capacitors

Schematic/Block Diagram:

/media/uploads/arahgozar3/image-1.jpg

Source Code:

musical

#include "mbed.h"
#include "ultrasonic.h"
#include "rtos.h"
#include "uLCD_4DGL.h"
#include <string>

// Define some colors:
#define Q 0x808000 //OLIVE 
#define I 0x008000 //GREEN 
#define S 0xC0C0C0 //SILVER 
#define C 0x17202A //UFO GLASS 
#define D 0x797D7F //DARK GREY 
#define L 0x00FF00 //LIME 
#define P 0xFF00FF //PINK 
#define R 0xF1C40F //YELLOW 
#define O 0xF39C12 //ORANGE 
#define G 0xAAB7B8 //GREY 
#define _ 0x000000 //BLACK 
#define X 0xFFFFFF //WHITE 
#define B 0x0000FF //BLUE 
#define r 0xFF0000 //RED


PwmOut audio(p26); //output to speaker amp or audio jack
DigitalOut led(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
Serial pc(USBTX,USBRX);
uLCD_4DGL uLCD(p28, p27, p29);
Serial bluemod(p9,p10);
Mutex myMut;

volatile float half_cycle_time = 1;
volatile float secondhalf_cycle_time = 1;

//Buffers
volatile float half_cycle_time2;
volatile float half_cycle_time3;
volatile float half_cycle_time4;
volatile float half_cycle_time5;
volatile float half_cycle_time6;
volatile float half_cycle_time7;
volatile float half_cycle_time8;
volatile float half_cycle_time9;
volatile float half_cycle_time10;

volatile float secondhalf_cycle_time2;
volatile float secondhalf_cycle_time3;
volatile float secondhalf_cycle_time4;
volatile float secondhalf_cycle_time5;
volatile float secondhalf_cycle_time6;
volatile float secondhalf_cycle_time7;
volatile float secondhalf_cycle_time8;
volatile float secondhalf_cycle_time9;
volatile float secondhalf_cycle_time10;

// Averaging Values:
volatile float avghalf_cycle_time;
volatile float secavghalf_cycle_time;
volatile float pitch;
volatile int x = 1;
volatile int xpos = 1;
volatile int y = 120; // Offset to start printing sinusoid
volatile int color = WHITE;
volatile bool major = true; // play D Major = true
volatile bool cont = true; // continue with main thread once name inputted
volatile string name; // Holds person's name
volatile int scalar = 2;
string s;
//volatile 
string key;

void Dmajor(void const *argument)
{
    while(1) {
        led = !led;

// Buffer for the key of D Major
        if(avghalf_cycle_time>300/scalar&&avghalf_cycle_time<700/scalar) {
            pitch = 1.0/294;
            audio.period(pitch);//D
            color = X;
        } else if(avghalf_cycle_time>700/scalar&&avghalf_cycle_time<1100/scalar) {

            pitch = 1.0/274;
            audio.period(pitch);//c#
            color = B;      // blue
        } else if(avghalf_cycle_time>1100/scalar&&avghalf_cycle_time<1500/scalar) {
            if(major) {
                pitch = 1.0/246;
                audio.period(pitch); //B
                color = I;
            }

            else if(!major) {
                pitch = 1.0/232;
                audio.period(pitch); //Bflat
                color = I;
            }
        } else if(avghalf_cycle_time>1500/scalar&&avghalf_cycle_time<1900/scalar) {
            pitch = 1.0/220;
            audio.period(pitch);// A
            color = R;             //yellow
        } else if(avghalf_cycle_time>1900/scalar&&avghalf_cycle_time<2300/scalar) {
            pitch = 1.0/194;
            audio.period(pitch); //G
            color = O;                // ORANGE
        } else if(avghalf_cycle_time>2300/scalar&&avghalf_cycle_time<2700/scalar) {
            if(major) {

                pitch = 1.0/184;
                audio.period(pitch); //f#
                color = r;          // red

            }

            else if(!major) {
                pitch = 1.0/176;
                audio.period(pitch); //fnat
                color = r;          // red
            }
        } else if(avghalf_cycle_time>2700/scalar&&avghalf_cycle_time<3100/scalar) {
            pitch = 1.0/166;
            audio.period(pitch); //E
            color = G; // grey
        } else if(avghalf_cycle_time>3100/scalar) {
            pitch = 1.0/146;
            audio.period(pitch); //D
            color = Q; //OLIVE
        }

// Buffer to control the volume
        if(secavghalf_cycle_time>200&&secavghalf_cycle_time<300)audio = .001;
        else if(secavghalf_cycle_time>200&&secavghalf_cycle_time<500) audio = .02;
        else if(secavghalf_cycle_time>500&&secavghalf_cycle_time<800) audio = .03;
        else if(secavghalf_cycle_time>800&&secavghalf_cycle_time<1100) audio = .05;
        else if(secavghalf_cycle_time>1100&&secavghalf_cycle_time<1400) audio = .1;
        else if(secavghalf_cycle_time>1400&&secavghalf_cycle_time<1700) audio = .2;
        else if(secavghalf_cycle_time>1700&&secavghalf_cycle_time<2000) audio = .25;
        else if(secavghalf_cycle_time>2000&&secavghalf_cycle_time<2300) audio = .4;
        else if(secavghalf_cycle_time>2300&&secavghalf_cycle_time<2700) audio = .5;

        Thread::wait(50);
    }
}

void newdist(int distance)
{
    //update frequency based on new sonar data
    led2 = !led2;
    half_cycle_time5 = half_cycle_time4;
    half_cycle_time4 = half_cycle_time3;
    half_cycle_time3 = half_cycle_time2;
    half_cycle_time2 = half_cycle_time;
    half_cycle_time = distance<<3;
    avghalf_cycle_time = (half_cycle_time + half_cycle_time2 + half_cycle_time3 +half_cycle_time4 +half_cycle_time5)/5.0;
}

void secnewdist(int distance2)
{
    led3 = !led3;
    secondhalf_cycle_time5 = secondhalf_cycle_time4;
    secondhalf_cycle_time4 = secondhalf_cycle_time3;
    secondhalf_cycle_time3 = secondhalf_cycle_time2;
    secondhalf_cycle_time2 = secondhalf_cycle_time;
    secondhalf_cycle_time = distance2<<3;
    secavghalf_cycle_time = (secondhalf_cycle_time + secondhalf_cycle_time2 + secondhalf_cycle_time3 +secondhalf_cycle_time4 +secondhalf_cycle_time5)/5.0;
}

//2 HC-SR04 Sonar modules
ultrasonic mu(p12, p13, .1, 1, &newdist); // Pitch
ultrasonic mu2(p14,p15,.1,1,&secnewdist); // Volume


void sonar1(void const *argument)
{
    while(1) {
        myMut.lock();
        mu.checkDistance();
        uLCD.locate(0,7);
        myMut.unlock();
        Thread::wait(1);
    }
}

void sonar2(void const *argument)
{
    while(1) {
        myMut.lock();
        mu2.checkDistance();
        myMut.unlock();
        Thread::wait(50);
    }
}

void screen(void const *argument)
{
    while(1) {
        myMut.lock();
        uLCD.text_height(2);
        uLCD.locate(0,2);
        uLCD.printf("Pitch(Hz): %3.1f",1/pitch);
        uLCD.locate(0,4);
        uLCD.printf("Volume(%%): %2.2f", ((float(audio))/.5)*100);
        myMut.unlock();
        Thread::wait(1000);
    }
}

void sinusoid(void const *argument)
{
    while(1) {


        xpos = xpos+1;
        x = xpos % (144);
        y = 100 + 20*audio*sin(3.14*x/(pitch));
        myMut.lock();
        uLCD.filled_rectangle(x, 85,x+1 , 115, BLACK);
        uLCD.pixel(x, y, color);
        myMut.unlock();
        Thread::wait(1);
    }
}



int main()
{
    audio = 0;
    led = 0;
    char Temp1;
    char Temp2;
    
    uLCD.baudrate(3000000);
    
    uLCD.line(0,10,144,10,WHITE);
    uLCD.line(0,14,144,14,WHITE);
    uLCD.line(0,18,144,18,WHITE);
    uLCD.line(0,22,144,22,WHITE);
    uLCD.filled_circle(10,12,3,WHITE);
    uLCD.filled_circle(20,10,3,WHITE);
    uLCD.filled_circle(30,22,3,WHITE);
    uLCD.filled_circle(40,20,3,WHITE);
    uLCD.filled_circle(50,18,3,WHITE);
    
        uLCD.line(0,110,144,110,WHITE);
    uLCD.line(0,114,144,114,WHITE);
    uLCD.line(0,118,144,118,WHITE);
    uLCD.line(0,122,144,122,WHITE);
    uLCD.filled_circle(60,120,3,WHITE);
    uLCD.filled_circle(70,110,3,WHITE);
    uLCD.filled_circle(80,115,3,WHITE);
    uLCD.filled_circle(90,118,3,WHITE);
    uLCD.filled_circle(100,121,3,WHITE);
    uLCD.filled_circle(110,120,3,WHITE);
    
    uLCD.locate(0,6);
    uLCD.text_height(1);
    uLCD.text_width(1);    
    uLCD.color(G);
    uLCD.printf("Welcome to\n\nMBED Theremin!");
    
    wait(5);
    uLCD.cls();
    uLCD.printf("\n\nPlease enter your name.\n\nPress:\n\n1) for minor \n\n2) for major\n\nPress the down key when finished.");
    wait(3);
    while(cont) {
        char bnum=0;
        char bhit=0;
        if(bluemod.readable()) {
            Temp1 = bluemod.getc();
            if (Temp1 <= 'z' && Temp1 >= 'A') {
                s.push_back(Temp1); 
            } else if (Temp1 == '!') {
                Temp2 = bluemod.getc();
                if (Temp2=='B') { //button data packet
                    bnum = bluemod.getc(); //button number
                    bhit = bluemod.getc(); //1=hit, 0=release
                    if (bluemod.getc()==char(~('!' + 'B' + bnum + bhit))) { //checksum OK?
                        switch (bnum) {
                            case '1': //button 1, number 1 (minor
                                if (bhit=='1') {
                                    major = false;
                                    key = "d minor";
                                }
                                break;
                            case '2': //button 2, number 2 (major
                                if (bhit=='1') {
                                    major = true;
                                    key = "D major";
                                }
                                break;
                            case '6': //button 6 down arrow
                                if (bhit=='1') {
                                    uLCD.cls();
                                    cont = false;
                                }
                                break;
                        } // end switch
                    }
                }

            } // End of button check
        }//end if(readable)
    }//end while
    uLCD.locate(0,5);
   uLCD.printf("You look great \ntoday, %s\n",s);
   
   wait(5);
   
    uLCD.locate(0,10);
   uLCD.printf("Enjoy your\nnew theremin!");
   
   wait(2);
   
   uLCD.cls();
   myMut.lock();
   uLCD.locate(0,0);
   uLCD.text_height(2);
   uLCD.printf("Key: %s",key);
   myMut.unlock();
    mu.startUpdates();//start measuring the distance with the sonar
    mu2.startUpdates();
    
    Thread thread1(sonar1);
    Thread thread2(sonar2);
    Thread thread3(Dmajor);
    Thread thread5(screen);
    Thread thread6(sinusoid);


    while(1) {
        led4 = !led4;
        Thread::yield();
    }
}

Below is our project:

Import programFinalProjectRTOSVIII

Musical Theremin Using Bluetooth

Video Demo:

Images:

/media/uploads/arahgozar3/img_5272.jpg /media/uploads/arahgozar3/img_5273.jpg /media/uploads/arahgozar3/image-1-1.jpg /media/uploads/arahgozar3/img_5275.jpg /media/uploads/arahgozar3/img_5290.jpg /media/uploads/arahgozar3/img_0146.jpeg


Please log in to post comments.