Merge branch 'dev' into feature/usb

This commit is contained in:
Francois Best 2014-06-11 11:20:17 +02:00
commit cc79d38e69
18 changed files with 2187 additions and 1587 deletions

View File

@ -1,9 +1,9 @@
#Arduino MIDI Library v4.1 #Arduino MIDI Library v4.2
This library enables MIDI I/O communications on the Arduino serial ports. This library enables MIDI I/O communications on the Arduino serial ports.
The purpose of this library is not to make a big synthetizer out of an Arduino board, the application remains yours. However, it will help you interfacing it with other MIDI devices. The purpose of this library is not to make a big synthetizer out of an Arduino board, the application remains yours. However, it will help you interfacing it with other MIDI devices.
Download the latest version [here](https://github.com/FortySevenEffects/arduino_midi_library/releases/download/4.1/Arduino_MIDI_Library_v4.1.zip). Download the latest version [here](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest).
### Features ### Features
* Compatible with all Arduino boards (and clones with an AVR processor) * Compatible with all Arduino boards (and clones with an AVR processor)
@ -12,12 +12,13 @@ Download the latest version [here](https://github.com/FortySevenEffects/arduino_
* Software Thru, with message filtering. * Software Thru, with message filtering.
* [Callbacks](http://playground.arduino.cc/Main/MIDILibraryCallbacks) to handle input messages more easily. * [Callbacks](http://playground.arduino.cc/Main/MIDILibraryCallbacks) to handle input messages more easily.
* Last received message is saved until a new one arrives. * Last received message is saved until a new one arrives.
* Configurable: disable input or output if you don't need it, and get the pin back for IO use (and save some flash space). * Configurable: [overridable template-based settings](http://arduinomidilib.fortyseveneffects.com/a00013.html#details).
* Create more than one MIDI port for mergers/splitters applications. * Create more than one MIDI port for mergers/splitters applications.
* Use any serial port, hardware or software. * Use any serial port, hardware or software.
#### Changelog #### Changelog
* 11/06/2014 : Version 4.2 released. Bug fix for SysEx, overridable template settings.
* 16/04/2014 : Version 4.1 released. Bug fixes regarding running status. * 16/04/2014 : Version 4.1 released. Bug fixes regarding running status.
* 13/02/2014 : Version 4.0 released. Moved to GitHub, added multiple instances & software serial support, and a few bug fixes. * 13/02/2014 : Version 4.0 released. Moved to GitHub, added multiple instances & software serial support, and a few bug fixes.
* 29/01/2012 : Version 3.2 released. Release notes are [here](http://sourceforge.net/news/?group_id=265194) * 29/01/2012 : Version 3.2 released. Release notes are [here](http://sourceforge.net/news/?group_id=265194)
@ -26,7 +27,7 @@ Download the latest version [here](https://github.com/FortySevenEffects/arduino_
* 14/12/2009 : Version 2.5 released. * 14/12/2009 : Version 2.5 released.
* 28/07/2009 : Version 2.0 released. * 28/07/2009 : Version 2.0 released.
* 28/03/2009 : Simplified version of MIDI.begin, Fast mode is now on by default. * 28/03/2009 : Simplified version of MIDI.begin, Fast mode is now on by default.
* 08/03/2009 : Thru method operationnal. Added some features to enable thru. * 08/03/2009 : Thru method operational. Added some features to enable thru.
@ -35,9 +36,10 @@ Download the latest version [here](https://github.com/FortySevenEffects/arduino_
### What do I need to do? ### What do I need to do?
* Download the library ([link](https://github.com/FortySevenEffects/arduino_midi_library/releases/download/4.1/Arduino_MIDI_Library_v4.1.zip)) * Download the library ([link](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest))
* Follow the installation instructions there: http://arduino.cc/en/Guide/Libraries * Follow the installation instructions there: http://arduino.cc/en/Guide/Libraries
* Include the library in your sketch using the menu in the IDE, or type `#include <MIDI.h>` * Include the library in your sketch using the menu in the IDE, or type `#include <MIDI.h>`
* Create the MIDI instance using `MIDI_CREATE_DEFAULT_INSTANCE();` or take a look at the documentation for custom serial port, settings etc..
You are now ready to use the library. Look at the reference page to learn how to use it, or the examples given. Just don't forget to enable the I/O communications with MIDI.begin... You are now ready to use the library. Look at the reference page to learn how to use it, or the examples given. Just don't forget to enable the I/O communications with MIDI.begin...
@ -46,9 +48,6 @@ You are now ready to use the library. Look at the reference page to learn how to
See the extended reference [here](http://arduinomidilib.fortyseveneffects.com) ([Mirror](http://fortyseveneffects.github.io/arduino_midi_library/)). See the extended reference [here](http://arduinomidilib.fortyseveneffects.com) ([Mirror](http://fortyseveneffects.github.io/arduino_midi_library/)).
To know how to use the callback feature, see the dedicated page [here](http://playground.arduino.cc/Main/MIDILibraryCallbacks).
### Using MIDI.begin ### Using MIDI.begin
In the `setup()` function of the Arduino, you must call the `MIDI.begin()` method. If you don't give any argument to this method, the input channel for MIDI in will be set to 1 (channels are going from 1 to 16, plus `MIDI_CHANNEL_OMNI to listen to all channels at the same time). In the `setup()` function of the Arduino, you must call the `MIDI.begin()` method. If you don't give any argument to this method, the input channel for MIDI in will be set to 1 (channels are going from 1 to 16, plus `MIDI_CHANNEL_OMNI to listen to all channels at the same time).
@ -78,4 +77,3 @@ Take a look at [the MIDI.org schematic](http://www.midi.org/techspecs/electrispe
if you have any comment or support request to make, feel free to contact me: francois.best@fortyseveneffects.com if you have any comment or support request to make, feel free to contact me: francois.best@fortyseveneffects.com
You can also get informations about bug fixes and updates on my twitter account: [@fortysevenfx](http://twitter.com/fortysevenfx). You can also get informations about bug fixes and updates on my twitter account: [@fortysevenfx](http://twitter.com/fortysevenfx).

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,7 @@
\example MIDI_Bench.ino \example MIDI_Bench.ino
\example MIDI_DualMerger.ino \example MIDI_DualMerger.ino
\example MIDI_Input.ino \example MIDI_Input.ino
\example MIDI_SimpleSynth.ino
*/ */
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -1,9 +1,11 @@
#include <MIDI.h> #include <MIDI.h>
// Simple tutorial on how to receive and send MIDI messages. // Simple tutorial on how to receive and send MIDI messages.
// Here, when receiving any message on channel 4, the Arduino // Here, when receiving any message on channel 4, the Arduino
// will blink a led and play back a note for 1 second. // will blink a led and play back a note for 1 second.
MIDI_CREATE_DEFAULT_INSTANCE();
#define LED 13 // LED pin on Arduino Uno #define LED 13 // LED pin on Arduino Uno
void setup() void setup()

View File

@ -1,33 +1,39 @@
#include <MIDI.h> #include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------
// This function will be automatically called when a NoteOn is received. // This function will be automatically called when a NoteOn is received.
// It must be a void-returning function with the correct parameters, // It must be a void-returning function with the correct parameters,
// see documentation here: // see documentation here:
// http://arduinomidilib.fortyseveneffects.com/a00022.html // http://arduinomidilib.fortyseveneffects.com/a00022.html
void HandleNoteOn(byte channel, byte pitch, byte velocity) void handleNoteOn(byte channel, byte pitch, byte velocity)
{ {
// Do whatever you want when you receive a Note On. // Do whatever you want when a note is pressed.
if (velocity == 0)
{
// This acts like a NoteOff. You can ask the library to call the NoteOff
// callback when receiving null-velocity NoteOn messages.
// See MIDI_HANDLE_NULL_VELOCITY_NOTE_ON_AS_NOTE_OFF in midi_Settings.h
}
// Try to keep your callbacks short (no delays ect) // Try to keep your callbacks short (no delays ect)
// otherwise it would slow down the loop() and have a bad impact // otherwise it would slow down the loop() and have a bad impact
// on real-time performance. // on real-time performance.
} }
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Do something when the note is released.
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void setup() void setup()
{ {
// Connect the HandleNoteOn function to the library, // Connect the handleNoteOn function to the library,
// so it is called upon reception of a NoteOn. // so it is called upon reception of a NoteOn.
MIDI.setHandleNoteOn(HandleNoteOn); // Put only the name of the function MIDI.setHandleNoteOn(handleNoteOn); // Put only the name of the function
// Do the same for NoteOffs
MIDI.setHandleNoteOff(handleNoteOff);
// Initiate MIDI communications, listen to all channels // Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI); MIDI.begin(MIDI_CHANNEL_OMNI);

View File

@ -1,5 +1,9 @@
#include <MIDI.h> #include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------
// This example shows the old way of checking for input messages. // This example shows the old way of checking for input messages.
// It's simpler to use the callbacks now, check out the dedicated example. // It's simpler to use the callbacks now, check out the dedicated example.
@ -8,7 +12,7 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void BlinkLed(byte num) // Basic blink function void BlinkLed(byte num) // Basic blink function
{ {
for (byte i=0;i<num;i++) for (byte i=0;i<num;i++)
{ {
digitalWrite(LED,HIGH); digitalWrite(LED,HIGH);
@ -29,12 +33,12 @@ void setup()
void loop() void loop()
{ {
if (MIDI.read()) // Is there a MIDI message incoming ? if (MIDI.read()) // Is there a MIDI message incoming ?
{ {
switch(MIDI.getType()) // Get the type of the message we caught switch(MIDI.getType()) // Get the type of the message we caught
{ {
case midi::ProgramChange: // If it is a Program Change, case midi::ProgramChange: // If it is a Program Change,
BlinkLed(MIDI.getData1()); // blink the LED a number of times BlinkLed(MIDI.getData1()); // blink the LED a number of times
// correponding to the program number // correponding to the program number
// (0 to 127, it can last a while..) // (0 to 127, it can last a while..)
break; break;
// See the online reference for other message types // See the online reference for other message types

View File

@ -2,6 +2,8 @@
#include "noteList.h" #include "noteList.h"
#include "pitches.h" #include "pitches.h"
MIDI_CREATE_DEFAULT_INSTANCE();
#ifdef ARDUINO_SAM_DUE // Due has no tone function (yet), overriden to prevent build errors. #ifdef ARDUINO_SAM_DUE // Due has no tone function (yet), overriden to prevent build errors.
#define tone(...) #define tone(...)
#define noTone(...) #define noTone(...)
@ -18,12 +20,12 @@ MidiNoteList<sMaxNumNotes> midiNotes;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void handleGateChanged(bool inGateActive) inline void handleGateChanged(bool inGateActive)
{ {
digitalWrite(sGatePin, inGateActive ? HIGH : LOW); digitalWrite(sGatePin, inGateActive ? HIGH : LOW);
} }
void pulseGate() inline void pulseGate()
{ {
handleGateChanged(false); handleGateChanged(false);
delay(1); delay(1);
@ -32,7 +34,7 @@ void pulseGate()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void handleNotesChanged() void handleNotesChanged(bool isFirstNote = false)
{ {
if (midiNotes.empty()) if (midiNotes.empty())
{ {
@ -41,11 +43,24 @@ void handleNotesChanged()
} }
else else
{ {
// Possible playing modes:
// Mono Low: use midiNotes.getLow
// Mono High: use midiNotes.getHigh
// Mono Last: use midiNotes.getLast
byte currentNote = 0; byte currentNote = 0;
if (midiNotes.getTail(currentNote)) if (midiNotes.getLast(currentNote))
{ {
tone(sAudioOutPin, sNotePitches[currentNote]); tone(sAudioOutPin, sNotePitches[currentNote]);
pulseGate(); // Retrigger envelopes. Remove for legato effect.
if (isFirstNote)
{
handleGateChanged(true);
}
else
{
pulseGate(); // Retrigger envelopes. Remove for legato effect.
}
} }
} }
} }
@ -54,8 +69,9 @@ void handleNotesChanged()
void handleNoteOn(byte inChannel, byte inNote, byte inVelocity) void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{ {
const bool firstNote = midiNotes.empty();
midiNotes.add(MidiNote(inNote, inVelocity)); midiNotes.add(MidiNote(inNote, inVelocity));
handleNotesChanged(); handleNotesChanged(firstNote);
} }
void handleNoteOff(byte inChannel, byte inNote, byte inVelocity) void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
@ -67,7 +83,7 @@ void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void setup() void setup()
{ {
pinMode(sGatePin, OUTPUT); pinMode(sGatePin, OUTPUT);
pinMode(sAudioOutPin, OUTPUT); pinMode(sAudioOutPin, OUTPUT);
MIDI.setHandleNoteOn(handleNoteOn); MIDI.setHandleNoteOn(handleNoteOn);

View File

@ -1,7 +1,8 @@
/*! /*!
* \file synth-core_NoteList.h * \file noteList.h
* \author Francois Best * \author Francois Best
* \date 24/05/2013 * \date 24/05/2013
* \brief Linked list of notes, for Low, Last & High playing modes.
* \license GPL v3.0 - Copyright Forty Seven Effects 2013 * \license GPL v3.0 - Copyright Forty Seven Effects 2013
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -65,7 +66,9 @@ public:
public: public:
inline bool get(byte inIndex, byte& outPitch) const; inline bool get(byte inIndex, byte& outPitch) const;
inline bool getTail(byte& outPitch) const; inline bool getLast(byte& outPitch) const;
inline bool getHigh(byte& outPitch) const;
inline bool getLow(byte& outPitch) const;
public: public:
inline bool empty() const; inline bool empty() const;
@ -105,7 +108,7 @@ inline MidiNote::MidiNote(const MidiNote& inOther)
inline MidiNote& MidiNote::operator= (const MidiNote& inOther) inline MidiNote& MidiNote::operator= (const MidiNote& inOther)
{ {
pitch = inOther.pitch; pitch = inOther.pitch;
velocity = inOther.velocity; velocity = inOther.velocity;
return *this; return *this;
} }
@ -145,8 +148,6 @@ inline typename MidiNoteList<Size>::Cell& MidiNoteList<Size>::Cell::operator= (c
template<byte Size> template<byte Size>
inline MidiNoteList<Size>::MidiNoteList() inline MidiNoteList<Size>::MidiNoteList()
{ {
// Check that size is a power of two
//AVR_STATIC_ASSERT(Size != 0 && !(Size & (Size - 1)));
} }
template<byte Size> template<byte Size>
@ -156,6 +157,10 @@ inline MidiNoteList<Size>::~MidiNoteList()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! \brief Add a note, sorting it by time.
Call this when receiving a NoteOn event. This will add the new note as the tail
of the list.
*/
template<byte Size> template<byte Size>
inline void MidiNoteList<Size>::add(const MidiNote& inNote) inline void MidiNoteList<Size>::add(const MidiNote& inNote)
{ {
@ -186,12 +191,15 @@ inline void MidiNoteList<Size>::add(const MidiNote& inNote)
print(); print();
} }
/*! \brief Remove a note
Call this when receiving a NoteOff event.
*/
template<byte Size> template<byte Size>
inline void MidiNoteList<Size>::remove(byte inPitch) inline void MidiNoteList<Size>::remove(byte inPitch)
{ {
if (mHead != 0) if (mTail != 0)
{ {
for (Cell* it = mHead; it != 0; it = it->next) for (Cell* it = mTail; it != 0; it = it->prev)
{ {
if (it->note.pitch == inPitch) if (it->note.pitch == inPitch)
{ {
@ -235,6 +243,9 @@ inline void MidiNoteList<Size>::remove(byte inPitch)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! \brief Get a note at an arbitrary position
This can be interesting for duo/multi/polyphony operations.
*/
template<byte Size> template<byte Size>
inline bool MidiNoteList<Size>::get(byte inIndex, byte& outPitch) const inline bool MidiNoteList<Size>::get(byte inIndex, byte& outPitch) const
{ {
@ -258,15 +269,75 @@ inline bool MidiNoteList<Size>::get(byte inIndex, byte& outPitch) const
return false; return false;
} }
/*! \brief Get the last active note played
This implements the Mono Last playing mode.
*/
template<byte Size> template<byte Size>
inline bool MidiNoteList<Size>::getTail(byte& outPitch) const inline bool MidiNoteList<Size>::getLast(byte& outPitch) const
{ {
if (mTail) if (!mTail)
{ {
outPitch = mTail->note.pitch; return false;
return true;
} }
return false;
outPitch = mTail->note.pitch;
return true;
}
/*! \brief Get the highest pitched active note
This implements the Mono High playing mode.
*/
template<byte Size>
inline bool MidiNoteList<Size>::getHigh(byte& outPitch) const
{
if (!mTail)
{
return false;
}
outPitch = 0;
const Cell* it = mTail;
for (byte i = 0; i < mSize; ++i)
{
if (it->note.pitch > outPitch)
{
outPitch = it->note.pitch;
}
if (it->prev)
{
it = it->prev;
}
}
return true;
}
/*! \brief Get the lowest pitched active note
This implements the Mono Low playing mode.
*/
template<byte Size>
inline bool MidiNoteList<Size>::getLow(byte& outPitch) const
{
if (!mTail)
{
return false;
}
outPitch = 0xff;
const Cell* it = mTail;
for (byte i = 0; i < mSize; ++i)
{
if (it->note.pitch < outPitch)
{
outPitch = it->note.pitch;
}
if (it->prev)
{
it = it->prev;
}
}
return true;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -277,6 +348,8 @@ inline bool MidiNoteList<Size>::empty() const
return mSize == 0; return mSize == 0;
} }
/*! \brief Get the number of active notes.
*/
template<byte Size> template<byte Size>
inline byte MidiNoteList<Size>::size() const inline byte MidiNoteList<Size>::size() const
{ {
@ -284,6 +357,7 @@ inline byte MidiNoteList<Size>::size() const
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Private implementations, for internal use only.
template<byte Size> template<byte Size>
inline typename MidiNoteList<Size>::Cell* MidiNoteList<Size>::getFirstEmptyCell() inline typename MidiNoteList<Size>::Cell* MidiNoteList<Size>::getFirstEmptyCell()

View File

@ -9,6 +9,7 @@
MIDI KEYWORD1 MIDI KEYWORD1
MIDI.h KEYWORD1 MIDI.h KEYWORD1
MidiInterface KEYWORD1 MidiInterface KEYWORD1
DefaultSettings KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
@ -106,7 +107,6 @@ SameChannel LITERAL1
DifferentChannel LITERAL1 DifferentChannel LITERAL1
MIDI_CHANNEL_OMNI LITERAL1 MIDI_CHANNEL_OMNI LITERAL1
MIDI_CHANNEL_OFF LITERAL1 MIDI_CHANNEL_OFF LITERAL1
MIDI_BAUDRATE LITERAL1 MIDI_CREATE_INSTANCE LITERAL1
MIDI_SYSEX_ARRAY_SIZE LITERAL1 MIDI_CREATE_DEFAULT_INSTANCE LITERAL1
MIDI_CREATE_INSTANCE LITERAL1 MIDI_CREATE_CUSTOM_INSTANCE LITERAL1
MIDI_AUTO_INSTANCIATE LITERAL1

View File

@ -12,8 +12,8 @@ cd "`dirname "${0}"`"
root="${PWD}/.." root="${PWD}/.."
build="$root/build/MIDI" build="$root/build/MIDI"
echo root $root echo "root: $root"
echo build $build echo "build: $build"
# Create a temporary destination folder # Create a temporary destination folder
mkdir -p "$build" mkdir -p "$build"
@ -34,3 +34,7 @@ cp -r * "$build/examples"
# Generate package # Generate package
cd "$build/.." cd "$build/.."
zip -r Arduino_MIDI_Library.zip MIDI zip -r Arduino_MIDI_Library.zip MIDI
# Generate doc
cd "$root/doc"
doxygen

View File

@ -4,6 +4,7 @@ import sys
import os import os
import shutil import shutil
import subprocess import subprocess
import argparse
from pprint import pprint from pprint import pprint
from midi import * from midi import *
from tester import * from tester import *
@ -138,28 +139,42 @@ class ArduinoMidiLibrary:
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
def main(): def main():
midiInterface = MidiInterface()
tester = Tester(midiInterface)
midiInterface.listenerCallback = tester.handleMidiInput
tester.checkThru([Midi.NoteOn, 64, 80]) info = "Validator script for the Arduino MIDI Library."
tester.checkThru([Midi.AfterTouchChannel, 1]) arg_parser = argparse.ArgumentParser(description = info)
tester.checkThru([2])
tester.checkThru([3])
tester.checkThru([Midi.NoteOn, 64, 0])
tester.checkThru([65, 127])
tester.checkThru([65, 0])
tester.checkThru([66, 127])
tester.checkThru([66, 0])
#lib = ArduinoMidiLibrary() arg_parser.add_argument('--compile', '-c',
#lib.install() action="store_true",
#if lib.validate(): help="Test compilation of the example sketches")
# print('Validation passed')
#else:
# print('Validation failed')
arg_parser.add_argument('--runtime', '-r',
action="store_true",
help="Test runtime")
args = arg_parser.parse_args()
if args.compile:
lib = ArduinoMidiLibrary()
lib.install()
if lib.validate():
print('Compilation test passed')
else:
print('Compilation test failed')
if args.runtime:
midiInterface = MidiInterface()
tester = Tester(midiInterface)
midiInterface.listenerCallback = tester.handleMidiInput
tester.checkThru([Midi.NoteOn, 64, 80])
tester.checkThru([Midi.AfterTouchChannel, 1])
tester.checkThru([2])
tester.checkThru([3])
tester.checkThru([Midi.NoteOn, 64, 0])
tester.checkThru([65, 127])
tester.checkThru([65, 0])
tester.checkThru([66, 127])
tester.checkThru([66, 0])
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -2,7 +2,7 @@
* @file MIDI.cpp * @file MIDI.cpp
* Project Arduino MIDI Library * Project Arduino MIDI Library
* @brief MIDI Library for the Arduino * @brief MIDI Library for the Arduino
* @version 4.1 * @version 4.2
* @author Francois Best * @author Francois Best
* @date 24/02/11 * @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014 * @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -25,27 +25,6 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#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 && defined(ARDUINO)
MIDI_CREATE_INSTANCE(MIDI_DEFAULT_SERIAL_CLASS,
MIDI_DEFAULT_SERIAL_PORT,
MIDI);
#endif
// -----------------------------------------------------------------------------
BEGIN_MIDI_NAMESPACE BEGIN_MIDI_NAMESPACE
/*! \brief Encode System Exclusive messages. /*! \brief Encode System Exclusive messages.

View File

@ -2,7 +2,7 @@
* @file MIDI.h * @file MIDI.h
* Project Arduino MIDI Library * Project Arduino MIDI Library
* @brief MIDI Library for the Arduino * @brief MIDI Library for the Arduino
* @version 4.1 * @version 4.2
* @author Francois Best * @author Francois Best
* @date 24/02/11 * @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014 * @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -23,12 +23,9 @@
#pragma once #pragma once
#include "midi_Settings.h"
#include "midi_Defs.h" #include "midi_Defs.h"
#include "midi_Settings.h"
#ifdef FSE_AVR #include "midi_Message.h"
#include <ak47.h>
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -40,12 +37,12 @@ the hardware interface, meaning you can use HardwareSerial, SoftwareSerial
or ak47's Uart classes. The only requirement is that the class implements or ak47's Uart classes. The only requirement is that the class implements
the begin, read, write and available methods. the begin, read, write and available methods.
*/ */
template<class SerialPort> template<class SerialPort, class Settings = DefaultSettings>
class MidiInterface class MidiInterface
{ {
public: public:
MidiInterface(SerialPort& inSerial); inline MidiInterface(SerialPort& inSerial);
~MidiInterface(); inline ~MidiInterface();
public: public:
void begin(Channel inChannel = 1); void begin(Channel inChannel = 1);
@ -53,8 +50,6 @@ public:
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// MIDI Output // MIDI Output
#if MIDI_BUILD_OUTPUT
public: public:
inline void sendNoteOn(DataByte inNoteNumber, inline void sendNoteOn(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
@ -100,17 +95,9 @@ public:
DataByte inData2, DataByte inData2,
Channel inChannel); Channel inChannel);
private:
inline StatusByte getStatus(MidiType inType,
Channel inChannel) const;
#endif // MIDI_BUILD_OUTPUT
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// MIDI Input // MIDI Input
#if MIDI_BUILD_INPUT
public: public:
inline bool read(); inline bool read();
inline bool read(Channel inChannel); inline bool read(Channel inChannel);
@ -133,25 +120,10 @@ public:
static inline Channel getChannelFromStatusByte(byte inStatus); static inline Channel getChannelFromStatusByte(byte inStatus);
static inline bool isChannelMessage(MidiType inType); static inline bool isChannelMessage(MidiType inType);
private:
bool parse();
inline void handleNullVelocityNoteOnAsNoteOff();
inline bool inputFilter(Channel inChannel);
inline void resetInput();
private:
StatusByte mRunningStatus_RX;
Channel mInputChannel;
byte mPendingMessage[3];
unsigned mPendingMessageExpectedLenght;
unsigned mPendingMessageIndex;
Message mMessage;
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Input Callbacks // Input Callbacks
#if MIDI_USE_CALLBACKS
public: public:
inline void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)); inline void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity));
inline void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)); inline void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity));
@ -160,7 +132,7 @@ public:
inline void setHandleProgramChange(void (*fptr)(byte channel, byte number)); inline void setHandleProgramChange(void (*fptr)(byte channel, byte number));
inline void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)); inline void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure));
inline void setHandlePitchBend(void (*fptr)(byte channel, int bend)); inline void setHandlePitchBend(void (*fptr)(byte channel, int bend));
inline void setHandleSystemExclusive(void (*fptr)(byte * array, byte size)); inline void setHandleSystemExclusive(void (*fptr)(byte * array, unsigned size));
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)); inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
inline void setHandleSongPosition(void (*fptr)(unsigned beats)); inline void setHandleSongPosition(void (*fptr)(unsigned beats));
inline void setHandleSongSelect(void (*fptr)(byte songnumber)); inline void setHandleSongSelect(void (*fptr)(byte songnumber));
@ -184,7 +156,7 @@ private:
void (*mProgramChangeCallback)(byte channel, byte); void (*mProgramChangeCallback)(byte channel, byte);
void (*mAfterTouchChannelCallback)(byte channel, byte); void (*mAfterTouchChannelCallback)(byte channel, byte);
void (*mPitchBendCallback)(byte channel, int); void (*mPitchBendCallback)(byte channel, int);
void (*mSystemExclusiveCallback)(byte * array, byte size); void (*mSystemExclusiveCallback)(byte * array, unsigned size);
void (*mTimeCodeQuarterFrameCallback)(byte data); void (*mTimeCodeQuarterFrameCallback)(byte data);
void (*mSongPositionCallback)(unsigned beats); void (*mSongPositionCallback)(unsigned beats);
void (*mSongSelectCallback)(byte songnumber); void (*mSongSelectCallback)(byte songnumber);
@ -196,14 +168,9 @@ private:
void (*mActiveSensingCallback)(void); void (*mActiveSensingCallback)(void);
void (*mSystemResetCallback)(void); void (*mSystemResetCallback)(void);
#endif // MIDI_USE_CALLBACKS
#endif // MIDI_BUILD_INPUT
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// MIDI Soft Thru // MIDI Soft Thru
#if MIDI_BUILD_THRU
public: public:
inline MidiFilterMode getFilterMode() const; inline MidiFilterMode getFilterMode() const;
inline bool getThruState() const; inline bool getThruState() const;
@ -212,21 +179,34 @@ public:
inline void turnThruOff(); inline void turnThruOff();
inline void setThruFilterMode(MidiFilterMode inThruFilterMode); inline void setThruFilterMode(MidiFilterMode inThruFilterMode);
private: private:
void thruFilter(byte inChannel); void thruFilter(byte inChannel);
private:
bool parse();
inline void handleNullVelocityNoteOnAsNoteOff();
inline bool inputFilter(Channel inChannel);
inline void resetInput();
private: private:
bool mThruActivated : 1; bool mThruActivated : 1;
MidiFilterMode mThruFilterMode : 7; MidiFilterMode mThruFilterMode : 7;
#endif // MIDI_BUILD_THRU
#if MIDI_USE_RUNNING_STATUS
private: private:
StatusByte mRunningStatus_TX; typedef Message<Settings::SysExMaxSize> MidiMessage;
#endif
private:
StatusByte mRunningStatus_RX;
StatusByte mRunningStatus_TX;
Channel mInputChannel;
byte mPendingMessage[3];
unsigned mPendingMessageExpectedLenght;
unsigned mPendingMessageIndex;
MidiMessage mMessage;
private:
inline StatusByte getStatus(MidiType inType,
Channel inChannel) const;
private: private:
SerialPort& mSerial; SerialPort& mSerial;
@ -241,10 +221,4 @@ END_MIDI_NAMESPACE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MIDI_AUTO_INSTANCIATE && defined(ARDUINO)
extern MIDI_NAMESPACE::MidiInterface<MIDI_DEFAULT_SERIAL_CLASS> MIDI;
#endif
// -----------------------------------------------------------------------------
#include "MIDI.hpp" #include "MIDI.hpp"

View File

@ -1,8 +1,8 @@
/*! /*!
* @file midi_Inline.hpp * @file MIDI.hpp
* Project Arduino MIDI Library * Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Inline implementations * @brief MIDI Library for the Arduino - Inline implementations
* @version 4.1 * @version 4.2
* @author Francois Best * @author Francois Best
* @date 24/02/11 * @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014 * @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -26,11 +26,10 @@
BEGIN_MIDI_NAMESPACE BEGIN_MIDI_NAMESPACE
/// \brief Constructor for MidiInterface. /// \brief Constructor for MidiInterface.
template<class SerialPort> template<class SerialPort, class Settings>
MidiInterface<SerialPort>::MidiInterface(SerialPort& inSerial) inline MidiInterface<SerialPort, Settings>::MidiInterface(SerialPort& inSerial)
: mSerial(inSerial) : mSerial(inSerial)
{ {
#if MIDI_BUILD_INPUT && MIDI_USE_CALLBACKS
mNoteOffCallback = 0; mNoteOffCallback = 0;
mNoteOnCallback = 0; mNoteOnCallback = 0;
mAfterTouchPolyCallback = 0; mAfterTouchPolyCallback = 0;
@ -49,15 +48,14 @@ MidiInterface<SerialPort>::MidiInterface(SerialPort& inSerial)
mStopCallback = 0; mStopCallback = 0;
mActiveSensingCallback = 0; mActiveSensingCallback = 0;
mSystemResetCallback = 0; mSystemResetCallback = 0;
#endif
} }
/*! \brief Destructor for MidiInterface. /*! \brief Destructor for MidiInterface.
This is not really useful for the Arduino, as it is never called... This is not really useful for the Arduino, as it is never called...
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
MidiInterface<SerialPort>::~MidiInterface() inline MidiInterface<SerialPort, Settings>::~MidiInterface()
{ {
} }
@ -69,27 +67,20 @@ MidiInterface<SerialPort>::~MidiInterface()
- Input channel set to 1 if no value is specified - Input channel set to 1 if no value is specified
- Full thru mirroring - Full thru mirroring
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::begin(Channel inChannel) void MidiInterface<SerialPort, Settings>::begin(Channel inChannel)
{ {
// Initialise the Serial port // Initialise the Serial port
#if defined(ARDUINO) #if defined(FSE_AVR)
mSerial.begin(MIDI_BAUDRATE); mSerial. template open<Settings::BaudRate>();
#elif defined(FSE_AVR) #else
mSerial. template open<MIDI_BAUDRATE>(); mSerial.begin(Settings::BaudRate);
#endif #endif
#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; mInputChannel = inChannel;
mRunningStatus_TX = InvalidType;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0; mPendingMessageExpectedLenght = 0;
@ -99,25 +90,14 @@ void MidiInterface<SerialPort>::begin(Channel inChannel)
mMessage.data1 = 0; mMessage.data1 = 0;
mMessage.data2 = 0; mMessage.data2 = 0;
#endif // MIDI_BUILD_INPUT
#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru
mThruFilterMode = Full; mThruFilterMode = Full;
mThruActivated = true; mThruActivated = true;
#endif // Thru
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Output // Output
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MIDI_BUILD_OUTPUT
/*! \addtogroup output /*! \addtogroup output
@{ @{
*/ */
@ -133,22 +113,21 @@ void MidiInterface<SerialPort>::begin(Channel inChannel)
This is an internal method, use it only if you need to send raw data This is an internal method, use it only if you need to send raw data
from your code, at your own risks. from your code, at your own risks.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::send(MidiType inType, void MidiInterface<SerialPort, Settings>::send(MidiType inType,
DataByte inData1, DataByte inData1,
DataByte inData2, DataByte inData2,
Channel inChannel) Channel inChannel)
{ {
// Then test if channel is valid // Then test if channel is valid
if (inChannel >= MIDI_CHANNEL_OFF || if (inChannel >= MIDI_CHANNEL_OFF ||
inChannel == MIDI_CHANNEL_OMNI || inChannel == MIDI_CHANNEL_OMNI ||
inType < NoteOff) inType < 0x80)
{ {
if (Settings::UseRunningStatus)
#if MIDI_USE_RUNNING_STATUS {
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif }
return; // Don't send anything return; // Don't send anything
} }
@ -160,28 +139,32 @@ void MidiInterface<SerialPort>::send(MidiType inType,
const StatusByte status = getStatus(inType, inChannel); const StatusByte status = getStatus(inType, inChannel);
#if MIDI_USE_RUNNING_STATUS if (Settings::UseRunningStatus)
// Check Running Status
if (mRunningStatus_TX != status)
{ {
// New message, memorise and send header if (mRunningStatus_TX != status)
mRunningStatus_TX = status; {
mSerial.write(mRunningStatus_TX); // New message, memorise and send header
mRunningStatus_TX = status;
mSerial.write(mRunningStatus_TX);
}
}
else
{
// Don't care about running status, send the status byte.
mSerial.write(status);
} }
#else
// Don't care about running status, send the status byte.
mSerial.write(status);
#endif
// Then send data // Then send data
mSerial.write(inData1); mSerial.write(inData1);
if (inType != ProgramChange && inType != AfterTouchChannel) if (inType != ProgramChange && inType != AfterTouchChannel)
{
mSerial.write(inData2); mSerial.write(inData2);
}
return;
} }
else if (inType >= TuneRequest && inType <= SystemReset) else if (inType >= TuneRequest && inType <= SystemReset)
{
sendRealTime(inType); // System Real-time and 1 byte. sendRealTime(inType); // System Real-time and 1 byte.
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -195,10 +178,10 @@ void MidiInterface<SerialPort>::send(MidiType inType,
Take a look at the values, names and frequencies of notes here: Take a look at the values, names and frequencies of notes here:
http://www.phys.unsw.edu.au/jw/notes.html http://www.phys.unsw.edu.au/jw/notes.html
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendNoteOn(DataByte inNoteNumber, void MidiInterface<SerialPort, Settings>::sendNoteOn(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
Channel inChannel) Channel inChannel)
{ {
send(NoteOn, inNoteNumber, inVelocity, inChannel); send(NoteOn, inNoteNumber, inVelocity, inChannel);
} }
@ -210,14 +193,14 @@ void MidiInterface<SerialPort>::sendNoteOn(DataByte inNoteNumber,
Note: you can send NoteOn with zero velocity to make a NoteOff, this is based 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 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. sending only NoteOn data. sendNoteOff will always send a real NoteOff message.
Take a look at the values, names and frequencies of notes here: Take a look at the values, names and frequencies of notes here:
http://www.phys.unsw.edu.au/jw/notes.html http://www.phys.unsw.edu.au/jw/notes.html
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendNoteOff(DataByte inNoteNumber, void MidiInterface<SerialPort, Settings>::sendNoteOff(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
Channel inChannel) Channel inChannel)
{ {
send(NoteOff, inNoteNumber, inVelocity, inChannel); send(NoteOff, inNoteNumber, inVelocity, inChannel);
} }
@ -226,9 +209,9 @@ void MidiInterface<SerialPort>::sendNoteOff(DataByte inNoteNumber,
\param inProgramNumber The Program to select (0 to 127). \param inProgramNumber The Program to select (0 to 127).
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendProgramChange(DataByte inProgramNumber, void MidiInterface<SerialPort, Settings>::sendProgramChange(DataByte inProgramNumber,
Channel inChannel) Channel inChannel)
{ {
send(ProgramChange, inProgramNumber, 0, inChannel); send(ProgramChange, inProgramNumber, 0, inChannel);
} }
@ -239,10 +222,10 @@ void MidiInterface<SerialPort>::sendProgramChange(DataByte inProgramNumber,
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
@see MidiControlChangeNumber @see MidiControlChangeNumber
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendControlChange(DataByte inControlNumber, void MidiInterface<SerialPort, Settings>::sendControlChange(DataByte inControlNumber,
DataByte inControlValue, DataByte inControlValue,
Channel inChannel) Channel inChannel)
{ {
send(ControlChange, inControlNumber, inControlValue, inChannel); send(ControlChange, inControlNumber, inControlValue, inChannel);
} }
@ -252,10 +235,10 @@ void MidiInterface<SerialPort>::sendControlChange(DataByte inControlNumber,
\param inPressure The amount of AfterTouch to apply (0 to 127). \param inPressure The amount of AfterTouch to apply (0 to 127).
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendPolyPressure(DataByte inNoteNumber, void MidiInterface<SerialPort, Settings>::sendPolyPressure(DataByte inNoteNumber,
DataByte inPressure, DataByte inPressure,
Channel inChannel) Channel inChannel)
{ {
send(AfterTouchPoly, inNoteNumber, inPressure, inChannel); send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
} }
@ -264,9 +247,9 @@ void MidiInterface<SerialPort>::sendPolyPressure(DataByte inNoteNumber,
\param inPressure The amount of AfterTouch to apply to all notes. \param inPressure The amount of AfterTouch to apply to all notes.
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendAfterTouch(DataByte inPressure, void MidiInterface<SerialPort, Settings>::sendAfterTouch(DataByte inPressure,
Channel inChannel) Channel inChannel)
{ {
send(AfterTouchChannel, inPressure, 0, inChannel); send(AfterTouchChannel, inPressure, 0, inChannel);
} }
@ -277,9 +260,9 @@ void MidiInterface<SerialPort>::sendAfterTouch(DataByte inPressure,
center value is 0. center value is 0.
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendPitchBend(int inPitchValue, void MidiInterface<SerialPort, Settings>::sendPitchBend(int inPitchValue,
Channel inChannel) Channel inChannel)
{ {
const unsigned bend = inPitchValue - MIDI_PITCHBEND_MIN; const unsigned bend = inPitchValue - MIDI_PITCHBEND_MIN;
send(PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel); send(PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel);
@ -292,11 +275,11 @@ void MidiInterface<SerialPort>::sendPitchBend(int inPitchValue,
and +1.0f (max upwards bend), center value is 0.0f. and +1.0f (max upwards bend), center value is 0.0f.
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendPitchBend(double inPitchValue, void MidiInterface<SerialPort, Settings>::sendPitchBend(double inPitchValue,
Channel inChannel) Channel inChannel)
{ {
const int value = inPitchValue * MIDI_PITCHBEND_MAX; const int value = inPitchValue * MIDI_PITCHBEND_MAX * Settings::Toto;
sendPitchBend(value, inChannel); sendPitchBend(value, inChannel);
} }
@ -309,29 +292,32 @@ void MidiInterface<SerialPort>::sendPitchBend(double inPitchValue,
default value for ArrayContainsBoundaries is set to 'false' for compatibility default value for ArrayContainsBoundaries is set to 'false' for compatibility
with previous versions of the library. with previous versions of the library.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendSysEx(unsigned inLength, void MidiInterface<SerialPort, Settings>::sendSysEx(unsigned inLength,
const byte* inArray, const byte* inArray,
bool inArrayContainsBoundaries) bool inArrayContainsBoundaries)
{ {
if (inArrayContainsBoundaries == false) const bool writeBeginEndBytes = !inArrayContainsBoundaries;
if (writeBeginEndBytes)
{ {
mSerial.write(0xf0); mSerial.write(0xf0);
}
for (unsigned i = 0; i < inLength; ++i) for (unsigned i = 0; i < inLength; ++i)
mSerial.write(inArray[i]); {
mSerial.write(inArray[i]);
}
if (writeBeginEndBytes)
{
mSerial.write(0xf7); mSerial.write(0xf7);
} }
else
{
for (unsigned i = 0; i < inLength; ++i)
mSerial.write(inArray[i]);
}
#if MIDI_USE_RUNNING_STATUS if (Settings::UseRunningStatus)
mRunningStatus_TX = InvalidType; {
#endif mRunningStatus_TX = InvalidType;
}
} }
/*! \brief Send a Tune Request message. /*! \brief Send a Tune Request message.
@ -339,8 +325,8 @@ void MidiInterface<SerialPort>::sendSysEx(unsigned inLength,
When a MIDI unit receives this message, When a MIDI unit receives this message,
it should tune its oscillators (if equipped with any). it should tune its oscillators (if equipped with any).
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendTuneRequest() void MidiInterface<SerialPort, Settings>::sendTuneRequest()
{ {
sendRealTime(TuneRequest); sendRealTime(TuneRequest);
} }
@ -351,9 +337,9 @@ void MidiInterface<SerialPort>::sendTuneRequest()
\param inValuesNibble MTC data \param inValuesNibble MTC data
See MIDI Specification for more information. See MIDI Specification for more information.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendTimeCodeQuarterFrame(DataByte inTypeNibble, void MidiInterface<SerialPort, Settings>::sendTimeCodeQuarterFrame(DataByte inTypeNibble,
DataByte inValuesNibble) DataByte inValuesNibble)
{ {
const byte data = (((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f)); const byte data = (((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f));
sendTimeCodeQuarterFrame(data); sendTimeCodeQuarterFrame(data);
@ -365,42 +351,45 @@ void MidiInterface<SerialPort>::sendTimeCodeQuarterFrame(DataByte inTypeNibble,
\param inData if you want to encode directly the nibbles in your program, \param inData if you want to encode directly the nibbles in your program,
you can send the byte here. you can send the byte here.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendTimeCodeQuarterFrame(DataByte inData) void MidiInterface<SerialPort, Settings>::sendTimeCodeQuarterFrame(DataByte inData)
{ {
mSerial.write((byte)TimeCodeQuarterFrame); mSerial.write((byte)TimeCodeQuarterFrame);
mSerial.write(inData); mSerial.write(inData);
#if MIDI_USE_RUNNING_STATUS if (Settings::UseRunningStatus)
mRunningStatus_TX = InvalidType; {
#endif mRunningStatus_TX = InvalidType;
}
} }
/*! \brief Send a Song Position Pointer message. /*! \brief Send a Song Position Pointer message.
\param inBeats The number of beats since the start of the song. \param inBeats The number of beats since the start of the song.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendSongPosition(unsigned inBeats) void MidiInterface<SerialPort, Settings>::sendSongPosition(unsigned inBeats)
{ {
mSerial.write((byte)SongPosition); mSerial.write((byte)SongPosition);
mSerial.write(inBeats & 0x7f); mSerial.write(inBeats & 0x7f);
mSerial.write((inBeats >> 7) & 0x7f); mSerial.write((inBeats >> 7) & 0x7f);
#if MIDI_USE_RUNNING_STATUS if (Settings::UseRunningStatus)
mRunningStatus_TX = InvalidType; {
#endif mRunningStatus_TX = InvalidType;
}
} }
/*! \brief Send a Song Select message */ /*! \brief Send a Song Select message */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendSongSelect(DataByte inSongNumber) void MidiInterface<SerialPort, Settings>::sendSongSelect(DataByte inSongNumber)
{ {
mSerial.write((byte)SongSelect); mSerial.write((byte)SongSelect);
mSerial.write(inSongNumber & 0x7f); mSerial.write(inSongNumber & 0x7f);
#if MIDI_USE_RUNNING_STATUS if (Settings::UseRunningStatus)
mRunningStatus_TX = InvalidType; {
#endif mRunningStatus_TX = InvalidType;
}
} }
/*! \brief Send a Real Time (one byte) message. /*! \brief Send a Real Time (one byte) message.
@ -410,8 +399,8 @@ void MidiInterface<SerialPort>::sendSongSelect(DataByte inSongNumber)
You can also send a Tune Request with this method. You can also send a Tune Request with this method.
@see MidiType @see MidiType
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::sendRealTime(MidiType inType) void MidiInterface<SerialPort, Settings>::sendRealTime(MidiType inType)
{ {
switch (inType) switch (inType)
{ {
@ -432,31 +421,27 @@ void MidiInterface<SerialPort>::sendRealTime(MidiType inType)
// Do not cancel Running Status for real-time messages as they can be // Do not cancel Running Status for real-time messages as they can be
// interleaved within any message. Though, TuneRequest can be sent here, // interleaved within any message. Though, TuneRequest can be sent here,
// and as it is a System Common message, it must reset Running Status. // and as it is a System Common message, it must reset Running Status.
#if MIDI_USE_RUNNING_STATUS if (Settings::UseRunningStatus && inType == TuneRequest)
if (inType == TuneRequest) mRunningStatus_TX = InvalidType; {
#endif mRunningStatus_TX = InvalidType;
}
} }
/*! @} */ // End of doc group MIDI Output /*! @} */ // End of doc group MIDI Output
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
template<class SerialPort> template<class SerialPort, class Settings>
StatusByte MidiInterface<SerialPort>::getStatus(MidiType inType, StatusByte MidiInterface<SerialPort, Settings>::getStatus(MidiType inType,
Channel inChannel) const Channel inChannel) const
{ {
return ((byte)inType | ((inChannel - 1) & 0x0f)); return ((byte)inType | ((inChannel - 1) & 0x0f));
} }
#endif // MIDI_BUILD_OUTPUT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Input // Input
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MIDI_BUILD_INPUT
/*! \addtogroup input /*! \addtogroup input
@{ @{
*/ */
@ -469,16 +454,16 @@ StatusByte MidiInterface<SerialPort>::getStatus(MidiType inType,
it is sent back on the MIDI output. it is sent back on the MIDI output.
@see see setInputChannel() @see see setInputChannel()
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
inline bool MidiInterface<SerialPort>::read() inline bool MidiInterface<SerialPort, Settings>::read()
{ {
return read(mInputChannel); return read(mInputChannel);
} }
/*! \brief Read messages on a specified channel. /*! \brief Read messages on a specified channel.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
inline bool MidiInterface<SerialPort>::read(Channel inChannel) inline bool MidiInterface<SerialPort, Settings>::read(Channel inChannel)
{ {
if (inChannel >= MIDI_CHANNEL_OFF) if (inChannel >= MIDI_CHANNEL_OFF)
return false; // MIDI Input disabled. return false; // MIDI Input disabled.
@ -489,14 +474,12 @@ inline bool MidiInterface<SerialPort>::read(Channel inChannel)
handleNullVelocityNoteOnAsNoteOff(); handleNullVelocityNoteOnAsNoteOff();
const bool channelMatch = inputFilter(inChannel); const bool channelMatch = inputFilter(inChannel);
if (MIDI_USE_CALLBACKS && channelMatch) if (channelMatch)
{ {
launchCallback(); launchCallback();
} }
#if MIDI_BUILD_THRU
thruFilter(inChannel); thruFilter(inChannel);
#endif
return channelMatch; return channelMatch;
} }
@ -504,8 +487,8 @@ inline bool MidiInterface<SerialPort>::read(Channel inChannel)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Private method: MIDI parser // Private method: MIDI parser
template<class SerialPort> template<class SerialPort, class Settings>
bool MidiInterface<SerialPort>::parse() bool MidiInterface<SerialPort, Settings>::parse()
{ {
if (mSerial.available() == 0) if (mSerial.available() == 0)
// No data available. // No data available.
@ -593,8 +576,8 @@ bool MidiInterface<SerialPort>::parse()
case SystemExclusive: case SystemExclusive:
// The message can be any lenght // The message can be any lenght
// between 3 and MIDI_SYSEX_ARRAY_SIZE bytes // between 3 and MidiMessage::sSysExMaxSize bytes
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; mPendingMessageExpectedLenght = MidiMessage::sSysExMaxSize;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
mMessage.sysexArray[0] = SystemExclusive; mMessage.sysexArray[0] = SystemExclusive;
break; break;
@ -631,15 +614,17 @@ bool MidiInterface<SerialPort>::parse()
mPendingMessageIndex++; mPendingMessageIndex++;
} }
#if USE_1BYTE_PARSING if (Settings::Use1ByteParsing)
// Message is not complete. {
return false; // Message is not complete.
#else return false;
// Call the parser recursively }
// to parse the rest of the message. else
return parse(); {
#endif // Call the parser recursively
// to parse the rest of the message.
return parse();
}
} }
else else
{ {
@ -682,8 +667,8 @@ bool MidiInterface<SerialPort>::parse()
mMessage.type = SystemExclusive; mMessage.type = SystemExclusive;
// Get length // Get length
mMessage.data1 = mPendingMessageIndex & 0xff; mMessage.data1 = mPendingMessageIndex & 0xff; // LSB
mMessage.data2 = mPendingMessageIndex >> 8; mMessage.data2 = mPendingMessageIndex >> 8; // MSB
mMessage.channel = 0; mMessage.channel = 0;
mMessage.valid = true; mMessage.valid = true;
@ -714,7 +699,7 @@ bool MidiInterface<SerialPort>::parse()
{ {
// "FML" case: fall down here with an overflown SysEx.. // "FML" case: fall down here with an overflown SysEx..
// This means we received the last possible data byte that can fit // This means we received the last possible data byte that can fit
// the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. // the buffer. If this happens, try increasing MidiMessage::sSysExMaxSize.
if (mPendingMessage[0] == SystemExclusive) if (mPendingMessage[0] == SystemExclusive)
{ {
resetInput(); resetInput();
@ -768,33 +753,34 @@ bool MidiInterface<SerialPort>::parse()
// Then update the index of the pending message. // Then update the index of the pending message.
mPendingMessageIndex++; mPendingMessageIndex++;
#if USE_1BYTE_PARSING if (Settings::Use1ByteParsing)
// Message is not complete. {
return false; // Message is not complete.
#else return false;
// Call the parser recursively }
// to parse the rest of the message. else
return parse(); {
#endif // Call the parser recursively to parse the rest of the message.
return parse();
}
} }
} }
} }
// Private method, see midi_Settings.h for documentation // Private method, see midi_Settings.h for documentation
template<class SerialPort> template<class SerialPort, class Settings>
inline void MidiInterface<SerialPort>::handleNullVelocityNoteOnAsNoteOff() inline void MidiInterface<SerialPort, Settings>::handleNullVelocityNoteOnAsNoteOff()
{ {
#if MIDI_HANDLE_NULL_VELOCITY_NOTE_ON_AS_NOTE_OFF if (Settings::HandleNullVelocityNoteOnAsNoteOff &&
if (getType() == NoteOn && getData2() == 0) getType() == NoteOn && getData2() == 0)
{ {
mMessage.type = NoteOff; mMessage.type = NoteOff;
} }
#endif
} }
// Private method: check if the received message is on the listened channel // Private method: check if the received message is on the listened channel
template<class SerialPort> template<class SerialPort, class Settings>
inline bool MidiInterface<SerialPort>::inputFilter(Channel inChannel) inline bool MidiInterface<SerialPort, Settings>::inputFilter(Channel inChannel)
{ {
// This method handles recognition of channel // This method handles recognition of channel
// (to know if the message is destinated to the Arduino) // (to know if the message is destinated to the Arduino)
@ -825,8 +811,8 @@ inline bool MidiInterface<SerialPort>::inputFilter(Channel inChannel)
} }
// Private method: reset input attributes // Private method: reset input attributes
template<class SerialPort> template<class SerialPort, class Settings>
inline void MidiInterface<SerialPort>::resetInput() inline void MidiInterface<SerialPort, Settings>::resetInput()
{ {
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0; mPendingMessageExpectedLenght = 0;
@ -839,8 +825,8 @@ inline void MidiInterface<SerialPort>::resetInput()
Returns an enumerated type. @see MidiType Returns an enumerated type. @see MidiType
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
MidiType MidiInterface<SerialPort>::getType() const inline MidiType MidiInterface<SerialPort, Settings>::getType() const
{ {
return mMessage.type; return mMessage.type;
} }
@ -850,22 +836,22 @@ MidiType MidiInterface<SerialPort>::getType() const
\return Channel range is 1 to 16. \return Channel range is 1 to 16.
For non-channel messages, this will return 0. For non-channel messages, this will return 0.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
Channel MidiInterface<SerialPort>::getChannel() const inline Channel MidiInterface<SerialPort, Settings>::getChannel() const
{ {
return mMessage.channel; return mMessage.channel;
} }
/*! \brief Get the first data byte of the last received message. */ /*! \brief Get the first data byte of the last received message. */
template<class SerialPort> template<class SerialPort, class Settings>
DataByte MidiInterface<SerialPort>::getData1() const inline DataByte MidiInterface<SerialPort, Settings>::getData1() const
{ {
return mMessage.data1; return mMessage.data1;
} }
/*! \brief Get the second data byte of the last received message. */ /*! \brief Get the second data byte of the last received message. */
template<class SerialPort> template<class SerialPort, class Settings>
DataByte MidiInterface<SerialPort>::getData2() const inline DataByte MidiInterface<SerialPort, Settings>::getData2() const
{ {
return mMessage.data2; return mMessage.data2;
} }
@ -874,8 +860,8 @@ DataByte MidiInterface<SerialPort>::getData2() const
@see getSysExArrayLength to get the array's length in bytes. @see getSysExArrayLength to get the array's length in bytes.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
const byte* MidiInterface<SerialPort>::getSysExArray() const inline const byte* MidiInterface<SerialPort, Settings>::getSysExArray() const
{ {
return mMessage.sysexArray; return mMessage.sysexArray;
} }
@ -885,24 +871,23 @@ const byte* MidiInterface<SerialPort>::getSysExArray() const
It is coded using data1 as LSB and data2 as MSB. It is coded using data1 as LSB and data2 as MSB.
\return The array's length, in bytes. \return The array's length, in bytes.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
unsigned MidiInterface<SerialPort>::getSysExArrayLength() const inline unsigned MidiInterface<SerialPort, Settings>::getSysExArrayLength() const
{ {
const unsigned size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1; return mMessage.getSysExSize();
return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size;
} }
/*! \brief Check if a valid message is stored in the structure. */ /*! \brief Check if a valid message is stored in the structure. */
template<class SerialPort> template<class SerialPort, class Settings>
bool MidiInterface<SerialPort>::check() const inline bool MidiInterface<SerialPort, Settings>::check() const
{ {
return mMessage.valid; return mMessage.valid;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
template<class SerialPort> template<class SerialPort, class Settings>
Channel MidiInterface<SerialPort>::getInputChannel() const inline Channel MidiInterface<SerialPort, Settings>::getInputChannel() const
{ {
return mInputChannel; return mInputChannel;
} }
@ -911,8 +896,8 @@ Channel MidiInterface<SerialPort>::getInputChannel() const
\param inChannel the channel value. Valid values are 1 to 16, MIDI_CHANNEL_OMNI \param inChannel 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. if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::setInputChannel(Channel inChannel) inline void MidiInterface<SerialPort, Settings>::setInputChannel(Channel inChannel)
{ {
mInputChannel = inChannel; mInputChannel = inChannel;
} }
@ -924,8 +909,8 @@ void MidiInterface<SerialPort>::setInputChannel(Channel inChannel)
This is a utility static method, used internally, This is a utility static method, used internally,
made public so you can handle MidiTypes more easily. made public so you can handle MidiTypes more easily.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(byte inStatus) MidiType MidiInterface<SerialPort, Settings>::getTypeFromStatusByte(byte inStatus)
{ {
if ((inStatus < 0x80) || if ((inStatus < 0x80) ||
(inStatus == 0xf4) || (inStatus == 0xf4) ||
@ -939,22 +924,22 @@ MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(byte inStatus)
if (inStatus < 0xf0) if (inStatus < 0xf0)
{ {
// Channel message, remove channel nibble. // Channel message, remove channel nibble.
return (MidiType)(inStatus & 0xf0); return MidiType(inStatus & 0xf0);
} }
return (MidiType)inStatus; return MidiType(inStatus);
} }
/*! \brief Returns channel in the range 1-16 /*! \brief Returns channel in the range 1-16
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
inline Channel MidiInterface<SerialPort>::getChannelFromStatusByte(byte inStatus) inline Channel MidiInterface<SerialPort, Settings>::getChannelFromStatusByte(byte inStatus)
{ {
return (inStatus & 0x0f) + 1; return (inStatus & 0x0f) + 1;
} }
template<class SerialPort> template<class SerialPort, class Settings>
bool MidiInterface<SerialPort>::isChannelMessage(MidiType inType) bool MidiInterface<SerialPort, Settings>::isChannelMessage(MidiType inType)
{ {
return (inType == NoteOff || return (inType == NoteOff ||
inType == NoteOn || inType == NoteOn ||
@ -967,30 +952,28 @@ bool MidiInterface<SerialPort>::isChannelMessage(MidiType inType)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MIDI_USE_CALLBACKS
/*! \addtogroup callbacks /*! \addtogroup callbacks
@{ @{
*/ */
template<class SerialPort> void MidiInterface<SerialPort>::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSystemExclusive(void (*fptr)(byte* array, byte size)) { mSystemExclusiveCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSystemExclusive(void (*fptr)(byte* array, unsigned size)) { mSystemExclusiveCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; } template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; }
/*! \brief Detach an external function from the given type. /*! \brief Detach an external function from the given type.
@ -998,8 +981,8 @@ template<class SerialPort> void MidiInterface<SerialPort>::setHandleSystemReset(
\param inType The type of message to unbind. \param inType The type of message to unbind.
When a message of this type is received, no function will be called. When a message of this type is received, no function will be called.
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::disconnectCallbackFromType(MidiType inType) void MidiInterface<SerialPort, Settings>::disconnectCallbackFromType(MidiType inType)
{ {
switch (inType) switch (inType)
{ {
@ -1029,8 +1012,8 @@ void MidiInterface<SerialPort>::disconnectCallbackFromType(MidiType inType)
/*! @} */ // End of doc group MIDI Callbacks /*! @} */ // End of doc group MIDI Callbacks
// Private - launch callback function based on received type. // Private - launch callback function based on received type.
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::launchCallback() void MidiInterface<SerialPort, Settings>::launchCallback()
{ {
// The order is mixed to allow frequent messages to trigger their callback faster. // The order is mixed to allow frequent messages to trigger their callback faster.
switch (mMessage.type) switch (mMessage.type)
@ -1053,7 +1036,7 @@ void MidiInterface<SerialPort>::launchCallback()
case AfterTouchChannel: if (mAfterTouchChannelCallback != 0) mAfterTouchChannelCallback(mMessage.channel, mMessage.data1); break; case AfterTouchChannel: if (mAfterTouchChannelCallback != 0) mAfterTouchChannelCallback(mMessage.channel, mMessage.data1); break;
case ProgramChange: if (mProgramChangeCallback != 0) mProgramChangeCallback(mMessage.channel, mMessage.data1); break; case ProgramChange: if (mProgramChangeCallback != 0) mProgramChangeCallback(mMessage.channel, mMessage.data1); break;
case SystemExclusive: if (mSystemExclusiveCallback != 0) mSystemExclusiveCallback(mMessage.sysexArray, mMessage.data1); break; case SystemExclusive: if (mSystemExclusiveCallback != 0) mSystemExclusiveCallback(mMessage.sysexArray, mMessage.getSysExSize()); break;
// Occasional messages // Occasional messages
case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != 0) mTimeCodeQuarterFrameCallback(mMessage.data1); break; case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != 0) mTimeCodeQuarterFrameCallback(mMessage.data1); break;
@ -1068,19 +1051,12 @@ void MidiInterface<SerialPort>::launchCallback()
} }
} }
#endif // MIDI_USE_CALLBACKS
/*! @} */ // End of doc group MIDI Input /*! @} */ // End of doc group MIDI Input
#endif // MIDI_BUILD_INPUT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Thru // Thru
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
/*! \addtogroup thru /*! \addtogroup thru
@{ @{
*/ */
@ -1090,8 +1066,8 @@ void MidiInterface<SerialPort>::launchCallback()
@see MidiFilterMode @see MidiFilterMode
*/ */
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::setThruFilterMode(MidiFilterMode inThruFilterMode) void MidiInterface<SerialPort, Settings>::setThruFilterMode(MidiFilterMode inThruFilterMode)
{ {
mThruFilterMode = inThruFilterMode; mThruFilterMode = inThruFilterMode;
if (mThruFilterMode != Off) if (mThruFilterMode != Off)
@ -1100,27 +1076,27 @@ void MidiInterface<SerialPort>::setThruFilterMode(MidiFilterMode inThruFilterMod
mThruActivated = false; mThruActivated = false;
} }
template<class SerialPort> template<class SerialPort, class Settings>
MidiFilterMode MidiInterface<SerialPort>::getFilterMode() const MidiFilterMode MidiInterface<SerialPort, Settings>::getFilterMode() const
{ {
return mThruFilterMode; return mThruFilterMode;
} }
template<class SerialPort> template<class SerialPort, class Settings>
bool MidiInterface<SerialPort>::getThruState() const bool MidiInterface<SerialPort, Settings>::getThruState() const
{ {
return mThruActivated; return mThruActivated;
} }
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::turnThruOn(MidiFilterMode inThruFilterMode) void MidiInterface<SerialPort, Settings>::turnThruOn(MidiFilterMode inThruFilterMode)
{ {
mThruActivated = true; mThruActivated = true;
mThruFilterMode = inThruFilterMode; mThruFilterMode = inThruFilterMode;
} }
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::turnThruOff() void MidiInterface<SerialPort, Settings>::turnThruOff()
{ {
mThruActivated = false; mThruActivated = false;
mThruFilterMode = Off; mThruFilterMode = Off;
@ -1134,8 +1110,8 @@ void MidiInterface<SerialPort>::turnThruOff()
// to output unless filter is set to Off. // to output unless filter is set to Off.
// - Channel messages are passed to the output whether their channel // - Channel messages are passed to the output whether their channel
// is matching the input channel and the filter setting // is matching the input channel and the filter setting
template<class SerialPort> template<class SerialPort, class Settings>
void MidiInterface<SerialPort>::thruFilter(Channel inChannel) void MidiInterface<SerialPort, Settings>::thruFilter(Channel inChannel)
{ {
// If the feature is disabled, don't do anything. // If the feature is disabled, don't do anything.
if (!mThruActivated || (mThruFilterMode == Off)) if (!mThruActivated || (mThruFilterMode == Off))
@ -1225,8 +1201,4 @@ void MidiInterface<SerialPort>::thruFilter(Channel inChannel)
} }
} }
#endif // MIDI_BUILD_THRU
// -----------------------------------------------------------------------------
END_MIDI_NAMESPACE END_MIDI_NAMESPACE

View File

@ -2,7 +2,7 @@
* @file midi_Defs.h * @file midi_Defs.h
* Project Arduino MIDI Library * Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Definitions * @brief MIDI Library for the Arduino - Definitions
* @version 4.1 * @version 4.2
* @author Francois Best * @author Francois Best
* @date 24/02/11 * @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014 * @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -24,7 +24,13 @@
#pragma once #pragma once
#include "midi_Namespace.h" #include "midi_Namespace.h"
#if ARDUINO
#include <Arduino.h>
#else
#include <inttypes.h> #include <inttypes.h>
typedef uint8_t byte;
#endif
BEGIN_MIDI_NAMESPACE BEGIN_MIDI_NAMESPACE
@ -39,8 +45,6 @@ BEGIN_MIDI_NAMESPACE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Type definitions // Type definitions
typedef uint8_t byte;
typedef byte StatusByte; typedef byte StatusByte;
typedef byte DataByte; typedef byte DataByte;
typedef byte Channel; typedef byte Channel;
@ -155,50 +159,6 @@ enum MidiControlChangeNumber
PolyModeOn = 127 PolyModeOn = 127
}; };
// -----------------------------------------------------------------------------
/*! The midimsg structure contains decoded data of a MIDI message
read from the serial port with read()
*/
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 sysexArray[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;
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! \brief Create an instance of the library attached to a serial port. /*! \brief Create an instance of the library attached to a serial port.
@ -209,11 +169,25 @@ struct Message
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \ #define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
midi::MidiInterface<Type> Name((Type&)SerialPort); midi::MidiInterface<Type> Name((Type&)SerialPort);
/*! \brief Shortcut for MIDI Interface class with template argument. #if defined(ARDUINO_SAM_DUE) || defined(USBCON)
The class name for a MIDI object using the hardware UART would be // Leonardo, Due and other USB boards use Serial1 by default.
midi::MidiInterface<HardwareSerial>, when the macro is MIDI_CLASS(HardwareSerial). #define MIDI_CREATE_DEFAULT_INSTANCE() \
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
#else
/*! \brief Create an instance of the library with default name, serial port
and settings, for compatibility with sketches written with pre-v4.2 MIDI Lib,
or if you don't bother using custom names, serial port or settings.
*/
#define MIDI_CREATE_DEFAULT_INSTANCE() \
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
#endif
/*! \brief Create an instance of the library attached to a serial port with
custom settings.
@see DefaultSettings
@see MIDI_CREATE_INSTANCE
*/ */
#define MIDI_CLASS(Type) \ #define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
midi::MidiInterface<Type> midi::MidiInterface<Type, Settings> Name((Type&)SerialPort);
END_MIDI_NAMESPACE END_MIDI_NAMESPACE

81
src/midi_Message.h Normal file
View File

@ -0,0 +1,81 @@
/*!
* @file midi_Message.h
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Message struct definition
* @version 4.2
* @author Francois Best
* @date 11/06/14
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "midi_Namespace.h"
#include "midi_Defs.h"
BEGIN_MIDI_NAMESPACE
/*! The Message structure contains decoded data of a MIDI message
read from the serial port with read()
*/
template<unsigned SysExMaxSize>
struct Message
{
/*! The maximum size for the System Exclusive array.
*/
static const unsigned sSysExMaxSize = SysExMaxSize;
/*! 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 sysexArray[sSysExMaxSize];
/*! 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;
inline unsigned getSysExSize() const
{
const unsigned size = unsigned(data2) << 8 | data1;
return size > sSysExMaxSize ? sSysExMaxSize : size;
}
};
END_MIDI_NAMESPACE

View File

@ -2,7 +2,7 @@
* @file midi_Namespace.h * @file midi_Namespace.h
* Project Arduino MIDI Library * Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Namespace declaration * @brief MIDI Library for the Arduino - Namespace declaration
* @version 4.1 * @version 4.2
* @author Francois Best * @author Francois Best
* @date 24/02/11 * @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014 * @license GPL v3.0 - Copyright Forty Seven Effects 2014

View File

@ -2,7 +2,7 @@
* @file midi_Settings.h * @file midi_Settings.h
* Project Arduino MIDI Library * Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Settings * @brief MIDI Library for the Arduino - Settings
* @version 4.1 * @version 4.2
* @author Francois Best * @author Francois Best
* @date 24/02/11 * @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014 * @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -23,80 +23,54 @@
#pragma once #pragma once
#include "midi_Namespace.h" #include "midi_Defs.h"
// -----------------------------------------------------------------------------
// 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 thru can work only if input and output 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.
#ifndef MIDI_AUTO_INSTANCIATE
# ifdef ARDUINO
# define MIDI_AUTO_INSTANCIATE 1
# else
# define MIDI_AUTO_INSTANCIATE 0 ///< @see MIDI_CREATE_INSTANCE
# endif
#endif
// -----------------------------------------------------------------------------
// Default serial port configuration (if MIDI_AUTO_INSTANCIATE is set)
// Set the default port to use for MIDI.
#if MIDI_AUTO_INSTANCIATE
# ifdef ARDUINO
# include "Arduino.h"
# ifdef USBCON
# define MIDI_DEFAULT_SERIAL_PORT Serial1 // For Leonardo
# else
# define MIDI_DEFAULT_SERIAL_PORT Serial // For other Arduinos
# endif
# define MIDI_DEFAULT_SERIAL_CLASS HardwareSerial
# include "HardwareSerial.h"
# else
# error Auto-instanciation disabled. Use MIDI_CREATE_INSTANCE macro.
# endif
#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
// NoteOn with 0 velocity should be handled as NoteOf.
// Set to 1 to get NoteOff events when receiving null-velocity NoteOn messages.
// Set to 0 to get NoteOn events when receiving null-velocity NoteOn messages.
#define MIDI_HANDLE_NULL_VELOCITY_NOTE_ON_AS_NOTE_OFF 1
// Setting this to 1 will make MIDI.read parse only one byte of data for each
// call when data is available. This can speed up your application if receiving
// a lot of traffic, but might induce MIDI Thru and treatment latency.
#define MIDI_USE_1BYTE_PARSING 1
#define MIDI_BAUDRATE 31250
#define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes.
// -----------------------------------------------------------------------------
BEGIN_MIDI_NAMESPACE BEGIN_MIDI_NAMESPACE
/*! \brief Default Settings for the MIDI Library.
To change the default settings, don't edit them there, create a subclass and
override the values in that subclass, then use the MIDI_CREATE_CUSTOM_INSTANCE
macro to create your instance. The settings you don't override will keep their
default value. Eg:
\code{.cpp}
struct MySettings : public midi::DefaultSettings
{
static const bool UseRunningStatus = false; // Messes with my old equipment!
};
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial2, midi, MySettings);
\endcode
*/
struct DefaultSettings
{
/*! Running status enables short messages when sending multiple values
of the same type and channel.\n
Set to 0 if you have troubles controlling your hardware.
*/
static const bool UseRunningStatus = true;
/* NoteOn with 0 velocity should be handled as NoteOf.\n
Set to 1 to get NoteOff events when receiving null-velocity NoteOn messages.\n
Set to 0 to get NoteOn events when receiving null-velocity NoteOn messages.
*/
static const bool HandleNullVelocityNoteOnAsNoteOff = true;
// Setting this to 1 will make MIDI.read parse only one byte of data for each
// call when data is available. This can speed up your application if receiving
// a lot of traffic, but might induce MIDI Thru and treatment latency.
static const bool Use1ByteParsing = true;
/*! Override the default MIDI baudrate to transmit over USB serial, to
a decoding program such as Hairless MIDI (set baudrate to 115200)\n
http://projectgus.github.io/hairless-midiserial/
*/
static const long BaudRate = 31250;
/*! Maximum size of SysEx receivable. Decrease to save RAM if you don't expect
to receive SysEx, or adjust accordingly.
*/
static const unsigned SysExMaxSize = 128;
};
END_MIDI_NAMESPACE END_MIDI_NAMESPACE