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.
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
* 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.
* [Callbacks](http://playground.arduino.cc/Main/MIDILibraryCallbacks) to handle input messages more easily.
* 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.
* Use any serial port, hardware or software.
#### 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.
* 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)
@ -26,7 +27,7 @@ Download the latest version [here](https://github.com/FortySevenEffects/arduino_
* 14/12/2009 : Version 2.5 released.
* 28/07/2009 : Version 2.0 released.
* 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?
* 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
* 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...
@ -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/)).
To know how to use the callback feature, see the dedicated page [here](http://playground.arduino.cc/Main/MIDILibraryCallbacks).
### 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).
@ -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
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_DualMerger.ino
\example MIDI_Input.ino
\example MIDI_SimpleSynth.ino
*/
// -----------------------------------------------------------------------------

View File

@ -1,9 +1,11 @@
#include <MIDI.h>
// 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.
MIDI_CREATE_DEFAULT_INSTANCE();
#define LED 13 // LED pin on Arduino Uno
void setup()

View File

@ -1,33 +1,39 @@
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------
// 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.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.
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
}
// Do whatever you want when a note is pressed.
// Try to keep your callbacks short (no delays ect)
// otherwise it would slow down the loop() and have a bad impact
// 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()
{
// Connect the HandleNoteOn function to the library,
// 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
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
MIDI.begin(MIDI_CHANNEL_OMNI);

View File

@ -1,5 +1,9 @@
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// -----------------------------------------------------------------------------
// This example shows the old way of checking for input messages.
// It's simpler to use the callbacks now, check out the dedicated example.
@ -8,7 +12,7 @@
// -----------------------------------------------------------------------------
void BlinkLed(byte num) // Basic blink function
{
{
for (byte i=0;i<num;i++)
{
digitalWrite(LED,HIGH);
@ -29,12 +33,12 @@ void setup()
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
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

View File

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

View File

@ -1,7 +1,8 @@
/*!
* \file synth-core_NoteList.h
* \file noteList.h
* \author Francois Best
* \date 24/05/2013
* \brief Linked list of notes, for Low, Last & High playing modes.
* \license GPL v3.0 - Copyright Forty Seven Effects 2013
*
* This program is free software: you can redistribute it and/or modify
@ -65,7 +66,9 @@ public:
public:
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:
inline bool empty() const;
@ -105,7 +108,7 @@ inline MidiNote::MidiNote(const MidiNote& inOther)
inline MidiNote& MidiNote::operator= (const MidiNote& inOther)
{
pitch = inOther.pitch;
pitch = inOther.pitch;
velocity = inOther.velocity;
return *this;
}
@ -145,8 +148,6 @@ inline typename MidiNoteList<Size>::Cell& MidiNoteList<Size>::Cell::operator= (c
template<byte Size>
inline MidiNoteList<Size>::MidiNoteList()
{
// Check that size is a power of two
//AVR_STATIC_ASSERT(Size != 0 && !(Size & (Size - 1)));
}
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>
inline void MidiNoteList<Size>::add(const MidiNote& inNote)
{
@ -186,12 +191,15 @@ inline void MidiNoteList<Size>::add(const MidiNote& inNote)
print();
}
/*! \brief Remove a note
Call this when receiving a NoteOff event.
*/
template<byte Size>
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)
{
@ -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>
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;
}
/*! \brief Get the last active note played
This implements the Mono Last playing mode.
*/
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 true;
return false;
}
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;
}
/*! \brief Get the number of active notes.
*/
template<byte Size>
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>
inline typename MidiNoteList<Size>::Cell* MidiNoteList<Size>::getFirstEmptyCell()

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import sys
import os
import shutil
import subprocess
import argparse
from pprint import pprint
from midi import *
from tester import *
@ -138,28 +139,42 @@ class ArduinoMidiLibrary:
# ------------------------------------------------------------------------------
def main():
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])
info = "Validator script for the Arduino MIDI Library."
arg_parser = argparse.ArgumentParser(description = info)
#lib = ArduinoMidiLibrary()
#lib.install()
#if lib.validate():
# print('Validation passed')
#else:
# print('Validation failed')
arg_parser.add_argument('--compile', '-c',
action="store_true",
help="Test compilation of the example sketches")
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
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino
* @version 4.1
* @version 4.2
* @author Francois Best
* @date 24/02/11
* @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
/*! \brief Encode System Exclusive messages.

View File

@ -2,7 +2,7 @@
* @file MIDI.h
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino
* @version 4.1
* @version 4.2
* @author Francois Best
* @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -23,12 +23,9 @@
#pragma once
#include "midi_Settings.h"
#include "midi_Defs.h"
#ifdef FSE_AVR
#include <ak47.h>
#endif
#include "midi_Settings.h"
#include "midi_Message.h"
// -----------------------------------------------------------------------------
@ -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
the begin, read, write and available methods.
*/
template<class SerialPort>
template<class SerialPort, class Settings = DefaultSettings>
class MidiInterface
{
public:
MidiInterface(SerialPort& inSerial);
~MidiInterface();
inline MidiInterface(SerialPort& inSerial);
inline ~MidiInterface();
public:
void begin(Channel inChannel = 1);
@ -53,8 +50,6 @@ public:
// -------------------------------------------------------------------------
// MIDI Output
#if MIDI_BUILD_OUTPUT
public:
inline void sendNoteOn(DataByte inNoteNumber,
DataByte inVelocity,
@ -100,17 +95,9 @@ public:
DataByte inData2,
Channel inChannel);
private:
inline StatusByte getStatus(MidiType inType,
Channel inChannel) const;
#endif // MIDI_BUILD_OUTPUT
// -------------------------------------------------------------------------
// MIDI Input
#if MIDI_BUILD_INPUT
public:
inline bool read();
inline bool read(Channel inChannel);
@ -133,25 +120,10 @@ public:
static inline Channel getChannelFromStatusByte(byte inStatus);
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
#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));
@ -160,7 +132,7 @@ public:
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 setHandleSystemExclusive(void (*fptr)(byte * array, unsigned size));
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
inline void setHandleSongPosition(void (*fptr)(unsigned beats));
inline void setHandleSongSelect(void (*fptr)(byte songnumber));
@ -184,7 +156,7 @@ private:
void (*mProgramChangeCallback)(byte channel, byte);
void (*mAfterTouchChannelCallback)(byte channel, byte);
void (*mPitchBendCallback)(byte channel, int);
void (*mSystemExclusiveCallback)(byte * array, byte size);
void (*mSystemExclusiveCallback)(byte * array, unsigned size);
void (*mTimeCodeQuarterFrameCallback)(byte data);
void (*mSongPositionCallback)(unsigned beats);
void (*mSongSelectCallback)(byte songnumber);
@ -196,14 +168,9 @@ private:
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;
@ -212,21 +179,34 @@ public:
inline void turnThruOff();
inline void setThruFilterMode(MidiFilterMode inThruFilterMode);
private:
void thruFilter(byte inChannel);
private:
bool parse();
inline void handleNullVelocityNoteOnAsNoteOff();
inline bool inputFilter(Channel inChannel);
inline void resetInput();
private:
bool mThruActivated : 1;
MidiFilterMode mThruFilterMode : 7;
#endif // MIDI_BUILD_THRU
#if MIDI_USE_RUNNING_STATUS
private:
StatusByte mRunningStatus_TX;
#endif
typedef Message<Settings::SysExMaxSize> MidiMessage;
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:
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"

View File

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

View File

@ -2,7 +2,7 @@
* @file midi_Defs.h
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Definitions
* @version 4.1
* @version 4.2
* @author Francois Best
* @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -24,7 +24,13 @@
#pragma once
#include "midi_Namespace.h"
#if ARDUINO
#include <Arduino.h>
#else
#include <inttypes.h>
typedef uint8_t byte;
#endif
BEGIN_MIDI_NAMESPACE
@ -39,8 +45,6 @@ BEGIN_MIDI_NAMESPACE
// -----------------------------------------------------------------------------
// Type definitions
typedef uint8_t byte;
typedef byte StatusByte;
typedef byte DataByte;
typedef byte Channel;
@ -155,50 +159,6 @@ enum MidiControlChangeNumber
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.
@ -209,11 +169,25 @@ struct Message
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
midi::MidiInterface<Type> Name((Type&)SerialPort);
/*! \brief Shortcut for MIDI Interface class with template argument.
The class name for a MIDI object using the hardware UART would be
midi::MidiInterface<HardwareSerial>, when the macro is MIDI_CLASS(HardwareSerial).
#if defined(ARDUINO_SAM_DUE) || defined(USBCON)
// Leonardo, Due and other USB boards use Serial1 by default.
#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) \
midi::MidiInterface<Type>
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
midi::MidiInterface<Type, Settings> Name((Type&)SerialPort);
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
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Namespace declaration
* @version 4.1
* @version 4.2
* @author Francois Best
* @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014

View File

@ -2,7 +2,7 @@
* @file midi_Settings.h
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino - Settings
* @version 4.1
* @version 4.2
* @author Francois Best
* @date 24/02/11
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
@ -23,80 +23,54 @@
#pragma once
#include "midi_Namespace.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.
// -----------------------------------------------------------------------------
#include "midi_Defs.h"
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