/* USB-to-MIDI demonstration for Flexible MIDI Shield: * http://ruggedcircuits.com/html/flexible_midi_shield.html * * This sketch takes MIDI IN data and displays it in a serial terminal at * 38400bps. It also takes keystrokes coming in on the serial port and * generates MIDI OUT note events. The following keys are used by default, * but can be changed below: * * +----+----+ +----+----+----+ +----+----+ * | | | | | | | | | | * | C# | D# | | F# | G# | A# | | C# | D# | * | | | | | | | | | | * | w | e | | t | y | u | | o | p | * | | | | | | | | | | * +--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+--+-+----+ * | | | | | | | | | | | | * | C | D | E | F | G | A | B | C | D | E | F | * | | | | | | | | | | | | * | a | s | d | f | g | h | j | k | l | ; | ' | * | | | | | | | | | | | | * +----+----+----+----+----+----+----+----+----+----+----+ * * In addition, the '+' and '-' keys increase/decrease the duration * of notes generated from the computer. * * This code is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * * This code is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * A copy of the GNU General Public License can be viewed at * * * Rugged Circuits LLC * http://ruggedcircuits.com */ /* Assumptions: Flexible MIDI Shield has the following jumpers installed: * D2 for MIDI IN * D3 for MIDI OUT * ON for MIDI Enable (MIDI always enabled) */ #include // How long should a computer-initiated note last static uint16_t noteDuration = 250; // milliseconds // Note velocity from computer-initiated notes #define NOTE_VELOCITY 20 // Pins here must match RxD/TxD jumpers installed on Flexible MIDI Shield SoftwareSerial SSerial(2, 3); // The note queue stores notes that we have initiated from the computer. // They are stored here so we can generate note-off events after some // note duration. #define QUEUE_SIZE 16 // Maximum notes all sounding at the same time static struct { long endTime; uint8_t note; } noteQueue[QUEUE_SIZE]; static uint8_t qHead, qTail, qCount; // Generate a MIDI OUT note-on event. void note_on(uint8_t notenum, uint8_t velocity) { notenum &= 0x7FU; velocity &= 0x3F; SSerial.write(0x90); // Note on SSerial.write(notenum); SSerial.write(velocity); } // Generate a MIDI OUT note-off event. void note_off(uint8_t notenum) { notenum &= 0x7FU; SSerial.write(0x80); // Note off SSerial.write(notenum); SSerial.write(32); } // Iterate through all notes in the note queue and turn off all notes // that have exceeded their note duration. static void turnOffNotes(void) { while (qCount) { if (((long)millis() - noteQueue[qTail].endTime) >= 0) { note_off(noteQueue[qTail++].note); if (qTail >= QUEUE_SIZE) qTail=0; qCount--; } else return; } } // Add a note to the note queue to indicate it should have a note-off // event after noteDuration milliseconds. static void enqueue(uint8_t note) { if (note==0) return; if (qCount < QUEUE_SIZE) { noteQueue[qHead].note = note; noteQueue[qHead++].endTime = millis() + noteDuration; note_on(note, NOTE_VELOCITY); if (qHead >= QUEUE_SIZE) qHead = 0; qCount++; } } // This array maps the 256 ASCII characters to the MIDI note number // to generate in response, or 0 to ignore the keypress. static uint8_t key2note[256]; static struct { uint8_t key; uint8_t note; } noteMap[] = { { 'a', 60 }, { 'w', 61 }, { 's', 62 }, { 'e', 63 }, { 'd', 64 }, { 'f', 65 }, { 't', 66 }, { 'g', 67 }, { 'y', 68 }, { 'h', 69 }, { 'u', 70 }, { 'j', 71 }, { 'k', 72 }, { 'o', 73 }, { 'l', 74 }, { 'p', 75 }, { ';', 76 }, { '\'', 77 } }; #define NOTE_MAP_SIZE (sizeof(noteMap)/sizeof(noteMap[0])) // MIDI IN data is displayed on one line as long as there is "activity", // then we generate a newline. These variables keep track. long inactivityTimer; uint8_t activity; void setup(void) { uint8_t i; SSerial.begin(31250); // MIDI IN/OUT Serial.begin(38400); // USB port activity = 0; qHead = qTail = qCount = 0; for (i=0 ; i < NOTE_MAP_SIZE; i++) { key2note[noteMap[i].key] = noteMap[i].note; } } void loop() { uint8_t event; // If no MIDI IN data has arrived in a while generate a newline // to terminate the previous line of displayed activity if (activity && (((long)millis() - inactivityTimer) >= 0)) { Serial.println(); activity = 0; } // Process all MIDI IN data while (SSerial.available()) { event = SSerial.read(); if ((activity == 0) && (event == 0xFE /* heartbeat */)) continue; inactivityTimer = millis() + 100; activity = 1; Serial.print(event, HEX); Serial.print(' '); } // Process all computer data coming from USB if (Serial.available()) { uint8_t c; c = Serial.read(); enqueue(key2note[c]); switch (c) { case 'a': case 'k': Serial.println("C"); break; case 'w': case 'o': Serial.println("C#"); break; case 's': case 'l': Serial.println("D"); break; case 'e': case 'p': Serial.println("D#"); break; case 'd': case ';': Serial.println("E"); break; case 'f': case '\'': Serial.println("F"); break; case 't': Serial.println("F#"); break; case 'g': Serial.println("G"); break; case 'y': Serial.println("G#"); break; case 'h': Serial.println("A"); break; case 'u': Serial.println("A#"); break; case 'j': Serial.println("B"); break; case '=': case '+': noteDuration += 20; break; case '-': case '_': if (noteDuration > 20) noteDuration -= 20; break; } } turnOffNotes(); } // vim: expandtab ts=2 sw=2 ai cindent syntax=cpp