Joseph Constan's Log

August 18 - Speech Recognition

posted Aug 18, 2010, 7:46 PM by Unknown user

Today I worked on the speech recognition part of my final. I used the voce library (http://voce.sourceforge.net/) to set up a simple test app which recognizes the two phrases required for the project and reads them back for confirmation. For this to run, a few things need to be done: the voce library must be unzipped to the "libraries" directory in the sketchbook folder, the sketch must be in a folder called SpeechRec in the sketchbook (or if it isn't the path in the code should be changed accordingly), and in the sketch folder, a folder named "grammar" must be created, with a file called "test.gram" inside it, containing the code in this post. Likewise, there is a setting in preferences to increase the memory that your sketch uses to 256 mb - this needs to be checked, otherwise your sketch will get an Out Of Memory error. Thanks to the nice folks in this thread at processing.org for supplying the info needed to get this library up and running. I attached a short swf video of myself saying the phrases into my laptop mic to demonstrate.
 


Processing Sketch:

 
import voce.*;

void setup() {
  size(200,200);
  background(0);
  voce.SpeechInterface.init("../Users/Joseph/Documents/Processing/libraries/voce/lib", true, true, 
 "../Users/Joseph/Documents/Processing/SpeechRec/grammar", "test");
  System.out.println("This is a speech recognition test. ");
}
void draw() { 
  stroke(255);
  while (voce.SpeechInterface.getRecognizerQueueSize() > 0){
    String s = voce.SpeechInterface.popRecognizedString();
    println("You said: " + s);
    voce.SpeechInterface.synthesize(s);
  }
}

test.gram:

 
#JSGF V1.0;

grammar Test;

public <TEST> = (I solemnly swear that I am up to no good | mischief managed) * ;




Final Project Pitch - Marauder's Map

posted Aug 10, 2010, 7:04 PM by Unknown user

Okay, so I was in the new Harry Potter theme park in Florida about a month ago and noticed how realistic everything was, how many items they included from the books and movies - although I noticed their marauder's map was surprisingly not interactive. I had expected it to track the footsteps of people walking around the store, but it just looped an animation on a computer screen. One idea I have for a final project would be the following:

Create a bunch of pressure sensors in a manner similar (not necessarily identical) to that outlined in this short tutorial: http://www.ehow.com/how_5038316_make-pressure-sensor.html
Hook these up to Arduino* and send the data to Processing.
 
 
Before any of the data in Processing will be used, the sketch will wait for the user to say into the microphone "I solemnly swear I am up to no good" using the java library here: http://java.sun.com/products/java-media/speech/forDevelopers/jsapi-guide/Recognition.html#11644
once this is done, the locations of the steps will be interpreted in processing relative to the previous location as well as the status of adjacent sensors in order to determine the direction of the footsteps.
 
 
the footstep images will then be rotated accordingly and printed to the screen at the best approximate location, over a background image of a piece of parchment with a map drawn reminiscent of the one from the movie (not exactly the same though as my sensors would probably not even fill a small room, let alone an entire magical british boarding school)
this would be looped until the sketch detects the words "mischief managed" through the microphone, at which point it would return to its initial state.

 
 
As far as the speech recognition goes, I would test the library itself extensively to ensure accuracy, making note of any phrases it might confuse the desired ones for so that they could be accepted as well in the final project, or (heaven forbid) if the library is altogether not accurate enough, the program would obviously still work fine without the on and off commands, it would just be much less cool.

 
 
*One problem I can foresee with hooking up multiple sensors to the arduino is the lack of a crapton of inputs on the arduino, and I would certainly prefer having more than 10 or 20 sensors, because without more the project would either be less accurate or cover less space than I would like. So if anyone knows of a way around this (I have one or two ideas in mind but I'm not sure if they're feasible / reliable) definitely let me know.

 
Also feel free to point out any glaring oversights I may have made in coming up with this, it was more or less a spur of the moment idea. All in all, I think it would be really fun and probably not too difficult, not to mention just being a cool thing to have and say you made (as well as appeasing my nostalgic love for the harry potter series).

Assignment 11.1

posted Aug 2, 2010, 2:33 PM by Unknown user   [ updated Aug 2, 2010, 3:12 PM ]

I turned this:
 
 
into this:
 
 
To get these:
 
So I could do this:

YouTube Video

(The tiny incandescent light was already attached to the motor as the "headlight" of the motorcycle, so I just left it on there. Also the thing jumping around is quite obviously the motor.)

Code:

const int switch1 = 4;           // Digital pin 6 connected to the push button
const int transistor1 = 9;    // connected to the base of the transistor

void setup() {
  // set  the transistor pin as output:
  pinMode(transistor1, OUTPUT);
}

void loop() {
  // turn the motor on or off based on the state of the push button
  digitalWrite(transistor1, digitalRead(switch1));
}
 
 
 
 


 

(Quick note the I used a 4 battery pack, not 2. Fritzing just didn't have a part for it and I didn't exactly find it necessary to make a new part just for the sake of adding 2 batteries.)

Assignment 10.3

posted Aug 1, 2010, 8:01 PM by Unknown user

I spent about 4 hours trying to get either a call-and-response or punctuation system to work when going from processing to the arduino but nothing I tried would work - it would only set on of the 3 leds, flash constantly between the 3 values, just stay bright white, pretty much do anything other than what I wanted to, and being unable to find any good information on what I was trying to do, I wound up having to split up the byte being sent into 3 parts for each of the RGB values. In processing this image (called "colorWheel.png") is displayed:
 
 

 
and the color data of the pixel that the mouse is currently over is remapped so that red values take up the lower third of a byte (0 - 84), greens take up the middle (85 - 170), and blues the highest third (171 - 255). These vales are then sent to arduino via serial, which it uses to set the level of each LED in the RGB LED. Technically, each piece of information is sent separately, their unique ranges allow the receiver to interpret them properly, requiring no punctuation or call-and-response.

PROCESSING CODE:

 
import processing.serial.*;  //import all serial libraries
Serial serPort;              // Create object from Serial class
PImage colorWheel;
 
void setup() {
  colorWheel = loadImage("colorWheel.png");
  size(colorWheel.width, colorWheel.height);
  serPort = new Serial(this, Serial.list()[0], 9600);
  image(colorWheel, 0, 0);
}
void draw() {
  // get color data of pixel currently being moused over
  color mousedColor = get(mouseX, mouseY);
    
  // print color values to terminal (for debugging purposes)
  print("red = ");
  println(byte(red(mousedColor)-128)+128);
  print("green = ");
  println(byte(green(mousedColor)-128)+128);
  print("blue = ");
  println(byte(blue(mousedColor)-128)+128);    

  // send remapped RGB values to serial:
  // (the +128 - 128 thing is there to make the number signed so it can go through byte(), and then make it unsigned again)
  serPort.write(byte(map(red(mousedColor), 0, 255, 0, 84)-128)+128);      //write red value to serial
  serPort.write(byte(map(green(mousedColor), 0, 255, 85, 170)-128)+128);    //write green value to serial
  serPort.write(byte(map(blue(mousedColor), 0, 255, 171, 255)-128)+128);     //write blue value to serial  

  delay(50);
}

ARDUINO CODE:

 
const int led[3] = {9, 10, 11};

void setup() {
  Serial.begin(9600);
}

void loop() {
  //if bytes are available
  if (Serial.available() > 0) {
    int inByte = Serial.read();    //save last serial byte
    if(inByte > 0 && inByte < 85)  //red
      analogWrite(led[0], map(inByte, 0, 84, 0, 255));  
    if(inByte > 84 && inByte < 171)  //green
      analogWrite(led[1], map(inByte, 85, 170, 0, 255));  
    if(inByte > 170 && inByte < 256)  //blue
      analogWrite(led[2], map(inByte, 171, 255, 0, 255));
  }
}
 

 

Assignment 10.2

posted Aug 1, 2010, 10:44 AM by Unknown user

Move your mouse to the right in the processing sketch, the LED gets bright. Move it to the left, it gets darker. Genius.
Also the background in processing gets brighter and darker along with the LED. I thought that was fun.

PROCESSING CODE:

 
import processing.serial.*;  //import all serial libraries
Serial serPort;              // Create object from Serial class
 
void setup() {
  size(600, 150);
  serPort = new Serial(this, Serial.list()[0], 9600);
}
void draw() {
  int briteness = int(map(mouseX, 0, 600, 0, 255));  //set variable relative to mouse X
  serPort.write(briteness);                          //write value to serial
  background(0, briteness, 0);                       //set background green to value
  delay(50);                                //delay 50 ms so arduino can catch up
}

ARDUINO CODE:

 
const int led1 = 9;
int brightness;

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {  //check if serial isn't empty
    brightness = Serial.read();  //set varible to last byte in serial
    analogWrite(led1, brightness);  //write value to LED
  }
  delay(50);
}
 

 

