Loading...
Pages: [1]   Go Down
Author Topic: Tone() + MIDI = ToneMIDISynth  (Read 657 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Kuk on the old forum provided the basics for MIDI In using the USART on Arduino: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1187962258  However, his version just lit some LEDs on the Note On commands.

That's boring.  I coupled it with tone() to produce a square wave synth.  I do have MIDI gear but no spare jacks (yet), so instead I routed MIDI over Serial using this awesome freeware.  http://projectgus.github.com/hairless-midiserial/  Remember to match your baud rates.

Piezo buzzer on line 7.  You can USB this with the midi-serial bridge OR use Kuk's schematic to add a midi in jack and control it via your favorite MIDI controller.

Using the comments as a guide, this stub can be built out into a full blown MIDI controlled synth.

Video to follow.

Code:
// A very simple MIDI synth.
// Greg Kennedy 2011

#include <avr/pgmspace.h>

#define statusLed 13
#define tonePin 7

// MIDI channel to answer to, 0x00 - 0x0F
#define myChannel 0x00
// set to TRUE and the device will respond to all channels
#define respondAllChannels false

// midi commands
#define MIDI_CMD_NOTE_OFF 0x80
#define MIDI_CMD_NOTE_ON 0x90
#define MIDI_CMD_KEY_PRESSURE 0xA0
#define MIDI_CMD_CONTROLLER_CHANGE 0xB0
#define MIDI_CMD_PROGRAM_CHANGE 0xC0
#define MIDI_CMD_CHANNEL_PRESSURE 0xD0
#define MIDI_CMD_PITCH_BEND 0xE0

// this is a placeholder: there are
//  in fact real midi commands from F0-FF which
//  are not channel specific.
// this simple synth will just ignore those though.
#define MIDI_CMD_SYSEX 0xF0

// a dummy "ignore" state for commands which
//  we wish to ignore.
#define MIDI_IGNORE 0x00

// midi "state" - which data byte we are receiving
#define MIDI_STATE_BYTE1 0x00
#define MIDI_STATE_BYTE2 0x01

// MIDI note to frequency
//  This isn't exact and may sound a bit detuned at lower notes, because
//  the floating point values have been rounded to uint16.
//  Based on A440 tuning.

// I would prefer to use the typedef for this (prog_uint16_t), but alas that triggers a gcc bug
// and does not put anything into the flash memory.

// Also note the limitations of tone() which at 16mhz specifies a minimum frequency of 31hz - in other words, notes below
// B0 will play at the wrong frequency since the timer can't run that slowly!
uint16_t frequency[128] PROGMEM = {8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459, 7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544};

//setup: declaring iputs and outputs and begin serial
void setup() {
  pinMode(statusLed,OUTPUT);   // declare the LED's pin as output

  pinMode(tonePin,OUTPUT);           // setup tone output pin

  //start serial with midi baudrate 31250
  // or 38400 for debugging (eg MIDI over serial from PC)
  Serial.begin(31250);

  // indicate we are ready to receive data!
  digitalWrite(statusLed,HIGH);
}

//loop: wait for serial data
void loop () {
  static byte note;
  static byte lastCommand = MIDI_IGNORE;
  static byte state;
  static byte lastByte;

  while (Serial.available()) {

    // read the incoming byte:
    byte incomingByte = Serial.read();

    // Command byte?
    if (incomingByte & 0b10000000) {
      if (respondAllChannels ||
             (incomingByte & 0x0F) == myChannel) { // See if this is our channel
        lastCommand = incomingByte & 0xF0;
      } else { // Not our channel.  Ignore command.
        lastCommand = MIDI_IGNORE;
      }
      state = MIDI_STATE_BYTE1; // Reset our state to byte1.
    } else if (state == MIDI_STATE_BYTE1) { // process first data byte
      if ( lastCommand==MIDI_CMD_NOTE_OFF )
      { // if we received a "note off", make sure that is what is currently playing
        if (note == incomingByte) noTone(tonePin);
        state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
      } else if ( lastCommand == MIDI_CMD_NOTE_ON ){ // if we received a "note on", we wait for the note (databyte)
        lastByte=incomingByte;    // save the current note
        state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
      }
      // implement whatever further commands you want here
    } else { // process second data byte
      if (lastCommand == MIDI_CMD_NOTE_ON) {
        if (incomingByte != 0) {
          note = lastByte;
          tone(tonePin,(unsigned int)pgm_read_word(&frequency[note]));
        } else if (note == lastByte) {
          noTone(tonePin);
        }
      }
      state = MIDI_STATE_BYTE1; // message data complete
                                 // This should be changed for SysEx
    }
  }
}
Logged

Offline Offline
Newbie
*
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset



Sound output begins at 1:10.
« Last Edit: November 17, 2011, 08:46:00 PM by greg-kennedy » Logged

Netherlands
Offline Offline
Tesla Member
***
Posts: 6814
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Nice work.

recall that you can built a frequency table by having one octave (octave smiley-cool and divide those values by (1 << (8-octave))

Check this thread somewhere in the middle of page 1 - http://arduino.cc/forum/index.php?topic=69163.0
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

0
Offline Offline
Newbie
*
Posts: 23
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I was playing around recently with converting midi to 3 tone AVR sounds and made a spreadsheet that looks like this http://jarv.org/files/midi-avr/arduino_all_piano_tones.html for match values that correspond to piano keys if it's helpful. (Here it is in open office format - http://jarv.org/files/midi-avr/arduino_all_piano_tones.ods )
Logged

Nowhere
Offline Offline
God Member
*****
Posts: 821
|-\ |\|\
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

arduinoroll?   smiley-roll
Logged

Youtube channel: http://www.youtube.com/user/beefinator14
Soundcloud page: http://soundcloud.com/beefinator
Second soundcloud page (ran out of minutes): http://soundcloud.com/beefinator-2

Offline Offline
Newbie
*
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Chip roll'd
Logged

Pages: [1]   Go Up
Print
 
Jump to: