-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
570 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
cmake_minimum_required(VERSION 3.6) | ||
project(MIDIUSB_1x1) | ||
|
||
find_package(GTest REQUIRED) | ||
include_directories(. ./test ${GTEST_INCLUDE_DIR}) | ||
|
||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") | ||
|
||
set(SOURCE_FILES test/test_midi_bridge.cpp ) | ||
add_executable(MIDIUSB_1x1 ${SOURCE_FILES}) | ||
target_link_libraries(MIDIUSB_1x1 ${GTEST_LIBRARIES}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#pragma once | ||
//------------------------------------------------------------------------------ | ||
// Generic led pin and level mapping, defines LED, LED_OFF, LED_ON | ||
// Author: Fabien | ||
// License: GPL V3 © 2016 Fabien (https://github.com/fab672000) | ||
//----------------------------------------------------------------------------- | ||
#if defined(ARDUINO_SAM_DUE) | ||
//#pragma message("Due") | ||
const byte LED = 13; | ||
#elif defined(STM32_MCU_SERIES) | ||
//#pragma message("STM32 MCU") | ||
const byte LED = PC13; | ||
#elif defined(ARDUINO_AVR_MICRO) | ||
//#pragma message("Micro") | ||
const byte LED = 17; // TX led on micro | ||
#elif defined(ARDUINO_AVR_PRO) || defined(ARDUINO_AVR_MINI) | ||
//#pragma message("Mini") | ||
const byte LED = 13; // TX led on micro | ||
#elif defined(ARDUINO_AVR_LEONARDO) | ||
//#pragma message("Leonardo") | ||
const byte LED = 13; // TX led on micro | ||
#elif defined(ESP8266_ESP12) | ||
//#pragma message("NodeMCU 1.0") | ||
const byte LED = 13; // TX led on micro | ||
#else | ||
//#pragma message("Default") | ||
const byte LED = 17; | ||
#endif | ||
#if defined(STM32_MCU_SERIES) || defined(ARDUINO_AVR_MICRO) | ||
# define LED_ON LOW | ||
# define LED_OFF HIGH | ||
#else | ||
# define LED_OFF LOW | ||
# define LED_ON HIGH | ||
#endif | ||
|
||
const int LED_DELAY_MS = 500; | ||
|
||
class Leds | ||
{ | ||
public: | ||
static void blink(byte led, int del=LED_DELAY_MS) | ||
{ | ||
digitalWrite(led, LED_ON); | ||
delay(del); | ||
digitalWrite(led, LED_OFF); | ||
delay(del); | ||
} | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
#define ARDUINO_MAX_POWER_MA 20 | ||
/* | ||
* MIDIUSB 1x1 interface, works with iPad. | ||
* | ||
* Important Note: needs D_CONFIG macro in USBCore.h to be patched to change USB_CONFIG_POWER_MA(500) | ||
* with a lower value (i.e 20) in order to work with iPad, as it is bus powered by the tablet. | ||
* | ||
* Created: October, 11 2016 | ||
* Author: Fabien | ||
* License: GPL V3 © 2016 Fabien (https://github.com/fab672000) | ||
*/ | ||
|
||
#include <avr/power.h> | ||
#include "TimerOne.h" | ||
#include "MIDIUSB.h" | ||
#include "MIDI.h" | ||
#include "MidiBridge.h" | ||
#include "Leds.h" | ||
|
||
USING_NAMESPACE_MIDI | ||
|
||
//------------------------------------------------------------------------------ | ||
const byte led1 = LED; | ||
const byte led2 = TXLED1; | ||
|
||
static bool timer_elapsed = false; | ||
|
||
// WARNING on leonardo / Pro Micro serial is Serial1 !!, Serial is USB | ||
MIDIBRIDGE_CREATE_INSTANCE(HardwareSerial, Serial1, midiA); | ||
|
||
//------------------------------------------------------------------------------ | ||
void power_save() | ||
{ // Disable ADC, SPI, I2C, Timer 2 and Timer 3 | ||
power_adc_disable(); | ||
power_spi_disable(); | ||
power_twi_disable(); | ||
|
||
power_timer2_disable(); | ||
power_timer3_disable(); | ||
} | ||
//------------------------------------------------------------------------------ | ||
void leds_off() | ||
{ | ||
digitalWrite(led1, LED_OFF); | ||
TXLED1; | ||
} | ||
//------------------------------------------------------------------------------ | ||
void note(byte pitch, int duration=250) | ||
{ | ||
const uint8_t channel =1, velocity = 100; | ||
|
||
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity}; | ||
MidiUSB.sendMIDI(noteOn); | ||
MidiUSB.flush(); | ||
//midiA.sendNoteOn(pitch, velocity, channel); | ||
|
||
delay(duration); | ||
midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity}; | ||
MidiUSB.sendMIDI(noteOff); | ||
MidiUSB.flush(); | ||
//midiA.sendNoteOff(pitch, velocity, channel); | ||
|
||
} | ||
//------------------------------------------------------------------------------ | ||
void timer1_callback() | ||
{ | ||
timer_elapsed = true; | ||
} | ||
//------------------------------------------------------------------------------ | ||
// Midi Interface Setup: initialize leds, serial midi, timer and power save | ||
//------------------------------------------------------------------------------ | ||
void setup() { | ||
pinMode(led1, OUTPUT); | ||
pinMode(led2, OUTPUT); | ||
|
||
// briefly blink leds as an alive signal | ||
for (int i=0; i< 2; i++) { | ||
delay(150); | ||
digitalWrite(led1, LED_ON); | ||
TXLED1; | ||
delay(150); | ||
digitalWrite(led1, LED_OFF); | ||
TXLED0; | ||
} | ||
leds_off(); | ||
|
||
midiA.begin(MIDI_CHANNEL_OMNI); | ||
|
||
// Set timer to save even more power after a period of inactivity | ||
Timer1.initialize(500000); // initialize timer1, and set a period in us | ||
Timer1.attachInterrupt(timer1_callback); | ||
|
||
power_save(); // save extra millamps by disabling what we do not use | ||
} | ||
//------------------------------------------------------------------------------ | ||
// Main loop: Handle serial rx first and send it to host if any, | ||
// then read host and send to serial if any. | ||
//------------------------------------------------------------------------------ | ||
void loop() { | ||
bool sent = false, recv=false; | ||
|
||
// Handle serial midi messages, if any ; send them to host: | ||
while (midiA.read()) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
fab672000
Author
Owner
|
||
{ | ||
sent = midiA.SendToUSB(); | ||
// state = state==LED_OFF ? LED_ON : LED_OFF; | ||
} | ||
if(sent) {MidiUSB.flush();} | ||
|
||
// Handle host messages, if any ; send them to serial: | ||
midiEventPacket_t rx; | ||
do | ||
{ | ||
rx = MidiUSB.read(); | ||
midiA.SendToSerial(rx); | ||
} while (rx.header != 0); | ||
|
||
// Handle timer and leds to save power while no midi is received / sent | ||
if(sent || recv) | ||
{ | ||
timer_elapsed = sent = recv = false; | ||
Timer1.restart(); | ||
} | ||
else if(timer_elapsed) | ||
{ | ||
leds_off(); | ||
timer_elapsed = false; | ||
} | ||
} | ||
//------------------------------------------------------------------------------ | ||
// EOF | ||
//------------------------------------------------------------------------------ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
#pragma once | ||
// | ||
// MidiBridge Class declaration | ||
// | ||
// Author: Fabien | ||
// License: GPL V3.0 © 2016 Fabien (https://github.com/fab672000) | ||
|
||
#include "MIDI.h" | ||
#include "MIDIUSB.h" | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#define MIDIBRIDGE_CREATE_INSTANCE(Type, SerialPort, Name) MidiBridge<Type> Name((Type&)SerialPort); | ||
|
||
namespace midi | ||
{ | ||
|
||
/** | ||
Utility class template to convert between serial and usb midi. | ||
*/ | ||
template<typename SerialPort, typename Settings = DefaultSettings> | ||
class MidiBridge : public MidiInterface<SerialPort, Settings> | ||
{ | ||
public: | ||
|
||
MidiBridge(SerialPort &inSerial):MidiInterface<SerialPort, Settings>(inSerial){} | ||
|
||
bool SendToSerial(const midiEventPacket_t& rx); | ||
bool SendToUSB(); | ||
|
||
//! Given a midiusb data buffer, create and return the corresonding serial midi | ||
//! contiguous buffer for sending to uart. Caller must delete[] the returned value when done. | ||
static uint8_t* GetUsbToSerialSysExBuffer(const uint8_t *data, size_t size, size_t& outSize); | ||
|
||
//! Given a serial midi data buffer, create and return the corresonding usb midi packets | ||
//! for sending to USB connection. Caller must delete[] the returned value when done. | ||
static uint8_t* GetSerialToUsbSysExBuffer(const uint8_t* data, size_t size, size_t& outSize); | ||
|
||
//! Given a midiusb data buffer, send the corresonding serial midi data to uart | ||
bool UsbToSerialSysEx(const uint8_t *data, size_t size); | ||
|
||
//! Given a serial midi data buffer, send the corresonding serial midi data to USB | ||
bool SerialToUsbSysEx(const uint8_t *data, size_t size); | ||
}; | ||
|
||
// Template implementation | ||
template<class SerialPort, class Settings> | ||
uint8_t* MidiBridge<SerialPort, Settings>::GetUsbToSerialSysExBuffer(const uint8_t* data, size_t size, size_t& midiDataSize) | ||
{ | ||
if (data == nullptr || size == 0 || (size % 4) != 0) return nullptr; // usb packets must be DWORDs | ||
midiDataSize = (size >> 2) * 3; | ||
uint8_t lastPacketHdr = data[size - 4]; | ||
if (lastPacketHdr > 4) midiDataSize -= (7 - lastPacketHdr); // adjust size if needed | ||
auto midiData = new uint8_t[midiDataSize]; | ||
auto *p = midiData; | ||
auto bytesRemaining = midiDataSize; | ||
|
||
for (const uint8_t *d = data + 1; d<data + size; d += 4) | ||
{ | ||
switch (bytesRemaining) { | ||
case 1: | ||
*p = d[0]; // SysEx ends with following single uint8_t | ||
break; | ||
case 2: | ||
*p++ = d[0]; // SysEx ends with following two uint8_ts | ||
*p = d[1]; | ||
break; | ||
default: | ||
*p++ = d[0]; // SysEx with following three uint8_ts | ||
*p++ = d[1]; | ||
*p++ = d[2]; | ||
break; | ||
} | ||
bytesRemaining = bytesRemaining>3 ? bytesRemaining - 3 : 0; | ||
} | ||
return midiData; // it is caller responsibility to delete[] this data | ||
} | ||
|
||
template<class SerialPort, class Settings> | ||
bool MidiBridge<SerialPort, Settings>::UsbToSerialSysEx(const uint8_t* data, size_t size) | ||
{ | ||
size_t midiDataSize; | ||
auto buffer = GetUsbToSerialSysExBuffer(data, size, midiDataSize); | ||
|
||
if (buffer == nullptr) return false; | ||
|
||
this->sendSysEx((unsigned)midiDataSize, buffer, true); | ||
delete[] buffer; | ||
|
||
return true; | ||
} | ||
|
||
template<class SerialPort, class Settings> | ||
uint8_t* MidiBridge<SerialPort, Settings>::GetSerialToUsbSysExBuffer(const uint8_t* data, size_t size, size_t& outDataSize) | ||
{ | ||
if (data == nullptr || size == 0) return nullptr; | ||
|
||
outDataSize = (size + 2) / 3 * 4; | ||
uint8_t* midiData = new uint8_t[outDataSize]; | ||
auto d = data; | ||
auto p = midiData; | ||
auto bytesRemaining = size; | ||
|
||
while (bytesRemaining > 0) { | ||
switch (bytesRemaining) { | ||
case 1: | ||
*p++ = 5; // SysEx ends with following single uint8_t | ||
*p++ = *d; | ||
*p++ = 0; | ||
*p = 0; | ||
bytesRemaining = 0; | ||
break; | ||
case 2: | ||
*p++ = 6; // SysEx ends with following two uint8_ts | ||
*p++ = *d++; | ||
*p++ = *d; | ||
*p = 0; | ||
bytesRemaining = 0; | ||
break; | ||
case 3: | ||
*p++ = 7; // SysEx ends with following three uint8_ts | ||
*p++ = *d++; | ||
*p++ = *d++; | ||
*p = *d; | ||
bytesRemaining = 0; | ||
break; | ||
default: | ||
*p++ = 4; // SysEx starts or continues | ||
*p++ = *d++; | ||
*p++ = *d++; | ||
*p++ = *d++; | ||
bytesRemaining -= 3; | ||
break; | ||
} | ||
} | ||
return midiData; | ||
} | ||
|
||
template<class SerialPort, class Settings> | ||
bool MidiBridge<SerialPort, Settings>::SerialToUsbSysEx(const uint8_t* data, size_t size) | ||
{ | ||
size_t outSize = 0; | ||
auto buffer = GetSerialToUsbSysExBuffer(data, size, outSize); | ||
if (buffer == nullptr) return false; | ||
|
||
MidiUSB.write(data, size); | ||
delete[] buffer; | ||
return true; | ||
} | ||
|
||
template<class SerialPort, class Settings> | ||
bool MidiBridge<SerialPort, Settings>::SendToSerial(const midiEventPacket_t &rx) { | ||
if (rx.header == 0) return false; | ||
else if(rx.header==0xF) | ||
{ | ||
this->send((MidiType)rx.byte1, rx.byte2, rx.byte3, (Channel)0); | ||
} | ||
else if (rx.header>=0x8) | ||
{ | ||
this->send((MidiType)(rx.byte1&0xf0), rx.byte2, rx.byte3, (Channel) (rx.byte1&0x0f)+1); | ||
} | ||
else if(rx.header>=0x4) | ||
{ | ||
UsbToSerialSysEx((const byte*) &rx, sizeof(rx)); | ||
} | ||
return true; | ||
} | ||
|
||
template<class SerialPort, class Settings> | ||
bool MidiBridge<SerialPort, Settings>::SendToUSB() | ||
{ | ||
MidiType msgType = this->getType(); | ||
|
||
if (msgType==0) return false; | ||
else if(msgType==0xF0) | ||
{ | ||
SerialToUsbSysEx(this->getSysExArray(), this->getSysExArrayLength()); | ||
} | ||
else | ||
{ | ||
midiEventPacket_t tx= {byte(msgType>>4), byte(msgType | this->getChannel()), this->getData1(), this->getData2()}; | ||
MidiUSB.sendMIDI(tx); | ||
} | ||
return true; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
# MIDIUSB_1x1 | ||
MIDI Interface 1 in 1 out based on Arduino MidiUSB and Midi libraries | ||
|
||
Works with iPad and ios native midi drivers (must patch MidiUSB power settings as instructed in the .ino file) |
Oops, something went wrong.
IIUC, the example simply bridges serial->USB and USB->serial correct?
Would it be possible to also add a test example to produce MIDI outputs on both USB and serial? That would cover applications like Arduino-based MIDI controller that want to use both serial and USB ports. I can send a pull request if you think that's feasible.