Assignment 10.1

posted Aug 1, 2010, 10:14 AM by Unknown user   [ updated Aug 1, 2010, 10:24 AM ]

Pretty simple, so I'll just get right to it. Code:
 
 

const int pot1 = 4;
const int led1 = 9;
int brightness;

void setup() {
}

void loop() {
  brightness = map(analogRead(pot1), 0, 1023, 0, 255);
  analogWrite(led1, brightness);
  delay(50);
}
 
 

 
 

Assignment 9.3

posted Jul 30, 2010, 11:41 PM by Unknown user   [ updated Jul 31, 2010, 12:10 AM ]

In the interest of creating larger, more complex projects as opposed to more frequent but simpler ones, I more or less combined 9.2 and 9.3 into a techno synth lead / drum machine / loop machine. The top 2 rows of keys act as a piano-type keyboard to control the synth, the bottom keys are a drum machine, and keys o and p control the loop ('o' starts and stops the loop because it is a circle, the standard symbol for record, and 'p' plays / pauses the loop). it can stack up to 3 tracks on top of each other (2 recorded and the one you are playing) although due to limitations in the minim library, the drum machine cannot be looped back. I wanted this to be able to play chords so I created a seperate sineWave instance for each key (although the audio does start clipping with the bigger chords, so if you want to rock out some jazz on this thing for whatever reason, just set the volume lower). At any rate, enjoy the product of about a day's worth of poking around in the minim documentation and subsequent frustrated debugging. I included an swf video of me hacking through the synth lead and drum part of "kids" by MGMT (great song).
Code:
 
 

