Introduction: Ultrasonic Theremin

This Instructable is part 2 in the series of how to create music using the ultrasonic sensor connected to and Arduino and then run through Openframeworks using the audio library ofxMaxim.

Find part 1 here:
https://www.instructables.com/id/Ultrasonic-sensor-...

Hardware

  • Arduino
  • Ultrasonic sensor (I am using an HC-SR04, can use others)
  • Breadboard
  • Jumper cables
  • USB

Software

  • Arduino IDE (https://www.arduino.cc/en/Main/Software)
  • New Ping Library for Arduino IDE
  • Visual Studios 2015 (Windows 10 and 8 - https://www.visualstudio.com/) or Xcode (Mac - https://www.visualstudio.com/)
  • OpenFrameworks installed on one of the above (http://openframeworks.cc/)
  • Maximillian/ ofxMaxim (github.com/micknoise/Maximilian) addon for OpenFrameworks

Step 1: Step 1: Setting Up the Arduino

To set up the Arduino you should look at my first Instructable.

You can find it here:

https://www.instructables.com/id/Ultrasonic-sensor-in-openFrameworks-using-Arduino/

Step 2: Step 2: Installing OfxMaxim

The first thing you need to do is to download the ofxMaxim library for openFrameworks from https://github.com/micknoise/Maximilian

Then you download the zip-file or you can use git clone in your terminal to get the ofxMaxim repository or download to desktop.

Then you put the ofxMaxim folder inside your addons folder.

The next step is to create a new openFrameworks project using the project generator and before you create the project you click on the addon button at the bottom and inside that you choose your new addon ofxMaxim.

Then you click on the GENERATE PROJECT button in the lower right corner.

Step 3: Step 3: Code

Download the attached files and copy and past the code into the relevant files. Adding keyClass.cpp and keyClass.h as new files.


ofApp.cpp Code

#include "ofApp.h"
//int for storing the byte data from Arduino.
int byteData;
//--------------------------------------------------------------
void ofApp::setup(){
    //General setup of look of window.
    ofBackground(255);
    ofSetColor(0);
    //serial port setup. using COM3 for Windows port.
    //Also using baud rate 9600, same in Arduino sketch.
    sampleRate = 44100; // setting up the sampleRate
    bufferSize = 512; // setting up the bufferSize
    for( int i = 0; i < n.size(); i ++) { // looping through the vector of notes
    keys.emplace_back( new Key(n[i])); // because I'm using a uniqe_ptr I call emplace_back instead of push_back
    }
    ofSoundStreamSetup(2, 0, this, sampleRate, bufferSize, 4); // here I'm setting up the connection with the sound card using ofSoundStreamSetup
    serial.setup("/dev/cu.usbmodem1421", 9600); // the port number will be different on every device and this is running on a Mac and that's why the port number is different. Make sure that the port number is correct before you run the code
}
//--------------------------------------------------------------
void ofApp::update(){
    //Simple if statement to inform user if Arduino is sending serial messages.
    if (serial.available() < 0) {
        msg = "Arduino Error";
    }
    else {
        //While statement looping through serial messages when serial is being provided.
        while (serial.available() > 0) {
            //byte data is being writen into byteData as int.
            byteData = serial.readByte();
            //byteData is converted into a string for drawing later.
            msg = "cm: " + ofToString(byteData);
        }        
    }
}
//--------------------------------------------------------------
void ofApp::draw(){
    //drawing the string version pf byteData on oF window.
   ofDrawBitmapString(msg, 50, 100);
    //printing byteData into console.
    cout << byteData << endl;    
// These if statements checks the specific distance from the ultrasonic sensor and so depending in the distance a different note will be triggered 
    if(byteData >= 1 && byteData <= 2 ){
        keys[0]->on();
    }else{
        keys[0]->off();
    }
    if(byteData >= 3 && byteData <= 4){
        keys[1]->on();
    }else{
        keys[1]->off();    }
    if(byteData >= 5 && byteData <= 6){
        keys[2]->on();
    }else{
        keys[2]->off();
    }
    if(byteData >= 7 && byteData <= 8){
        keys[3]->on();
    }else{
        keys[3]->off();
    }
    if(byteData >= 9 && byteData <= 10){
        keys[4]->on();
    }else{
        keys[4]->off();
    }
    if(byteData >= 11 && byteData <= 12){
        keys[5]->on();
    }else{
        keys[5]->off();
    }
    if(byteData >= 13 && byteData <= 14){
        keys[6]->on();
    }else{
        keys[6]->off();
    }
    if(byteData >= 15 && byteData <= 16){
        keys[7]->on();
    }else{
        keys[7]->off();
    }    
}
void ofApp::audioOut( float * output, int bufferSize, int nChannels){
    for(int i = 0; i < bufferSize; i ++) { // you need to loop through the bufferSize
        for(int j = 0; j < n.size(); j ++) { // looping through the vector of notes
            notes += keys[j]->play() / n.size();
        }
        myMix.stereo(notes, outputs, 0.5);
        output[i * nChannels] = outputs[0];
        output[i * nChannels + 1] = outputs[1];       
    }
}
<strong>ofApp.h</strong>
#pragma once<br>
#include "ofMain.h"
#include "ofxMaxim.h"
#include "keyClass.h"
class ofApp : public ofBaseApp{
public:
    //Standard oF functions.
    void setup();
    void update();
    void draw();    
    void audioOut( float * ouput, int bufferSize, int nChannels);
    int bufferSize, sampleRate;
    double notes;
    double outputs[ 2 ];
//array of frequencies     
    vector n {261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25 };
    vector> keys;
    maxiMix myMix;
    //Custom variables for on screen string and font.
    string msg;
    //ofTrueTypeFont font;
    //New serial object.
    ofSerial serial;
};


keyClass.cpp Code

#include "keyClass.h"<br>
Key::Key(double _f) {
    freq = _f;
    env.setAttack(1000);
    env.setDecay(1);
    env.setSustain(1);
    env.setRelease(1000);
}
void Key::on() {
    env.trigger = 1;
}
void Key::off() {
    env.trigger = 0;
}
double Key:: play(){  
    return env.adsr(osc.sinewave(freq), env.trigger);
}

keyClass.h Code

#ifndef __keyboardofxMaxim__keyClass__<br>
#define __keyboardofxMaxim__keyClass__
#include 
#include "ofxMaxim.h"
class Key {
public:
    maxiOsc osc;
    maxiEnv env;
    double freq;
    Key( double _f);    
    double play();
    void on();
    void off();
};
#endif /* defined(__keyboardofxMaxim__keyClass__) */


Step 4: Step 4: Let's Play Some Music!

In the video you can see us playing with a C scale!

Have fun!!