From f95db9c5355ed5d56f9f7b073345248d19270452 Mon Sep 17 00:00:00 2001 From: Lukas Bachschwell Date: Fri, 15 Jul 2016 13:23:57 +0200 Subject: [PATCH] Added teensy mods --- teensy-midi-clock-mods/README.md | 91 ++++ teensy-midi-clock-mods/Teensy_midi.ino | 80 ++++ teensy-midi-clock-mods/usb_api.cpp | 561 +++++++++++++++++++++++++ teensy-midi-clock-mods/usb_api.h | 125 ++++++ 4 files changed, 857 insertions(+) create mode 100755 teensy-midi-clock-mods/README.md create mode 100755 teensy-midi-clock-mods/Teensy_midi.ino create mode 100644 teensy-midi-clock-mods/usb_api.cpp create mode 100644 teensy-midi-clock-mods/usb_api.h diff --git a/teensy-midi-clock-mods/README.md b/teensy-midi-clock-mods/README.md new file mode 100755 index 0000000..4da9572 --- /dev/null +++ b/teensy-midi-clock-mods/README.md @@ -0,0 +1,91 @@ +Teensy-USB-MIDI-CLOCK-SEQUENER-INTERFACE +======================================== + +USB MIDI CLOCK SYNC FOR ANALOG CV/GATE SEQUENCERS! + +Original Source! http://www.goo.gl/Xh2skw + +// Teensyduino MIDI Beat Clock Example +// by Sebastian Tomczak +// 29 August 2011 + + +How To Deal With Real Time MIDI Beat Clock Data in Teensyduino + +The Teensy is an affordable, compact and easy-to-use microcontroller development board. Part of the attraction of Teensy is theTeensyduino add-on for the Arduino software environment, which adds support for the Teensy board. One of the strengths of Teensy and Teensyduino is the ability to have the Teensy board appear as a USB HID MIDI device. This allows for the creation of interfaces and projects that register as native USB MIDI devices on the major operating systems. + +Currently, the usbMIDI.read() function in Teensyduino reads a range of standard MIDI commands (sent from the computer host to the Teensy board), such as note on, note off, continuous controllers etc. However, there is no support for detecting and dealing with MIDI sync signals (also known as MIDI beat clock and real time system bytes). + +I've edited the Teensyduino package to include a function made for dealing with real time system bytes. Thanks to Paul Stoffregen for his fantastic work with the Teensy board, and for his help in pushing me in the right direction with this. My edited files can be found here:http://milkcrate.com.au/_other/downloads/other/TeensyduinoRealTimeClockUpdate_20110829/ + +To add the functions to the Teensyduino code, simply right-click on the Arduino application and select "Show Package Contents". Copy and replace the files from the link above to Contents > Resources > Java > hardware > teensy > cores > usb_midi. + +This will add a new handle (setHandleRealTimeSystem()) and a new function (RealTimeSystem()) that can be used in conjunction with usbMIDI.read() to receive MIDI beat clock data. To use the new code, set up a MIDI input sketch as you would normally with Teensyduino. Then: + +1) add: +usbMIDI.setHandleRealTimeSystem(RealTimeSystem); +to void setup() + +2) add a new function to the sketch: +void RealTimeSystem(byte realtimebyte) { +} + +In the new function in (2) above, the byte realtimebyte is used for the MIDI beat clock data. The following bytes indicate which MIDI beat clock bytes have been received. + +clock tick byte (at 24 pulses per quarter note) = 248; +start byte = 250; +continue byte = 251; +stop byte = 252; + +Below is a working example, with code and a demo video. The LED that is part of the Teensy board lights up for every quarter note of sync data that is received via the USB cable. + + + + + +// Teensyduino MIDI Beat Clock Example +// by Sebastian Tomczak +// 29 August 2011 + +byte counter; +byte CLOCK = 248; +byte START = 250; +byte CONTINUE = 251; +byte STOP = 252; + +void setup() { +Serial.begin(31250); +pinMode(11, OUTPUT); +digitalWrite(11, HIGH); +usbMIDI.setHandleRealTimeSystem(RealTimeSystem); +} + +void loop() { +usbMIDI.read(); + +} + +void RealTimeSystem(byte realtimebyte) { +if(realtimebyte == 248) { +counter++; +if(counter == 24) { +counter = 0; +digitalWrite(11, HIGH); +} + +if(counter == 12) { +digitalWrite(11, LOW); +} +} + +if(realtimebyte == START || realtimebyte == CONTINUE) { +counter = 0; +digitalWrite(11, HIGH); +} + +if(realtimebyte == STOP) { +digitalWrite(11, LOW); +} + +} + diff --git a/teensy-midi-clock-mods/Teensy_midi.ino b/teensy-midi-clock-mods/Teensy_midi.ino new file mode 100755 index 0000000..f89f2c4 --- /dev/null +++ b/teensy-midi-clock-mods/Teensy_midi.ino @@ -0,0 +1,80 @@ +// Teensy2.0 MIDI CLOCK INTERFACE // +// by 2kohm // +// 18. Aprill 2014 // +// http://stromakustik.de // +// // +// Teensyduino MIDI Beat Clock // +// by Sebastian Tomczak // +// 29 August 2011 // +// http://www.goo.gl/Xh2skw // + + + +// Bytes +byte counter; +byte CLOCK = 1; +byte START = 1; +byte CONTINUE = 5; +byte STOP = 20; +byte divider; + +// Var +int pot1 = 0; +int clockdivider = clockdivider; // MIDI Clock Divider + +// Setup + void setup() +{ + Serial.begin(31250); + pinMode(10, OUTPUT); + pinMode(1, INPUT); + pinMode(11, OUTPUT); + + digitalWrite(10, HIGH); + +// MIDI RealTime System + usbMIDI.setHandleRealTimeSystem(RealTimeSystem); +} + void loop() +{ + +// Midi Clock Divider + divider = (analogRead(0)/250); // Read Pot 1 (scale 0 - 4) + + if(divider == 0) { clockdivider = 24;} // 1/4 + if(divider == 1) { clockdivider = 12;} // 1/8 + if(divider == 2) { clockdivider = 6;} // 1/16 + if(divider == 3) { clockdivider = 3;} // 1/32 + if(divider == 4) { clockdivider = 2;} // 1/64 + Serial.println(divider); // Display the Pot value ( 0 - 4) + + usbMIDI.read(); +} + + void RealTimeSystem(byte realtimebyte) +{ + if(realtimebyte == 248) +{ + counter++; + if(counter == clockdivider) +{ + counter = 0; + digitalWrite(10, HIGH); +} + if(counter == 1) +{ + digitalWrite(10, LOW); +} + +} + if(realtimebyte == START || realtimebyte == CONTINUE) +{ + counter = 0; + digitalWrite(10, HIGH) ; +} + if(realtimebyte == STOP) +{ + digitalWrite(10, LOW); +} + +} diff --git a/teensy-midi-clock-mods/usb_api.cpp b/teensy-midi-clock-mods/usb_api.cpp new file mode 100644 index 0000000..c212ffc --- /dev/null +++ b/teensy-midi-clock-mods/usb_api.cpp @@ -0,0 +1,561 @@ +/* USB API for Teensy USB Development Board + * http://www.pjrc.com/teensy/teensyduino.html + * Copyright (c) 2008 PJRC.COM, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include "usb_common.h" +#include "usb_private.h" +#include "usb_api.h" +#include "wiring.h" + + +void usb_midi_class::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel) +{ + send_raw(0x08, 0x80 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F); +} +void usb_midi_class::sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel) +{ + send_raw(0x09, 0x90 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F); +} +void usb_midi_class::sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel) +{ + send_raw(0x0A, 0xA0 | ((channel - 1) & 0x0F), note & 0x7F, pressure & 0x7F); +} +void usb_midi_class::sendControlChange(uint8_t control, uint8_t value, uint8_t channel) +{ + send_raw(0x0B, 0xB0 | ((channel - 1) & 0x0F), control & 0x7F, value & 0x7F); +} +void usb_midi_class::sendProgramChange(uint8_t program, uint8_t channel) +{ + send_raw(0x0C, 0xC0 | ((channel - 1) & 0x0F), program & 0x7F, 0); +} +void usb_midi_class::sendAfterTouch(uint8_t pressure, uint8_t channel) +{ + send_raw(0x0D, 0xD0 | ((channel - 1) & 0x0F), pressure & 0x7F, 0); +} +void usb_midi_class::sendPitchBend(uint16_t value, uint8_t channel) +{ + send_raw(0x0E, 0xE0 | ((channel - 1) & 0x0F), value & 0x7F, (value >> 7) & 0x7F); +} + +void usb_midi_class::sendSysEx(uint8_t length, const uint8_t *data) +{ + // TODO: MIDI 2.5 lib automatically adds start and stop bytes + while (length > 3) { + send_raw(0x04, data[0], data[1], data[2]); + data += 3; + length -= 3; + } + if (length == 3) { + send_raw(0x07, data[0], data[1], data[2]); + } else if (length == 2) { + send_raw(0x06, data[0], data[1], 0); + } else if (length == 1) { + send_raw(0x05, data[0], 0, 0); + } +} + +void usb_midi_class::send_raw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) +{ + uint8_t intr_state, timeout; + + if (!usb_configuration) return; + intr_state = SREG; + cli(); + UENUM = MIDI_TX_ENDPOINT; + timeout = UDFNUML + 2; + while (1) { + // are we ready to transmit? + if (UEINTX & (1<= 1023) return 127; + for (e=0; (val & 512) == 0; e++) val <<= 1; + for (i=0; i<9; i++) { // cordic algorithm + uint16_t x = val + (val >> (i + 1)); + if (x < 1024) { + val = x; + s += pgm_read_byte(table + i); + } + } + s += e * 385; + s <<= 4; + s += (range >> 1); + s /= range; + if (s >= 1024) return 0; + s = 1024 - s; + if (s > 511) { + s -= 512; + b = 64; + } else if (s > 255) { + s -= 256; + b = 32; + } else { + b = 0; + } + return b + ((s * 127) >> 10); +#endif +} + + + + + + + +bool usb_midi_class::read(uint8_t channel) +{ + uint8_t c, intr_state; + uint8_t b0, b1, b2, b3, type1, type2; + + intr_state = SREG; + cli(); + if (!usb_configuration) { + SREG = intr_state; + return false; + } + UENUM = MIDI_RX_ENDPOINT; + retry: + c = UEINTX; + if (!(c & (1<= 0x08 && type1 <= 0x0F) { + if (channel && channel != c) { + // ignore other channels when user wants single channel read + return false; + } + if (type1 == 0x08 && type2 == 0x80) { + msg_type = 0; // Note off + if (handleNoteOff) (*handleNoteOff)(c, b2, b3); + goto return_message; + } + if (type1 == 0x09 && type2 == 0x90) { + if (b3) { + msg_type = 1; // Note on + if (handleNoteOn) (*handleNoteOn)(c, b2, b3); + } else { + msg_type = 0; // Note off + if (handleNoteOff) (*handleNoteOff)(c, b2, b3); + } + goto return_message; + } + if (type1 == 0x0A && type2 == 0xA0) { + msg_type = 2; // Poly Pressure + if (handleVelocityChange) (*handleVelocityChange)(c, b2, b3); + goto return_message; + } + if (type1 == 0x0B && type2 == 0xB0) { + msg_type = 3; // Control Change + if (handleControlChange) (*handleControlChange)(c, b2, b3); + goto return_message; + } + if (type1 == 0x0C && type2 == 0xC0) { + msg_type = 4; // Program Change + if (handleProgramChange) (*handleProgramChange)(c, b2); + goto return_message; + } + if (type1 == 0x0D && type2 == 0xD0) { + msg_type = 5; // After Touch + if (handleAfterTouch) (*handleAfterTouch)(c, b2); + goto return_message; + } + if (type1 == 0x0E && type2 == 0xE0) { + msg_type = 6; // Pitch Bend + if (handlePitchChange) (*handlePitchChange)(c, + (b2 & 0x7F) | ((b3 & 0x7F) << 7)); + goto return_message; + } + if (type1 == 0x0F && type2 == 0xF0) { + msg_type = 8; // Real Time System + if (handleRealTimeSystem) (*handleRealTimeSystem)(b1); + goto return_message; + } + return false; + return_message: + // only update these when returning true for a parsed message + // all other return cases will preserve these user-visible values + msg_channel = c; + msg_data1 = b2; + msg_data2 = b3; + return true; + } + if (type1 == 0x04) { + read_sysex_byte(b1); + read_sysex_byte(b2); + read_sysex_byte(b3); + return false; + } + if (type1 >= 0x05 && type1 <= 0x07) { + read_sysex_byte(b1); + if (type1 >= 0x06) read_sysex_byte(b2); + if (type1 == 0x07) read_sysex_byte(b3); + msg_data1 = msg_sysex_len; + msg_sysex_len = 0; + return true; + } + return false; +} + +void usb_midi_class::read_sysex_byte(uint8_t b) +{ + if (msg_sysex_len < USB_MIDI_SYSEX_MAX) { + msg_sysex[msg_sysex_len++] = b; + } +} + + + + + +static volatile uint8_t prev_byte=0; + +void usb_serial_class::begin(long speed) +{ + // make sure USB is initialized + usb_init(); + uint16_t begin_wait = (uint16_t)millis(); + while (1) { + if (usb_configuration) { + delay(200); // a little time for host to load a driver + return; + } + if (usb_suspended) { + uint16_t begin_suspend = (uint16_t)millis(); + while (usb_suspended) { + // must remain suspended for a while, because + // normal USB enumeration causes brief suspend + // states, typically under 0.1 second + if ((uint16_t)millis() - begin_suspend > 250) { + return; + } + } + } + // ... or a timout (powered by a USB power adaptor that + // wiggles the data lines to keep a USB device charging) + if ((uint16_t)millis() - begin_wait > 2500) return; + } + prev_byte = 0; +} + +void usb_serial_class::end() +{ + usb_shutdown(); + delay(25); +} + + + +// number of bytes available in the receive buffer +int usb_serial_class::available() +{ + uint8_t c; + + c = prev_byte; // assume 1 byte static volatile access is atomic + if (c) return 1; + c = readnext(); + if (c) { + prev_byte = c; + return 1; + } + return 0; +} + +// get the next character, or -1 if nothing received +int usb_serial_class::read() +{ + uint8_t c; + + c = prev_byte; + if (c) { + prev_byte = 0; + return c; + } + c = readnext(); + if (c) return c; + return -1; +} + +int usb_serial_class::peek() +{ + uint8_t c; + + c = prev_byte; + if (c) return c; + c = readnext(); + if (c) { + prev_byte = c; + return c; + } + return -1; +} + +// get the next character, or 0 if nothing +uint8_t usb_serial_class::readnext(void) +{ + uint8_t c, c2, intr_state; + + // interrupts are disabled so these functions can be + // used from the main program or interrupt context, + // even both in the same program! + intr_state = SREG; + cli(); + if (!usb_configuration) { + SREG = intr_state; + return 0; + } + UENUM = DEBUG_RX_ENDPOINT; +try_again: + if (!(UEINTX & (1<= 100 +size_t usb_serial_class::write(uint8_t c) +#else +#define setWriteError() +void usb_serial_class::write(uint8_t c) +#endif +{ + //static uint8_t previous_timeout=0; + uint8_t timeout, intr_state; + + // if we're not online (enumerated and configured), error + if (!usb_configuration) goto error; + // interrupts are disabled so these functions can be + // used from the main program or interrupt context, + // even both in the same program! + intr_state = SREG; + cli(); + UENUM = DEBUG_TX_ENDPOINT; + // if we gave up due to timeout before, don't wait again +#if 0 + // this seems to be causig a lockup... why???? + if (previous_timeout) { + if (!(UEINTX & (1<= 100 + return 1; +#endif +error: +#if ARDUINO >= 100 + setWriteError(); + return 0; +#else + return; +#endif +} + + +// These are Teensy-specific extensions to the Serial object + +// immediately transmit any buffered output. +// This doesn't actually transmit the data - that is impossible! +// USB devices only transmit when the host allows, so the best +// we can do is release the FIFO buffer for when the host wants it +void usb_serial_class::send_now(void) +{ + uint8_t intr_state; + + intr_state = SREG; + cli(); + if (debug_flush_timer) { + UENUM = DEBUG_TX_ENDPOINT; + while ((UEINTX & (1< + +#include "Stream.h" + +#define USB_MIDI_SYSEX_MAX 60 // maximum sysex length we can receive + +#define NoteOff 0 +#define NoteOn 1 +#define AfterTouchPoly 2 +#define ControlChange 3 +#define ProgramChange 4 +#define AfterTouchChannel 5 +#define PitchBend 6 +#define SystemExclusive 7 + +class usb_midi_class +{ +public: + void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel); + void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel); + void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel); + void sendControlChange(uint8_t control, uint8_t value, uint8_t channel); + void sendProgramChange(uint8_t program, uint8_t channel); + void sendAfterTouch(uint8_t pressure, uint8_t channel); + void sendPitchBend(uint16_t value, uint8_t channel); + void sendSysEx(uint8_t length, const uint8_t *data); + void send_now(void); + uint8_t analog2velocity(uint16_t val, uint8_t range); + bool read(uint8_t channel=0); + inline uint8_t getType(void) { + return msg_type; + }; + inline uint8_t getChannel(void) { + return msg_channel; + }; + inline uint8_t getData1(void) { + return msg_data1; + }; + inline uint8_t getData2(void) { + return msg_data2; + }; + inline uint8_t * getSysExArray(void) { + return msg_sysex; + }; + inline void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { + handleNoteOff = fptr; + }; + inline void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { + handleNoteOn = fptr; + }; + inline void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) { + handleVelocityChange = fptr; + }; + inline void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) { + handleControlChange = fptr; + }; + inline void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) { + handleProgramChange = fptr; + }; + inline void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) { + handleAfterTouch = fptr; + }; + inline void setHandlePitchChange(void (*fptr)(uint8_t channel, uint16_t pitch)) { + handlePitchChange = fptr; + }; + inline void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) { + handleRealTimeSystem = fptr; + }; +private: + void send_raw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3); + void read_sysex_byte(uint8_t b); + uint8_t msg_channel; + uint8_t msg_type; + uint8_t msg_data1; + uint8_t msg_data2; + uint8_t msg_sysex[USB_MIDI_SYSEX_MAX]; + uint8_t msg_sysex_len; + void (*handleNoteOff)(uint8_t ch, uint8_t note, uint8_t vel); + void (*handleNoteOn)(uint8_t ch, uint8_t note, uint8_t vel); + void (*handleVelocityChange)(uint8_t ch, uint8_t note, uint8_t vel); + void (*handleControlChange)(uint8_t ch, uint8_t, uint8_t); + void (*handleProgramChange)(uint8_t ch, uint8_t); + void (*handleAfterTouch)(uint8_t ch, uint8_t); + void (*handlePitchChange)(uint8_t ch, uint16_t); + void (*handleRealTimeSystem)(uint8_t rtb); +}; + +extern usb_midi_class usbMIDI; + + +class usb_serial_class : public Stream +{ +public: + // standard Arduino functions + void begin(long); + void end(); + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); +#if ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + using Print::write; + // Teensy extensions + void send_now(void); + uint32_t baud(void); + uint8_t stopbits(void); + uint8_t paritytype(void); + uint8_t numbits(void); + uint8_t dtr(void); + uint8_t rts(void); +private: + uint8_t readnext(void); +}; + +extern usb_serial_class Serial; + + +#endif