import ddf.minim.*;
import ddf.minim.signals.*;

Minim minim;                           //sound library base class
AudioOutput out;                      //output channel for sinewaves
SineWave[] sine;                      //array of SineWave oscillator subclasses
AudioRecorder loopRecorder;    //recorder objects for 2 loop tracks
AudioRecorder loopRecorder2;
AudioPlayer loopPlayer;            //player objects for loop tracks
AudioPlayer loopPlayer2;

byte timesRecorded;                //simple counter that goes up every time a track is recorded
AudioSample kick;                   //various audio samples for the drum machine
AudioSample snare;
AudioSample cowbell;
AudioSample hihat;
AudioSample crash;
AudioSample ride;

void setup()
{
  size(512, 200, P2D);
  
  minim = new Minim(this);
  // get a line out from Minim, default bufferSize is 1024, default sample rate is 44100, bit depth is 16
  out = minim.getLineOut(Minim.MONO);
  
  //set loopRecorders to record "out" to file "recording(2).wav", with buffering true
  loopRecorder = minim.createRecorder(out, "recording.wav", true);
  loopRecorder2 = minim.createRecorder(out, "recording2.wav", true);
 
 
  
  //allocate sinewaves
  sine = new SineWave[26];
  for(int i = 0; i < 26; i++) {
    // create a sine wave Oscillator, set to respective, at 0.5 amplitude, sample rate from line out
    sine[i] = new SineWave(0, 0.5, out.sampleRate());
    // set the portamento speed on the oscillator to 200 milliseconds
    sine[i].portamento(30);
    // add the oscillator to the line out
    out.addSignal(sine[i]);
  }
  //load samples from files into 256 byte buffers
  kick = minim.loadSample("bassdrum.wav", 256);
  snare = minim.loadSample("snare.wav", 256);
  hihat = minim.loadSample("hihat.wav", 256);
  crash = minim.loadSample("crash.wav", 256);
  ride = minim.loadSample("ride.wav", 256);
  cowbell = minim.loadSample("cowbell.wav", 256);
}

