Merge wip into master.
This commit is contained in:
commit
6c66adc885
|
|
@ -0,0 +1,2 @@
|
||||||
|
html
|
||||||
|
latex
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
/*
|
||||||
|
Basic I/O MIDI tutorial
|
||||||
|
by Franky
|
||||||
|
28/07/2009
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LED 13 // LED pin on Arduino board
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(LED, OUTPUT);
|
||||||
|
MIDI.begin(4); // Launch MIDI with default options
|
||||||
|
// input channel is set to 4
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (MIDI.read()) {
|
||||||
|
digitalWrite(LED,HIGH); // Blink the LED
|
||||||
|
MIDI.sendNoteOn(42,127,1); // Send a Note (pitch 42, velo 127 on channel 1)
|
||||||
|
delay(1000); // Wait for a second
|
||||||
|
MIDI.sendNoteOff(42,0,1); // Stop the note
|
||||||
|
digitalWrite(LED,LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
|
||||||
|
unsigned long gTime_start = 0;
|
||||||
|
unsigned long gTime_stop = 0;
|
||||||
|
unsigned gCounter = 0;
|
||||||
|
unsigned long gTime_sum = 0;
|
||||||
|
unsigned long gTime_min = -1;
|
||||||
|
unsigned long gTime_max = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void handleNoteOn(byte inChannel,byte inNote,byte inVelocity)
|
||||||
|
{
|
||||||
|
|
||||||
|
gTime_stop = micros();
|
||||||
|
|
||||||
|
const unsigned long diff = gTime_stop - gTime_start;
|
||||||
|
gTime_sum += diff;
|
||||||
|
|
||||||
|
if (diff > gTime_max) gTime_max = diff;
|
||||||
|
if (diff < gTime_min) gTime_min = diff;
|
||||||
|
|
||||||
|
gCounter++;
|
||||||
|
|
||||||
|
if (gCounter >= 100) {
|
||||||
|
|
||||||
|
const unsigned long average = gTime_sum / (float)gCounter;
|
||||||
|
|
||||||
|
Serial.println("Time to receive NoteOn: ");
|
||||||
|
|
||||||
|
Serial.print("Average: ");
|
||||||
|
Serial.print(average);
|
||||||
|
Serial.println(" microsecs");
|
||||||
|
|
||||||
|
Serial.print("Min: ");
|
||||||
|
Serial.print(gTime_min);
|
||||||
|
Serial.println(" microsecs");
|
||||||
|
|
||||||
|
Serial.print("Max: ");
|
||||||
|
Serial.print(gTime_max);
|
||||||
|
Serial.println(" microsecs");
|
||||||
|
|
||||||
|
gCounter = 0;
|
||||||
|
gTime_sum = 0;
|
||||||
|
gTime_max = 0;
|
||||||
|
gTime_min = -1;
|
||||||
|
|
||||||
|
MIDI.turnThruOff();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
MIDI.begin();
|
||||||
|
|
||||||
|
Serial.begin(38400);
|
||||||
|
Serial.print("MCU Ready");
|
||||||
|
|
||||||
|
MIDI.sendNoteOn(69,127,1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
gTime_start = micros();
|
||||||
|
|
||||||
|
MIDI.read();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
|
||||||
|
|
||||||
|
// This function will be automatically called when a NoteOn is received.
|
||||||
|
// It must be a void-returning function with the correct parameters,
|
||||||
|
// see documentation here:
|
||||||
|
// http://arduinomidilib.sourceforge.net/class_m_i_d_i___class.html
|
||||||
|
|
||||||
|
void HandleNoteOn(byte channel, byte pitch, byte velocity)
|
||||||
|
{
|
||||||
|
// Do whatever you want when you receive a Note On.
|
||||||
|
|
||||||
|
if (velocity == 0) {
|
||||||
|
// This acts like a NoteOff.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to keep your callbacks short (no delays ect) as the contrary would slow down the loop()
|
||||||
|
// and have a bad impact on real-time performance.
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Initiate MIDI communications, listen to all channels
|
||||||
|
MIDI.begin(MIDI_CHANNEL_OMNI);
|
||||||
|
|
||||||
|
// Connect the HandleNoteOn function to the library, so it is called upon reception of a NoteOn.
|
||||||
|
MIDI.setHandleNoteOn(HandleNoteOn); // Put only the name of the function
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Call MIDI.read the fastest you can for real-time performance.
|
||||||
|
MIDI.read();
|
||||||
|
|
||||||
|
// There is no need to check if there are messages incoming if they are bound to a Callback function.
|
||||||
|
// The attached method will be called automatically when the corresponding message has been received.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
/*
|
||||||
|
MIDI Input tutorial
|
||||||
|
by Franky
|
||||||
|
28/07/2009
|
||||||
|
|
||||||
|
NOTE: for easier MIDI input reading,
|
||||||
|
take a look a the Callbacks example.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LED 13 // LED pin on Arduino board
|
||||||
|
|
||||||
|
void BlinkLed(byte num) { // Basic blink function
|
||||||
|
for (byte i=0;i<num;i++) {
|
||||||
|
digitalWrite(LED,HIGH);
|
||||||
|
delay(50);
|
||||||
|
digitalWrite(LED,LOW);
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(LED, OUTPUT);
|
||||||
|
MIDI.begin(); // Launch MIDI with default options
|
||||||
|
// (input channel is default set to 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (MIDI.read()) { // Is there a MIDI message incoming ?
|
||||||
|
switch(MIDI.getType()) { // Get the type of the message we caught
|
||||||
|
case midi::ProgramChange: // If it is a Program Change
|
||||||
|
BlinkLed(MIDI.getData1()); // Blink the LED a number of times
|
||||||
|
// correponding to the program number
|
||||||
|
// (0 to 127, it can last a while..)
|
||||||
|
break;
|
||||||
|
// See the online reference for other message types
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Use this script to install the library directy from the git clone.
|
||||||
|
|
||||||
|
|
||||||
|
if [[ -d /Applications/Arduino.app ]]
|
||||||
|
then
|
||||||
|
|
||||||
|
# Define locations
|
||||||
|
|
||||||
|
lib_path=/Applications/Arduino.app/Contents/Resources/Java/libraries/MIDI
|
||||||
|
|
||||||
|
if [[ -d $lib_path ]]
|
||||||
|
then
|
||||||
|
# Remove old lib
|
||||||
|
rm -rf $lib_path
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create folder
|
||||||
|
mkdir $lib_path
|
||||||
|
|
||||||
|
# Copy sources
|
||||||
|
cp ../src/MIDI.cpp $lib_path
|
||||||
|
cp ../src/MIDI.h $lib_path
|
||||||
|
cp ../src/midi_* $lib_path
|
||||||
|
|
||||||
|
# Copy resources
|
||||||
|
cp ../res/keywords.txt $lib_path
|
||||||
|
|
||||||
|
# Copy examples
|
||||||
|
mkdir $lib_path/examples
|
||||||
|
|
||||||
|
cp -r examples/* $lib_path/examples
|
||||||
|
|
||||||
|
# Copy doc
|
||||||
|
mkdir $lib_path/doc
|
||||||
|
|
||||||
|
cp -r ../doc/* $lib_path/doc
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Arduino application not found."
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# This script installs the Arduino MIDI Library into the Arduino application,
|
||||||
|
# so that every sketch can include it directly, without having to copy anything.
|
||||||
|
#
|
||||||
|
# To install the library, run this script by double-clicking it,
|
||||||
|
# it should be directly executable and seen as such by Mac OS X.
|
||||||
|
# If not, open a terminal, cd to the script location, and run ./install_mac
|
||||||
|
#
|
||||||
|
# Open the Arduino IDE, and you're ready to go!
|
||||||
|
|
||||||
|
# The script assumes the Arduino application
|
||||||
|
# is installed in the default location.
|
||||||
|
|
||||||
|
if [[ -d /Applications/Arduino.app ]]
|
||||||
|
then
|
||||||
|
|
||||||
|
# Define locations
|
||||||
|
lib_path=/Applications/Arduino.app/Contents/Resources/Java/libraries/MIDI
|
||||||
|
|
||||||
|
if [[ -d $lib_path ]]
|
||||||
|
then
|
||||||
|
# Remove old lib
|
||||||
|
rm -rf $lib_path
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create folder
|
||||||
|
mkdir $lib_path
|
||||||
|
|
||||||
|
# Install contents
|
||||||
|
cp -r * $lib_path
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm $lib_path/install_mac
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Arduino application not found."
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For Test
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
MIDI KEYWORD1
|
||||||
|
MIDI.h KEYWORD1
|
||||||
|
MidiInterface KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
send KEYWORD2
|
||||||
|
sendNoteOn KEYWORD2
|
||||||
|
sendNoteOff KEYWORD2
|
||||||
|
sendProgramChange KEYWORD2
|
||||||
|
sendControlChange KEYWORD2
|
||||||
|
sendPitchBend KEYWORD2
|
||||||
|
sendPolyPressure KEYWORD2
|
||||||
|
sendAfterTouch KEYWORD2
|
||||||
|
sendSysEx KEYWORD2
|
||||||
|
sendTimeCodeQuarterFrame KEYWORD2
|
||||||
|
sendSongPosition KEYWORD2
|
||||||
|
sendSongSelect KEYWORD2
|
||||||
|
sendTuneRequest KEYWORD2
|
||||||
|
sendRealTime KEYWORD2
|
||||||
|
begin KEYWORD2
|
||||||
|
read KEYWORD2
|
||||||
|
getType KEYWORD2
|
||||||
|
getChannel KEYWORD2
|
||||||
|
getData1 KEYWORD2
|
||||||
|
getData2 KEYWORD2
|
||||||
|
getSysExArray KEYWORD2
|
||||||
|
getFilterMode KEYWORD2
|
||||||
|
getThruState KEYWORD2
|
||||||
|
getInputChannel KEYWORD2
|
||||||
|
check KEYWORD2
|
||||||
|
delMsg KEYWORD2
|
||||||
|
delSysEx KEYWORD2
|
||||||
|
setInputChannel KEYWORD2
|
||||||
|
setStatus KEYWORD2
|
||||||
|
turnThruOn KEYWORD2
|
||||||
|
turnThruOff KEYWORD2
|
||||||
|
setThruFilterMode KEYWORD2
|
||||||
|
disconnectCallbackFromType KEYWORD2
|
||||||
|
setHandleNoteOff KEYWORD2
|
||||||
|
setHandleNoteOn KEYWORD2
|
||||||
|
setHandleAfterTouchPoly KEYWORD2
|
||||||
|
setHandleControlChange KEYWORD2
|
||||||
|
setHandleProgramChange KEYWORD2
|
||||||
|
setHandleAfterTouchChannel KEYWORD2
|
||||||
|
setHandlePitchBend KEYWORD2
|
||||||
|
setHandleSystemExclusive KEYWORD2
|
||||||
|
setHandleTimeCodeQuarterFrame KEYWORD2
|
||||||
|
setHandleSongPosition KEYWORD2
|
||||||
|
setHandleSongSelect KEYWORD2
|
||||||
|
setHandleTuneRequest KEYWORD2
|
||||||
|
setHandleClock KEYWORD2
|
||||||
|
setHandleStart KEYWORD2
|
||||||
|
setHandleContinue KEYWORD2
|
||||||
|
setHandleStop KEYWORD2
|
||||||
|
setHandleActiveSensing KEYWORD2
|
||||||
|
setHandleSystemReset KEYWORD2
|
||||||
|
getTypeFromStatusByte KEYWORD2
|
||||||
|
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Instances (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
# Namespace, considering it as a literal
|
||||||
|
midi LITERAL1
|
||||||
|
|
||||||
|
NoteOff LITERAL1
|
||||||
|
NoteOn LITERAL1
|
||||||
|
AfterTouchPoly LITERAL1
|
||||||
|
ControlChange LITERAL1
|
||||||
|
ProgramChange LITERAL1
|
||||||
|
AfterTouchChannel LITERAL1
|
||||||
|
PitchBend LITERAL1
|
||||||
|
SystemExclusive LITERAL1
|
||||||
|
TimeCodeQuarterFrame LITERAL1
|
||||||
|
SongPosition LITERAL1
|
||||||
|
SongSelect LITERAL1
|
||||||
|
TuneRequest LITERAL1
|
||||||
|
Clock LITERAL1
|
||||||
|
Start LITERAL1
|
||||||
|
Stop LITERAL1
|
||||||
|
Continue LITERAL1
|
||||||
|
ActiveSensing LITERAL1
|
||||||
|
SystemReset LITERAL1
|
||||||
|
InvalidType LITERAL1
|
||||||
|
Off LITERAL1
|
||||||
|
Full LITERAL1
|
||||||
|
SameChannel LITERAL1
|
||||||
|
DifferentChannel LITERAL1
|
||||||
|
MIDI_CHANNEL_OMNI LITERAL1
|
||||||
|
MIDI_CHANNEL_OFF LITERAL1
|
||||||
|
MIDI_BAUDRATE LITERAL1
|
||||||
|
MIDI_SYSEX_ARRAY_SIZE LITERAL1
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Generate an archive with packaged content for easier delivery.
|
||||||
|
# The generated archive contains:
|
||||||
|
# - Source files (MIDI.cpp / MIDI.h)
|
||||||
|
# - Resources (keywords.txt)
|
||||||
|
# - Documentation (Doxygen)
|
||||||
|
# - Examples for Arduino IDE
|
||||||
|
# - Installation scripts
|
||||||
|
|
||||||
|
|
||||||
|
# Create a temporary destination folder
|
||||||
|
mkdir -p temp/doc
|
||||||
|
mkdir -p temp/examples
|
||||||
|
|
||||||
|
# Copy sources
|
||||||
|
cp ../src/* temp
|
||||||
|
|
||||||
|
# Copy resources
|
||||||
|
cp keywords.txt temp
|
||||||
|
cp install_* temp
|
||||||
|
rm temp/install_local_*
|
||||||
|
|
||||||
|
# Copy examples
|
||||||
|
cp -r examples/* temp/examples
|
||||||
|
|
||||||
|
# Generate & copy doc
|
||||||
|
cd ../doc
|
||||||
|
/Applications/Doxygen.app/Contents/Resources/doxygen Doxyfile
|
||||||
|
rm -rf latex
|
||||||
|
cd ../res
|
||||||
|
|
||||||
|
cp -r ../doc/* temp/doc
|
||||||
|
|
||||||
|
# Generate package
|
||||||
|
mv temp MIDI
|
||||||
|
zip -r MIDI.zip MIDI
|
||||||
|
|
||||||
|
|
||||||
|
# Remove temp folder
|
||||||
|
rm -rf MIDI
|
||||||
|
|
||||||
|
# Archive generated packaged
|
||||||
|
|
||||||
|
if [[ !( -d ../bin ) ]]
|
||||||
|
then
|
||||||
|
mkdir ../bin # Create archives location
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv MIDI.zip ../bin/Arduino_MIDI_Library.zip
|
||||||
|
|
@ -0,0 +1,744 @@
|
||||||
|
/*!
|
||||||
|
* @file MIDI.cpp
|
||||||
|
* Project Arduino MIDI Library
|
||||||
|
* @brief MIDI Library for the Arduino
|
||||||
|
* @version 4.0
|
||||||
|
* @author Francois Best
|
||||||
|
* @date 24/02/11
|
||||||
|
* license GPL Forty Seven Effects - 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MIDI.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if !(MIDI_BUILD_INPUT) && !(MIDI_BUILD_OUTPUT)
|
||||||
|
# error To use MIDI, you need to enable at least input or output.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MIDI_BUILD_THRU && !(MIDI_BUILD_OUTPUT)
|
||||||
|
# error For thru to work, you need to enable output.
|
||||||
|
#endif
|
||||||
|
#if MIDI_BUILD_THRU && !(MIDI_BUILD_INPUT)
|
||||||
|
# error For thru to work, you need to enable input.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_AUTO_INSTANCIATE
|
||||||
|
# if MIDI_USE_SOFTWARE_SERIAL
|
||||||
|
# ifndef FSE_AVR
|
||||||
|
# include "../SoftwareSerial/SoftwareSerial.h"
|
||||||
|
SoftwareSerial softSerialClass(MIDI_SOFTSERIAL_RX_PIN,
|
||||||
|
MIDI_SOFTSERIAL_TX_PIN);
|
||||||
|
# else
|
||||||
|
# error Todo: implement SoftwareSerial for avr core.
|
||||||
|
# endif
|
||||||
|
# undef MIDI_SERIAL_PORT
|
||||||
|
# define MIDI_SERIAL_PORT softSerialClass
|
||||||
|
# else
|
||||||
|
# ifdef FSE_AVR
|
||||||
|
# include <hardware_Serial.h>
|
||||||
|
# else
|
||||||
|
# include "HardwareSerial.h"
|
||||||
|
# endif
|
||||||
|
# endif // MIDI_USE_SOFTWARE_SERIAL
|
||||||
|
|
||||||
|
MIDI_NAMESPACE::MidiInterface MIDI;
|
||||||
|
|
||||||
|
#endif // MIDI_AUTO_INSTANCIATE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BEGIN_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*! \brief Constructor for MidiInterface. */
|
||||||
|
MidiInterface::MidiInterface()
|
||||||
|
{
|
||||||
|
#if MIDI_BUILD_INPUT && MIDI_USE_CALLBACKS
|
||||||
|
mNoteOffCallback = 0;
|
||||||
|
mNoteOnCallback = 0;
|
||||||
|
mAfterTouchPolyCallback = 0;
|
||||||
|
mControlChangeCallback = 0;
|
||||||
|
mProgramChangeCallback = 0;
|
||||||
|
mAfterTouchChannelCallback = 0;
|
||||||
|
mPitchBendCallback = 0;
|
||||||
|
mSystemExclusiveCallback = 0;
|
||||||
|
mTimeCodeQuarterFrameCallback = 0;
|
||||||
|
mSongPositionCallback = 0;
|
||||||
|
mSongSelectCallback = 0;
|
||||||
|
mTuneRequestCallback = 0;
|
||||||
|
mClockCallback = 0;
|
||||||
|
mStartCallback = 0;
|
||||||
|
mContinueCallback = 0;
|
||||||
|
mStopCallback = 0;
|
||||||
|
mActiveSensingCallback = 0;
|
||||||
|
mSystemResetCallback = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Destructor for MidiInterface.
|
||||||
|
|
||||||
|
This is not really useful for the Arduino, as it is never called...
|
||||||
|
*/
|
||||||
|
MidiInterface::~MidiInterface()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*! \brief Call the begin method in the setup() function of the Arduino.
|
||||||
|
|
||||||
|
All parameters are set to their default values:
|
||||||
|
- Input channel set to 1 if no value is specified
|
||||||
|
- Full thru mirroring
|
||||||
|
*/
|
||||||
|
void MidiInterface::begin(Channel inChannel)
|
||||||
|
{
|
||||||
|
// Initialise the Serial port
|
||||||
|
MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE);
|
||||||
|
|
||||||
|
#if MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS
|
||||||
|
|
||||||
|
mRunningStatus_TX = InvalidType;
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS
|
||||||
|
|
||||||
|
|
||||||
|
#if MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
mInputChannel = inChannel;
|
||||||
|
mRunningStatus_RX = InvalidType;
|
||||||
|
mPendingMessageIndex = 0;
|
||||||
|
mPendingMessageExpectedLenght = 0;
|
||||||
|
|
||||||
|
mMessage.valid = false;
|
||||||
|
mMessage.type = InvalidType;
|
||||||
|
mMessage.channel = 0;
|
||||||
|
mMessage.data1 = 0;
|
||||||
|
mMessage.data2 = 0;
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
|
||||||
|
#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru
|
||||||
|
|
||||||
|
mThruFilterMode = Full;
|
||||||
|
mThruActivated = true;
|
||||||
|
|
||||||
|
#endif // Thru
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Output
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_BUILD_OUTPUT
|
||||||
|
|
||||||
|
/*! \brief Generate and send a MIDI message from the values given.
|
||||||
|
\param inType The message type (see type defines for reference)
|
||||||
|
\param inData1 The first data byte.
|
||||||
|
\param inData2 The second data byte (if the message contains only 1 data byte,
|
||||||
|
set this one to 0).
|
||||||
|
\param inChannel The output channel on which the message will be sent
|
||||||
|
(values from 1 to 16). Note: you cannot send to OMNI.
|
||||||
|
|
||||||
|
This is an internal method, use it only if you need to send raw data
|
||||||
|
from your code, at your own risks.
|
||||||
|
*/
|
||||||
|
void MidiInterface::send(MidiType inType,
|
||||||
|
DataByte inData1,
|
||||||
|
DataByte inData2,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
// Then test if channel is valid
|
||||||
|
if (inChannel >= MIDI_CHANNEL_OFF ||
|
||||||
|
inChannel == MIDI_CHANNEL_OMNI ||
|
||||||
|
inType < NoteOff)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
mRunningStatus_TX = InvalidType;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return; // Don't send anything
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inType <= PitchBend) // Channel messages
|
||||||
|
{
|
||||||
|
// Protection: remove MSBs on data
|
||||||
|
inData1 &= 0x7F;
|
||||||
|
inData2 &= 0x7F;
|
||||||
|
|
||||||
|
const StatusByte status = getStatus(inType, inChannel);
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
// Check Running Status
|
||||||
|
if (mRunningStatus_TX != status)
|
||||||
|
{
|
||||||
|
// New message, memorise and send header
|
||||||
|
mRunningStatus_TX = status;
|
||||||
|
MIDI_SERIAL_PORT.write(mRunningStatus_TX);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Don't care about running status, send the status byte.
|
||||||
|
MIDI_SERIAL_PORT.write(status);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Then send data
|
||||||
|
MIDI_SERIAL_PORT.write(inData1);
|
||||||
|
if (inType != ProgramChange && inType != AfterTouchChannel)
|
||||||
|
MIDI_SERIAL_PORT.write(inData2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (inType >= TuneRequest && inType <= SystemReset)
|
||||||
|
sendRealTime(inType); // System Real-time and 1 byte.
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Input
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
/*! \brief Read a MIDI message from the serial port
|
||||||
|
using the main input channel (see setInputChannel() for reference).
|
||||||
|
|
||||||
|
\return True if a valid message has been stored in the structure, false if not.
|
||||||
|
A valid message is a message that matches the input channel. \n\n
|
||||||
|
If the Thru is enabled and the messages matches the filter,
|
||||||
|
it is sent back on the MIDI output.
|
||||||
|
*/
|
||||||
|
bool MidiInterface::read()
|
||||||
|
{
|
||||||
|
return read(mInputChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Reading/thru-ing method, the same as read()
|
||||||
|
with a given input channel to read on.
|
||||||
|
*/
|
||||||
|
bool MidiInterface::read(Channel inChannel)
|
||||||
|
{
|
||||||
|
if (inChannel >= MIDI_CHANNEL_OFF)
|
||||||
|
return false; // MIDI Input disabled.
|
||||||
|
|
||||||
|
if (parse(inChannel))
|
||||||
|
{
|
||||||
|
if (inputFilter(inChannel))
|
||||||
|
{
|
||||||
|
|
||||||
|
#if (MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
|
||||||
|
thruFilter(inChannel);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MIDI_USE_CALLBACKS
|
||||||
|
launchCallback();
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Private method: MIDI parser
|
||||||
|
bool MidiInterface::parse(Channel inChannel)
|
||||||
|
{
|
||||||
|
const byte bytes_available = MIDI_SERIAL_PORT.available();
|
||||||
|
|
||||||
|
if (bytes_available == 0)
|
||||||
|
// No data available.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
/* Parsing algorithm:
|
||||||
|
Get a byte from the serial buffer.
|
||||||
|
* If there is no pending message to be recomposed, start a new one.
|
||||||
|
- Find type and channel (if pertinent)
|
||||||
|
- Look for other bytes in buffer, call parser recursively,
|
||||||
|
until the message is assembled or the buffer is empty.
|
||||||
|
* Else, add the extracted byte to the pending message, and check validity.
|
||||||
|
When the message is done, store it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const byte extracted = MIDI_SERIAL_PORT.read();
|
||||||
|
|
||||||
|
if (mPendingMessageIndex == 0)
|
||||||
|
{
|
||||||
|
// Start a new pending message
|
||||||
|
mPendingMessage[0] = extracted;
|
||||||
|
|
||||||
|
// Check for running status first
|
||||||
|
switch (getTypeFromStatusByte(mRunningStatus_RX))
|
||||||
|
{
|
||||||
|
// Only these types allow Running Status:
|
||||||
|
case NoteOff:
|
||||||
|
case NoteOn:
|
||||||
|
case AfterTouchPoly:
|
||||||
|
case ControlChange:
|
||||||
|
case ProgramChange:
|
||||||
|
case AfterTouchChannel:
|
||||||
|
case PitchBend:
|
||||||
|
|
||||||
|
// If the status byte is not received, prepend it
|
||||||
|
// to the pending message
|
||||||
|
if (extracted < 0x80)
|
||||||
|
{
|
||||||
|
mPendingMessage[0] = mRunningStatus_RX;
|
||||||
|
mPendingMessage[1] = extracted;
|
||||||
|
mPendingMessageIndex = 1;
|
||||||
|
}
|
||||||
|
// Else: well, we received another status byte,
|
||||||
|
// so the running status does not apply here.
|
||||||
|
// It will be updated upon completion of this message.
|
||||||
|
|
||||||
|
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1))
|
||||||
|
{
|
||||||
|
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
|
||||||
|
mMessage.channel = (mPendingMessage[0] & 0x0F)+1;
|
||||||
|
mMessage.data1 = mPendingMessage[1];
|
||||||
|
|
||||||
|
// Save data2 only if applicable
|
||||||
|
if (mPendingMessageExpectedLenght == 3)
|
||||||
|
mMessage.data2 = mPendingMessage[2];
|
||||||
|
else
|
||||||
|
mMessage.data2 = 0;
|
||||||
|
|
||||||
|
mPendingMessageIndex = 0;
|
||||||
|
mPendingMessageExpectedLenght = 0;
|
||||||
|
mMessage.valid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// No running status
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch (getTypeFromStatusByte(mPendingMessage[0]))
|
||||||
|
{
|
||||||
|
// 1 byte messages
|
||||||
|
case Start:
|
||||||
|
case Continue:
|
||||||
|
case Stop:
|
||||||
|
case Clock:
|
||||||
|
case ActiveSensing:
|
||||||
|
case SystemReset:
|
||||||
|
case TuneRequest:
|
||||||
|
// Handle the message type directly here.
|
||||||
|
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
|
||||||
|
mMessage.channel = 0;
|
||||||
|
mMessage.data1 = 0;
|
||||||
|
mMessage.data2 = 0;
|
||||||
|
mMessage.valid = true;
|
||||||
|
|
||||||
|
// \fix Running Status broken when receiving Clock messages.
|
||||||
|
// Do not reset all input attributes, Running Status must remain unchanged.
|
||||||
|
//resetInput();
|
||||||
|
|
||||||
|
// We still need to reset these
|
||||||
|
mPendingMessageIndex = 0;
|
||||||
|
mPendingMessageExpectedLenght = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 2 bytes messages
|
||||||
|
case ProgramChange:
|
||||||
|
case AfterTouchChannel:
|
||||||
|
case TimeCodeQuarterFrame:
|
||||||
|
case SongSelect:
|
||||||
|
mPendingMessageExpectedLenght = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 3 bytes messages
|
||||||
|
case NoteOn:
|
||||||
|
case NoteOff:
|
||||||
|
case ControlChange:
|
||||||
|
case PitchBend:
|
||||||
|
case AfterTouchPoly:
|
||||||
|
case SongPosition:
|
||||||
|
mPendingMessageExpectedLenght = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SystemExclusive:
|
||||||
|
// The message can be any lenght
|
||||||
|
// between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
|
||||||
|
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE;
|
||||||
|
mRunningStatus_RX = InvalidType;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InvalidType:
|
||||||
|
default:
|
||||||
|
// This is obviously wrong. Let's get the hell out'a here.
|
||||||
|
resetInput();
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then update the index of the pending message.
|
||||||
|
mPendingMessageIndex++;
|
||||||
|
|
||||||
|
#if USE_1BYTE_PARSING
|
||||||
|
// Message is not complete.
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
// Call the parser recursively
|
||||||
|
// to parse the rest of the message.
|
||||||
|
return parse(inChannel);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First, test if this is a status byte
|
||||||
|
if (extracted >= 0x80)
|
||||||
|
{
|
||||||
|
// Reception of status bytes in the middle of an uncompleted message
|
||||||
|
// are allowed only for interleaved Real Time message or EOX
|
||||||
|
switch (extracted)
|
||||||
|
{
|
||||||
|
case Clock:
|
||||||
|
case Start:
|
||||||
|
case Continue:
|
||||||
|
case Stop:
|
||||||
|
case ActiveSensing:
|
||||||
|
case SystemReset:
|
||||||
|
|
||||||
|
// Here we will have to extract the one-byte message,
|
||||||
|
// pass it to the structure for being read outside
|
||||||
|
// the MIDI class, and recompose the message it was
|
||||||
|
// interleaved into. Oh, and without killing the running status..
|
||||||
|
// This is done by leaving the pending message as is,
|
||||||
|
// it will be completed on next calls.
|
||||||
|
|
||||||
|
mMessage.type = (MidiType)extracted;
|
||||||
|
mMessage.data1 = 0;
|
||||||
|
mMessage.data2 = 0;
|
||||||
|
mMessage.channel = 0;
|
||||||
|
mMessage.valid = true;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// End of Exclusive
|
||||||
|
case 0xF7:
|
||||||
|
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive)
|
||||||
|
{
|
||||||
|
// Store System Exclusive array in midimsg structure
|
||||||
|
for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++)
|
||||||
|
mMessage.sysex_array[i] = mPendingMessage[i];
|
||||||
|
|
||||||
|
mMessage.type = SystemExclusive;
|
||||||
|
|
||||||
|
// Get length
|
||||||
|
mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;
|
||||||
|
mMessage.data2 = (mPendingMessageIndex+1) >> 8;
|
||||||
|
mMessage.channel = 0;
|
||||||
|
mMessage.valid = true;
|
||||||
|
|
||||||
|
resetInput();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Well well well.. error.
|
||||||
|
resetInput();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add extracted data byte to pending message
|
||||||
|
mPendingMessage[mPendingMessageIndex] = extracted;
|
||||||
|
|
||||||
|
// Now we are going to check if we have reached the end of the message
|
||||||
|
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1))
|
||||||
|
{
|
||||||
|
// "FML" case: fall down here with an overflown SysEx..
|
||||||
|
// This means we received the last possible data byte that can fit
|
||||||
|
// the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
|
||||||
|
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive)
|
||||||
|
{
|
||||||
|
resetInput();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
|
||||||
|
// Don't check if it is a Channel Message
|
||||||
|
mMessage.channel = (mPendingMessage[0] & 0x0F)+1;
|
||||||
|
|
||||||
|
mMessage.data1 = mPendingMessage[1];
|
||||||
|
|
||||||
|
// Save data2 only if applicable
|
||||||
|
if (mPendingMessageExpectedLenght == 3)
|
||||||
|
mMessage.data2 = mPendingMessage[2];
|
||||||
|
else
|
||||||
|
mMessage.data2 = 0;
|
||||||
|
|
||||||
|
// Reset local variables
|
||||||
|
mPendingMessageIndex = 0;
|
||||||
|
mPendingMessageExpectedLenght = 0;
|
||||||
|
|
||||||
|
mMessage.valid = true;
|
||||||
|
|
||||||
|
// Activate running status (if enabled for the received type)
|
||||||
|
switch (mMessage.type)
|
||||||
|
{
|
||||||
|
case NoteOff:
|
||||||
|
case NoteOn:
|
||||||
|
case AfterTouchPoly:
|
||||||
|
case ControlChange:
|
||||||
|
case ProgramChange:
|
||||||
|
case AfterTouchChannel:
|
||||||
|
case PitchBend:
|
||||||
|
// Running status enabled: store it from received message
|
||||||
|
mRunningStatus_RX = mPendingMessage[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// No running status
|
||||||
|
mRunningStatus_RX = InvalidType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Then update the index of the pending message.
|
||||||
|
mPendingMessageIndex++;
|
||||||
|
|
||||||
|
#if USE_1BYTE_PARSING
|
||||||
|
// Message is not complete.
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
// Call the parser recursively
|
||||||
|
// to parse the rest of the message.
|
||||||
|
return parse(inChannel);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// What are our chances to fall here?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private method: check if the received message is on the listened channel
|
||||||
|
bool MidiInterface::inputFilter(Channel inChannel)
|
||||||
|
{
|
||||||
|
// This method handles recognition of channel
|
||||||
|
// (to know if the message is destinated to the Arduino)
|
||||||
|
|
||||||
|
if (mMessage.type == InvalidType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// First, check if the received message is Channel
|
||||||
|
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
||||||
|
{
|
||||||
|
// Then we need to know if we listen to it
|
||||||
|
if ((mMessage.channel == mInputChannel) ||
|
||||||
|
(mInputChannel == MIDI_CHANNEL_OMNI))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We don't listen to this channel
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// System messages are always received
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private method: reset input attributes
|
||||||
|
void MidiInterface::resetInput()
|
||||||
|
{
|
||||||
|
mPendingMessageIndex = 0;
|
||||||
|
mPendingMessageExpectedLenght = 0;
|
||||||
|
mRunningStatus_RX = InvalidType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_USE_CALLBACKS
|
||||||
|
|
||||||
|
// Private - launch callback function based on received type.
|
||||||
|
void MidiInterface::launchCallback()
|
||||||
|
{
|
||||||
|
// The order is mixed to allow frequent messages to trigger their callback faster.
|
||||||
|
switch (mMessage.type)
|
||||||
|
{
|
||||||
|
// Notes
|
||||||
|
case NoteOff: if (mNoteOffCallback != 0) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
||||||
|
case NoteOn: if (mNoteOnCallback != 0) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
||||||
|
|
||||||
|
// Real-time messages
|
||||||
|
case Clock: if (mClockCallback != 0) mClockCallback(); break;
|
||||||
|
case Start: if (mStartCallback != 0) mStartCallback(); break;
|
||||||
|
case Continue: if (mContinueCallback != 0) mContinueCallback(); break;
|
||||||
|
case Stop: if (mStopCallback != 0) mStopCallback(); break;
|
||||||
|
case ActiveSensing: if (mActiveSensingCallback != 0) mActiveSensingCallback(); break;
|
||||||
|
|
||||||
|
// Continuous controllers
|
||||||
|
case ControlChange: if (mControlChangeCallback != 0) mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
||||||
|
case PitchBend: if (mPitchBendCallback != 0) mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) + MIDI_PITCHBEND_MIN); break; // TODO: check this
|
||||||
|
case AfterTouchPoly: if (mAfterTouchPolyCallback != 0) mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
||||||
|
case AfterTouchChannel: if (mAfterTouchChannelCallback != 0) mAfterTouchChannelCallback(mMessage.channel,mMessage.data1); break;
|
||||||
|
|
||||||
|
case ProgramChange: if (mProgramChangeCallback != 0) mProgramChangeCallback(mMessage.channel,mMessage.data1); break;
|
||||||
|
case SystemExclusive: if (mSystemExclusiveCallback != 0) mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1); break;
|
||||||
|
|
||||||
|
// Occasional messages
|
||||||
|
case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != 0) mTimeCodeQuarterFrameCallback(mMessage.data1); break;
|
||||||
|
case SongPosition: if (mSongPositionCallback != 0) mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break;
|
||||||
|
case SongSelect: if (mSongSelectCallback != 0) mSongSelectCallback(mMessage.data1); break;
|
||||||
|
case TuneRequest: if (mTuneRequestCallback != 0) mTuneRequestCallback(); break;
|
||||||
|
|
||||||
|
case SystemReset: if (mSystemResetCallback != 0) mSystemResetCallback(); break;
|
||||||
|
case InvalidType:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MIDI_USE_CALLBACKS
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Thru
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_BUILD_THRU
|
||||||
|
|
||||||
|
// This method is called upon reception of a message
|
||||||
|
// and takes care of Thru filtering and sending.
|
||||||
|
|
||||||
|
void MidiInterface::thruFilter(Channel inChannel)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
This method handles Soft-Thru filtering.
|
||||||
|
|
||||||
|
Soft-Thru filtering:
|
||||||
|
- All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off
|
||||||
|
- Channel messages are passed to the output whether their channel is matching the input channel and the filter setting
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// If the feature is disabled, don't do anything.
|
||||||
|
if (!mThruActivated || (mThruFilterMode == Off))
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
// First, check if the received message is Channel
|
||||||
|
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
||||||
|
{
|
||||||
|
const bool filter_condition = ((mMessage.channel == mInputChannel) ||
|
||||||
|
(mInputChannel == MIDI_CHANNEL_OMNI));
|
||||||
|
|
||||||
|
// Now let's pass it to the output
|
||||||
|
switch (mThruFilterMode)
|
||||||
|
{
|
||||||
|
case Full:
|
||||||
|
send(mMessage.type,
|
||||||
|
mMessage.data1,
|
||||||
|
mMessage.data2,
|
||||||
|
mMessage.channel);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case SameChannel:
|
||||||
|
if (filter_condition)
|
||||||
|
{
|
||||||
|
send(mMessage.type,
|
||||||
|
mMessage.data1,
|
||||||
|
mMessage.data2,
|
||||||
|
mMessage.channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DifferentChannel:
|
||||||
|
if (!filter_condition)
|
||||||
|
{
|
||||||
|
send(mMessage.type,
|
||||||
|
mMessage.data1,
|
||||||
|
mMessage.data2,
|
||||||
|
mMessage.channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Off:
|
||||||
|
// Do nothing.
|
||||||
|
// Technically it's impossible to get there because
|
||||||
|
// the case was already tested earlier.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Send the message to the output
|
||||||
|
switch (mMessage.type)
|
||||||
|
{
|
||||||
|
// Real Time and 1 byte
|
||||||
|
case Clock:
|
||||||
|
case Start:
|
||||||
|
case Stop:
|
||||||
|
case Continue:
|
||||||
|
case ActiveSensing:
|
||||||
|
case SystemReset:
|
||||||
|
case TuneRequest:
|
||||||
|
sendRealTime(mMessage.type);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SystemExclusive:
|
||||||
|
// Send SysEx (0xF0 and 0xF7 are included in the buffer)
|
||||||
|
sendSysEx(mMessage.data1,mMessage.sysex_array,true);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SongSelect:
|
||||||
|
sendSongSelect(mMessage.data1);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SongPosition:
|
||||||
|
sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7));
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TimeCodeQuarterFrame:
|
||||||
|
sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_THRU
|
||||||
|
|
||||||
|
END_MIDI_NAMESPACE
|
||||||
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*!
|
||||||
|
* @file MIDI.h
|
||||||
|
* Project Arduino MIDI Library
|
||||||
|
* @brief MIDI Library for the Arduino
|
||||||
|
* @version 4.0
|
||||||
|
* @author Francois Best
|
||||||
|
* @date 24/02/11
|
||||||
|
* license GPL Forty Seven Effects - 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "midi_Settings.h"
|
||||||
|
#include "midi_Defs.h"
|
||||||
|
|
||||||
|
#ifdef FSE_AVR
|
||||||
|
# include "hardware_Serial.h"
|
||||||
|
#else
|
||||||
|
# include "Arduino.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BEGIN_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
/*! \brief The main class for MIDI handling.
|
||||||
|
*/
|
||||||
|
class MidiInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MidiInterface();
|
||||||
|
~MidiInterface();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void begin(Channel inChannel = 1);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// MIDI Output
|
||||||
|
|
||||||
|
#if MIDI_BUILD_OUTPUT
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline void sendNoteOn(DataByte inNoteNumber,
|
||||||
|
DataByte inVelocity,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendNoteOff(DataByte inNoteNumber,
|
||||||
|
DataByte inVelocity,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendProgramChange(DataByte inProgramNumber,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendControlChange(DataByte inControlNumber,
|
||||||
|
DataByte inControlValue,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendPitchBend(int inPitchValue, Channel inChannel);
|
||||||
|
inline void sendPitchBend(double inPitchValue, Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendPolyPressure(DataByte inNoteNumber,
|
||||||
|
DataByte inPressure,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendAfterTouch(DataByte inPressure,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
inline void sendSysEx(unsigned int inLength,
|
||||||
|
const byte* inArray,
|
||||||
|
bool inArrayContainsBoundaries = false);
|
||||||
|
|
||||||
|
inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble,
|
||||||
|
DataByte inValuesNibble);
|
||||||
|
inline void sendTimeCodeQuarterFrame(DataByte inData);
|
||||||
|
|
||||||
|
inline void sendSongPosition(unsigned int inBeats);
|
||||||
|
inline void sendSongSelect(DataByte inSongNumber);
|
||||||
|
inline void sendTuneRequest();
|
||||||
|
inline void sendRealTime(MidiType inType);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void send(MidiType inType,
|
||||||
|
DataByte inData1,
|
||||||
|
DataByte inData2,
|
||||||
|
Channel inChannel);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline StatusByte getStatus(MidiType inType,
|
||||||
|
Channel inChannel) const;
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// MIDI Input
|
||||||
|
|
||||||
|
#if MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool read();
|
||||||
|
bool read(Channel inChannel);
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline MidiType getType() const;
|
||||||
|
inline Channel getChannel() const;
|
||||||
|
inline DataByte getData1() const;
|
||||||
|
inline DataByte getData2() const;
|
||||||
|
inline const byte* getSysExArray() const;
|
||||||
|
inline unsigned int getSysExArrayLength() const;
|
||||||
|
inline bool check() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline Channel getInputChannel() const;
|
||||||
|
inline void setInputChannel(Channel inChannel);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static inline MidiType getTypeFromStatusByte(const byte inStatus);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool inputFilter(Channel inChannel);
|
||||||
|
bool parse(Channel inChannel);
|
||||||
|
void resetInput();
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusByte mRunningStatus_RX;
|
||||||
|
Channel mInputChannel;
|
||||||
|
|
||||||
|
byte mPendingMessage[3]; // SysEx are dumped into mMessage directly.
|
||||||
|
unsigned int mPendingMessageExpectedLenght;
|
||||||
|
unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads.
|
||||||
|
Message mMessage;
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Input Callbacks
|
||||||
|
|
||||||
|
#if MIDI_USE_CALLBACKS
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity));
|
||||||
|
inline void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity));
|
||||||
|
inline void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure));
|
||||||
|
inline void setHandleControlChange(void (*fptr)(byte channel, byte number, byte value));
|
||||||
|
inline void setHandleProgramChange(void (*fptr)(byte channel, byte number));
|
||||||
|
inline void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure));
|
||||||
|
inline void setHandlePitchBend(void (*fptr)(byte channel, int bend));
|
||||||
|
inline void setHandleSystemExclusive(void (*fptr)(byte * array, byte size));
|
||||||
|
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
|
||||||
|
inline void setHandleSongPosition(void (*fptr)(unsigned int beats));
|
||||||
|
inline void setHandleSongSelect(void (*fptr)(byte songnumber));
|
||||||
|
inline void setHandleTuneRequest(void (*fptr)(void));
|
||||||
|
inline void setHandleClock(void (*fptr)(void));
|
||||||
|
inline void setHandleStart(void (*fptr)(void));
|
||||||
|
inline void setHandleContinue(void (*fptr)(void));
|
||||||
|
inline void setHandleStop(void (*fptr)(void));
|
||||||
|
inline void setHandleActiveSensing(void (*fptr)(void));
|
||||||
|
inline void setHandleSystemReset(void (*fptr)(void));
|
||||||
|
|
||||||
|
inline void disconnectCallbackFromType(MidiType inType);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void launchCallback();
|
||||||
|
|
||||||
|
void (*mNoteOffCallback)(byte channel, byte note, byte velocity);
|
||||||
|
void (*mNoteOnCallback)(byte channel, byte note, byte velocity);
|
||||||
|
void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity);
|
||||||
|
void (*mControlChangeCallback)(byte channel, byte, byte);
|
||||||
|
void (*mProgramChangeCallback)(byte channel, byte);
|
||||||
|
void (*mAfterTouchChannelCallback)(byte channel, byte);
|
||||||
|
void (*mPitchBendCallback)(byte channel, int);
|
||||||
|
void (*mSystemExclusiveCallback)(byte * array, byte size);
|
||||||
|
void (*mTimeCodeQuarterFrameCallback)(byte data);
|
||||||
|
void (*mSongPositionCallback)(unsigned int beats);
|
||||||
|
void (*mSongSelectCallback)(byte songnumber);
|
||||||
|
void (*mTuneRequestCallback)(void);
|
||||||
|
void (*mClockCallback)(void);
|
||||||
|
void (*mStartCallback)(void);
|
||||||
|
void (*mContinueCallback)(void);
|
||||||
|
void (*mStopCallback)(void);
|
||||||
|
void (*mActiveSensingCallback)(void);
|
||||||
|
void (*mSystemResetCallback)(void);
|
||||||
|
|
||||||
|
#endif // MIDI_USE_CALLBACKS
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// MIDI Soft Thru
|
||||||
|
|
||||||
|
#if MIDI_BUILD_THRU
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline MidiFilterMode getFilterMode() const;
|
||||||
|
inline bool getThruState() const;
|
||||||
|
|
||||||
|
inline void turnThruOn(MidiFilterMode inThruFilterMode = Full);
|
||||||
|
inline void turnThruOff();
|
||||||
|
inline void setThruFilterMode(MidiFilterMode inThruFilterMode);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void thruFilter(byte inChannel);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mThruActivated : 1;
|
||||||
|
MidiFilterMode mThruFilterMode : 7;
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_THRU
|
||||||
|
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
|
||||||
|
private:
|
||||||
|
StatusByte mRunningStatus_TX;
|
||||||
|
|
||||||
|
#endif // MIDI_USE_RUNNING_STATUS
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
END_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_AUTO_INSTANCIATE
|
||||||
|
extern MIDI_NAMESPACE::MidiInterface MIDI;
|
||||||
|
#endif // MIDI_AUTO_INSTANCIATE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "midi_Inline.hpp"
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*!
|
||||||
|
* @file midi_Defs.h
|
||||||
|
* Project Arduino MIDI Library
|
||||||
|
* @brief MIDI Library for the Arduino - Definitions
|
||||||
|
* @version 4.0
|
||||||
|
* @author Francois Best
|
||||||
|
* @date 24/02/11
|
||||||
|
* license GPL Forty Seven Effects - 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "midi_Namespace.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef FSE_AVR
|
||||||
|
# include <core_Types.h>
|
||||||
|
#else
|
||||||
|
# include <inttypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BEGIN_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define MIDI_CHANNEL_OMNI 0
|
||||||
|
#define MIDI_CHANNEL_OFF 17 // and over
|
||||||
|
|
||||||
|
#define MIDI_PITCHBEND_MIN -8192
|
||||||
|
#define MIDI_PITCHBEND_MAX 8191
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Type definitions
|
||||||
|
|
||||||
|
#ifndef FSE_AVR
|
||||||
|
typedef uint8_t byte;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef byte StatusByte;
|
||||||
|
typedef byte DataByte;
|
||||||
|
typedef byte Channel;
|
||||||
|
typedef byte FilterMode;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*! Enumeration of MIDI types */
|
||||||
|
enum MidiType
|
||||||
|
{
|
||||||
|
InvalidType = 0x00, ///< For notifying errors
|
||||||
|
NoteOff = 0x80, ///< Note Off
|
||||||
|
NoteOn = 0x90, ///< Note On
|
||||||
|
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
|
||||||
|
ControlChange = 0xB0, ///< Control Change / Channel Mode
|
||||||
|
ProgramChange = 0xC0, ///< Program Change
|
||||||
|
AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch
|
||||||
|
PitchBend = 0xE0, ///< Pitch Bend
|
||||||
|
SystemExclusive = 0xF0, ///< System Exclusive
|
||||||
|
TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
|
||||||
|
SongPosition = 0xF2, ///< System Common - Song Position Pointer
|
||||||
|
SongSelect = 0xF3, ///< System Common - Song Select
|
||||||
|
TuneRequest = 0xF6, ///< System Common - Tune Request
|
||||||
|
Clock = 0xF8, ///< System Real Time - Timing Clock
|
||||||
|
Start = 0xFA, ///< System Real Time - Start
|
||||||
|
Continue = 0xFB, ///< System Real Time - Continue
|
||||||
|
Stop = 0xFC, ///< System Real Time - Stop
|
||||||
|
ActiveSensing = 0xFE, ///< System Real Time - Active Sensing
|
||||||
|
SystemReset = 0xFF, ///< System Real Time - System Reset
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*! Enumeration of Thru filter modes */
|
||||||
|
enum MidiFilterMode
|
||||||
|
{
|
||||||
|
Off = 0, ///< Thru disabled (nothing passes through).
|
||||||
|
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
|
||||||
|
SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
|
||||||
|
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
enum MidiControlChangeNumber
|
||||||
|
{
|
||||||
|
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
|
||||||
|
BankSelect = 0,
|
||||||
|
ModulationWheel = 1,
|
||||||
|
BreathController = 2,
|
||||||
|
// CC3 undefined
|
||||||
|
FootController = 4,
|
||||||
|
PortamentoTime = 5,
|
||||||
|
DataEntry = 6,
|
||||||
|
ChannelVolume = 7,
|
||||||
|
Balance = 8,
|
||||||
|
// CC9 undefined
|
||||||
|
Pan = 10,
|
||||||
|
ExpressionController = 11,
|
||||||
|
EffectControl1 = 12,
|
||||||
|
EffectControl2 = 13,
|
||||||
|
// CC14 undefined
|
||||||
|
// CC15 undefined
|
||||||
|
GeneralPurposeController1 = 16,
|
||||||
|
GeneralPurposeController2 = 17,
|
||||||
|
GeneralPurposeController3 = 18,
|
||||||
|
GeneralPurposeController4 = 19,
|
||||||
|
|
||||||
|
// Switches ----------------------------------------------------------------
|
||||||
|
Sustain = 64,
|
||||||
|
Portamento = 65,
|
||||||
|
Sostenuto = 66,
|
||||||
|
SoftPedal = 67,
|
||||||
|
Legato = 68,
|
||||||
|
Hold = 69,
|
||||||
|
|
||||||
|
// Low resolution continuous controllers -----------------------------------
|
||||||
|
SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
|
||||||
|
SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
|
||||||
|
SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
|
||||||
|
SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
|
||||||
|
SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
|
||||||
|
SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
|
||||||
|
SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
|
||||||
|
SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
|
||||||
|
SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
|
||||||
|
SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
|
||||||
|
GeneralPurposeController5 = 80,
|
||||||
|
GeneralPurposeController6 = 81,
|
||||||
|
GeneralPurposeController7 = 82,
|
||||||
|
GeneralPurposeController8 = 83,
|
||||||
|
PortamentoControl = 84,
|
||||||
|
// CC85 to CC90 undefined
|
||||||
|
Effects1 = 91, ///< Reverb send level
|
||||||
|
Effects2 = 92, ///< Tremolo depth
|
||||||
|
Effects3 = 93, ///< Chorus send level
|
||||||
|
Effects4 = 94, ///< Celeste depth
|
||||||
|
Effects5 = 95, ///< Phaser depth
|
||||||
|
|
||||||
|
// Channel Mode messages ---------------------------------------------------
|
||||||
|
AllSoundOff = 120,
|
||||||
|
ResetAllControllers = 121,
|
||||||
|
LocalControl = 122,
|
||||||
|
AllNotesOff = 123,
|
||||||
|
OmniModeOff = 124,
|
||||||
|
OmniModeOn = 125,
|
||||||
|
MonoModeOn = 126,
|
||||||
|
PolyModeOn = 127
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*! The midimsg structure contains decoded data
|
||||||
|
of a MIDI message read from the serial port
|
||||||
|
with read() or thru().
|
||||||
|
*/
|
||||||
|
struct Message
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! The MIDI channel on which the message was recieved.
|
||||||
|
\n Value goes from 1 to 16.
|
||||||
|
*/
|
||||||
|
Channel channel;
|
||||||
|
|
||||||
|
/*! The type of the message
|
||||||
|
(see the MidiType enum for types reference)
|
||||||
|
*/
|
||||||
|
MidiType type;
|
||||||
|
|
||||||
|
/*! The first data byte.
|
||||||
|
\n Value goes from 0 to 127.
|
||||||
|
*/
|
||||||
|
DataByte data1;
|
||||||
|
|
||||||
|
/*! The second data byte.
|
||||||
|
If the message is only 2 bytes long, this one is null.
|
||||||
|
\n Value goes from 0 to 127.
|
||||||
|
*/
|
||||||
|
DataByte data2;
|
||||||
|
|
||||||
|
/*! System Exclusive dedicated byte array.
|
||||||
|
\n Array length is stocked on 16 bits,
|
||||||
|
in data1 (LSB) and data2 (MSB)
|
||||||
|
*/
|
||||||
|
DataByte sysex_array[MIDI_SYSEX_ARRAY_SIZE];
|
||||||
|
|
||||||
|
/*! This boolean indicates if the message is valid or not.
|
||||||
|
There is no channel consideration here,
|
||||||
|
validity means the message respects the MIDI norm.
|
||||||
|
*/
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
END_MIDI_NAMESPACE
|
||||||
|
|
@ -0,0 +1,477 @@
|
||||||
|
/*!
|
||||||
|
* @file midi_Inline.hpp
|
||||||
|
* Project Arduino MIDI Library
|
||||||
|
* @brief MIDI Library for the Arduino - Inline implementations
|
||||||
|
* @version 4.0
|
||||||
|
* @author Francois Best
|
||||||
|
* @date 24/02/11
|
||||||
|
* license GPL Forty Seven Effects - 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
BEGIN_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Output
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_BUILD_OUTPUT
|
||||||
|
|
||||||
|
/*! \brief Send a Note On message
|
||||||
|
\param inNoteNumber Pitch value in the MIDI format (0 to 127).
|
||||||
|
\param inVelocity Note attack velocity (0 to 127). A NoteOn with 0 velocity
|
||||||
|
is considered as a NoteOff.
|
||||||
|
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||||
|
|
||||||
|
Take a look at the values, names and frequencies of notes here:
|
||||||
|
http://www.phys.unsw.edu.au/jw/notes.html
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendNoteOn(DataByte inNoteNumber,
|
||||||
|
DataByte inVelocity,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
send(NoteOn, inNoteNumber, inVelocity, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Note Off message
|
||||||
|
\param inNoteNumber Pitch value in the MIDI format (0 to 127).
|
||||||
|
\param inVelocity Release velocity (0 to 127).
|
||||||
|
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||||
|
|
||||||
|
Note: you can send NoteOn with zero velocity to make a NoteOff, this is based
|
||||||
|
on the Running Status principle, to avoid sending status messages and thus
|
||||||
|
sending only NoteOn data. This method will always send a real NoteOff message.
|
||||||
|
Take a look at the values, names and frequencies of notes here:
|
||||||
|
http://www.phys.unsw.edu.au/jw/notes.html
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendNoteOff(DataByte inNoteNumber,
|
||||||
|
DataByte inVelocity,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
send(NoteOff, inNoteNumber, inVelocity, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Program Change message
|
||||||
|
\param inProgramNumber The Program to select (0 to 127).
|
||||||
|
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendProgramChange(DataByte inProgramNumber,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
send(ProgramChange, inProgramNumber, 0, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Control Change message
|
||||||
|
\param ControlNumber The controller number (0 to 127).
|
||||||
|
\param ControlValue The value for the specified controller (0 to 127).
|
||||||
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
|
||||||
|
See the detailed controllers numbers & description here:
|
||||||
|
http://www.somascape.org/midi/tech/spec.html#ctrlnums
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendControlChange(DataByte inControlNumber,
|
||||||
|
DataByte inControlValue,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
send(ControlChange, inControlNumber, inControlValue, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
|
||||||
|
\param NoteNumber The note to apply AfterTouch to (0 to 127).
|
||||||
|
\param Pressure The amount of AfterTouch to apply (0 to 127).
|
||||||
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendPolyPressure(DataByte inNoteNumber,
|
||||||
|
DataByte inPressure,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a MonoPhonic AfterTouch message (applies to all notes)
|
||||||
|
\param Pressure The amount of AfterTouch to apply to all notes.
|
||||||
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendAfterTouch(DataByte inPressure,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
send(AfterTouchChannel, inPressure, 0, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Pitch Bend message using a signed integer value.
|
||||||
|
\param PitchValue The amount of bend to send (in a signed integer format),
|
||||||
|
between MIDI_PITCHBEND_MIN and MIDI_PITCHBEND_MAX,
|
||||||
|
center value is 0.
|
||||||
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendPitchBend(int inPitchValue,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
const unsigned int bend = inPitchValue - MIDI_PITCHBEND_MIN;
|
||||||
|
send(PitchBend, (bend & 0x7F), (bend >> 7) & 0x7F, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Send a Pitch Bend message using a floating point value.
|
||||||
|
\param PitchValue The amount of bend to send (in a floating point format),
|
||||||
|
between -1.0f (maximum downwards bend)
|
||||||
|
and +1.0f (max upwards bend), center value is 0.0f.
|
||||||
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendPitchBend(double inPitchValue,
|
||||||
|
Channel inChannel)
|
||||||
|
{
|
||||||
|
const int value = inPitchValue * MIDI_PITCHBEND_MAX;
|
||||||
|
sendPitchBend(value, inChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Generate and send a System Exclusive frame.
|
||||||
|
\param length The size of the array to send
|
||||||
|
\param array The byte array containing the data to send
|
||||||
|
\param ArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes
|
||||||
|
(start & stop SysEx) will NOT be sent
|
||||||
|
(and therefore must be included in the array).
|
||||||
|
default value for ArrayContainsBoundaries is set to 'false' for compatibility
|
||||||
|
with previous versions of the library.
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendSysEx(unsigned int inLength,
|
||||||
|
const byte* inArray,
|
||||||
|
bool inArrayContainsBoundaries)
|
||||||
|
{
|
||||||
|
if (inArrayContainsBoundaries == false)
|
||||||
|
{
|
||||||
|
MIDI_SERIAL_PORT.write(0xF0);
|
||||||
|
|
||||||
|
for (unsigned int i=0;i<inLength;++i)
|
||||||
|
MIDI_SERIAL_PORT.write(inArray[i]);
|
||||||
|
|
||||||
|
MIDI_SERIAL_PORT.write(0xF7);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (unsigned int i=0;i<inLength;++i)
|
||||||
|
MIDI_SERIAL_PORT.write(inArray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
mRunningStatus_TX = InvalidType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Tune Request message.
|
||||||
|
|
||||||
|
When a MIDI unit receives this message,
|
||||||
|
it should tune its oscillators (if equipped with any).
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendTuneRequest()
|
||||||
|
{
|
||||||
|
sendRealTime(TuneRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a MIDI Time Code Quarter Frame.
|
||||||
|
|
||||||
|
\param TypeNibble MTC type
|
||||||
|
\param ValuesNibble MTC data
|
||||||
|
See MIDI Specification for more information.
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendTimeCodeQuarterFrame(DataByte inTypeNibble,
|
||||||
|
DataByte inValuesNibble)
|
||||||
|
{
|
||||||
|
const byte data = ( ((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0F) );
|
||||||
|
sendTimeCodeQuarterFrame(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a MIDI Time Code Quarter Frame.
|
||||||
|
|
||||||
|
See MIDI Specification for more information.
|
||||||
|
\param data if you want to encode directly the nibbles in your program,
|
||||||
|
you can send the byte here.
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendTimeCodeQuarterFrame(DataByte inData)
|
||||||
|
{
|
||||||
|
MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame);
|
||||||
|
MIDI_SERIAL_PORT.write(inData);
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
mRunningStatus_TX = InvalidType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Song Position Pointer message.
|
||||||
|
\param Beats The number of beats since the start of the song.
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendSongPosition(unsigned int inBeats)
|
||||||
|
{
|
||||||
|
MIDI_SERIAL_PORT.write((byte)SongPosition);
|
||||||
|
MIDI_SERIAL_PORT.write(inBeats & 0x7F);
|
||||||
|
MIDI_SERIAL_PORT.write((inBeats >> 7) & 0x7F);
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
mRunningStatus_TX = InvalidType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Song Select message */
|
||||||
|
void MidiInterface::sendSongSelect(DataByte inSongNumber)
|
||||||
|
{
|
||||||
|
MIDI_SERIAL_PORT.write((byte)SongSelect);
|
||||||
|
MIDI_SERIAL_PORT.write(inSongNumber & 0x7F);
|
||||||
|
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
mRunningStatus_TX = InvalidType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Send a Real Time (one byte) message.
|
||||||
|
|
||||||
|
\param Type The available Real Time types are:
|
||||||
|
Start, Stop, Continue, Clock, ActiveSensing and SystemReset.
|
||||||
|
You can also send a Tune Request with this method.
|
||||||
|
@see MidiType
|
||||||
|
*/
|
||||||
|
void MidiInterface::sendRealTime(MidiType inType)
|
||||||
|
{
|
||||||
|
switch (inType)
|
||||||
|
{
|
||||||
|
case TuneRequest: // Not really real-time, but one byte anyway.
|
||||||
|
case Clock:
|
||||||
|
case Start:
|
||||||
|
case Stop:
|
||||||
|
case Continue:
|
||||||
|
case ActiveSensing:
|
||||||
|
case SystemReset:
|
||||||
|
MIDI_SERIAL_PORT.write((byte)inType);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Invalid Real Time marker
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not cancel Running Status for real-time messages as they can be
|
||||||
|
// interleaved within any message. Though, TuneRequest can be sent here,
|
||||||
|
// and as it is a System Common message, it must reset Running Status.
|
||||||
|
#if MIDI_USE_RUNNING_STATUS
|
||||||
|
if (inType == TuneRequest) mRunningStatus_TX = InvalidType;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
StatusByte MidiInterface::getStatus(MidiType inType,
|
||||||
|
Channel inChannel) const
|
||||||
|
{
|
||||||
|
return ((byte)inType | ((inChannel - 1) & 0x0F));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Input
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
/*! \brief Get the last received message's type
|
||||||
|
|
||||||
|
Returns an enumerated type. @see MidiType
|
||||||
|
*/
|
||||||
|
MidiType MidiInterface::getType() const
|
||||||
|
{
|
||||||
|
return mMessage.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the channel of the message stored in the structure.
|
||||||
|
|
||||||
|
\return Channel range is 1 to 16.
|
||||||
|
For non-channel messages, this will return 0.
|
||||||
|
*/
|
||||||
|
Channel MidiInterface::getChannel() const
|
||||||
|
{
|
||||||
|
return mMessage.channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the first data byte of the last received message. */
|
||||||
|
DataByte MidiInterface::getData1() const
|
||||||
|
{
|
||||||
|
return mMessage.data1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the second data byte of the last received message. */
|
||||||
|
DataByte MidiInterface::getData2() const
|
||||||
|
{
|
||||||
|
return mMessage.data2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the System Exclusive byte array.
|
||||||
|
|
||||||
|
@see getSysExArrayLength to get the array's length in bytes.
|
||||||
|
*/
|
||||||
|
const byte* MidiInterface::getSysExArray() const
|
||||||
|
{
|
||||||
|
return mMessage.sysex_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the lenght of the System Exclusive array.
|
||||||
|
|
||||||
|
It is coded using data1 as LSB and data2 as MSB.
|
||||||
|
\return The array's length, in bytes.
|
||||||
|
*/
|
||||||
|
unsigned int MidiInterface::getSysExArrayLength() const
|
||||||
|
{
|
||||||
|
const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1;
|
||||||
|
return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Check if a valid message is stored in the structure. */
|
||||||
|
bool MidiInterface::check() const
|
||||||
|
{
|
||||||
|
return mMessage.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Channel MidiInterface::getInputChannel() const
|
||||||
|
{
|
||||||
|
return mInputChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the value for the input MIDI channel
|
||||||
|
\param Channel the channel value. Valid values are 1 to 16, MIDI_CHANNEL_OMNI
|
||||||
|
if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input.
|
||||||
|
*/
|
||||||
|
void MidiInterface::setInputChannel(Channel inChannel)
|
||||||
|
{
|
||||||
|
mInputChannel = inChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*! \brief Extract an enumerated MIDI type from a status byte.
|
||||||
|
|
||||||
|
This is a utility static method, used internally,
|
||||||
|
made public so you can handle MidiTypes more easily.
|
||||||
|
*/
|
||||||
|
MidiType MidiInterface::getTypeFromStatusByte(const byte inStatus)
|
||||||
|
{
|
||||||
|
if ((inStatus < 0x80) ||
|
||||||
|
(inStatus == 0xF4) ||
|
||||||
|
(inStatus == 0xF5) ||
|
||||||
|
(inStatus == 0xF9) ||
|
||||||
|
(inStatus == 0xFD)) return InvalidType; // data bytes and undefined.
|
||||||
|
if (inStatus < 0xF0) return (MidiType)(inStatus & 0xF0); // Channel message, remove channel nibble.
|
||||||
|
else return (MidiType)inStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if MIDI_USE_CALLBACKS
|
||||||
|
|
||||||
|
void MidiInterface::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; }
|
||||||
|
void MidiInterface::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleSystemExclusive(void (*fptr)(byte* array, byte size)) { mSystemExclusiveCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; }
|
||||||
|
void MidiInterface::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; }
|
||||||
|
|
||||||
|
/*! \brief Detach an external function from the given type.
|
||||||
|
|
||||||
|
Use this method to cancel the effects of setHandle********.
|
||||||
|
\param Type The type of message to unbind.
|
||||||
|
When a message of this type is received, no function will be called.
|
||||||
|
*/
|
||||||
|
void MidiInterface::disconnectCallbackFromType(MidiType inType)
|
||||||
|
{
|
||||||
|
switch (inType)
|
||||||
|
{
|
||||||
|
case NoteOff: mNoteOffCallback = 0; break;
|
||||||
|
case NoteOn: mNoteOnCallback = 0; break;
|
||||||
|
case AfterTouchPoly: mAfterTouchPolyCallback = 0; break;
|
||||||
|
case ControlChange: mControlChangeCallback = 0; break;
|
||||||
|
case ProgramChange: mProgramChangeCallback = 0; break;
|
||||||
|
case AfterTouchChannel: mAfterTouchChannelCallback = 0; break;
|
||||||
|
case PitchBend: mPitchBendCallback = 0; break;
|
||||||
|
case SystemExclusive: mSystemExclusiveCallback = 0; break;
|
||||||
|
case TimeCodeQuarterFrame: mTimeCodeQuarterFrameCallback = 0; break;
|
||||||
|
case SongPosition: mSongPositionCallback = 0; break;
|
||||||
|
case SongSelect: mSongSelectCallback = 0; break;
|
||||||
|
case TuneRequest: mTuneRequestCallback = 0; break;
|
||||||
|
case Clock: mClockCallback = 0; break;
|
||||||
|
case Start: mStartCallback = 0; break;
|
||||||
|
case Continue: mContinueCallback = 0; break;
|
||||||
|
case Stop: mStopCallback = 0; break;
|
||||||
|
case ActiveSensing: mActiveSensingCallback = 0; break;
|
||||||
|
case SystemReset: mSystemResetCallback = 0; break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MIDI_USE_CALLBACKS
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_INPUT
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Thru
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
|
||||||
|
|
||||||
|
MidiFilterMode MidiInterface::getFilterMode() const
|
||||||
|
{
|
||||||
|
return mThruFilterMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MidiInterface::getThruState() const
|
||||||
|
{
|
||||||
|
return mThruActivated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Setter method: turn message mirroring on. */
|
||||||
|
void MidiInterface::turnThruOn(MidiFilterMode inThruFilterMode)
|
||||||
|
{
|
||||||
|
mThruActivated = true;
|
||||||
|
mThruFilterMode = inThruFilterMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Setter method: turn message mirroring off. */
|
||||||
|
void MidiInterface::turnThruOff()
|
||||||
|
{
|
||||||
|
mThruActivated = false;
|
||||||
|
mThruFilterMode = Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the filter for thru mirroring
|
||||||
|
\param inThruFilterMode a filter mode
|
||||||
|
|
||||||
|
@see MidiFilterMode
|
||||||
|
*/
|
||||||
|
void MidiInterface::setThruFilterMode(MidiFilterMode inThruFilterMode)
|
||||||
|
{
|
||||||
|
mThruFilterMode = inThruFilterMode;
|
||||||
|
if (mThruFilterMode != Off)
|
||||||
|
mThruActivated = true;
|
||||||
|
else
|
||||||
|
mThruActivated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MIDI_BUILD_THRU
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
END_MIDI_NAMESPACE
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*!
|
||||||
|
* @file midi_Namespace.h
|
||||||
|
* Project Arduino MIDI Library
|
||||||
|
* @brief MIDI Library for the Arduino - Namespace declaration
|
||||||
|
* @version 4.0
|
||||||
|
* @author Francois Best
|
||||||
|
* @date 24/02/11
|
||||||
|
* license GPL Forty Seven Effects - 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define MIDI_NAMESPACE midi
|
||||||
|
#define BEGIN_MIDI_NAMESPACE namespace MIDI_NAMESPACE {
|
||||||
|
#define END_MIDI_NAMESPACE }
|
||||||
|
|
||||||
|
#define USING_NAMESPACE_MIDI using namespace MIDI_NAMESPACE;
|
||||||
|
|
||||||
|
BEGIN_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
END_MIDI_NAMESPACE
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*!
|
||||||
|
* @file midi_Settings.h
|
||||||
|
* Project Arduino MIDI Library
|
||||||
|
* @brief MIDI Library for the Arduino - Settings
|
||||||
|
* @version 3.5
|
||||||
|
* @author Francois Best
|
||||||
|
* @date 24/02/11
|
||||||
|
* license GPL Forty Seven Effects - 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "midi_Namespace.h"
|
||||||
|
|
||||||
|
BEGIN_MIDI_NAMESPACE
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Here are a few settings you can change to customize
|
||||||
|
// the library for your own project. You can for example
|
||||||
|
// choose to compile only parts of it so you gain flash
|
||||||
|
// space and optimise the speed of your sketch.
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Compilation flags. Set them to 1 to build the associated feature
|
||||||
|
// (MIDI in, out, thru), or to 0 to disable the feature and save space.
|
||||||
|
// Note that the Thru can only work if in and out are enabled.
|
||||||
|
|
||||||
|
#define MIDI_BUILD_INPUT 1
|
||||||
|
#define MIDI_BUILD_OUTPUT 1
|
||||||
|
#define MIDI_BUILD_THRU 1
|
||||||
|
|
||||||
|
#define MIDI_USE_CALLBACKS 1
|
||||||
|
|
||||||
|
// Create a MIDI object automatically on the port defined with MIDI_SERIAL_PORT.
|
||||||
|
#define MIDI_AUTO_INSTANCIATE 1
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Serial port configuration
|
||||||
|
|
||||||
|
// Set the default port to use for MIDI.
|
||||||
|
#define MIDI_SERIAL_PORT Serial
|
||||||
|
|
||||||
|
// Software serial options
|
||||||
|
#define MIDI_USE_SOFTWARE_SERIAL 0
|
||||||
|
|
||||||
|
#if MIDI_USE_SOFTWARE_SERIAL
|
||||||
|
#define MIDI_SOFTSERIAL_RX_PIN 1 // Pin number to use for MIDI Input
|
||||||
|
#define MIDI_SOFTSERIAL_TX_PIN 2 // Pin number to use for MIDI Output
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Misc. options
|
||||||
|
|
||||||
|
// Running status enables short messages when sending multiple values
|
||||||
|
// of the same type and channel.
|
||||||
|
// Set to 0 if you have troubles controlling your hardware.
|
||||||
|
#define MIDI_USE_RUNNING_STATUS 1
|
||||||
|
#define MIDI_USE_1BYTE_PARSING 1
|
||||||
|
|
||||||
|
#define MIDI_BAUDRATE 31250
|
||||||
|
#define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes.
|
||||||
|
|
||||||
|
END_MIDI_NAMESPACE
|
||||||
Loading…
Reference in New Issue