Hi everyone;
I'm trying to get a simple tone sketch to respond to midi inputs with absolutely no success. I tried using the MIDI library and none of the example sketches respond to midi input (I've googled this problem a LOT and only found threads where people never got it working and gave up), so I tried this simple sketch with no library:
/ 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 true
// 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] = {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() {
tone(7, 440);
delay(100);
noTone(7);
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(38400);
// 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
}
}
}
Not my sketch, I found it here on the forums but I can't see any problem with it. In fact it almost works, but the notes are almost inaudible, I have to crank my headphone volume all the way up to hear the tone. Its almost like the tone pin isn't set as an output (it is). I added this to setup():
tone(7, 440);
delay(100);
noTone(7);
The beep occurs really REALLY loudly (as i'd expect, its a 5v squarewave); yet the subsequent tones are whisper
quiet. I have no idea how to start debugging this, the MIDI messages are getting through fine, I'm using zterm to spy on the serial port, and the Rx light on the arduino blinks.
Any ideas how to fix it?
EDIT: Tried with a MIDI keyboard instead of a PC and it will play C4 at the right volume but no other notes...
EDIT AGAIN: I fixed it:
line 105:
tone(tonePin,(unsigned int)pgm_read_word(&frequency[note]));
should be
tone(tonePin, frequency[note]);
I'm guessing the notes were meant to be stored in flash or something...