void draw()
{
  make background green if playing, red if recording, black otherwise
  if (timesRecorded > 0) {
    if (loopRecorder.isRecording() || loopRecorder2.isRecording())
      background(255, 0, 0);
    else if (loopPlayer.isPlaying())
      background(0, 255, 0);
    else
      background(0);
  } else {
  if (loopRecorder.isRecording())
      background(255, 0, 0);
    else
      background(0);
  }
  stroke(255);
  // draw the waveforms
  for(int i = 0; i < out.bufferSize() - 1; i++)
  {
    float x1 = map(i, 0, out.bufferSize(), 0, width);
    float x2 = map(i+1, 0, out.bufferSize(), 0, width);
    line(x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50);
    line(x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50);
  }
  // if loop starts getting out of sync while recording, end and save recording for playback
  if (timesRecorded > 0) {
    if (!loopPlayer.isPlaying() && loopRecorder2.isRecording()) {
      loopRecorder2.endRecord();
      loopRecorder2.save();
      timesRecorded++;
    }
  }
}

void keyPressed() {
  if (!(key-97 > 26) && !(key-97 < 0)) {    //if 'key' == ASCII code of any of the letters of the alphabet (lowercase)
    if (keysToNotes[int(key)-97] != 0) {    //and the key corresponds to a note
      sine[int(key)-97].setFreq(keysToNotes[int(key)-97]);    //set the frequency to that note, based on the key's respective frequency in the keysToNotes array
    }
  } 
  if ( key == 'z' ) kick.trigger();        //if the key corresponds to a drum piece, play the clip
  if ( key == 'c' ) snare.trigger();
  if ( key == 'b' ) hihat.trigger();
  if ( key == 'n' ) crash.trigger();
  if ( key == 'x' ) cowbell.trigger();    //needs more cowbell
  if ( key == 'm') cowbell.trigger();    //there it is
  if ( key == 'v' ) ride.trigger();
  if ( key == 'o' ) {
    if (loopRecorder.isRecording() == true || loopRecorder2.isRecording() == true) {
      if (timesRecorded > 0) {
        loopRecorder2.endRecord();        //if recording and have recorded previous loops, save as recording2
        loopRecorder2.save();
        loopPlayer.pause();
      } else {
        loopRecorder.endRecord();        //else save as recording
        loopRecorder.save();
      }
      timesRecorded++;                        //increment counter
      if (timesRecorded > 0) loopPlayer = minim.loadFile("../recording.wav", 2048);        //reload player files
      if (timesRecorded > 1) loopPlayer = minim.loadFile("../recording2.wav", 2048);
    } else {
      if (timesRecorded > 0) {                //if not currently recording, start recording
        loopRecorder2.beginRecord();
        loopPlayer = minim.loadFile("../recording.wav", 2048);
        loopPlayer.play();
      } else {
        loopRecorder.beginRecord();
      }
    }
  }
  if ( key == 'p' && timesRecorded > 0) {        //if pressed key p and have recorded a track
    if (loopPlayer.isPlaying()) {
      loopPlayer.pause();                                //pause it if it's playing
      if (timesRecorded > 1) loopPlayer2.pause();
    } else {
      loopPlayer = minim.loadFile("../recording.wav", 2048);
      loopPlayer.loop();                                                        //but load and play the track if it's not playing
    } 
    if (timesRecorded > 1) {
      loopPlayer2 = minim.loadFile("../recording2.wav", 2048);    //and play recording2 if it exists
      loopPlayer2.loop();
    } 
  }
}

void keyReleased() {                                //if key is released
  if (!(key-97 > 26) && !(key-97 < 0)) {        //set the frequency for the sinewave class corresponding to that key to 0
    if (keysToNotes[int(key)-97] != 0) {
      sine[int(key)-97].setFreq(0);
    }
  }
}

