/* Rotator demonstration for Flexible MIDI Shield: * http://ruggedcircuits.com/html/flexible_midi_shield.html * * Based on the algorithm described by Robby Kilgore in this project: * http://www.youtube.com/watch?v=4kBpxBJkknY * * This sketch takes MIDI input note events and generates two additional * notes for each note coming in. One note is always in parallel with * the incoming note (e.g., always a fifth up) while the other note * rotates among a list of intervals relative to the incoming note. * * 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: * D0 for MIDI IN * D1 for MIDI OUT * D17 for MIDI Enable */ // Define pin numbers #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) # define MIDI_EN 57 // PF3 = 57 #else # define MIDI_EN 17 #endif // Define parallel voice offset and velocity #define PARALLEL_OFFSET 7 // semitones #define PARALLEL_VELOCITY 102 // velocity scaling (0-128). 102 is ~80% // Define rotator offsets and velocity static uint8_t rotations[] = { -5, -10, -7, -14 }; // semitones #define NUM_ROTATIONS (sizeof(rotations)/sizeof(rotations[0])) #define ROTATOR_VELOCITY 102 // velocity scaling (0-128). 102 is ~80% // This stores the index into the rotations[] array that will be the // next rotation generated. static uint8_t currRotation; // These store the notes we generate programmatically, so we know which // notes should be turned off. static uint8_t mainNoteOn; // Which note triggered our own notes static uint8_t parNoteOn; // Which note was played for parallel static uint8_t rotNoteOn; // Which note was played for rotator void setup(void) { Serial.begin(31250); pinMode(MIDI_EN, OUTPUT); digitalWrite(MIDI_EN, HIGH); currRotation = 0; mainNoteOn = parNoteOn = rotNoteOn = 0; pinMode(13, OUTPUT); // Just for a little light show } // Generate a MIDI OUT note-on event. The 'event' parameter is really // passed just so we can preserve the lower 4 bits (MIDI channel number). void note_on(uint8_t event, uint8_t notenum, uint8_t velocity) { notenum &= 0x7FU; velocity &= 0x3F; Serial.write(event); // Note on Serial.write(notenum); Serial.write(velocity); } // Generate a MIDI OUT note-off event. The 'event' parameter is really // passed just so we can preserve the lower 4 bits (MIDI channel number). void note_off(uint8_t event, uint8_t notenum) { notenum &= 0x7FU; Serial.write(event & 0xEF); // Note off Serial.write(notenum); Serial.write(32); } // Turn off the parallel and rotation notes. The 'event' parameter // is passed just so we can preserved MIDI channel number. static void slaveNotesOff(uint8_t event) { if (parNoteOn) { note_off(event, parNoteOn); parNoteOn = 0; } if (rotNoteOn) { note_off(event, rotNoteOn); rotNoteOn = 0; } } // Handle an incoming note-on or note-off event. static void doEvent(uint8_t event, uint8_t note, uint8_t velocity) { if ( ((event & 0xF0) == 0x80) // MIDI command 0x80 is note off || (velocity==0) // ...also accept note-on with velocity 0 as a note-off event ) { // Note off if (note == mainNoteOn) { // Slave our notes off too slaveNotesOff(event); } } else if ((event & 0xF0) == 0x90) { // Note on...should always be true by this point // If any slave notes are playing, turn them off first slaveNotesOff(event); // Now keep track of what note is generating parallel+rotated notes, so only when // this same note has a note-off event do we turn off the slave notes. mainNoteOn = note; // Generate parallel note parNoteOn = note + PARALLEL_OFFSET; note_on(event, parNoteOn, ((uint16_t)PARALLEL_VELOCITY*velocity+64) >> 7); // Generate rotated note rotNoteOn = note + rotations[currRotation++]; note_on(event, rotNoteOn, ((uint16_t)ROTATOR_VELOCITY*velocity+64) >> 7); if (currRotation >= NUM_ROTATIONS) currRotation=0; } } static uint8_t toggle; void loop() { uint8_t event, note, velocity; if (Serial.available()) { digitalWrite(13, toggle); toggle = ! toggle; event = Serial.read(); if (event == 0xFE) return; // System activity heartbeat if ((event & 0xE0) == 0x80) { // Note-on event or note-off event while (! Serial.available()) /* NULL */ ; note = Serial.read(); while (! Serial.available()) /* NULL */ ; velocity = Serial.read() & 0x3F; doEvent(event, note, velocity); } } } // vim: expandtab ts=2 sw=2 ai cindent syntax=cpp