void stop()
{
  out.close();
  minim.stop();
  
  super.stop();
}

PITCHES FILE:
 
 
 /*************************************************
  * Public Constants
  *************************************************/

 public static int NOTE_B0  =31;        //these need to be public static ints
 public static int NOTE_C1  =33;        //because apparently lame Java doesn't
 public static int NOTE_CS1 =35;       //have a preprocessor!! Obj-C is so much better
 public static int NOTE_D1  =37;
 public static int NOTE_DS1 =39;
 public static int NOTE_E1  =41;
 public static int NOTE_F1  =44;
 public static int NOTE_FS1 =46;
 public static int NOTE_G1  =49;
 public static int NOTE_GS1 =52;
 public static int NOTE_A1  =55;
 public static int NOTE_AS1 =58;
 public static int NOTE_B1  =62;
 public static int NOTE_C2  =65;
 public static int NOTE_CS2 =69;
 public static int NOTE_D2  =73;
 public static int NOTE_DS2 =78;
 public static int NOTE_E2  =82;
 public static int NOTE_F2  =87;
 public static int NOTE_FS2 =93;
 public static int NOTE_G2  =98;
 public static int NOTE_GS2 =104;
 public static int NOTE_A2  =110;
 public static int NOTE_AS2 =117;
 public static int NOTE_B2  =123;
 public static int NOTE_C3  =131;
 public static int NOTE_CS3 =139;
 public static int NOTE_D3  =147;
 public static int NOTE_DS3 =156;
 public static int NOTE_E3  =165;
 public static int NOTE_F3  =175;
 public static int NOTE_FS3 =185;
 public static int NOTE_G3  =196;
 public static int NOTE_GS3 =208;
 public static int NOTE_A3  =220;
 public static int NOTE_AS3 =233;
 public static int NOTE_B3  =247;
 public static int NOTE_C4  =262;
 public static int NOTE_CS4 =277;
 public static int NOTE_D4  =294;
 public static int NOTE_DS4 =311;
 public static int NOTE_E4  =330;
 public static int NOTE_F4  =349;
 public static int NOTE_FS4 =370;
 public static int NOTE_G4  =392;
 public static int NOTE_GS4 =415;
 public static int NOTE_A4  =440;
 public static int NOTE_AS4 =466;
 public static int NOTE_B4  =494;
 public static int NOTE_C5  =523;
 public static int NOTE_CS5 =554;
 public static int NOTE_D5  =587;
 public static int NOTE_DS5 =622;
 public static int NOTE_E5  =659;
 public static int NOTE_F5  =698;
 public static int NOTE_FS5 =740;
 public static int NOTE_G5  =784;
 public static int NOTE_GS5 =831;
 public static int NOTE_A5  =880;
 public static int NOTE_AS5 =932;
 public static int NOTE_B5  =988;
 public static int NOTE_C6  =1047;
 public static int NOTE_CS6 =1109;
 public static int NOTE_D6  =1175;
 public static int NOTE_DS6 =1245;
 public static int NOTE_E6  =1319;
 public static int NOTE_F6  =1397;
 public static int NOTE_FS6 =1480;
 public static int NOTE_G6  =1568;
 public static int NOTE_GS6 =1661;
 public static int NOTE_A6  =1760;
 public static int NOTE_AS6 =1865;
 public static int NOTE_B6  =1976;
 public static int NOTE_C7  =2093;
 public static int NOTE_CS7 =2217;
 public static int NOTE_D7  =2349;
 public static int NOTE_DS7 =2489;
 public static int NOTE_E7  =2637;
 public static int NOTE_F7  =2794;
 public static int NOTE_FS7 =2960;
 public static int NOTE_G7  =3136;
 public static int NOTE_GS7 =3322;
 public static int NOTE_A7  =3520;
 public static int NOTE_AS7 =3729;
 public static int NOTE_B7  =3951;
 public static int NOTE_C8  =4186;
 public static int NOTE_CS8 =4435;
 public static int NOTE_D8  =4699;
 public static int NOTE_DS8 =4978;
 
 //table for key to note conversion
int[] keysToNotes = {NOTE_C5, 0, 0, NOTE_E5, NOTE_DS5, NOTE_F5, NOTE_G5, 
      NOTE_A5, 0, NOTE_B5, NOTE_C6, 0, 0, 0, 0, 0, NOTE_B4, 0, 
      NOTE_D5, NOTE_FS5, NOTE_AS5, 0, NOTE_CS5, 0, NOTE_GS5, 0};
 

Assignment 6.3

posted Jul 26, 2010, 12:12 PM by Unknown user

I just looked at edline and realized I forgot to do 6.3 from way back when... Obviously pretty late but it's better than nothing. I like using the photoresistor because it has a lot of interesting real world applications (my favorite probably being the one in my macbook that tells the keyboard to light up in the dark.) In as complete darkness as I could manage, the lowest value I could get from the resistor was around 190 and the highest was just under 400, so I set the "map" parameters accordingly. Also I'd be interested to know if they measure strictly from the visible spectrum, because my UV light seemed to have little effect on it.
 

ARDUINO CODE:

 
const int photo1 = 5;    // select the input pin for the photoresistor
const int led1 = 13;   // select the pin for the LED

void setup() {
  pinMode(led1, OUTPUT);  // declare led1 as an OUTPUT
  Serial.begin(9600);
}

void loop() {
  // read and map the value from the resistor
  int val = map(analogRead(photo1), 180, 400, 0, 1023); 
  Serial.println(val, DEC);    // print val to serial (for debugging purposes)

  digitalWrite(led1, HIGH);  // turn led1 on
  delay(val);                  // delay for 'val' milliseconds
  digitalWrite(led1, LOW);   // turn led1 off
  delay(val);                  // delay for 'val' milliseconds
}

Assignment 9.1

posted Jul 17, 2010, 5:53 PM by Unknown user   [ updated Jul 17, 2010, 6:04 PM ]

Hooked up potentiometers to the arduino board, sent values through serial, read them in processing, same old stuff, the only addition being at the end the values are converted into frequencies / amplitudes and played using the minim library. Pretty simple - I did add an extra octave for a little extra complexity though.

ARDUINO CODE:
 
 
const int pot1 = 5;
const int pot2 = 4;

void setup () {
  Serial.begin(9600);  //begin serial communication at 9600 bps
}
void loop () {
  //print to serial port
  Serial.print(analogRead(pot1));  
  Serial.print(',');
  Serial.println(analogRead(pot2));  
  delay(20);
}

PROCESSING CODE:
 
 
import processing.serial.*;
 
 
import ddf.minim.*;
import ddf.minim.signals.*;

Serial serPort;

Minim minim;
AudioOutput out;
SineWave sine;
SineWave octaveDown;

void setup () {
  
  size(512, 200, P2D);

  minim = new Minim(this);
  // get a line out from Minim, default bufferSize is 1024, default sample rate is 44100, bit depth is 16
  out = minim.getLineOut(Minim.STEREO);

  // create a sine wave Oscillator, set to 440 Hz, at 0.5 amplitude, sample rate from line out
  sine = new SineWave(440, 0.5, out.sampleRate());

  // create second wave at half frequency (one octave down)
  octaveDown = new SineWave(220, 0.5, out.sampleRate());

  // set the portamento speed on the oscillator to 200 milliseconds
  octaveDown.portamento(20);
  sine.portamento(20);

  // add the oscillator to the line out
  out.addSignal(sine);
  out.addSignal(octaveDown);

  // set up serial buffer
  serPort = new Serial(this, Serial.list()[0], 9600);
  serPort.bufferUntil('\n');
}

void draw () {
  background(0);
  stroke(255);
  // draw the waveforms
  for(int i = 0; i < out.bufferSize() - 1; i++)
  {
    float x1 = map(i, 0, out.bufferSize(), 0, width);
    float x2 = map(i+1, 0, out.bufferSize(), 0, width);
    line(x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50);
    line(x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50);
  }
}

void serialEvent(Serial serPort) {
   //store message from buffer in string
  String message = serPort.readStringUntil('\n');
  if(message != null) {
    //remove whitespace and other nonsense
    message = trim(message);

    //set frequency to the pot value remapped to 60 - 1500 Hz
    float freq = int(split(message, ','))[0];
    sine.setFreq(map(freq, 0, 1023, 1500, 60));
    //do the same to the octaveDown but cut the freq in half
    octaveDown.setFreq(map(freq, 0, 1023, 1500, 60)/2);
    
    //set amp to value of 2nd pot, remapped to 0 - 1
    float amp = int(split(message, ','))[1];
    sine.setAmp(map(amp, 0, 1023, 0, 1));
    octaveDown.setAmp(map(amp, 0, 1023, 0, 1));
  }
}
void stop()
{
  //deallocate classes and whatnot
  out.close();
  minim.stop();
  
  super.stop();
}

FRITZING DIAGRAM:
 
 
 
 

Assignment 8.1

posted Jul 15, 2010, 6:37 PM by Unknown user   [ updated Jul 15, 2010, 7:28 PM ]

At first I was going to use push buttons for piano keys, but then I realized I only had 5 or so, not to mention pushing them wouldn't exactly be a very light action (musical term), so I decided the best way to emulate a musical keyboard was with the keyboard on my computer. I used the processing commands for determining the key that is currently being pressed and sent that key through serial to the arduino board, where it tested what note the key represented (asdfghjk = cdefgabc starting in octave 5 with the sharps / flats on wetyu) and played that note. To keep things interesting, I connected a potentiometer and used it as a pitch bender (like the ones on those fancy $500+ electronic keyboards). I attached a short sound clip of me demonstrating the keyboard as well as the pitch bend.

 
 
There are a few flaws, however. Only one note can be played at a time, however there's not much that I can do about this considering I only have one piezo at the moment. Also the pitch bend will detune the piano slightly while it is bent: this is because the relationship from one note to the next is not perfectly linear, so if every frequency is raised, say 20 Hz, the intervals will no longer be those in any standard western scale. However, I'm sure a formula exists for this relationship, so it could be fixed if necessary.

Code:

ARDUINO:

 
 
 
#include "pitches.h"
#define ASCIIOFFSET 97    //the ascii code of the start of the alphabet (letter 'a')
#define PITCHBEND  (analogRead(pot1)/20)    //pitch bend = value from pot1 / 20, because 0-1023 is too drastic of a bend

 const int piezo1 = 12;
 const int pot1 = 5;
 
// set up an array to convert the ASCII codes of letters a-z (lowercase) into the desired notes
// "home keys" are the c scale (white keys) while the keys in the row above are the sharps and flats (black keys), if applicable
const int keysToNotes[26] = {NOTE_C5, 0, 0, NOTE_E5, NOTE_DS5, NOTE_F5, NOTE_G5,
                                           NOTE_A5, 0, NOTE_B5, NOTE_C6, 0, 0, 0, 0, 0, 0, 0,
                                           NOTE_D5, NOTE_FS5, NOTE_AS5, 0, NOTE_CS5, 0,
                                           NOTE_GS5, 0};
                              
 void setup() {
    Serial.begin(9600);    //begin serial communication at 9600 bps
}
void loop() {
   // convert the current serial byte (key pressed) to a musical note, then store the note in an int
   int currentNote = keysToNotes[int(Serial.read())-ASCIIOFFSET];
  
   if(currentNote != 0) {    //if there is a corresponding note for the current key
      tone(piezo1, currentNote - PITCHBEND, 50);    //play the tone for 50 ms through piezo1, accounting for pitch bend
   }
}

PROCESSING:

 
import processing.serial.*;
Serial serPort;  // Create object from Serial class

void setup() 
{
  size(200, 200);
  // set up serial port
  serPort = new Serial(this, Serial.list()[0], 9600);
}

void draw() {
  if (keyPressed) {
    serPort.write(byte(key));    //write the letter of the key currently being pressed to the serial port
  }
 

1-10 of 28