Merge branch 'master' into dev.
This commit is contained in:
commit
6b295e6a47
|
|
@ -2,3 +2,4 @@
|
|||
*.pyc
|
||||
logs/
|
||||
build/
|
||||
.vscode/.cmaketools.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
[submodule "external/google-test"]
|
||||
path = external/google-test
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "external/midi-usb"]
|
||||
path = external/midi-usb
|
||||
url = https://github.com/arduino-libraries/MIDIUSB.git
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
# Kudos to these guys:
|
||||
# https://github.com/Return-To-The-Roots/s25client/blob/master/.travis.yml
|
||||
# http://docs.platformio.org/en/stable/ci/travis.html
|
||||
|
||||
sudo: false
|
||||
language: python
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
|
||||
# Cache PlatformIO packages using Travis CI container-based infrastructure
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
|
||||
env:
|
||||
global:
|
||||
- BUILD_TYPE=Debug
|
||||
matrix:
|
||||
- BUILD_UNIT_TESTS=1
|
||||
- PLATFORMIO_CI_SRC=examples/Basic_IO
|
||||
- PLATFORMIO_CI_SRC=examples/Bench
|
||||
- PLATFORMIO_CI_SRC=examples/Callbacks
|
||||
- PLATFORMIO_CI_SRC=examples/DualMerger
|
||||
- PLATFORMIO_CI_SRC=examples/Input
|
||||
- PLATFORMIO_CI_SRC=examples/MidiUSB REQUIRES_USB=1
|
||||
- PLATFORMIO_CI_SRC=examples/RPN_NRPN
|
||||
- PLATFORMIO_CI_SRC=examples/SimpleSynth
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- cmake
|
||||
|
||||
install:
|
||||
- |
|
||||
if [ "${BUILD_UNIT_TESTS}" ]; then
|
||||
# GCov 4.6 cannot handle the file structure
|
||||
export CXX="g++-4.8"
|
||||
export GCOV="gcov-4.8"
|
||||
|
||||
# Install newer lcov (1.9 seems to fail: http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/)
|
||||
export LCOV_ROOT="$HOME/lcov"
|
||||
mkdir -p "$LCOV_ROOT"
|
||||
wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.12.orig.tar.gz --output-document="$LCOV_ROOT/lcov.tar.gz"
|
||||
tar xf "$LCOV_ROOT/lcov.tar.gz" --strip-components=1 -C $LCOV_ROOT
|
||||
export PATH="$LCOV_ROOT/bin:$PATH"
|
||||
which lcov
|
||||
|
||||
# Install coveralls tool
|
||||
gem install coveralls-lcov
|
||||
export GENERATE_COVERAGE=1
|
||||
else
|
||||
# Install PlatformIO
|
||||
pip install -U platformio
|
||||
fi
|
||||
|
||||
script:
|
||||
# Build unit tests & generate code coverage
|
||||
- |
|
||||
if [ "${BUILD_UNIT_TESTS}" ]; then
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILDER_ENABLE_PROFILING=${GENERATE_COVERAGE} --generator="Unix Makefiles" ..
|
||||
make all
|
||||
ctest --verbose
|
||||
fi
|
||||
|
||||
# Build current example
|
||||
- |
|
||||
if [ ! "${BUILD_UNIT_TESTS}" ]; then
|
||||
if [ "${REQUIRES_USB}" ]; then
|
||||
platformio ci --lib="." --lib=external/midi-usb --board="due" --board="dueUSB" --board="zero" --board="zeroUSB" --board="leonardo"
|
||||
else
|
||||
platformio ci --lib="." --board=uno --board="due" --board="zero" --board="leonardo" --board="micro" --board="nanoatmega328" --board="megaatmega2560" --board="teensy20" --board="teensy20pp" --board="teensy30" --board="teensy31"
|
||||
fi
|
||||
fi
|
||||
|
||||
after_success:
|
||||
- |
|
||||
if [ "${GENERATE_COVERAGE}" ]; then
|
||||
# Generate code coverage information & send to Coveralls
|
||||
lcov --gcov-tool $GCOV --directory . --capture --output-file coverage.info
|
||||
lcov --gcov-tool $GCOV --remove coverage.info 'test/*' '/usr/*' 'external/*' --output-file coverage.info
|
||||
lcov --list coverage.info
|
||||
coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info
|
||||
fi
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Mac",
|
||||
"includePath": [
|
||||
"/Applications/Arduino.app/Contents/Java/hardware/tools/avr/include",
|
||||
"/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino"
|
||||
],
|
||||
"browse" : {
|
||||
"limitSymbolsToIncludedHeaders" : true,
|
||||
"databaseFilename" : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": ["/usr/include"],
|
||||
"browse" : {
|
||||
"limitSymbolsToIncludedHeaders" : true,
|
||||
"databaseFilename" : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": ["c:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"],
|
||||
"browse" : {
|
||||
"limitSymbolsToIncludedHeaders" : true,
|
||||
"databaseFilename" : ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"cmake.experimental.enableTargetDebugging": true
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
cmake_minimum_required(VERSION 2.8.7)
|
||||
project(arduino_midi_library CXX)
|
||||
|
||||
add_subdirectory(builder)
|
||||
|
||||
setup_builder()
|
||||
|
||||
add_subdirectory(external)
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(test)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
## Requirements
|
||||
|
||||
Requirements to build and run the unit tests:
|
||||
- CMake 2.8 or later
|
||||
- GCC / Clang with C++11 support
|
||||
|
||||
## Setup
|
||||
|
||||
Pull Google Test / Google Mock subrepository:
|
||||
```
|
||||
$ git init submodules
|
||||
```
|
||||
|
||||
Create build directory, run CMake, build and run unit tests:
|
||||
```
|
||||
$ mkdir build && cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ ctest --verbose
|
||||
```
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 Francois Best
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
92
README.md
92
README.md
|
|
@ -1,79 +1,61 @@
|
|||
#Arduino MIDI Library v4.2
|
||||
# Arduino MIDI Library
|
||||
|
||||
[](https://travis-ci.org/FortySevenEffects/arduino_midi_library)
|
||||
[](https://coveralls.io/github/FortySevenEffects/arduino_midi_library)
|
||||
[](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest)
|
||||
[](LICENSE)
|
||||
|
||||
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/latest).
|
||||
|
||||
### Features
|
||||
* Compatible with all Arduino boards (and clones with an AVR processor)
|
||||
* Compatible with all Arduino boards (and clones with an AVR processor).
|
||||
* Simple and fast way to send and receive every kind of MIDI message (including all System messages, SysEx, Clock, etc..).
|
||||
* OMNI input reading (read all channels).
|
||||
* 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: [overridable template-based settings](http://arduinomidilib.fortyseveneffects.com/a00013.html#details).
|
||||
* Configurable: [overridable template-based settings](https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-custom-Settings).
|
||||
* Create more than one MIDI port for mergers/splitters applications.
|
||||
* Use any serial port, hardware or software.
|
||||
|
||||
### Getting Started
|
||||
|
||||
#### 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)
|
||||
* 06/05/2011 : Version 3.1 released. Added [callback](http://playground.arduino.cc/Main/MIDILibraryCallbacks) support.
|
||||
* 06/03/2011 : Version 3.0 released. Project is now hosted on [SourceForge](http://sourceforge.net/projects/arduinomidilib).
|
||||
* 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 operational. Added some features to enable thru.
|
||||
1. Use Arduino's Library Manager to install the library.
|
||||
2. Start coding:
|
||||
```c++
|
||||
#include <MIDI.h>
|
||||
|
||||
// Created and binds the MIDI interface to the default hardware Serial port
|
||||
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||
|
||||
void setup()
|
||||
{
|
||||
MIDI.begin(MIDI_CHANNEL_OMNI); // Listen to all incoming messages
|
||||
}
|
||||
|
||||
**__Warning: this library requires Arduino 1.0 or more recent versions.__**
|
||||
void loop()
|
||||
{
|
||||
// Send note 42 with velocity 127 on channel 1
|
||||
MIDI.sendNoteOn(42, 127, 1);
|
||||
|
||||
// Read incoming messages
|
||||
MIDI.read();
|
||||
}
|
||||
```
|
||||
|
||||
### What do I need to do?
|
||||
3. Read the [documentation](#documentation) or watch the awesome video tutorials from [Notes & Volts](https://www.youtube.com/playlist?list=PL4_gPbvyebyH2xfPXePHtx8gK5zPBrVkg).
|
||||
|
||||
* 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...
|
||||
|
||||
|
||||
##Reference
|
||||
|
||||
See the extended reference [here](http://arduinomidilib.fortyseveneffects.com) ([Mirror](http://fortyseveneffects.github.io/arduino_midi_library/)).
|
||||
|
||||
### 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).
|
||||
|
||||
This method will:
|
||||
* Start the serial port at the MIDI baudrate (31250)
|
||||
* Set the input channel at the argument given (if any, else 1)
|
||||
* Enable Soft Thru, without filtering (everything at the input is sent back)
|
||||
|
||||
|
||||
|
||||
### MIDI Thru
|
||||
|
||||
The MIDI Thru allows you to redirect your incoming messages to the MIDI output. It replaces the need of a MIDI Thru connector, as it copies every valid incoming message from the input. For good performance, you might want to call read() in a fast loop, for low latency.
|
||||
|
||||
Incoming unread bytes/messages are kept in the Arduino serial buffer, in order not to flood it, check regularily with MIDI.read. See the reference for Thru explanations.
|
||||
|
||||
Thru is enabled by default, you can turn it off using appropriate methods.
|
||||
|
||||
|
||||
### Hardware
|
||||
|
||||
Take a look at [the MIDI.org schematic](http://www.midi.org/techspecs/electrispec.php)
|
||||
## Documentation
|
||||
|
||||
- [Doxygen Extended Documentation](http://fortyseveneffects.github.io/arduino_midi_library/).
|
||||
- [GitHub wiki](https://github.com/FortySevenEffects/arduino_midi_library/wiki).
|
||||
|
||||
## Contact
|
||||
if you have any comment or support request to make, feel free to contact me: francois.best@fortyseveneffects.com
|
||||
|
||||
To report a bug, contribute, discuss on usage, or simply request support, please [create an issue here](https://github.com/FortySevenEffects/arduino_midi_library/issues/new).
|
||||
|
||||
You can also get informations about bug fixes and updates on my twitter account: [@fortysevenfx](http://twitter.com/fortysevenfx).
|
||||
|
||||
## License
|
||||
|
||||
MIT © 2016 [Francois Best](http://fortyseveneffects.com)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
#### 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).
|
||||
* 06/05/2011 : Version 3.1 released. Added [callback](http://playground.arduino.cc/Main/MIDILibraryCallbacks) support.
|
||||
* 06/03/2011 : Version 3.0 released. Project is now hosted on [SourceForge](http://sourceforge.net/projects/arduinomidilib).
|
||||
* 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 operational. Added some features to enable thru.
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
!!MIDI Library v4.1
|
||||
|
||||
This library enables MIDI I/O communications on the Arduino serial ports. You can send and receive messages of all kinds (including System Exclusive, RealTime etc..). 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 [[ https://github.com/FortySevenEffects/arduino_midi_library/releases/download/4.1/Arduino_MIDI_Library_v4.1.zip | here ]].
|
||||
|
||||
You can now follow the developments on [[ http://github.com/FortySevenEffects/arduino_midi_library/ | GitHub]].
|
||||
|
||||
|
||||
!!!!Features:
|
||||
* Compatible with all Arduino boards (and clones with an AVR processor)
|
||||
* Simple and fast way to send and receive every kind of MIDI message (including all System messages, SysEx, Clock, etc..).
|
||||
* OMNI input reading (read all channels).
|
||||
* Software Thru, with message filtering.
|
||||
* [[ http://playground.arduino.cc/Main/MIDILibraryCallbacks | Callbacks]] 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).
|
||||
* Create more than one MIDI port for mergers/splitters applications.
|
||||
* Use any serial port, hardware or software.
|
||||
|
||||
|
||||
!!!!Changelog:
|
||||
* 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 [[ http://sourceforge.net/news/?group_id=265194 | here ]]
|
||||
* 06/05/2011 : Version 3.1 released. Added [[ http://playground.arduino.cc/Main/MIDILibraryCallbacks | callback]] support.
|
||||
* 06/03/2011 : Version 3.0 released. Project is now hosted on [[ http://sourceforge.net/projects/arduinomidilib/ | SourceForge]].
|
||||
* 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.
|
||||
|
||||
!!!!Warning: this library requires Arduino 1.0 or more recent versions.
|
||||
|
||||
|
||||
!!!What do I need to do?
|
||||
|
||||
* Download the library ([[ https://github.com/FortySevenEffects/arduino_midi_library/releases/download/4.1/Arduino_MIDI_Library_v4.1.zip | link ]])
|
||||
* 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>@]
|
||||
|
||||
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...
|
||||
|
||||
|
||||
!!Reference
|
||||
|
||||
See the extended reference [[ http://arduinomidilib.fortyseveneffects.com | here ]].
|
||||
Mirror: [[ http://fortyseveneffects.github.io/arduino_midi_library/ | GitHub ]].
|
||||
|
||||
To know how to use the callback feature, see the dedicated page [[ http://playground.arduino.cc/Main/MIDILibraryCallbacks | here]].
|
||||
|
||||
|
||||
!!A few things...
|
||||
|
||||
!!!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).
|
||||
|
||||
This method will:
|
||||
* Start the serial port at the MIDI baudrate (31250)
|
||||
* Set the input channel at the argument given (if any, else 1)
|
||||
* Enable Soft Thru, without filtering (everything at the input is sent back)
|
||||
|
||||
|
||||
|
||||
!!!MIDI Thru
|
||||
|
||||
The MIDI Thru allows you to redirect your incoming messages to the MIDI output. It replaces the need of a MIDI Thru connector, as it copies every valid incoming message from the input. For good performance, you might want to call read() in a fast loop, for low latency.
|
||||
|
||||
Incoming unread bytes/messages are kept in the Arduino serial buffer, in order not to flood it, check regularily with MIDI.read. See the reference for Thru explanations.
|
||||
|
||||
Thru is enabled by default, you can turn it off using appropriate methods.
|
||||
|
||||
|
||||
!!!Hardware
|
||||
|
||||
Take a look at [[ http://www.midi.org/techspecs/electrispec.php | the MIDI.org schematic ]]
|
||||
|
||||
|
||||
!!Contact
|
||||
if you have any comment or support request to make, feel free to contact me at francois.best{at}fortyseveneffects[dot]com.
|
||||
|
||||
You can follow the developments on GitHub:
|
||||
[[ http://github.com/FortySevenEffects/arduino_midi_library/tree/dev ]]
|
||||
|
||||
You can also get informations about bug fixes and updates on my twitter account: @fortysevenfx.
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": ".",
|
||||
"folder_exclude_patterns": [".*", "__pycache__"],
|
||||
"file_exclude_patterns": [".*"]
|
||||
}
|
||||
],
|
||||
"build_systems":
|
||||
[
|
||||
{
|
||||
"name": "Arduino MIDI Library Validator",
|
||||
"cmd": ["python3", "${project_path}/res/validate.py"],
|
||||
"path": "/usr/local/bin:/usr/bin:/bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
option(BUILDER_ENABLE_PROFILING OFF)
|
||||
|
||||
macro(setup_builder)
|
||||
enable_testing()
|
||||
|
||||
set(ROOT_SOURCE_DIR ${PROJECT_SOURCE_DIR} CACHE INTERNAL "Repository root directory")
|
||||
set(ROOT_BINARY_DIR "${ROOT_SOURCE_DIR}/build")
|
||||
|
||||
include_directories(${ROOT_SOURCE_DIR})
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wno-deprecated -Woverloaded-virtual")
|
||||
if (BUILDER_ENABLE_PROFILING)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --coverage")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
endmacro()
|
||||
|
|
@ -38,7 +38,7 @@ PROJECT_NAME = "Arduino MIDI Library"
|
|||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = "Version 4.2"
|
||||
PROJECT_NUMBER = "Version 4.3"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
#include <MIDI.h>
|
||||
|
||||
// Simple tutorial on how to receive and send MIDI messages.
|
||||
// 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();
|
||||
|
||||
static const unsigned ledPin = 13; // LED pin on Arduino Uno
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(ledPin, OUTPUT);
|
||||
MIDI.begin(4); // Launch MIDI and listen to channel 4
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (MIDI.read()) // If we have received a message
|
||||
{
|
||||
digitalWrite(ledPin, HIGH);
|
||||
MIDI.sendNoteOn(42, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1)
|
||||
delay(1000); // Wait for a second
|
||||
MIDI.sendNoteOff(42, 0, 1); // Stop the note
|
||||
digitalWrite(ledPin, LOW);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@
|
|||
#ifdef ARDUINO_SAM_DUE
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, midiA);
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiB);
|
||||
#elif defined(ARDUINO_SAMD_ZERO)
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, SerialUSB, midiA);
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiB);
|
||||
#else
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial softSerial(2,3);
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#include <MIDI.h>
|
||||
|
||||
#if defined(USBCON)
|
||||
#include <midi_UsbTransport.h>
|
||||
|
||||
static const unsigned sUsbTransportBufferSize = 16;
|
||||
typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport;
|
||||
|
||||
UsbTransport sUsbTransport;
|
||||
|
||||
MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, MIDI);
|
||||
|
||||
#else // No USB available, fallback to Serial
|
||||
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||
#endif
|
||||
|
||||
// --
|
||||
|
||||
void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity)
|
||||
{
|
||||
Serial.print("NoteOn ");
|
||||
Serial.print(inNumber);
|
||||
Serial.print("\tvelocity: ");
|
||||
Serial.println(inVelocity);
|
||||
}
|
||||
void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity)
|
||||
{
|
||||
Serial.print("NoteOff ");
|
||||
Serial.print(inNumber);
|
||||
Serial.print("\tvelocity: ");
|
||||
Serial.println(inVelocity);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
MIDI.begin();
|
||||
MIDI.setHandleNoteOn(handleNoteOn);
|
||||
MIDI.setHandleNoteOff(handleNoteOff);
|
||||
Serial.println("Arduino ready.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
MIDI.read();
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
#include <MIDI.h>
|
||||
#include "utility.h"
|
||||
|
||||
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||
|
||||
/* Listen to RPN & NRPN messages on all channels
|
||||
|
||||
The complexity of this example resides in the fact that keeping a state
|
||||
of all the 16384 * 2 RPN/NRPN values would not fit in memory.
|
||||
As we're only interested in a few of them, we use a separate state map.
|
||||
|
||||
If you'd like to go further, have a look at this thread:
|
||||
https://github.com/FortySevenEffects/arduino_midi_library/issues/60
|
||||
*/
|
||||
|
||||
template<class State, byte MsbSelectCCNumber, byte LsbSelectCCNumber>
|
||||
class ParameterNumberParser
|
||||
{
|
||||
public:
|
||||
ParameterNumberParser(State& inState)
|
||||
: mState(inState)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
inline void reset()
|
||||
{
|
||||
mState.reset();
|
||||
mSelected = false;
|
||||
mCurrentNumber = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
bool parseControlChange(byte inNumber, byte inValue)
|
||||
{
|
||||
switch (inNumber)
|
||||
{
|
||||
case MsbSelectCCNumber:
|
||||
mCurrentNumber.mMsb = inValue;
|
||||
break;
|
||||
case LsbSelectCCNumber:
|
||||
if (inValue == 0x7f && mCurrentNumber.mMsb == 0x7f)
|
||||
{
|
||||
// End of Null Function, disable parser.
|
||||
mSelected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentNumber.mLsb = inValue;
|
||||
mSelected = mState.has(mCurrentNumber.as14bits());
|
||||
}
|
||||
break;
|
||||
|
||||
case midi::DataIncrement:
|
||||
if (mSelected)
|
||||
{
|
||||
Value& currentValue = getCurrentValue();
|
||||
currentValue += inValue;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case midi::DataDecrement:
|
||||
if (mSelected)
|
||||
{
|
||||
Value& currentValue = getCurrentValue();
|
||||
currentValue -= inValue;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case midi::DataEntryMSB:
|
||||
if (mSelected)
|
||||
{
|
||||
Value& currentValue = getCurrentValue();
|
||||
currentValue.mMsb = inValue;
|
||||
currentValue.mLsb = 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case midi::DataEntryLSB:
|
||||
if (mSelected)
|
||||
{
|
||||
Value& currentValue = getCurrentValue();
|
||||
currentValue.mLsb = inValue;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Not part of the RPN/NRPN workflow, ignoring.
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
inline Value& getCurrentValue()
|
||||
{
|
||||
return mState.get(mCurrentNumber.as14bits());
|
||||
}
|
||||
inline const Value& getCurrentValue() const
|
||||
{
|
||||
return mState.get(mCurrentNumber.as14bits());
|
||||
}
|
||||
|
||||
public:
|
||||
State& mState;
|
||||
bool mSelected;
|
||||
Value mCurrentNumber;
|
||||
};
|
||||
|
||||
// --
|
||||
|
||||
typedef State<2> RpnState; // We'll listen to 2 RPN
|
||||
typedef State<4> NrpnState; // and 4 NRPN
|
||||
typedef ParameterNumberParser<RpnState, midi::RPNMSB, midi::RPNLSB> RpnParser;
|
||||
typedef ParameterNumberParser<NrpnState, midi::NRPNMSB, midi::NRPNLSB> NrpnParser;
|
||||
|
||||
struct ChannelSetup
|
||||
{
|
||||
inline ChannelSetup()
|
||||
: mRpnParser(mRpnState)
|
||||
, mNrpnParser(mNrpnState)
|
||||
{
|
||||
}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
mRpnParser.reset();
|
||||
mNrpnParser.reset();
|
||||
}
|
||||
inline void setup()
|
||||
{
|
||||
mRpnState.enable(midi::RPN::PitchBendSensitivity);
|
||||
mRpnState.enable(midi::RPN::ModulationDepthRange);
|
||||
|
||||
// Enable a few random NRPNs
|
||||
mNrpnState.enable(12);
|
||||
mNrpnState.enable(42);
|
||||
mNrpnState.enable(1234);
|
||||
mNrpnState.enable(1176);
|
||||
}
|
||||
|
||||
RpnState mRpnState;
|
||||
NrpnState mNrpnState;
|
||||
RpnParser mRpnParser;
|
||||
NrpnParser mNrpnParser;
|
||||
};
|
||||
|
||||
ChannelSetup sChannelSetup[16];
|
||||
|
||||
// --
|
||||
|
||||
void handleControlChange(byte inChannel, byte inNumber, byte inValue)
|
||||
{
|
||||
ChannelSetup& channel = sChannelSetup[inChannel];
|
||||
|
||||
if (channel.mRpnParser.parseControlChange(inNumber, inValue))
|
||||
{
|
||||
const Value& value = channel.mRpnParser.getCurrentValue();
|
||||
const unsigned number = channel.mRpnParser.mCurrentNumber.as14bits();
|
||||
|
||||
if (number == midi::RPN::PitchBendSensitivity)
|
||||
{
|
||||
// Here, we use the LSB and MSB separately as they have different meaning.
|
||||
const byte semitones = value.mMsb;
|
||||
const byte cents = value.mLsb;
|
||||
}
|
||||
else if (number == midi::RPN::ModulationDepthRange)
|
||||
{
|
||||
// But here, we want the full 14 bit value.
|
||||
const unsigned range = value.as14bits();
|
||||
}
|
||||
}
|
||||
else if (channel.mRpnParser.parseControlChange(inNumber, inValue))
|
||||
{
|
||||
// You get the idea..
|
||||
}
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void setup()
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
ChannelSetup& channel = sChannelSetup[i];
|
||||
channel.reset();
|
||||
channel.setup();
|
||||
}
|
||||
MIDI.setHandleControlChange(handleControlChange);
|
||||
MIDI.begin(MIDI_CHANNEL_OMNI);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
MIDI.read();
|
||||
|
||||
// Send a RPN sequence (Pitch Bend sensitivity) on channel 1
|
||||
{
|
||||
const midi::Channel channel = 1;
|
||||
const byte semitones = 12;
|
||||
const byte cents = 42;
|
||||
MIDI.beginRpn(midi::RPN::PitchBendSensitivity, channel);
|
||||
MIDI.sendRpnValue(semitones, cents, channel);
|
||||
MIDI.endRpn(channel);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*!
|
||||
* \file utility.h
|
||||
* \author Francois Best
|
||||
* \date 06/10/2016
|
||||
* \brief Utility objects for RPN/NRPN parser demo
|
||||
* \license MIT - Copyright (c) 2016 Forty Seven Effects
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
struct Value
|
||||
{
|
||||
inline unsigned as14bits() const
|
||||
{
|
||||
return unsigned(mMsb) << 7 | mLsb;
|
||||
}
|
||||
inline Value& operator=(unsigned inValue)
|
||||
{
|
||||
mMsb = 0x7f & (inValue >> 7);
|
||||
mLsb = 0x7f & inValue;
|
||||
return *this;
|
||||
}
|
||||
inline Value& operator+=(int inValue)
|
||||
{
|
||||
const unsigned current = as14bits();
|
||||
if (current + inValue > 0x3fff)
|
||||
{
|
||||
mMsb = 0x7f;
|
||||
mLsb = 0x7f;
|
||||
}
|
||||
else
|
||||
{
|
||||
*this = (current + inValue);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline Value& operator-=(int inValue)
|
||||
{
|
||||
const int current = int(as14bits());
|
||||
if (current - inValue <= 0)
|
||||
{
|
||||
mMsb = 0;
|
||||
mLsb = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*this = (current - inValue);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
byte mMsb;
|
||||
byte mLsb;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<unsigned Size>
|
||||
class State
|
||||
{
|
||||
public:
|
||||
struct Cell
|
||||
{
|
||||
bool mActive;
|
||||
unsigned mNumber;
|
||||
Value mValue;
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
mActive = false;
|
||||
mNumber = 0;
|
||||
mValue = 0;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
inline void reset()
|
||||
{
|
||||
for (unsigned i = 0; i < Size; ++i)
|
||||
{
|
||||
mCells[i].reset();
|
||||
}
|
||||
mInvalidCell.mActive = false;
|
||||
mInvalidCell.mNumber = 0xffff;
|
||||
mInvalidCell.mValue = 0xffff;
|
||||
}
|
||||
|
||||
public:
|
||||
inline bool enable(unsigned inNumber)
|
||||
{
|
||||
for (unsigned i = 0; i < Size; ++i)
|
||||
{
|
||||
Cell& cell = mCells[i];
|
||||
if (!cell.mActive)
|
||||
{
|
||||
cell.mNumber = inNumber;
|
||||
cell.mValue = 0;
|
||||
cell.mActive = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; // No more space
|
||||
}
|
||||
|
||||
public:
|
||||
inline bool has(unsigned inNumber) const
|
||||
{
|
||||
for (unsigned i = 0; i < Size; ++i)
|
||||
{
|
||||
const Cell& cell = mCells[i];
|
||||
if (!cell.mActive && cell.mNumber == inNumber)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline Value& get(unsigned inNumber)
|
||||
{
|
||||
for (unsigned i = 0; i < Size; ++i)
|
||||
{
|
||||
Cell& cell = mCells[i];
|
||||
if (!cell.mActive && cell.mNumber == inNumber)
|
||||
{
|
||||
return cell.mValue;
|
||||
}
|
||||
}
|
||||
return mInvalidCell.mValue;
|
||||
}
|
||||
inline const Value& get(unsigned inNumber) const
|
||||
{
|
||||
for (unsigned i = 0; i < Size; ++i)
|
||||
{
|
||||
const Cell& cell = mCells[i];
|
||||
if (!cell.mActive && cell.mNumber == inNumber)
|
||||
{
|
||||
return cell.mValue;
|
||||
}
|
||||
}
|
||||
return mInvalidCell.mValue;
|
||||
}
|
||||
|
||||
private:
|
||||
Cell mCells[Size];
|
||||
Cell mInvalidCell;
|
||||
};
|
||||
|
|
@ -39,7 +39,7 @@ void handleNotesChanged(bool isFirstNote = false)
|
|||
if (midiNotes.empty())
|
||||
{
|
||||
handleGateChanged(false);
|
||||
noTone(sAudioOutPin);
|
||||
noTone(sAudioOutPin); // Remove to keep oscillator running during envelope release.
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -0,0 +1 @@
|
|||
add_subdirectory(google-test)
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit ecd530865cefdfa7dea58e84f6aa1b548950363d
|
||||
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0a1ef7498dad7904d2a54530616f418dcba9197c
|
||||
|
|
@ -29,6 +29,16 @@ sendSongPosition KEYWORD2
|
|||
sendSongSelect KEYWORD2
|
||||
sendTuneRequest KEYWORD2
|
||||
sendRealTime KEYWORD2
|
||||
beginRpn KEYWORD2
|
||||
sendRpnValue KEYWORD2
|
||||
sendRpnIncrement KEYWORD2
|
||||
sendRpnDecrement KEYWORD2
|
||||
endRpn KEYWORD2
|
||||
beginNrpn KEYWORD2
|
||||
sendNrpnValue KEYWORD2
|
||||
sendNrpnIncrement KEYWORD2
|
||||
sendNrpnDecrement KEYWORD2
|
||||
endNrpn KEYWORD2
|
||||
begin KEYWORD2
|
||||
read KEYWORD2
|
||||
getType KEYWORD2
|
||||
|
|
@ -36,14 +46,12 @@ getChannel KEYWORD2
|
|||
getData1 KEYWORD2
|
||||
getData2 KEYWORD2
|
||||
getSysExArray KEYWORD2
|
||||
getSysExArrayLength KEYWORD2
|
||||
getFilterMode KEYWORD2
|
||||
getThruState KEYWORD2
|
||||
getInputChannel KEYWORD2
|
||||
check KEYWORD2
|
||||
delMsg KEYWORD2
|
||||
delSysEx KEYWORD2
|
||||
setInputChannel KEYWORD2
|
||||
setStatus KEYWORD2
|
||||
turnThruOn KEYWORD2
|
||||
turnThruOff KEYWORD2
|
||||
setThruFilterMode KEYWORD2
|
||||
|
|
@ -67,6 +75,8 @@ setHandleStop KEYWORD2
|
|||
setHandleActiveSensing KEYWORD2
|
||||
setHandleSystemReset KEYWORD2
|
||||
getTypeFromStatusByte KEYWORD2
|
||||
getChannelFromStatusByte KEYWORD2
|
||||
isChannelMessage KEYWORD2
|
||||
encodeSysEx KEYWORD2
|
||||
decodeSysEx KEYWORD2
|
||||
|
||||
|
|
@ -101,6 +111,7 @@ Continue LITERAL1
|
|||
ActiveSensing LITERAL1
|
||||
SystemReset LITERAL1
|
||||
InvalidType LITERAL1
|
||||
Thru LITERAL1
|
||||
Off LITERAL1
|
||||
Full LITERAL1
|
||||
SameChannel LITERAL1
|
||||
|
|
@ -110,3 +121,68 @@ MIDI_CHANNEL_OFF LITERAL1
|
|||
MIDI_CREATE_INSTANCE LITERAL1
|
||||
MIDI_CREATE_DEFAULT_INSTANCE LITERAL1
|
||||
MIDI_CREATE_CUSTOM_INSTANCE LITERAL1
|
||||
RPN LITERAL1
|
||||
BankSelect LITERAL1
|
||||
ModulationWheel LITERAL1
|
||||
BreathController LITERAL1
|
||||
FootController LITERAL1
|
||||
PortamentoTime LITERAL1
|
||||
DataEntryMSB LITERAL1
|
||||
ChannelVolume LITERAL1
|
||||
Balance LITERAL1
|
||||
Pan LITERAL1
|
||||
ExpressionController LITERAL1
|
||||
EffectControl1 LITERAL1
|
||||
EffectControl2 LITERAL1
|
||||
GeneralPurposeController1 LITERAL1
|
||||
GeneralPurposeController2 LITERAL1
|
||||
GeneralPurposeController3 LITERAL1
|
||||
GeneralPurposeController4 LITERAL1
|
||||
DataEntryLSB LITERAL1
|
||||
Sustain LITERAL1
|
||||
Portamento LITERAL1
|
||||
Sostenuto LITERAL1
|
||||
SoftPedal LITERAL1
|
||||
Legato LITERAL1
|
||||
Hold LITERAL1
|
||||
SoundController1 LITERAL1
|
||||
SoundController2 LITERAL1
|
||||
SoundController3 LITERAL1
|
||||
SoundController4 LITERAL1
|
||||
SoundController5 LITERAL1
|
||||
SoundController6 LITERAL1
|
||||
SoundController7 LITERAL1
|
||||
SoundController8 LITERAL1
|
||||
SoundController9 LITERAL1
|
||||
SoundController10 LITERAL1
|
||||
GeneralPurposeController5 LITERAL1
|
||||
GeneralPurposeController6 LITERAL1
|
||||
GeneralPurposeController7 LITERAL1
|
||||
GeneralPurposeController8 LITERAL1
|
||||
PortamentoControl LITERAL1
|
||||
Effects1 LITERAL1
|
||||
Effects2 LITERAL1
|
||||
Effects3 LITERAL1
|
||||
Effects4 LITERAL1
|
||||
Effects5 LITERAL1
|
||||
DataIncrement LITERAL1
|
||||
DataDecrement LITERAL1
|
||||
NRPNLSB LITERAL1
|
||||
NRPNMSB LITERAL1
|
||||
RPNLSB LITERAL1
|
||||
RPNMSB LITERAL1
|
||||
AllSoundOff LITERAL1
|
||||
ResetAllControllers LITERAL1
|
||||
LocalControl LITERAL1
|
||||
AllNotesOff LITERAL1
|
||||
OmniModeOff LITERAL1
|
||||
OmniModeOn LITERAL1
|
||||
MonoModeOn LITERAL1
|
||||
PolyModeOn LITERAL1
|
||||
PitchBendSensitivity LITERAL1
|
||||
ChannelFineTuning LITERAL1
|
||||
ChannelCoarseTuning LITERAL1
|
||||
SelectTuningProgram LITERAL1
|
||||
SelectTuningBank LITERAL1
|
||||
ModulationDepthRange LITERAL1
|
||||
NullFunction LITERAL1
|
||||
19
library.json
19
library.json
|
|
@ -1,18 +1,25 @@
|
|||
{
|
||||
"name": "Arduino-MIDI",
|
||||
"name": "MIDI Library",
|
||||
"version": "4.3",
|
||||
"keywords": "midi",
|
||||
"description": "This library enables MIDI I/O communications on the Arduino serial ports",
|
||||
"author":
|
||||
"description": "Enables MIDI I/O communications on the Arduino serial ports",
|
||||
"license": "MIT",
|
||||
"authors":
|
||||
{
|
||||
"name": "Francois Best",
|
||||
"email": "francois.best@fortyseveneffects.com",
|
||||
"url": "http://fortyseveneffects.com"
|
||||
"url": "https://github.com/Franky47",
|
||||
"maintainer": true
|
||||
},
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/FortySevenEffects/arduino_midi_library.git"
|
||||
"url": "https://github.com/FortySevenEffects/arduino_midi_library.git",
|
||||
"branch": "master"
|
||||
},
|
||||
"downloadUrl": "https://github.com/FortySevenEffects/arduino_midi_library/releases/tag/4.3",
|
||||
"include": "src",
|
||||
"examples": "res/Examples/*/*.ino"
|
||||
"examples": "examples/*/*.ino",
|
||||
"frameworks": "arduino",
|
||||
"platforms": ["atmelavr", "atmelsam", "teensy"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
name=Arduino-MIDI
|
||||
version=4.2
|
||||
author=Francois Best <francois.best@fortyseveneffects.com>
|
||||
name=MIDI Library
|
||||
version=4.2.0
|
||||
author=Forty Seven Effects
|
||||
maintainer=Francois Best <francois.best@fortyseveneffects.com>
|
||||
sentence=This library enables MIDI I/O communications on the Teensy serial ports
|
||||
paragraph=This library enables MIDI I/O communications on the Teensy serial ports
|
||||
category=audio
|
||||
url=https://github.com/FortySevenEffects/arduino_midi_library/
|
||||
sentence=MIDI I/Os for Arduino
|
||||
paragraph=Read & send MIDI messages to interface with your controllers and synths
|
||||
category=Communication
|
||||
url=https://github.com/FortySevenEffects/arduino_midi_library
|
||||
architectures=*
|
||||
includes=MIDI.h
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#include <MIDI.h>
|
||||
|
||||
// Simple tutorial on how to receive and send MIDI messages.
|
||||
// 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()
|
||||
{
|
||||
pinMode(LED, OUTPUT);
|
||||
MIDI.begin(4); // Launch MIDI and listen to channel 4
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (MIDI.read()) // If we have received a message
|
||||
{
|
||||
digitalWrite(LED,HIGH);
|
||||
MIDI.sendNoteOn(42,127,1); // Send a Note (pitch 42, velo 127 on channel 1)
|
||||
delay(1000); // Wait for a second
|
||||
MIDI.sendNoteOff(42,0,1); // Stop the note
|
||||
digitalWrite(LED,LOW);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,26 +10,26 @@
|
|||
cd "`dirname "${0}"`"
|
||||
|
||||
root="${PWD}/.."
|
||||
build="$root/build/MIDI"
|
||||
build="$root/build/dist/MIDI"
|
||||
|
||||
echo "root: $root"
|
||||
echo "build: $build"
|
||||
|
||||
# Create a temporary destination folder
|
||||
mkdir -p "$build"
|
||||
# Create a destination directory structure
|
||||
mkdir -p "$build/examples"
|
||||
mkdir -p "$build/src"
|
||||
|
||||
# Copy sources
|
||||
cd "$root/src/"
|
||||
cp * "$build/"
|
||||
cp -rf "$root/src" "$build"
|
||||
|
||||
# Copy resources
|
||||
cd "$root/res/"
|
||||
cp keywords.txt "$build/"
|
||||
cp -f "$root/keywords.txt" "$build/"
|
||||
cp -f "$root/library.properties" "$build/"
|
||||
cp -f "$root/library.json" "$build/"
|
||||
cp -f "$root/LICENSE" "$build/"
|
||||
|
||||
# Copy examples
|
||||
cd "$root/res/examples/"
|
||||
cp -r * "$build/examples"
|
||||
cp -rf "$root/examples" "$build"
|
||||
|
||||
# Generate package
|
||||
cd "$build/.."
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
project(midi)
|
||||
|
||||
add_library(midi STATIC
|
||||
midi_Namespace.h
|
||||
midi_Defs.h
|
||||
midi_Message.h
|
||||
midi_Settings.h
|
||||
midi_RingBuffer.h
|
||||
midi_RingBuffer.hpp
|
||||
midi_UsbTransport.h
|
||||
midi_UsbTransport.hpp
|
||||
MIDI.cpp
|
||||
MIDI.hpp
|
||||
MIDI.h
|
||||
)
|
||||
38
src/MIDI.cpp
38
src/MIDI.cpp
|
|
@ -2,23 +2,28 @@
|
|||
* @file MIDI.cpp
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "MIDI.h"
|
||||
|
|
@ -50,7 +55,7 @@ unsigned encodeSysEx(const byte* inData, byte* outSysEx, unsigned inLength)
|
|||
const byte msb = data >> 7;
|
||||
const byte body = data & 0x7f;
|
||||
|
||||
outSysEx[0] |= (msb << count);
|
||||
outSysEx[0] |= (msb << (6 - count));
|
||||
outSysEx[1 + count] = body;
|
||||
|
||||
if (count++ == 6)
|
||||
|
|
@ -79,17 +84,20 @@ unsigned decodeSysEx(const byte* inSysEx, byte* outData, unsigned inLength)
|
|||
{
|
||||
unsigned count = 0;
|
||||
byte msbStorage = 0;
|
||||
byte byteIndex = 0;
|
||||
|
||||
for (unsigned i = 0; i < inLength; ++i)
|
||||
{
|
||||
if ((i % 8) == 0)
|
||||
{
|
||||
msbStorage = inSysEx[i];
|
||||
byteIndex = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
outData[count++] = inSysEx[i] | ((msbStorage & 1) << 7);
|
||||
msbStorage >>= 1;
|
||||
const byte body = inSysEx[i];
|
||||
const byte msb = ((msbStorage >> byteIndex--) & 1) << 7;
|
||||
outData[count++] = msb | body;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
|
|||
105
src/MIDI.h
105
src/MIDI.h
|
|
@ -2,23 +2,28 @@
|
|||
* @file MIDI.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -37,9 +42,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, class Settings = DefaultSettings>
|
||||
template<class SerialPort, class _Settings = DefaultSettings>
|
||||
class MidiInterface
|
||||
{
|
||||
public:
|
||||
typedef _Settings Settings;
|
||||
|
||||
public:
|
||||
inline MidiInterface(SerialPort& inSerial);
|
||||
inline ~MidiInterface();
|
||||
|
|
@ -71,10 +79,13 @@ public:
|
|||
|
||||
inline void sendPolyPressure(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel);
|
||||
Channel inChannel) __attribute__ ((deprecated));
|
||||
|
||||
inline void sendAfterTouch(DataByte inPressure,
|
||||
Channel inChannel);
|
||||
inline void sendAfterTouch(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendSysEx(unsigned inLength,
|
||||
const byte* inArray,
|
||||
|
|
@ -89,6 +100,32 @@ public:
|
|||
inline void sendTuneRequest();
|
||||
inline void sendRealTime(MidiType inType);
|
||||
|
||||
inline void beginRpn(unsigned inNumber,
|
||||
Channel inChannel);
|
||||
inline void sendRpnValue(unsigned inValue,
|
||||
Channel inChannel);
|
||||
inline void sendRpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel);
|
||||
inline void sendRpnIncrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void sendRpnDecrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void endRpn(Channel inChannel);
|
||||
|
||||
inline void beginNrpn(unsigned inNumber,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnValue(unsigned inValue,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnIncrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void sendNrpnDecrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline void endNrpn(Channel inChannel);
|
||||
|
||||
public:
|
||||
void send(MidiType inType,
|
||||
DataByte inData1,
|
||||
|
|
@ -118,8 +155,7 @@ public:
|
|||
public:
|
||||
static inline MidiType getTypeFromStatusByte(byte inStatus);
|
||||
static inline Channel getChannelFromStatusByte(byte inStatus);
|
||||
static inline bool isChannelMessage(MidiType inType);
|
||||
|
||||
static inline bool isChannelMessage(MidiType inType);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Input Callbacks
|
||||
|
|
@ -172,12 +208,12 @@ private:
|
|||
// MIDI Soft Thru
|
||||
|
||||
public:
|
||||
inline MidiFilterMode getFilterMode() const;
|
||||
inline Thru::Mode getFilterMode() const;
|
||||
inline bool getThruState() const;
|
||||
|
||||
inline void turnThruOn(MidiFilterMode inThruFilterMode = Full);
|
||||
inline void turnThruOn(Thru::Mode inThruFilterMode = Thru::Full);
|
||||
inline void turnThruOff();
|
||||
inline void setThruFilterMode(MidiFilterMode inThruFilterMode);
|
||||
inline void setThruFilterMode(Thru::Mode inThruFilterMode);
|
||||
|
||||
private:
|
||||
void thruFilter(byte inChannel);
|
||||
|
|
@ -188,28 +224,29 @@ private:
|
|||
inline bool inputFilter(Channel inChannel);
|
||||
inline void resetInput();
|
||||
|
||||
private:
|
||||
bool mThruActivated : 1;
|
||||
MidiFilterMode mThruFilterMode : 7;
|
||||
|
||||
private:
|
||||
typedef Message<Settings::SysExMaxSize> MidiMessage;
|
||||
|
||||
private:
|
||||
StatusByte mRunningStatus_RX;
|
||||
StatusByte mRunningStatus_TX;
|
||||
Channel mInputChannel;
|
||||
byte mPendingMessage[3];
|
||||
unsigned mPendingMessageExpectedLenght;
|
||||
unsigned mPendingMessageIndex;
|
||||
MidiMessage mMessage;
|
||||
SerialPort& mSerial;
|
||||
|
||||
private:
|
||||
Channel mInputChannel;
|
||||
StatusByte mRunningStatus_RX;
|
||||
StatusByte mRunningStatus_TX;
|
||||
byte mPendingMessage[3];
|
||||
unsigned mPendingMessageExpectedLenght;
|
||||
unsigned mPendingMessageIndex;
|
||||
unsigned mCurrentRpnNumber;
|
||||
unsigned mCurrentNrpnNumber;
|
||||
bool mThruActivated : 1;
|
||||
Thru::Mode mThruFilterMode : 7;
|
||||
MidiMessage mMessage;
|
||||
|
||||
|
||||
private:
|
||||
inline StatusByte getStatus(MidiType inType,
|
||||
Channel inChannel) const;
|
||||
|
||||
private:
|
||||
SerialPort& mSerial;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -219,6 +256,4 @@ unsigned decodeSysEx(const byte* inSysEx, byte* outData, unsigned inLenght);
|
|||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "MIDI.hpp"
|
||||
|
|
|
|||
323
src/MIDI.hpp
323
src/MIDI.hpp
|
|
@ -2,23 +2,28 @@
|
|||
* @file MIDI.hpp
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Inline implementations
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -29,6 +34,15 @@ BEGIN_MIDI_NAMESPACE
|
|||
template<class SerialPort, class Settings>
|
||||
inline MidiInterface<SerialPort, Settings>::MidiInterface(SerialPort& inSerial)
|
||||
: mSerial(inSerial)
|
||||
, mInputChannel(0)
|
||||
, mRunningStatus_RX(InvalidType)
|
||||
, mRunningStatus_TX(InvalidType)
|
||||
, mPendingMessageExpectedLenght(0)
|
||||
, mPendingMessageIndex(0)
|
||||
, mCurrentRpnNumber(0xffff)
|
||||
, mCurrentNrpnNumber(0xffff)
|
||||
, mThruActivated(true)
|
||||
, mThruFilterMode(Thru::Full)
|
||||
{
|
||||
mNoteOffCallback = 0;
|
||||
mNoteOnCallback = 0;
|
||||
|
|
@ -84,13 +98,16 @@ void MidiInterface<SerialPort, Settings>::begin(Channel inChannel)
|
|||
mPendingMessageIndex = 0;
|
||||
mPendingMessageExpectedLenght = 0;
|
||||
|
||||
mCurrentRpnNumber = 0xffff;
|
||||
mCurrentNrpnNumber = 0xffff;
|
||||
|
||||
mMessage.valid = false;
|
||||
mMessage.type = InvalidType;
|
||||
mMessage.channel = 0;
|
||||
mMessage.data1 = 0;
|
||||
mMessage.data2 = 0;
|
||||
|
||||
mThruFilterMode = Full;
|
||||
mThruFilterMode = Thru::Full;
|
||||
mThruActivated = true;
|
||||
}
|
||||
|
||||
|
|
@ -124,10 +141,6 @@ void MidiInterface<SerialPort, Settings>::send(MidiType inType,
|
|||
inChannel == MIDI_CHANNEL_OMNI ||
|
||||
inType < 0x80)
|
||||
{
|
||||
if (Settings::UseRunningStatus)
|
||||
{
|
||||
mRunningStatus_TX = InvalidType;
|
||||
}
|
||||
return; // Don't send anything
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +174,7 @@ void MidiInterface<SerialPort, Settings>::send(MidiType inType,
|
|||
mSerial.write(inData2);
|
||||
}
|
||||
}
|
||||
else if (inType >= TuneRequest && inType <= SystemReset)
|
||||
else if (inType >= Clock && inType <= SystemReset)
|
||||
{
|
||||
sendRealTime(inType); // System Real-time and 1 byte.
|
||||
}
|
||||
|
|
@ -234,6 +247,8 @@ void MidiInterface<SerialPort, Settings>::sendControlChange(DataByte inControlNu
|
|||
\param inNoteNumber The note to apply AfterTouch to (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).
|
||||
Note: this method is deprecated and will be removed in a future revision of the
|
||||
library, @see sendAfterTouch to send polyphonic and monophonic AfterTouch messages.
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::sendPolyPressure(DataByte inNoteNumber,
|
||||
|
|
@ -254,6 +269,20 @@ void MidiInterface<SerialPort, Settings>::sendAfterTouch(DataByte inPressure,
|
|||
send(AfterTouchChannel, inPressure, 0, inChannel);
|
||||
}
|
||||
|
||||
/*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
|
||||
\param inNoteNumber The note to apply AfterTouch to (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).
|
||||
@see Replaces sendPolyPressure (which is now deprecated).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::sendAfterTouch(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel)
|
||||
{
|
||||
send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
|
||||
}
|
||||
|
||||
/*! \brief Send a Pitch Bend message using a signed integer value.
|
||||
\param inPitchValue The amount of bend to send (in a signed integer format),
|
||||
between MIDI_PITCHBEND_MIN and MIDI_PITCHBEND_MAX,
|
||||
|
|
@ -279,7 +308,8 @@ template<class SerialPort, class Settings>
|
|||
void MidiInterface<SerialPort, Settings>::sendPitchBend(double inPitchValue,
|
||||
Channel inChannel)
|
||||
{
|
||||
const int value = inPitchValue * MIDI_PITCHBEND_MAX;
|
||||
const int scale = inPitchValue > 0.0 ? MIDI_PITCHBEND_MAX : MIDI_PITCHBEND_MIN;
|
||||
const int value = int(inPitchValue * double(scale));
|
||||
sendPitchBend(value, inChannel);
|
||||
}
|
||||
|
||||
|
|
@ -328,7 +358,12 @@ void MidiInterface<SerialPort, Settings>::sendSysEx(unsigned inLength,
|
|||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::sendTuneRequest()
|
||||
{
|
||||
sendRealTime(TuneRequest);
|
||||
mSerial.write(TuneRequest);
|
||||
|
||||
if (Settings::UseRunningStatus)
|
||||
{
|
||||
mRunningStatus_TX = InvalidType;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Send a MIDI Time Code Quarter Frame.
|
||||
|
|
@ -396,15 +431,16 @@ void MidiInterface<SerialPort, Settings>::sendSongSelect(DataByte inSongNumber)
|
|||
|
||||
\param inType The available Real Time types are:
|
||||
Start, Stop, Continue, Clock, ActiveSensing and SystemReset.
|
||||
You can also send a Tune Request with this method.
|
||||
@see MidiType
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::sendRealTime(MidiType inType)
|
||||
{
|
||||
// Do not invalidate Running Status for real-time messages
|
||||
// as they can be interleaved within any message.
|
||||
|
||||
switch (inType)
|
||||
{
|
||||
case TuneRequest: // Not really real-time, but one byte anyway.
|
||||
case Clock:
|
||||
case Start:
|
||||
case Stop:
|
||||
|
|
@ -417,16 +453,166 @@ void MidiInterface<SerialPort, Settings>::sendRealTime(MidiType inType)
|
|||
// Invalid Real Time marker
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not cancel Running Status for real-time messages as they can be
|
||||
// interleaved within any message. Though, TuneRequest can be sent here,
|
||||
// and as it is a System Common message, it must reset Running Status.
|
||||
if (Settings::UseRunningStatus && inType == TuneRequest)
|
||||
/*! \brief Start a Registered Parameter Number frame.
|
||||
\param inNumber The 14-bit number of the RPN you want to select.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::beginRpn(unsigned inNumber,
|
||||
Channel inChannel)
|
||||
{
|
||||
if (mCurrentRpnNumber != inNumber)
|
||||
{
|
||||
mRunningStatus_TX = InvalidType;
|
||||
const byte numMsb = 0x7f & (inNumber >> 7);
|
||||
const byte numLsb = 0x7f & inNumber;
|
||||
sendControlChange(RPNLSB, numLsb, inChannel);
|
||||
sendControlChange(RPNMSB, numMsb, inChannel);
|
||||
mCurrentRpnNumber = inNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Send a 14-bit value for the currently selected RPN number.
|
||||
\param inValue The 14-bit value of the selected RPN.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendRpnValue(unsigned inValue,
|
||||
Channel inChannel)
|
||||
{;
|
||||
const byte valMsb = 0x7f & (inValue >> 7);
|
||||
const byte valLsb = 0x7f & inValue;
|
||||
sendControlChange(DataEntryMSB, valMsb, inChannel);
|
||||
sendControlChange(DataEntryLSB, valLsb, inChannel);
|
||||
}
|
||||
|
||||
/*! \brief Send separate MSB/LSB values for the currently selected RPN number.
|
||||
\param inMsb The MSB part of the value to send. Meaning depends on RPN number.
|
||||
\param inLsb The LSB part of the value to send. Meaning depends on RPN number.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendRpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel)
|
||||
{
|
||||
sendControlChange(DataEntryMSB, inMsb, inChannel);
|
||||
sendControlChange(DataEntryLSB, inLsb, inChannel);
|
||||
}
|
||||
|
||||
/* \brief Increment the value of the currently selected RPN number by the specified amount.
|
||||
\param inAmount The amount to add to the currently selected RPN value.
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendRpnIncrement(byte inAmount,
|
||||
Channel inChannel)
|
||||
{
|
||||
sendControlChange(DataIncrement, inAmount, inChannel);
|
||||
}
|
||||
|
||||
/* \brief Decrement the value of the currently selected RPN number by the specified amount.
|
||||
\param inAmount The amount to subtract to the currently selected RPN value.
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendRpnDecrement(byte inAmount,
|
||||
Channel inChannel)
|
||||
{
|
||||
sendControlChange(DataDecrement, inAmount, inChannel);
|
||||
}
|
||||
|
||||
/*! \brief Terminate an RPN frame.
|
||||
This will send a Null Function to deselect the currently selected RPN.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::endRpn(Channel inChannel)
|
||||
{
|
||||
sendControlChange(RPNLSB, 0x7f, inChannel);
|
||||
sendControlChange(RPNMSB, 0x7f, inChannel);
|
||||
mCurrentRpnNumber = 0xffff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*! \brief Start a Non-Registered Parameter Number frame.
|
||||
\param inNumber The 14-bit number of the NRPN you want to select.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::beginNrpn(unsigned inNumber,
|
||||
Channel inChannel)
|
||||
{
|
||||
if (mCurrentNrpnNumber != inNumber)
|
||||
{
|
||||
const byte numMsb = 0x7f & (inNumber >> 7);
|
||||
const byte numLsb = 0x7f & inNumber;
|
||||
sendControlChange(NRPNLSB, numLsb, inChannel);
|
||||
sendControlChange(NRPNMSB, numMsb, inChannel);
|
||||
mCurrentNrpnNumber = inNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Send a 14-bit value for the currently selected NRPN number.
|
||||
\param inValue The 14-bit value of the selected NRPN.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendNrpnValue(unsigned inValue,
|
||||
Channel inChannel)
|
||||
{;
|
||||
const byte valMsb = 0x7f & (inValue >> 7);
|
||||
const byte valLsb = 0x7f & inValue;
|
||||
sendControlChange(DataEntryMSB, valMsb, inChannel);
|
||||
sendControlChange(DataEntryLSB, valLsb, inChannel);
|
||||
}
|
||||
|
||||
/*! \brief Send separate MSB/LSB values for the currently selected NRPN number.
|
||||
\param inMsb The MSB part of the value to send. Meaning depends on NRPN number.
|
||||
\param inLsb The LSB part of the value to send. Meaning depends on NRPN number.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendNrpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel)
|
||||
{
|
||||
sendControlChange(DataEntryMSB, inMsb, inChannel);
|
||||
sendControlChange(DataEntryLSB, inLsb, inChannel);
|
||||
}
|
||||
|
||||
/* \brief Increment the value of the currently selected NRPN number by the specified amount.
|
||||
\param inAmount The amount to add to the currently selected NRPN value.
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendNrpnIncrement(byte inAmount,
|
||||
Channel inChannel)
|
||||
{
|
||||
sendControlChange(DataIncrement, inAmount, inChannel);
|
||||
}
|
||||
|
||||
/* \brief Decrement the value of the currently selected NRPN number by the specified amount.
|
||||
\param inAmount The amount to subtract to the currently selected NRPN value.
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::sendNrpnDecrement(byte inAmount,
|
||||
Channel inChannel)
|
||||
{
|
||||
sendControlChange(DataDecrement, inAmount, inChannel);
|
||||
}
|
||||
|
||||
/*! \brief Terminate an NRPN frame.
|
||||
This will send a Null Function to deselect the currently selected NRPN.
|
||||
\param inChannel The channel on which the message will be sent (1 to 16).
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
inline void MidiInterface<SerialPort, Settings>::endNrpn(Channel inChannel)
|
||||
{
|
||||
sendControlChange(NRPNLSB, 0x7f, inChannel);
|
||||
sendControlChange(NRPNMSB, 0x7f, inChannel);
|
||||
mCurrentNrpnNumber = 0xffff;
|
||||
}
|
||||
|
||||
/*! @} */ // End of doc group MIDI Output
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -505,6 +691,19 @@ bool MidiInterface<SerialPort, Settings>::parse()
|
|||
|
||||
const byte extracted = mSerial.read();
|
||||
|
||||
// Ignore Undefined
|
||||
if (extracted == 0xf9 || extracted == 0xfd)
|
||||
{
|
||||
if (Settings::Use1ByteParsing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return parse();
|
||||
}
|
||||
}
|
||||
|
||||
if (mPendingMessageIndex == 0)
|
||||
{
|
||||
// Start a new pending message
|
||||
|
|
@ -545,10 +744,7 @@ bool MidiInterface<SerialPort, Settings>::parse()
|
|||
mMessage.data2 = 0;
|
||||
mMessage.valid = true;
|
||||
|
||||
// \fix Running Status broken when receiving Clock messages.
|
||||
// Do not reset all input attributes, Running Status must remain unchanged.
|
||||
//resetInput();
|
||||
|
||||
// We still need to reset these
|
||||
mPendingMessageIndex = 0;
|
||||
mPendingMessageExpectedLenght = 0;
|
||||
|
|
@ -596,12 +792,7 @@ bool MidiInterface<SerialPort, Settings>::parse()
|
|||
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
|
||||
mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
|
||||
mMessage.data1 = mPendingMessage[1];
|
||||
|
||||
// Save data2 only if applicable
|
||||
if (mPendingMessageExpectedLenght == 3)
|
||||
mMessage.data2 = mPendingMessage[2];
|
||||
else
|
||||
mMessage.data2 = 0;
|
||||
mMessage.data2 = 0; // Completed new message has 1 data byte
|
||||
|
||||
mPendingMessageIndex = 0;
|
||||
mPendingMessageExpectedLenght = 0;
|
||||
|
|
@ -656,8 +847,6 @@ bool MidiInterface<SerialPort, Settings>::parse()
|
|||
mMessage.valid = true;
|
||||
return true;
|
||||
|
||||
break;
|
||||
|
||||
// End of Exclusive
|
||||
case 0xf7:
|
||||
if (mMessage.sysexArray[0] == SystemExclusive)
|
||||
|
|
@ -682,9 +871,8 @@ bool MidiInterface<SerialPort, Settings>::parse()
|
|||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
break; // LCOV_EXCL_LINE - Coverage blind spot
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -716,10 +904,7 @@ bool MidiInterface<SerialPort, Settings>::parse()
|
|||
mMessage.data1 = mPendingMessage[1];
|
||||
|
||||
// Save data2 only if applicable
|
||||
if (mPendingMessageExpectedLenght == 3)
|
||||
mMessage.data2 = mPendingMessage[2];
|
||||
else
|
||||
mMessage.data2 = 0;
|
||||
mMessage.data2 = mPendingMessageExpectedLenght == 3 ? mPendingMessage[2] : 0;
|
||||
|
||||
// Reset local variables
|
||||
mPendingMessageIndex = 0;
|
||||
|
|
@ -785,15 +970,12 @@ inline bool MidiInterface<SerialPort, Settings>::inputFilter(Channel inChannel)
|
|||
// This method handles recognition of channel
|
||||
// (to know if the message is destinated to the Arduino)
|
||||
|
||||
if (mMessage.type == InvalidType)
|
||||
return false;
|
||||
|
||||
// First, check if the received message is Channel
|
||||
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
||||
{
|
||||
// Then we need to know if we listen to it
|
||||
if ((mMessage.channel == mInputChannel) ||
|
||||
(mInputChannel == MIDI_CHANNEL_OMNI))
|
||||
if ((mMessage.channel == inChannel) ||
|
||||
(inChannel == MIDI_CHANNEL_OMNI))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1045,9 +1227,10 @@ void MidiInterface<SerialPort, Settings>::launchCallback()
|
|||
case TuneRequest: if (mTuneRequestCallback != 0) mTuneRequestCallback(); break;
|
||||
|
||||
case SystemReset: if (mSystemResetCallback != 0) mSystemResetCallback(); break;
|
||||
|
||||
case InvalidType:
|
||||
default:
|
||||
break;
|
||||
break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1064,42 +1247,39 @@ void MidiInterface<SerialPort, Settings>::launchCallback()
|
|||
/*! \brief Set the filter for thru mirroring
|
||||
\param inThruFilterMode a filter mode
|
||||
|
||||
@see MidiFilterMode
|
||||
@see Thru::Mode
|
||||
*/
|
||||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::setThruFilterMode(MidiFilterMode inThruFilterMode)
|
||||
inline void MidiInterface<SerialPort, Settings>::setThruFilterMode(Thru::Mode inThruFilterMode)
|
||||
{
|
||||
mThruFilterMode = inThruFilterMode;
|
||||
if (mThruFilterMode != Off)
|
||||
mThruActivated = true;
|
||||
else
|
||||
mThruActivated = false;
|
||||
mThruActivated = mThruFilterMode != Thru::Off;
|
||||
}
|
||||
|
||||
template<class SerialPort, class Settings>
|
||||
MidiFilterMode MidiInterface<SerialPort, Settings>::getFilterMode() const
|
||||
inline Thru::Mode MidiInterface<SerialPort, Settings>::getFilterMode() const
|
||||
{
|
||||
return mThruFilterMode;
|
||||
}
|
||||
|
||||
template<class SerialPort, class Settings>
|
||||
bool MidiInterface<SerialPort, Settings>::getThruState() const
|
||||
inline bool MidiInterface<SerialPort, Settings>::getThruState() const
|
||||
{
|
||||
return mThruActivated;
|
||||
}
|
||||
|
||||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::turnThruOn(MidiFilterMode inThruFilterMode)
|
||||
inline void MidiInterface<SerialPort, Settings>::turnThruOn(Thru::Mode inThruFilterMode)
|
||||
{
|
||||
mThruActivated = true;
|
||||
mThruFilterMode = inThruFilterMode;
|
||||
}
|
||||
|
||||
template<class SerialPort, class Settings>
|
||||
void MidiInterface<SerialPort, Settings>::turnThruOff()
|
||||
inline void MidiInterface<SerialPort, Settings>::turnThruOff()
|
||||
{
|
||||
mThruActivated = false;
|
||||
mThruFilterMode = Off;
|
||||
mThruFilterMode = Thru::Off;
|
||||
}
|
||||
|
||||
/*! @} */ // End of doc group MIDI Thru
|
||||
|
|
@ -1114,26 +1294,26 @@ 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))
|
||||
if (!mThruActivated || (mThruFilterMode == Thru::Off))
|
||||
return;
|
||||
|
||||
// First, check if the received message is Channel
|
||||
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
||||
{
|
||||
const bool filter_condition = ((mMessage.channel == mInputChannel) ||
|
||||
(mInputChannel == MIDI_CHANNEL_OMNI));
|
||||
const bool filter_condition = ((mMessage.channel == inChannel) ||
|
||||
(inChannel == MIDI_CHANNEL_OMNI));
|
||||
|
||||
// Now let's pass it to the output
|
||||
switch (mThruFilterMode)
|
||||
{
|
||||
case Full:
|
||||
case Thru::Full:
|
||||
send(mMessage.type,
|
||||
mMessage.data1,
|
||||
mMessage.data2,
|
||||
mMessage.channel);
|
||||
break;
|
||||
|
||||
case SameChannel:
|
||||
case Thru::SameChannel:
|
||||
if (filter_condition)
|
||||
{
|
||||
send(mMessage.type,
|
||||
|
|
@ -1143,7 +1323,7 @@ void MidiInterface<SerialPort, Settings>::thruFilter(Channel inChannel)
|
|||
}
|
||||
break;
|
||||
|
||||
case DifferentChannel:
|
||||
case Thru::DifferentChannel:
|
||||
if (!filter_condition)
|
||||
{
|
||||
send(mMessage.type,
|
||||
|
|
@ -1153,12 +1333,6 @@ void MidiInterface<SerialPort, Settings>::thruFilter(Channel inChannel)
|
|||
}
|
||||
break;
|
||||
|
||||
case Off:
|
||||
// Do nothing.
|
||||
// Technically it's impossible to get there because
|
||||
// the case was already tested earlier.
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1195,8 +1369,9 @@ void MidiInterface<SerialPort, Settings>::thruFilter(Channel inChannel)
|
|||
case TimeCodeQuarterFrame:
|
||||
sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,23 +2,28 @@
|
|||
* @file midi_Defs.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Definitions
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -34,6 +39,11 @@ typedef uint8_t byte;
|
|||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
#define MIDI_LIBRARY_VERSION 0x040300
|
||||
#define MIDI_LIBRARY_VERSION_MAJOR 4
|
||||
#define MIDI_LIBRARY_VERSION_MINOR 3
|
||||
#define MIDI_LIBRARY_VERSION_PATCH 0
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define MIDI_CHANNEL_OMNI 0
|
||||
|
|
@ -79,12 +89,26 @@ enum MidiType
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! Enumeration of Thru filter modes */
|
||||
enum MidiFilterMode
|
||||
struct Thru
|
||||
{
|
||||
Off = 0, ///< Thru disabled (nothing passes through).
|
||||
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
|
||||
SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
|
||||
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
|
||||
enum Mode
|
||||
{
|
||||
Off = 0, ///< Thru disabled (nothing passes through).
|
||||
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
|
||||
SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
|
||||
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
|
||||
};
|
||||
};
|
||||
|
||||
/*! Deprecated: use Thru::Mode instead.
|
||||
Will be removed in v5.0.
|
||||
*/
|
||||
enum __attribute__ ((deprecated)) MidiFilterMode
|
||||
{
|
||||
Off = Thru::Off,
|
||||
Full = Thru::Full,
|
||||
SameChannel = Thru::SameChannel,
|
||||
DifferentChannel = Thru::DifferentChannel,
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -102,7 +126,7 @@ enum MidiControlChangeNumber
|
|||
// CC3 undefined
|
||||
FootController = 4,
|
||||
PortamentoTime = 5,
|
||||
DataEntry = 6,
|
||||
DataEntryMSB = 6,
|
||||
ChannelVolume = 7,
|
||||
Balance = 8,
|
||||
// CC9 undefined
|
||||
|
|
@ -117,6 +141,8 @@ enum MidiControlChangeNumber
|
|||
GeneralPurposeController3 = 18,
|
||||
GeneralPurposeController4 = 19,
|
||||
|
||||
DataEntryLSB = 38,
|
||||
|
||||
// Switches ----------------------------------------------------------------
|
||||
Sustain = 64,
|
||||
Portamento = 65,
|
||||
|
|
@ -147,6 +173,12 @@ enum MidiControlChangeNumber
|
|||
Effects3 = 93, ///< Chorus send level
|
||||
Effects4 = 94, ///< Celeste depth
|
||||
Effects5 = 95, ///< Phaser depth
|
||||
DataIncrement = 96,
|
||||
DataDecrement = 97,
|
||||
NRPNLSB = 98, ///< Non-Registered Parameter Number (LSB)
|
||||
NRPNMSB = 99, ///< Non-Registered Parameter Number (MSB)
|
||||
RPNLSB = 100, ///< Registered Parameter Number (LSB)
|
||||
RPNMSB = 101, ///< Registered Parameter Number (MSB)
|
||||
|
||||
// Channel Mode messages ---------------------------------------------------
|
||||
AllSoundOff = 120,
|
||||
|
|
@ -159,6 +191,20 @@ enum MidiControlChangeNumber
|
|||
PolyModeOn = 127
|
||||
};
|
||||
|
||||
struct RPN
|
||||
{
|
||||
enum RegisteredParameterNumbers
|
||||
{
|
||||
PitchBendSensitivity = 0x0000,
|
||||
ChannelFineTuning = 0x0001,
|
||||
ChannelCoarseTuning = 0x0002,
|
||||
SelectTuningProgram = 0x0003,
|
||||
SelectTuningBank = 0x0004,
|
||||
ModulationDepthRange = 0x0005,
|
||||
NullFunction = (0x7f << 7) + 0x7f,
|
||||
};
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port.
|
||||
|
|
|
|||
|
|
@ -2,23 +2,28 @@
|
|||
* @file midi_Message.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Message struct definition
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 11/06/14
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -34,6 +39,19 @@ BEGIN_MIDI_NAMESPACE
|
|||
template<unsigned SysExMaxSize>
|
||||
struct Message
|
||||
{
|
||||
/*! Default constructor
|
||||
\n Initializes the attributes with their default values.
|
||||
*/
|
||||
inline Message()
|
||||
: channel(0)
|
||||
, type(midi::InvalidType)
|
||||
, data1(0)
|
||||
, data2(0)
|
||||
, valid(false)
|
||||
{
|
||||
memset(sysexArray, 0, sSysExMaxSize * sizeof(DataByte));
|
||||
}
|
||||
|
||||
/*! The maximum size for the System Exclusive array.
|
||||
*/
|
||||
static const unsigned sSysExMaxSize = SysExMaxSize;
|
||||
|
|
|
|||
|
|
@ -2,23 +2,28 @@
|
|||
* @file midi_Namespace.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Namespace declaration
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/*!
|
||||
* @file midi_RingBuffer.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for Arduino - Ring Buffer
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 10/10/2016
|
||||
* @license MIT - Copyright (c) 2016 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Namespace.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
template<typename DataType, int Size>
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer();
|
||||
~RingBuffer();
|
||||
|
||||
public:
|
||||
int getLength() const;
|
||||
bool isEmpty() const;
|
||||
|
||||
public:
|
||||
void write(DataType inData);
|
||||
void write(const DataType* inData, int inSize);
|
||||
void clear();
|
||||
|
||||
public:
|
||||
DataType read();
|
||||
void read(DataType* outData, int inSize);
|
||||
|
||||
private:
|
||||
DataType mData[Size];
|
||||
DataType* mWriteHead;
|
||||
DataType* mReadHead;
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
#include "midi_RingBuffer.hpp"
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*!
|
||||
* @file midi_RingBuffer.hpp
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for Arduino - Ring Buffer
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 10/10/2016
|
||||
* @license MIT - Copyright (c) 2016 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
template<typename DataType, int Size>
|
||||
RingBuffer<DataType, Size>::RingBuffer()
|
||||
: mWriteHead(mData)
|
||||
, mReadHead(mData)
|
||||
{
|
||||
memset(mData, DataType(0), Size * sizeof(DataType));
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
RingBuffer<DataType, Size>::~RingBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename DataType, int Size>
|
||||
int RingBuffer<DataType, Size>::getLength() const
|
||||
{
|
||||
if (mReadHead == mWriteHead)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (mWriteHead > mReadHead)
|
||||
{
|
||||
return int(mWriteHead - mReadHead);
|
||||
}
|
||||
else
|
||||
{
|
||||
return int(mWriteHead - mData) + Size - int(mReadHead - mData);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
bool RingBuffer<DataType, Size>::isEmpty() const
|
||||
{
|
||||
return mReadHead == mWriteHead;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::write(DataType inData)
|
||||
{
|
||||
*mWriteHead++ = inData;
|
||||
if (mWriteHead >= mData + Size)
|
||||
{
|
||||
mWriteHead = mData;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::write(const DataType* inData, int inSize)
|
||||
{
|
||||
for (int i = 0; i < inSize; ++i)
|
||||
{
|
||||
write(inData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::clear()
|
||||
{
|
||||
memset(mData, DataType(0), Size * sizeof(DataType));
|
||||
mReadHead = mData;
|
||||
mWriteHead = mData;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename DataType, int Size>
|
||||
DataType RingBuffer<DataType, Size>::read()
|
||||
{
|
||||
const DataType data = *mReadHead++;
|
||||
if (mReadHead >= mData + Size)
|
||||
{
|
||||
mReadHead = mData;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::read(DataType* outData, int inSize)
|
||||
{
|
||||
for (int i = 0; i < inSize; ++i)
|
||||
{
|
||||
outData[i] = read();
|
||||
}
|
||||
}
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -2,23 +2,28 @@
|
|||
* @file midi_Settings.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Settings
|
||||
* @version 4.2
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -36,7 +41,7 @@ BEGIN_MIDI_NAMESPACE
|
|||
\code{.cpp}
|
||||
struct MySettings : public midi::DefaultSettings
|
||||
{
|
||||
static const bool UseRunningStatus = false; // Messes with my old equipment!
|
||||
static const unsigned SysExMaxSize = 1024; // Accept SysEx messages up to 1024 bytes long.
|
||||
};
|
||||
|
||||
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial2, midi, MySettings);
|
||||
|
|
@ -45,20 +50,21 @@ BEGIN_MIDI_NAMESPACE
|
|||
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;
|
||||
of the same type and channel.\n
|
||||
Warning: does not work with some hardware, enable with caution.
|
||||
*/
|
||||
static const bool UseRunningStatus = false;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
/*! NoteOn with 0 velocity should be handled as NoteOf.\n
|
||||
Set to true to get NoteOff events when receiving null-velocity NoteOn messages.\n
|
||||
Set to false 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.
|
||||
/*! Setting this to true 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
/*!
|
||||
* @file midi_UsbDefs.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Definitions
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 24/02/11
|
||||
* @license MIT - Copyright (c) 2016 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Defs.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
struct CodeIndexNumbers
|
||||
{
|
||||
enum
|
||||
{
|
||||
reserved = 0x00,
|
||||
misc = reserved,
|
||||
|
||||
cableEvent = 0x01,
|
||||
systemCommon2Bytes = 0x02,
|
||||
systemCommon3Bytes = 0x03,
|
||||
|
||||
sysExStart = 0x04,
|
||||
sysExContinue = sysExStart,
|
||||
|
||||
systemCommon1Byte = 0x05,
|
||||
sysExEnds1Byte = systemCommon1Byte,
|
||||
|
||||
sysExEnds2Bytes = 0x06,
|
||||
sysExEnds3Bytes = 0x07,
|
||||
noteOff = 0x08,
|
||||
noteOn = 0x09,
|
||||
polyPressure = 0x0A,
|
||||
controlChange = 0x0B,
|
||||
programChange = 0x0C,
|
||||
channelPressure = 0x0D,
|
||||
pitchBend = 0x0E,
|
||||
singleByte = 0x0F,
|
||||
};
|
||||
|
||||
static inline byte getSize(byte inCodeIndexNumber)
|
||||
{
|
||||
switch (inCodeIndexNumber)
|
||||
{
|
||||
case noteOn:
|
||||
case noteOff:
|
||||
case controlChange:
|
||||
case pitchBend:
|
||||
case polyPressure:
|
||||
case systemCommon3Bytes:
|
||||
case sysExEnds3Bytes:
|
||||
case sysExStart:
|
||||
return 3;
|
||||
|
||||
case programChange:
|
||||
case channelPressure:
|
||||
case systemCommon2Bytes:
|
||||
case sysExEnds2Bytes:
|
||||
return 2;
|
||||
|
||||
case systemCommon1Byte:
|
||||
case singleByte:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0; // Can be any length (1, 2 or 3).
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct UsbMidiEventPacket
|
||||
{
|
||||
public:
|
||||
inline UsbMidiEventPacket()
|
||||
{
|
||||
memset(mData, 0, 4 * sizeof(byte));
|
||||
}
|
||||
|
||||
public:
|
||||
inline void setHeader(byte inCableNumber, byte inCodeIndexNumber)
|
||||
{
|
||||
const byte msb = (0x0f & inCableNumber) << 4;
|
||||
const byte lsb = (0x0f & inCodeIndexNumber);
|
||||
mData[0] = msb | lsb;
|
||||
}
|
||||
inline void setMidiData(const byte* inData)
|
||||
{
|
||||
mData[1] = *inData++;
|
||||
mData[2] = *inData++;
|
||||
mData[3] = *inData;
|
||||
}
|
||||
inline byte getCableNumber() const
|
||||
{
|
||||
return mData[0] >> 4;
|
||||
}
|
||||
inline byte getCodeIndexNumber() const
|
||||
{
|
||||
return mData[0] & 0x0f;
|
||||
}
|
||||
inline const byte* getMidiData() const
|
||||
{
|
||||
return mData + 1;
|
||||
}
|
||||
inline byte* getMidiData()
|
||||
{
|
||||
return mData + 1;
|
||||
}
|
||||
inline UsbMidiEventPacket& operator=(const byte* inData)
|
||||
{
|
||||
mData[0] = *inData++;
|
||||
setMidiData(inData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
byte mData[4];
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*!
|
||||
* @file midi_UsbTransport.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Transport layer for USB MIDI
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 10/10/2016
|
||||
* @license MIT - Copyright (c) 2016 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "midi_Defs.h"
|
||||
#include "midi_RingBuffer.h"
|
||||
#include <MIDIUSB.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
template<unsigned BuffersSize>
|
||||
class UsbTransport
|
||||
{
|
||||
public:
|
||||
inline UsbTransport();
|
||||
inline ~UsbTransport();
|
||||
|
||||
public: // Serial / Stream API required for template compatibility
|
||||
inline void begin(unsigned inBaudrate);
|
||||
inline unsigned available();
|
||||
inline byte read();
|
||||
inline void write(byte inData);
|
||||
|
||||
private:
|
||||
inline bool pollUsbMidi();
|
||||
inline void recomposeAndSendTxPackets();
|
||||
inline void resetTx();
|
||||
static inline byte encodePacketHeader(StatusByte inStatusByte);
|
||||
static inline int getPacketLength(const midiEventPacket_t& inPacket);
|
||||
|
||||
private:
|
||||
typedef RingBuffer<byte, BuffersSize> Buffer;
|
||||
Buffer mTxBuffer;
|
||||
Buffer mRxBuffer;
|
||||
|
||||
union TxPacket
|
||||
{
|
||||
byte mDataArray[4];
|
||||
midiEventPacket_t mPacket;
|
||||
};
|
||||
TxPacket mCurrentTxPacket;
|
||||
int mCurrentTxPacketByteIndex;
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
#include "midi_UsbTransport.hpp"
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
/*!
|
||||
* @file midi_UsbTransport.hpp
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Transport layer for USB MIDI
|
||||
* @version 4.3
|
||||
* @author Francois Best
|
||||
* @date 10/10/2016
|
||||
* @license MIT - Copyright (c) 2016 Francois Best
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline UsbTransport<BufferSize>::UsbTransport()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline UsbTransport<BufferSize>::~UsbTransport()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline void UsbTransport<BufferSize>::begin(unsigned inBaudrate)
|
||||
{
|
||||
mTxBuffer.clear();
|
||||
mRxBuffer.clear();
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline unsigned UsbTransport<BufferSize>::available()
|
||||
{
|
||||
pollUsbMidi();
|
||||
return mRxBuffer.getLength();
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline byte UsbTransport<BufferSize>::read()
|
||||
{
|
||||
return mRxBuffer.read();
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline void UsbTransport<BufferSize>::write(byte inData)
|
||||
{
|
||||
mTxBuffer.write(inData);
|
||||
recomposeAndSendTxPackets();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline bool UsbTransport<BufferSize>::pollUsbMidi()
|
||||
{
|
||||
bool received = false;
|
||||
midiEventPacket_t packet = MidiUSB.read();
|
||||
while (packet.header != 0)
|
||||
{
|
||||
received = true;
|
||||
|
||||
switch (packet.header << 4)
|
||||
{
|
||||
// 3 bytes messages
|
||||
case midi::NoteOff:
|
||||
case midi::NoteOn:
|
||||
case midi::AfterTouchPoly:
|
||||
case midi::ControlChange:
|
||||
case midi::PitchBend:
|
||||
mRxBuffer.write(packet.byte1);
|
||||
mRxBuffer.write(packet.byte2);
|
||||
mRxBuffer.write(packet.byte3);
|
||||
break;
|
||||
|
||||
// 2 bytes messages
|
||||
case midi::ProgramChange:
|
||||
case midi::AfterTouchChannel:
|
||||
mRxBuffer.write(packet.byte1);
|
||||
mRxBuffer.write(packet.byte2);
|
||||
break;
|
||||
|
||||
// 1 byte message
|
||||
case midi::TuneRequest:
|
||||
case midi::Clock:
|
||||
case midi::Start:
|
||||
case midi::Continue:
|
||||
case midi::Stop:
|
||||
case midi::ActiveSensing:
|
||||
case midi::SystemReset:
|
||||
mRxBuffer.write(packet.byte1);
|
||||
break;
|
||||
|
||||
// Special cases
|
||||
// case midi::SystemExclusive:
|
||||
// case midi::TimeCodeQuarterFrame:
|
||||
// case midi::SongPosition:
|
||||
// case midi::SongSelect:
|
||||
// break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
packet = MidiUSB.read();
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline void UsbTransport<BufferSize>::recomposeAndSendTxPackets()
|
||||
{
|
||||
while (!mTxBuffer.isEmpty())
|
||||
{
|
||||
const byte data = mTxBuffer.read();
|
||||
if (mCurrentTxPacketByteIndex == 0)
|
||||
{
|
||||
mCurrentTxPacket.mPacket.header = encodePacketHeader(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentTxPacket.mDataArray[mCurrentTxPacketByteIndex] = data;
|
||||
}
|
||||
mCurrentTxPacketByteIndex++;
|
||||
|
||||
const int packetLength = getPacketLength(mCurrentTxPacket.mPacket);
|
||||
|
||||
if (mCurrentTxPacketByteIndex == packetLength)
|
||||
{
|
||||
MidiUSB.write(mCurrentTxPacket.mDataArray, packetLength);
|
||||
resetTx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline void UsbTransport<BufferSize>::resetTx()
|
||||
{
|
||||
mCurrentTxPacket.mPacket.header = 0;
|
||||
mCurrentTxPacket.mPacket.byte1 = 0;
|
||||
mCurrentTxPacket.mPacket.byte2 = 0;
|
||||
mCurrentTxPacket.mPacket.byte3 = 0;
|
||||
mCurrentTxPacketByteIndex = 0;
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline byte UsbTransport<BufferSize>::encodePacketHeader(StatusByte inStatusByte)
|
||||
{
|
||||
// todo: fix me for other types than channel
|
||||
return inStatusByte >> 4;
|
||||
}
|
||||
|
||||
template<unsigned BufferSize>
|
||||
inline int UsbTransport<BufferSize>::getPacketLength(const midiEventPacket_t& inPacket)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
add_subdirectory(mocks)
|
||||
add_subdirectory(unit-tests)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
project(test-mocks)
|
||||
|
||||
add_library(test-mocks STATIC
|
||||
test-mocks.cpp
|
||||
test-mocks.h
|
||||
test-mocks_Namespace.h
|
||||
test-mocks_Defs.h
|
||||
test-mocks_SerialMock.cpp
|
||||
test-mocks_SerialMock.hpp
|
||||
test-mocks_SerialMock.h
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#include "test-mocks.h"
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "test-mocks_Namespace.h"
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "test-mocks.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
typedef uint8_t uint8;
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#define TEST_MOCKS_NAMESPACE test_mocks
|
||||
#define BEGIN_TEST_MOCKS_NAMESPACE namespace TEST_MOCKS_NAMESPACE {
|
||||
#define END_TEST_MOCKS_NAMESPACE }
|
||||
|
||||
#define USING_NAMESPACE_TEST_MOCKS using namespace TEST_MOCKS_NAMESPACE;
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#include "test-mocks_SerialMock.h"
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include "test-mocks.h"
|
||||
#include "test-mocks_Defs.h"
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
template<typename DataType, int Size>
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer();
|
||||
~RingBuffer();
|
||||
|
||||
public:
|
||||
int getLength() const;
|
||||
bool isEmpty() const;
|
||||
|
||||
public:
|
||||
void write(DataType inData);
|
||||
void write(const DataType* inData, int inSize);
|
||||
void clear();
|
||||
|
||||
public:
|
||||
DataType peek() const;
|
||||
DataType read();
|
||||
void read(DataType* outData, int inSize);
|
||||
|
||||
private:
|
||||
DataType mData[Size];
|
||||
DataType* mWriteHead;
|
||||
DataType* mReadHead;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<int BufferSize>
|
||||
class SerialMock
|
||||
{
|
||||
public:
|
||||
SerialMock();
|
||||
~SerialMock();
|
||||
|
||||
public: // Arduino Serial API
|
||||
void begin(int inBaudrate);
|
||||
int available() const;
|
||||
void write(uint8 inData);
|
||||
uint8 read();
|
||||
|
||||
public: // Test Helpers API
|
||||
void moveTxToRx(); // Simulate loopback
|
||||
|
||||
public:
|
||||
typedef RingBuffer<uint8, BufferSize> Buffer;
|
||||
Buffer mTxBuffer;
|
||||
Buffer mRxBuffer;
|
||||
int mBaudrate;
|
||||
};
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
||||
#include "test-mocks_SerialMock.hpp"
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#pragma once
|
||||
|
||||
BEGIN_TEST_MOCKS_NAMESPACE
|
||||
|
||||
template<typename DataType, int Size>
|
||||
RingBuffer<DataType, Size>::RingBuffer()
|
||||
: mWriteHead(mData)
|
||||
, mReadHead(mData)
|
||||
{
|
||||
memset(mData, DataType(0), Size * sizeof(DataType));
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
RingBuffer<DataType, Size>::~RingBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename DataType, int Size>
|
||||
int RingBuffer<DataType, Size>::getLength() const
|
||||
{
|
||||
if (mReadHead == mWriteHead)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (mWriteHead > mReadHead)
|
||||
{
|
||||
return int(mWriteHead - mReadHead);
|
||||
}
|
||||
else
|
||||
{
|
||||
return int(mWriteHead - mData) + Size - int(mReadHead - mData);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
bool RingBuffer<DataType, Size>::isEmpty() const
|
||||
{
|
||||
return mReadHead == mWriteHead;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::write(DataType inData)
|
||||
{
|
||||
*mWriteHead++ = inData;
|
||||
if (mWriteHead >= mData + Size)
|
||||
{
|
||||
mWriteHead = mData;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::write(const DataType* inData, int inSize)
|
||||
{
|
||||
for (int i = 0; i < inSize; ++i)
|
||||
{
|
||||
write(inData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::clear()
|
||||
{
|
||||
memset(mData, DataType(0), Size * sizeof(DataType));
|
||||
mReadHead = mData;
|
||||
mWriteHead = mData;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<typename DataType, int Size>
|
||||
DataType RingBuffer<DataType, Size>::peek() const
|
||||
{
|
||||
return *mReadHead;
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
DataType RingBuffer<DataType, Size>::read()
|
||||
{
|
||||
const DataType data = *mReadHead++;
|
||||
if (mReadHead >= mData + Size)
|
||||
{
|
||||
mReadHead = mData;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename DataType, int Size>
|
||||
void RingBuffer<DataType, Size>::read(DataType* outData, int inSize)
|
||||
{
|
||||
for (int i = 0; i < inSize; ++i)
|
||||
{
|
||||
outData[i] = read();
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
template<int BufferSize>
|
||||
SerialMock<BufferSize>::SerialMock()
|
||||
{
|
||||
}
|
||||
|
||||
template<int BufferSize>
|
||||
SerialMock<BufferSize>::~SerialMock()
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<int BufferSize>
|
||||
void SerialMock<BufferSize>::begin(int inBaudrate)
|
||||
{
|
||||
mBaudrate = inBaudrate;
|
||||
mTxBuffer.clear();
|
||||
mRxBuffer.clear();
|
||||
}
|
||||
|
||||
template<int BufferSize>
|
||||
int SerialMock<BufferSize>::available() const
|
||||
{
|
||||
return mRxBuffer.getLength();
|
||||
}
|
||||
|
||||
template<int BufferSize>
|
||||
void SerialMock<BufferSize>::write(uint8 inData)
|
||||
{
|
||||
mTxBuffer.write(inData);
|
||||
}
|
||||
|
||||
template<int BufferSize>
|
||||
uint8 SerialMock<BufferSize>::read()
|
||||
{
|
||||
return mRxBuffer.read();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template<int BufferSize>
|
||||
void SerialMock<BufferSize>::moveTxToRx()
|
||||
{
|
||||
mRxBuffer.clear();
|
||||
const int size = mTxBuffer.getSize();
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
mRxBuffer.write(mTxBuffer.read());
|
||||
}
|
||||
}
|
||||
|
||||
END_TEST_MOCKS_NAMESPACE
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
include(CMakeToolsHelpers OPTIONAL)
|
||||
|
||||
project(unit-tests)
|
||||
|
||||
include_directories(
|
||||
${unit-tests_SOURCE_DIR}
|
||||
${gtest_SOURCE_DIR}/include
|
||||
${gmock_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
add_executable(unit-tests
|
||||
|
||||
unit-tests.cpp
|
||||
unit-tests.h
|
||||
unit-tests_Namespace.h
|
||||
|
||||
tests/unit-tests_MidiMessage.cpp
|
||||
tests/unit-tests_Settings.cpp
|
||||
tests/unit-tests_Settings.h
|
||||
tests/unit-tests_SysExCodec.cpp
|
||||
tests/unit-tests_SerialMock.cpp
|
||||
tests/unit-tests_MidiInput.cpp
|
||||
tests/unit-tests_MidiInputCallbacks.cpp
|
||||
tests/unit-tests_MidiOutput.cpp
|
||||
tests/unit-tests_MidiThru.cpp
|
||||
tests/unit-tests_MidiUsb.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(unit-tests
|
||||
gtest
|
||||
gmock
|
||||
midi
|
||||
test-mocks
|
||||
)
|
||||
|
||||
add_test(unit-tests ${unit-tests_BINARY_DIR}/unit-tests --gtest_color=yes)
|
||||
add_custom_target(build-and-run-unit-tests
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} -V
|
||||
DEPENDS unit-tests
|
||||
)
|
||||
|
|
@ -0,0 +1,928 @@
|
|||
#include "unit-tests.h"
|
||||
#include "unit-tests_Settings.h"
|
||||
#include <src/MIDI.h>
|
||||
#include <test/mocks/test-mocks_SerialMock.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
using namespace testing;
|
||||
USING_NAMESPACE_UNIT_TESTS
|
||||
typedef test_mocks::SerialMock<32> SerialMock;
|
||||
typedef midi::MidiInterface<SerialMock> MidiInterface;
|
||||
|
||||
template<unsigned Size>
|
||||
struct VariableSysExSettings : midi::DefaultSettings
|
||||
{
|
||||
static const unsigned SysExMaxSize = Size;
|
||||
};
|
||||
|
||||
TEST(MidiInput, getTypeFromStatusByte)
|
||||
{
|
||||
// Channel Messages
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0x81), midi::NoteOff);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0x92), midi::NoteOn);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xa3), midi::AfterTouchPoly);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xb4), midi::ControlChange);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xc5), midi::ProgramChange);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xd6), midi::AfterTouchChannel);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xe7), midi::PitchBend);
|
||||
|
||||
// System Messages
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf0), midi::SystemExclusive);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf1), midi::TimeCodeQuarterFrame);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf2), midi::SongPosition);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf3), midi::SongSelect);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf6), midi::TuneRequest);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf8), midi::Clock);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xfa), midi::Start);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xfb), midi::Continue);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xfc), midi::Stop);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xfe), midi::ActiveSensing);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xff), midi::SystemReset);
|
||||
|
||||
// Invalid Messages
|
||||
for (int i = 0; i < 0x80; ++i)
|
||||
{
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(i), midi::InvalidType);
|
||||
}
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf4), midi::InvalidType);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf5), midi::InvalidType);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xf9), midi::InvalidType);
|
||||
EXPECT_EQ(MidiInterface::getTypeFromStatusByte(0xfd), midi::InvalidType);
|
||||
}
|
||||
|
||||
TEST(MidiInput, getChannelFromStatusByte)
|
||||
{
|
||||
EXPECT_EQ(MidiInterface::getChannelFromStatusByte(0x00), 1);
|
||||
EXPECT_EQ(MidiInterface::getChannelFromStatusByte(0x80), 1);
|
||||
EXPECT_EQ(MidiInterface::getChannelFromStatusByte(0x94), 5);
|
||||
EXPECT_EQ(MidiInterface::getChannelFromStatusByte(0xaf), 16);
|
||||
}
|
||||
|
||||
TEST(MidiInput, isChannelMessage)
|
||||
{
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::InvalidType), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::NoteOff), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::NoteOn), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::AfterTouchPoly), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::ControlChange), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::ProgramChange), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::AfterTouchChannel), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::PitchBend), true);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::SystemExclusive), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::TimeCodeQuarterFrame), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::SongPosition), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::SongSelect), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::TuneRequest), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::Clock), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::Start), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::Continue), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::Stop), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::ActiveSensing), false);
|
||||
EXPECT_EQ(MidiInterface::isChannelMessage(midi::SystemReset), false);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
TEST(MidiInput, begin)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
// Default channel
|
||||
midi.begin();
|
||||
EXPECT_EQ(serial.mBaudrate, 31250);
|
||||
EXPECT_EQ(midi.getInputChannel(), 1);
|
||||
|
||||
// Specific channel
|
||||
midi.begin(12);
|
||||
EXPECT_EQ(serial.mBaudrate, 31250);
|
||||
EXPECT_EQ(midi.getInputChannel(), 12);
|
||||
}
|
||||
|
||||
TEST(MidiInput, initInputChannel)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
EXPECT_EQ(midi.getInputChannel(), 0);
|
||||
midi.setInputChannel(12);
|
||||
EXPECT_EQ(midi.getInputChannel(), 12);
|
||||
}
|
||||
|
||||
TEST(MidiInput, initMessage)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
EXPECT_EQ(midi.getType(), midi::InvalidType);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
EXPECT_EQ(midi.getSysExArrayLength(), unsigned(0));
|
||||
EXPECT_EQ(midi.check(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, channelFiltering)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34 };
|
||||
midi.begin(4); // Mistmatching channel
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, noRxData)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
midi.begin();
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, inputDisabled)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34 };
|
||||
midi.begin(MIDI_CHANNEL_OFF); // Invalid channel
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, multiByteParsing)
|
||||
{
|
||||
typedef VariableSettings<false, false> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> MultiByteMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
MultiByteMidiInterface midi(serial);
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34 };
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
}
|
||||
|
||||
TEST(MidiInput, noteOn)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 10;
|
||||
static const byte rxData[rxSize] = {
|
||||
0x9b, 12, 34,
|
||||
0x9b, 56, 78,
|
||||
12, 34, // Running status
|
||||
56, 0 // NoteOn with null velocity interpreted as NoteOff
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
// First NoteOn
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 78);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, noteOff)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 8;
|
||||
static const byte rxData[rxSize] = {
|
||||
0x8b, 12, 34,
|
||||
0x8b, 56, 78,
|
||||
12, 34, // Running status
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
// First NoteOn
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 78);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
}
|
||||
|
||||
TEST(MidiInput, programChange)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xc3, 12, 34,
|
||||
0xc4, 56, 78
|
||||
};
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::ProgramChange);
|
||||
EXPECT_EQ(midi.getChannel(), 4);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::ProgramChange);
|
||||
EXPECT_EQ(midi.getChannel(), 4);
|
||||
EXPECT_EQ(midi.getData1(), 34);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::ProgramChange);
|
||||
EXPECT_EQ(midi.getChannel(), 5);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::ProgramChange);
|
||||
EXPECT_EQ(midi.getChannel(), 5);
|
||||
EXPECT_EQ(midi.getData1(), 78);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, controlChange)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 8;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xbb, 12, 34,
|
||||
0xbb, 56, 78,
|
||||
12, 34
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
// First NoteOn
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 78);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
}
|
||||
|
||||
TEST(MidiInput, pitchBend)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 8;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xeb, 12, 34,
|
||||
0xeb, 56, 78,
|
||||
12, 34
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
// First NoteOn
|
||||
EXPECT_EQ(midi.getType(), midi::PitchBend);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::PitchBend);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 78);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::PitchBend);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
}
|
||||
|
||||
TEST(MidiInput, afterTouchPoly)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 8;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xab, 12, 34,
|
||||
0xab, 56, 78,
|
||||
12, 34
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
// First NoteOn
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchPoly);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchPoly);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 78);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchPoly);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
}
|
||||
|
||||
TEST(MidiInput, afterTouchChannel)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xd3, 12, 34,
|
||||
0xd4, 56, 78
|
||||
};
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchChannel);
|
||||
EXPECT_EQ(midi.getChannel(), 4);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchChannel);
|
||||
EXPECT_EQ(midi.getChannel(), 4);
|
||||
EXPECT_EQ(midi.getData1(), 34);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchChannel);
|
||||
EXPECT_EQ(midi.getChannel(), 5);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::AfterTouchChannel);
|
||||
EXPECT_EQ(midi.getChannel(), 5);
|
||||
EXPECT_EQ(midi.getData1(), 78);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, sysExWithinBufferSize)
|
||||
{
|
||||
typedef VariableSysExSettings<1024> Settings;
|
||||
typedef test_mocks::SerialMock<2048> LargerSerialMock;
|
||||
typedef midi::MidiInterface<LargerSerialMock, Settings> LargerMidiInterface;
|
||||
|
||||
LargerSerialMock serial;
|
||||
LargerMidiInterface midi(serial);
|
||||
|
||||
// Short Frame < 256
|
||||
{
|
||||
static const unsigned frameLength = 15;
|
||||
static const byte frame[frameLength] = {
|
||||
0xf0, 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 0xf7
|
||||
};
|
||||
|
||||
midi.begin();
|
||||
serial.mRxBuffer.write(frame, frameLength);
|
||||
for (unsigned i = 0; i < frameLength - 1; ++i)
|
||||
{
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
EXPECT_EQ(midi.read(), true); // 0xf7
|
||||
|
||||
EXPECT_EQ(midi.getSysExArrayLength(), frameLength);
|
||||
const std::vector<byte> sysExData(midi.getSysExArray(),
|
||||
midi.getSysExArray() + frameLength);
|
||||
EXPECT_THAT(sysExData, ElementsAreArray(frame));
|
||||
}
|
||||
// Long Frame
|
||||
{
|
||||
static const unsigned frameLength = 957;
|
||||
static const byte frame[frameLength] = {
|
||||
0xf0,
|
||||
'L','o','r','e','m',' ','i','p','s','u','m',' ','d','o','l','o','r',' ','s','i','t',' ','a','m','e','t',',',' ',
|
||||
'c','o','n','s','e','c','t','e','t','u','r',' ','a','d','i','p','i','s','c','i','n','g',' ','e','l','i','t','.',' ','P','r','o','i','n',' ','m','a','x','i','m','u','s',' ','d','u','i',' ','a',' ','m','a','s','s','a',' ','m','a','x','i','m','u','s',',',' ',
|
||||
'a',' ','v','e','s','t','i','b','u','l','u','m',' ','m','i',' ','v','e','n','e','n','a','t','i','s','.',' ','C','r','a','s',' ','s','i','t',' ','a','m','e','t',' ','e','x',' ','i','d',' ','v','e','l','i','t',' ','s','u','s','c','i','p','i','t',' ','p','h','a','r','e','t','r','a',' ','e','g','e','t', ' ','a',' ','t','u','r','p','i','s','.',' ','P','h','a','s','e','l','l','u','s',' ','i','n','t','e','r','d','u','m',' ','m','e','t','u','s',' ','a','c',' ','s','a','g','i','t','t','i','s',' ','c','u','r','s','u','s','.',' ','N','a','m',' ','q','u','i','s',' ','e','s','t',' ','a','t',' ','n','i','s', 'l',' ','u','l','l','a','m','c','o','r','p','e','r',' ','e','g','e','s','t','a','s',' ','p','u','l','v','i','n','a','r',' ','e','u',' ','e','r','a','t','.',' ','D','u','i','s',' ','a',' ','e','l','i','t',' ','d','i','g','n','i','s','s','i','m',',',' ',
|
||||
'v','e','s','t','i','b','u','l','u','m',' ','e','r','o','s',' ','v','e','l',',',' ',
|
||||
't','e','m','p','u','s',' ','n','i','s','l','.',' ','A','e','n','e','a','n',' ','t','u','r','p','i','s',' ','n','u','n','c',',',' ',
|
||||
'c','u','r','s','u','s',' ','v','e','l',' ','l','a','c','i','n','i','a',' ','n','o','n',',',' ',
|
||||
'p','h','a','r','e','t','r','a',' ','e','g','e','t',' ','s','a','p','i','e','n','.',' ','D','u','i','s',' ','c','o','n','d','i','m','e','n','t','u','m',',',' ',
|
||||
'l','a','c','u','s',' ','a','t',' ','p','u','l','v','i','n','a','r',' ','t','e','m','p','o','r',',',' ',
|
||||
'l','e','o',' ','l','i','b','e','r','o',' ','v','o','l','u','t','p','a','t',' ','n','i','s','l',',',' ',
|
||||
'e','g','e','t',' ','p','o','r','t','t','i','t','o','r',' ','l','o','r','e','m',' ','m','i',' ','s','e','d',' ','m','a','g','n','a','.',' ','D','u','i','s',' ','d','i','c','t','u','m',',',' ',
|
||||
'm','a','s','s','a',' ','v','e','l',' ','e','u','i','s','m','o','d',' ','i','n','t','e','r','d','u','m',',',' ',
|
||||
'l','o','r','e','m',' ','m','i',' ','e','g','e','s','t','a','s',' ','e','l','i','t',',',' ',
|
||||
'h','e','n','d','r','e','r','i','t',' ','t','i','n','c','i','d','u','n','t',' ','e','s','t',' ','a','r','c','u',' ','a',' ','l','i','b','e','r','o','.',' ','I','n','t','e','r','d','u','m',' ','e','t',' ','m','a','l','e','s','u','a','d','a',' ','f','a','m','e','s',' ','a','c',' ','a','n','t','e',' ', 'i','p','s','u','m',' ','p','r','i','m','i','s',' ','i','n',' ','f','a','u','c','i','b','u','s','.',' ','C','u','r','a','b','i','t','u','r',' ','v','e','h','i','c','u','l','a',' ','m','a','g','n','a',' ','l','i','b','e','r','o',',',' ',
|
||||
'a','t',' ','r','h','o','n','c','u','s',' ','s','e','m',' ','o','r','n','a','r','e',' ','a','.',' ','I','n',' ','e','l','e','m','e','n','t','u','m',',',' ',
|
||||
'e','l','i','t',' ','e','t',' ','c','o','n','g','u','e',' ','p','u','l','v','i','n','a','r',',',' ',
|
||||
'm','a','s','s','a',' ','v','e','l','i','t',' ','c','o','m','m','o','d','o',' ','v','e','l','i','t',',',' ',
|
||||
'n','o','n',' ','e','l','e','m','e','n','t','u','m',' ','p','u','r','u','s',' ','l','i','g','u','l','a',' ','e','g','e','t',' ','l','a','c','u','s','.',' ','D','o','n','e','c',' ','e','f','f','i','c','i','t','u','r',' ','n','i','s','i',' ','e','u',' ','u','l','t','r','i','c','e','s',' ','e','f','f', 'i','c','i','t','u','r','.',' ','D','o','n','e','c',' ','n','e','q','u','e',' ','d','u','i',',',' ',
|
||||
'u','l','l','a','m','c','o','r','p','e','r',' ','i','d',' ','m','o','l','e','s','t','i','e',' ','q','u','i','s',',',' ',
|
||||
'c','o','n','s','e','q','u','a','t',' ','s','i','t',' ','a','m','e','t',' ','l','i','g','u','l','a','.',
|
||||
0xf7,
|
||||
};
|
||||
midi.begin();
|
||||
serial.mRxBuffer.write(frame, frameLength);
|
||||
for (unsigned i = 0; i < frameLength - 1; ++i)
|
||||
{
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
EXPECT_EQ(serial.mRxBuffer.getLength(), 1);
|
||||
EXPECT_EQ(serial.mRxBuffer.peek(), 0xf7);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiInput, sysExOverBufferSize)
|
||||
{
|
||||
typedef VariableSysExSettings<8> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> SmallMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
SmallMidiInterface midi(serial);
|
||||
|
||||
static const unsigned frameLength = 15;
|
||||
static const byte frame[frameLength] = {
|
||||
0xf0, 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 0xf7
|
||||
};
|
||||
|
||||
midi.begin();
|
||||
serial.mRxBuffer.write(frame, frameLength);
|
||||
|
||||
for (unsigned i = 0; i < frameLength - 1; ++i)
|
||||
{
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, mtcQuarterFrame)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 4;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf1, 12,
|
||||
0xf1, 42
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::TimeCodeQuarterFrame);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::TimeCodeQuarterFrame);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 42);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, songPosition)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf2, 12, 34,
|
||||
0xf2, 56, 78
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::SongPosition);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::SongPosition);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 56);
|
||||
EXPECT_EQ(midi.getData2(), 78);
|
||||
}
|
||||
|
||||
TEST(MidiInput, songSelect)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 4;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf3, 12,
|
||||
0xf3, 42
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
// 1 byte parsing
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::SongSelect);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(midi.getType(), midi::SongSelect);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 42);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, tuneRequest)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 1;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf6
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::TuneRequest);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, realTime)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 8;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Clock);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false); // 0xf9 = undefined
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Start);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Continue);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Stop);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false); // 0xfd = undefined
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ActiveSensing);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::SystemReset);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
TEST(MidiInput, interleavedRealTime)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
// Interleaved Clocks between NoteOn / Off messages (with running status)
|
||||
{
|
||||
static const unsigned rxSize = 13;
|
||||
static const byte rxData[rxSize] = {
|
||||
0x9b, 12, 0xf8, 34,
|
||||
12, 0,
|
||||
42, 0xf8, 127,
|
||||
0xf8,
|
||||
42, 0xf8, 0
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Clock);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Clock);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 42);
|
||||
EXPECT_EQ(midi.getData2(), 127);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Clock);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Clock);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 42);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
// Interleaved ActiveSensing between SysEx
|
||||
{
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf0, 12, 34, 0xfe, 56, 0xf7
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ActiveSensing);
|
||||
EXPECT_EQ(midi.getChannel(), 0);
|
||||
EXPECT_EQ(midi.getData1(), 0);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getSysExArrayLength(), rxSize - 1);
|
||||
const std::vector<byte> sysExData(midi.getSysExArray(),
|
||||
midi.getSysExArray() + rxSize - 1);
|
||||
EXPECT_THAT(sysExData, ElementsAreArray({
|
||||
0xf0, 12, 34, 56, 0xf7
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiInput, strayEox)
|
||||
{
|
||||
// A stray End of Exclusive will reset the parser, but should it ?
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
static const unsigned rxSize = 4;
|
||||
static const byte rxData[rxSize] = {
|
||||
0x8b, 42, 0xf7, 12
|
||||
};
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, strayUndefinedOneByteParsing)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
static const unsigned rxSize = 13;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xbb, 12, 0xf9, 34,
|
||||
12, 0,
|
||||
42, 0xfd, 127,
|
||||
0xf9,
|
||||
42, 0xfd, 0
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false); // Invalid, should not reset parser
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 42);
|
||||
EXPECT_EQ(midi.getData2(), 127);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 42);
|
||||
EXPECT_EQ(midi.getData2(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiInput, strayUndefinedMultiByteParsing)
|
||||
{
|
||||
typedef VariableSettings<false, false> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> MultiByteMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
MultiByteMidiInterface midi(serial);
|
||||
|
||||
static const unsigned rxSize = 4;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xbb, 12, 0xf9, 34,
|
||||
};
|
||||
midi.begin(12);
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(midi.getChannel(), 12);
|
||||
EXPECT_EQ(midi.getData1(), 12);
|
||||
EXPECT_EQ(midi.getData2(), 34);
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,590 @@
|
|||
#include "unit-tests.h"
|
||||
#include "unit-tests_Settings.h"
|
||||
#include <src/MIDI.h>
|
||||
#include <test/mocks/test-mocks_SerialMock.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
using namespace testing;
|
||||
USING_NAMESPACE_UNIT_TESTS
|
||||
|
||||
template<unsigned Size>
|
||||
struct VariableSysExSettings : midi::DefaultSettings
|
||||
{
|
||||
static const unsigned SysExMaxSize = Size;
|
||||
};
|
||||
|
||||
typedef test_mocks::SerialMock<256> SerialMock;
|
||||
typedef VariableSysExSettings<256> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> MidiInterface;
|
||||
|
||||
MidiInterface* midi;
|
||||
|
||||
class MidiInputCallbacks : public Test
|
||||
{
|
||||
public:
|
||||
MidiInputCallbacks()
|
||||
: mMidi(mSerial)
|
||||
{
|
||||
}
|
||||
virtual ~MidiInputCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
midi = &mMidi;
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
midi = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
SerialMock mSerial;
|
||||
MidiInterface mMidi;
|
||||
};
|
||||
|
||||
// --
|
||||
|
||||
void handleNoteOn(byte inChannel, byte inPitch, byte inVelocity)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendNoteOn(inPitch, inVelocity, inChannel);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, noteOn)
|
||||
{
|
||||
mMidi.setHandleNoteOn(handleNoteOn);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::NoteOn);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
byte buffer[3] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
|
||||
// Test null velocity note on
|
||||
EXPECT_EQ(MidiInterface::Settings::HandleNullVelocityNoteOnAsNoteOff, true);
|
||||
mSerial.mRxBuffer.write(0x9b);
|
||||
mSerial.mRxBuffer.write(12);
|
||||
mSerial.mRxBuffer.write(0);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 0);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleNoteOff(byte inChannel, byte inPitch, byte inVelocity)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendNoteOff(inPitch, inVelocity, inChannel);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, noteOff)
|
||||
{
|
||||
mMidi.setHandleNoteOff(handleNoteOff);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0x8b, 12, 34 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
byte buffer[3] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
|
||||
// Test null velocity note on
|
||||
EXPECT_EQ(MidiInterface::Settings::HandleNullVelocityNoteOnAsNoteOff, true);
|
||||
mSerial.mRxBuffer.write(0x9b);
|
||||
mSerial.mRxBuffer.write(12);
|
||||
mSerial.mRxBuffer.write(0);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::NoteOff);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x8b, 12, 0
|
||||
}));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
|
||||
|
||||
void handleAfterTouchPoly(byte inChannel, byte inNote, byte inValue)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendAfterTouch(inNote, inValue, inChannel);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, afterTouchPoly)
|
||||
{
|
||||
mMidi.setHandleAfterTouchPoly(handleAfterTouchPoly);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0xab, 12, 34 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::AfterTouchPoly);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
byte buffer[3] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleControlChange(byte inChannel, byte inNumber, byte inValue)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendControlChange(inNumber, inValue, inChannel);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, controlChange)
|
||||
{
|
||||
mMidi.setHandleControlChange(handleControlChange);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0xbb, 12, 34 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::ControlChange);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
byte buffer[3] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleProgramChange(byte inChannel, byte inNumber)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendProgramChange(inNumber, inChannel);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, programChange)
|
||||
{
|
||||
mMidi.setHandleProgramChange(handleProgramChange);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 2;
|
||||
static const byte rxData[rxSize] = { 0xcb, 12 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::ProgramChange);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2);
|
||||
byte buffer[2] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 2);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleAfterTouchChannel(byte inChannel, byte inPressure)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendAfterTouch(inPressure, inChannel);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(MidiInputCallbacks, afterTouchChannel)
|
||||
{
|
||||
mMidi.setHandleAfterTouchChannel(handleAfterTouchChannel);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 2;
|
||||
static const byte rxData[rxSize] = { 0xdb, 12 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::AfterTouchChannel);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2);
|
||||
byte buffer[2] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 2);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handlePitchBend(byte inChannel, int inValue)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendPitchBend(inValue, inChannel);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, pitchBend)
|
||||
{
|
||||
mMidi.setHandlePitchBend(handlePitchBend);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0xeb, 12, 34 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::PitchBend);
|
||||
EXPECT_EQ(mMidi.getChannel(), 12);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
byte buffer[3] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleSysEx(byte* inData, unsigned inSize)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendSysEx(inSize, inData, true);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, sysEx)
|
||||
{
|
||||
mMidi.setHandleSystemExclusive(handleSysEx);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 15;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf0, 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 0xf7
|
||||
};
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
for (unsigned i = 0; i < rxSize - 1; ++i)
|
||||
{
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
}
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::SystemExclusive);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getSysExArrayLength(), rxSize);
|
||||
|
||||
EXPECT_EQ(unsigned(mSerial.mTxBuffer.getLength()), rxSize);
|
||||
const std::vector<byte> sysExData(mMidi.getSysExArray(),
|
||||
mMidi.getSysExArray() + rxSize);
|
||||
EXPECT_THAT(sysExData, ElementsAreArray(rxData));
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, sysExLong)
|
||||
{
|
||||
mMidi.setHandleSystemExclusive(handleSysEx);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 210;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf0,
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!',
|
||||
0xf7
|
||||
};
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
for (unsigned i = 0; i < rxSize - 1; ++i)
|
||||
{
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
}
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::SystemExclusive);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getSysExArrayLength(), rxSize);
|
||||
|
||||
EXPECT_EQ(unsigned(mSerial.mTxBuffer.getLength()), rxSize);
|
||||
const std::vector<byte> sysExData(mMidi.getSysExArray(),
|
||||
mMidi.getSysExArray() + rxSize);
|
||||
EXPECT_THAT(sysExData, ElementsAreArray(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleMtcQuarterFrame(byte inData)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendTimeCodeQuarterFrame(inData);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, mtcQuarterFrame)
|
||||
{
|
||||
mMidi.setHandleTimeCodeQuarterFrame(handleMtcQuarterFrame);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 2;
|
||||
static const byte rxData[rxSize] = { 0xf1, 12 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::TimeCodeQuarterFrame);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2);
|
||||
byte buffer[2] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 2);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleSongPosition(unsigned inBeats)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendSongPosition(inBeats);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, songPosition)
|
||||
{
|
||||
mMidi.setHandleSongPosition(handleSongPosition);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 3;
|
||||
static const byte rxData[rxSize] = { 0xf2, 12, 34 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::SongPosition);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 34);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 3);
|
||||
byte buffer[3] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 3);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleSongSelect(byte inSongNumber)
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendSongSelect(inSongNumber);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, songSelect)
|
||||
{
|
||||
mMidi.setHandleSongSelect(handleSongSelect);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 2;
|
||||
static const byte rxData[rxSize] = { 0xf3, 12 };
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), false);
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::SongSelect);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getData1(), 12);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 2);
|
||||
byte buffer[2] = { 0 };
|
||||
mSerial.mTxBuffer.read(buffer, 2);
|
||||
EXPECT_THAT(buffer, ContainerEq(rxData));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleTuneRequest()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendTuneRequest();
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, tuneRequest)
|
||||
{
|
||||
mMidi.setHandleTuneRequest(handleTuneRequest);
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
mSerial.mRxBuffer.write(0xf6);
|
||||
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), midi::TuneRequest);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getData1(), 0);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 1);
|
||||
EXPECT_EQ(mSerial.mTxBuffer.read(), 0xf6);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
void handleClock()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendRealTime(midi::Clock);
|
||||
}
|
||||
void handleStart()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendRealTime(midi::Start);
|
||||
}
|
||||
void handleContinue()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendRealTime(midi::Continue);
|
||||
}
|
||||
void handleStop()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendRealTime(midi::Stop);
|
||||
}
|
||||
void handleActiveSensing()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendRealTime(midi::ActiveSensing);
|
||||
}
|
||||
void handleSystemReset()
|
||||
{
|
||||
EXPECT_NE(midi, nullptr);
|
||||
midi->sendRealTime(midi::SystemReset);
|
||||
}
|
||||
|
||||
TEST_F(MidiInputCallbacks, realTime)
|
||||
{
|
||||
mMidi.setHandleClock(handleClock);
|
||||
mMidi.setHandleStart(handleStart);
|
||||
mMidi.setHandleContinue(handleContinue);
|
||||
mMidi.setHandleStop(handleStop);
|
||||
mMidi.setHandleActiveSensing(handleActiveSensing);
|
||||
mMidi.setHandleSystemReset(handleSystemReset);
|
||||
|
||||
mMidi.begin(MIDI_CHANNEL_OMNI);
|
||||
mMidi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xf8, 0xfa, 0xfb, 0xfc, 0xfe, 0xff
|
||||
};
|
||||
mSerial.mRxBuffer.write(rxData, rxSize);
|
||||
static const midi::MidiType types[rxSize] = {
|
||||
midi::Clock,
|
||||
midi::Start,
|
||||
midi::Continue,
|
||||
midi::Stop,
|
||||
midi::ActiveSensing,
|
||||
midi::SystemReset,
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < rxSize; ++i)
|
||||
{
|
||||
EXPECT_EQ(mMidi.read(), true);
|
||||
EXPECT_EQ(mMidi.getType(), types[i]);
|
||||
EXPECT_EQ(mMidi.getChannel(), 0);
|
||||
EXPECT_EQ(mMidi.getData1(), 0);
|
||||
EXPECT_EQ(mMidi.getData2(), 0);
|
||||
EXPECT_EQ(mSerial.mTxBuffer.getLength(), 1);
|
||||
|
||||
const byte read = mSerial.mTxBuffer.read();
|
||||
EXPECT_EQ(read, rxData[i]);
|
||||
EXPECT_EQ(read, types[i]);
|
||||
}
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#include "unit-tests.h"
|
||||
#include <src/midi_Message.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
// Declare references:
|
||||
// http://stackoverflow.com/questions/4891067/weird-undefined-symbols-of-static-constants-inside-a-struct-class
|
||||
|
||||
template<unsigned Size>
|
||||
const unsigned Message<Size>::sSysExMaxSize;
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
TEST(MidiMessage, hasTheRightProperties)
|
||||
{
|
||||
typedef midi::Message<42> Message;
|
||||
const Message message = Message();
|
||||
EXPECT_EQ(message.channel, 0);
|
||||
EXPECT_EQ(message.type, 0);
|
||||
EXPECT_EQ(message.data1, 0);
|
||||
EXPECT_EQ(message.data2, 0);
|
||||
EXPECT_EQ(message.valid, false);
|
||||
EXPECT_EQ(message.getSysExSize(), unsigned(0));
|
||||
}
|
||||
|
||||
template<typename Message>
|
||||
inline void setSysExSize(Message& ioMessage, unsigned inSize)
|
||||
{
|
||||
ioMessage.data2 = inSize >> 8; // MSB
|
||||
ioMessage.data1 = inSize & 0xff; // LSB
|
||||
}
|
||||
|
||||
TEST(MidiMessage, getSysExSize)
|
||||
{
|
||||
// Small message
|
||||
{
|
||||
typedef midi::Message<32> Message;
|
||||
ASSERT_EQ(Message::sSysExMaxSize, unsigned(32));
|
||||
Message message = Message();
|
||||
|
||||
const unsigned sizeUnder = 20;
|
||||
setSysExSize(message, sizeUnder);
|
||||
ASSERT_EQ(message.getSysExSize(), sizeUnder);
|
||||
|
||||
const unsigned sizeOver = 64;
|
||||
setSysExSize(message, sizeOver);
|
||||
ASSERT_EQ(message.getSysExSize(), unsigned(32));
|
||||
}
|
||||
// Medium message
|
||||
{
|
||||
typedef midi::Message<256> Message;
|
||||
ASSERT_EQ(Message::sSysExMaxSize, unsigned(256));
|
||||
Message message = Message();
|
||||
|
||||
const unsigned sizeUnder = 200;
|
||||
setSysExSize(message, sizeUnder);
|
||||
ASSERT_EQ(message.getSysExSize(), sizeUnder);
|
||||
|
||||
const unsigned sizeOver = 300;
|
||||
setSysExSize(message, sizeOver);
|
||||
ASSERT_EQ(message.getSysExSize(), unsigned(256));
|
||||
}
|
||||
// Large message
|
||||
{
|
||||
typedef midi::Message<1024> Message;
|
||||
ASSERT_EQ(Message::sSysExMaxSize, unsigned(1024));
|
||||
Message message = Message();
|
||||
|
||||
const unsigned sizeUnder = 1000;
|
||||
setSysExSize(message, sizeUnder);
|
||||
ASSERT_EQ(message.getSysExSize(), sizeUnder);
|
||||
|
||||
const unsigned sizeOver = 2000;
|
||||
setSysExSize(message, sizeOver);
|
||||
ASSERT_EQ(message.getSysExSize(), unsigned(1024));
|
||||
}
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,837 @@
|
|||
#include "unit-tests.h"
|
||||
#include "unit-tests_Settings.h"
|
||||
#include <src/MIDI.h>
|
||||
#include <test/mocks/test-mocks_SerialMock.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
using namespace testing;
|
||||
USING_NAMESPACE_UNIT_TESTS;
|
||||
|
||||
typedef test_mocks::SerialMock<32> SerialMock;
|
||||
typedef midi::MidiInterface<SerialMock> MidiInterface;
|
||||
|
||||
typedef std::vector<uint8_t> Buffer;
|
||||
|
||||
// --
|
||||
|
||||
TEST(MidiOutput, sendInvalid)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
midi.begin();
|
||||
midi.send(midi::NoteOn, 42, 42, 42); // Invalid channel > OFF
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
|
||||
midi.send(midi::InvalidType, 0, 0, 12); // Invalid type
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
|
||||
midi.send(midi::NoteOn, 12, 42, MIDI_CHANNEL_OMNI); // OMNI not allowed
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendGenericSingle)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(3);
|
||||
|
||||
midi.begin();
|
||||
midi.send(midi::NoteOn, 47, 42, 12);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x9b, 47, 42}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendGenericWithRunningStatus)
|
||||
{
|
||||
typedef VariableSettings<true, false> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
RsMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(5);
|
||||
|
||||
midi.begin();
|
||||
EXPECT_EQ(RsMidiInterface::Settings::UseRunningStatus, true);
|
||||
EXPECT_EQ(serial.mTxBuffer.isEmpty(), true);
|
||||
midi.send(midi::NoteOn, 47, 42, 12);
|
||||
midi.send(midi::NoteOn, 42, 47, 12);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 5);
|
||||
serial.mTxBuffer.read(&buffer[0], 5);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x9b, 47, 42, 42, 47}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendGenericWithoutRunningStatus)
|
||||
{
|
||||
typedef VariableSettings<false, true> Settings; // No running status
|
||||
typedef midi::MidiInterface<SerialMock, Settings> NoRsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
NoRsMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
// Same status byte
|
||||
midi.begin();
|
||||
EXPECT_EQ(MidiInterface::Settings::UseRunningStatus, false);
|
||||
EXPECT_EQ(serial.mTxBuffer.isEmpty(), true);
|
||||
midi.send(midi::NoteOn, 47, 42, 12);
|
||||
midi.send(midi::NoteOn, 42, 47, 12);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x9b, 47, 42, 0x9b, 42, 47}));
|
||||
|
||||
// Different status byte
|
||||
midi.begin();
|
||||
midi.send(midi::NoteOn, 47, 42, 12);
|
||||
midi.send(midi::NoteOff, 47, 42, 12);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x9b, 47, 42, 0x8b, 47, 42}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendGenericBreakingRunningStatus)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.send(midi::NoteOn, 47, 42, 12);
|
||||
midi.send(midi::NoteOff, 47, 42, 12);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x9b, 47, 42, 0x8b, 47, 42}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendGenericRealTimeShortcut)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.send(midi::Clock, 47, 42, 12);
|
||||
midi.send(midi::Start, 47, 42, 12);
|
||||
midi.send(midi::Continue, 47, 42, 12);
|
||||
midi.send(midi::Stop, 47, 42, 12);
|
||||
midi.send(midi::ActiveSensing, 47, 42, 12);
|
||||
midi.send(midi::SystemReset, 47, 42, 12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xf8, 0xfa, 0xfb, 0xfc, 0xfe, 0xff}));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
TEST(MidiOutput, sendNoteOn)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendNoteOn(10, 11, 12);
|
||||
midi.sendNoteOn(12, 13, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x9b, 10, 11, 0x93, 12, 13}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendNoteOff)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendNoteOff(10, 11, 12);
|
||||
midi.sendNoteOff(12, 13, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0x8b, 10, 11, 0x83, 12, 13}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendProgramChange)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(4);
|
||||
|
||||
midi.begin();
|
||||
midi.sendProgramChange(42, 12);
|
||||
midi.sendProgramChange(47, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 4);
|
||||
serial.mTxBuffer.read(&buffer[0], 4);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xcb, 42, 0xc3, 47}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendControlChange)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendControlChange(42, 12, 12);
|
||||
midi.sendControlChange(47, 12, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb, 42, 12, 0xb3, 47, 12}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendPitchBend)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
// Int signature - arbitrary values
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(9);
|
||||
|
||||
midi.begin();
|
||||
midi.sendPitchBend(0, 12);
|
||||
midi.sendPitchBend(100, 4);
|
||||
midi.sendPitchBend(-100, 7);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 9);
|
||||
serial.mTxBuffer.read(&buffer[0], 9);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xeb, 0x00, 0x40,
|
||||
0xe3, 0x64, 0x40,
|
||||
0xe6, 0x1c, 0x3f}));
|
||||
}
|
||||
// Int signature - min/max
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(9);
|
||||
|
||||
midi.begin();
|
||||
midi.sendPitchBend(0, 12);
|
||||
midi.sendPitchBend(MIDI_PITCHBEND_MAX, 4);
|
||||
midi.sendPitchBend(MIDI_PITCHBEND_MIN, 7);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 9);
|
||||
serial.mTxBuffer.read(&buffer[0], 9);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xeb, 0x00, 0x40,
|
||||
0xe3, 0x7f, 0x7f,
|
||||
0xe6, 0x00, 0x00}));
|
||||
}
|
||||
// Float signature
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(9);
|
||||
|
||||
midi.begin();
|
||||
midi.sendPitchBend(0.0, 12);
|
||||
midi.sendPitchBend(1.0, 4);
|
||||
midi.sendPitchBend(-1.0, 7);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 9);
|
||||
serial.mTxBuffer.read(&buffer[0], 9);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xeb, 0x00, 0x40,
|
||||
0xe3, 0x7f, 0x7f,
|
||||
0xe6, 0x00, 0x00}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendPolyPressure)
|
||||
{
|
||||
// Note: sendPolyPressure is deprecated in favor of sendAfterTouch, which
|
||||
// now supports both mono and poly AfterTouch messages.
|
||||
// This test is kept for coverage until removal of sendPolyPressure.
|
||||
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendPolyPressure(42, 12, 12);
|
||||
midi.sendPolyPressure(47, 12, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xab, 42, 12, 0xa3, 47, 12}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendAfterTouchMono)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(4);
|
||||
|
||||
midi.begin();
|
||||
midi.sendAfterTouch(42, 12);
|
||||
midi.sendAfterTouch(47, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 4);
|
||||
serial.mTxBuffer.read(&buffer[0], 4);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xdb, 42, 0xd3, 47}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendAfterTouchPoly)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendAfterTouch(42, 12, 12);
|
||||
midi.sendAfterTouch(47, 12, 4);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xab, 42, 12, 0xa3, 47, 12}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendSysEx)
|
||||
{
|
||||
typedef test_mocks::SerialMock<1024> LargeSerialMock;
|
||||
typedef midi::MidiInterface<LargeSerialMock> LargeMidiInterface;
|
||||
|
||||
LargeSerialMock serial;
|
||||
LargeMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
// Short frame
|
||||
{
|
||||
static const char* frame = "Hello, World!";
|
||||
static const int frameLength = strlen(frame);
|
||||
static const byte expected[] = {
|
||||
0xf0,
|
||||
'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!',
|
||||
0xf7,
|
||||
};
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(frameLength + 2);
|
||||
|
||||
midi.begin();
|
||||
midi.sendSysEx(frameLength, reinterpret_cast<const byte*>(frame), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), frameLength + 2);
|
||||
serial.mTxBuffer.read(&buffer[0], frameLength + 2);
|
||||
EXPECT_THAT(buffer, ElementsAreArray(expected));
|
||||
}
|
||||
// Long frame
|
||||
{
|
||||
static const char* frame = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin maximus dui a massa maximus, a vestibulum mi venenatis. Cras sit amet ex id velit suscipit pharetra eget a turpis. Phasellus interdum metus ac sagittis cursus. Nam quis est at nisl ullamcorper egestas pulvinar eu erat. Duis a elit dignissim, vestibulum eros vel, tempus nisl. Aenean turpis nunc, cursus vel lacinia non, pharetra eget sapien. Duis condimentum, lacus at pulvinar tempor, leo libero volutpat nisl, eget porttitor lorem mi sed magna. Duis dictum, massa vel euismod interdum, lorem mi egestas elit, hendrerit tincidunt est arcu a libero. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur vehicula magna libero, at rhoncus sem ornare a. In elementum, elit et congue pulvinar, massa velit commodo velit, non elementum purus ligula eget lacus. Donec efficitur nisi eu ultrices efficitur. Donec neque dui, ullamcorper id molestie quis, consequat sit amet ligula.";
|
||||
static const int frameLength = strlen(frame);
|
||||
static const byte expected[] = {
|
||||
0xf0,
|
||||
'L','o','r','e','m',' ','i','p','s','u','m',' ','d','o','l','o','r',' ','s','i','t',' ','a','m','e','t',',',' ',
|
||||
'c','o','n','s','e','c','t','e','t','u','r',' ','a','d','i','p','i','s','c','i','n','g',' ','e','l','i','t','.',' ','P','r','o','i','n',' ','m','a','x','i','m','u','s',' ','d','u','i',' ','a',' ','m','a','s','s','a',' ','m','a','x','i','m','u','s',',',' ',
|
||||
'a',' ','v','e','s','t','i','b','u','l','u','m',' ','m','i',' ','v','e','n','e','n','a','t','i','s','.',' ','C','r','a','s',' ','s','i','t',' ','a','m','e','t',' ','e','x',' ','i','d',' ','v','e','l','i','t',' ','s','u','s','c','i','p','i','t',' ','p','h','a','r','e','t','r','a',' ','e','g','e','t', ' ','a',' ','t','u','r','p','i','s','.',' ','P','h','a','s','e','l','l','u','s',' ','i','n','t','e','r','d','u','m',' ','m','e','t','u','s',' ','a','c',' ','s','a','g','i','t','t','i','s',' ','c','u','r','s','u','s','.',' ','N','a','m',' ','q','u','i','s',' ','e','s','t',' ','a','t',' ','n','i','s', 'l',' ','u','l','l','a','m','c','o','r','p','e','r',' ','e','g','e','s','t','a','s',' ','p','u','l','v','i','n','a','r',' ','e','u',' ','e','r','a','t','.',' ','D','u','i','s',' ','a',' ','e','l','i','t',' ','d','i','g','n','i','s','s','i','m',',',' ',
|
||||
'v','e','s','t','i','b','u','l','u','m',' ','e','r','o','s',' ','v','e','l',',',' ',
|
||||
't','e','m','p','u','s',' ','n','i','s','l','.',' ','A','e','n','e','a','n',' ','t','u','r','p','i','s',' ','n','u','n','c',',',' ',
|
||||
'c','u','r','s','u','s',' ','v','e','l',' ','l','a','c','i','n','i','a',' ','n','o','n',',',' ',
|
||||
'p','h','a','r','e','t','r','a',' ','e','g','e','t',' ','s','a','p','i','e','n','.',' ','D','u','i','s',' ','c','o','n','d','i','m','e','n','t','u','m',',',' ',
|
||||
'l','a','c','u','s',' ','a','t',' ','p','u','l','v','i','n','a','r',' ','t','e','m','p','o','r',',',' ',
|
||||
'l','e','o',' ','l','i','b','e','r','o',' ','v','o','l','u','t','p','a','t',' ','n','i','s','l',',',' ',
|
||||
'e','g','e','t',' ','p','o','r','t','t','i','t','o','r',' ','l','o','r','e','m',' ','m','i',' ','s','e','d',' ','m','a','g','n','a','.',' ','D','u','i','s',' ','d','i','c','t','u','m',',',' ',
|
||||
'm','a','s','s','a',' ','v','e','l',' ','e','u','i','s','m','o','d',' ','i','n','t','e','r','d','u','m',',',' ',
|
||||
'l','o','r','e','m',' ','m','i',' ','e','g','e','s','t','a','s',' ','e','l','i','t',',',' ',
|
||||
'h','e','n','d','r','e','r','i','t',' ','t','i','n','c','i','d','u','n','t',' ','e','s','t',' ','a','r','c','u',' ','a',' ','l','i','b','e','r','o','.',' ','I','n','t','e','r','d','u','m',' ','e','t',' ','m','a','l','e','s','u','a','d','a',' ','f','a','m','e','s',' ','a','c',' ','a','n','t','e',' ', 'i','p','s','u','m',' ','p','r','i','m','i','s',' ','i','n',' ','f','a','u','c','i','b','u','s','.',' ','C','u','r','a','b','i','t','u','r',' ','v','e','h','i','c','u','l','a',' ','m','a','g','n','a',' ','l','i','b','e','r','o',',',' ',
|
||||
'a','t',' ','r','h','o','n','c','u','s',' ','s','e','m',' ','o','r','n','a','r','e',' ','a','.',' ','I','n',' ','e','l','e','m','e','n','t','u','m',',',' ',
|
||||
'e','l','i','t',' ','e','t',' ','c','o','n','g','u','e',' ','p','u','l','v','i','n','a','r',',',' ',
|
||||
'm','a','s','s','a',' ','v','e','l','i','t',' ','c','o','m','m','o','d','o',' ','v','e','l','i','t',',',' ',
|
||||
'n','o','n',' ','e','l','e','m','e','n','t','u','m',' ','p','u','r','u','s',' ','l','i','g','u','l','a',' ','e','g','e','t',' ','l','a','c','u','s','.',' ','D','o','n','e','c',' ','e','f','f','i','c','i','t','u','r',' ','n','i','s','i',' ','e','u',' ','u','l','t','r','i','c','e','s',' ','e','f','f', 'i','c','i','t','u','r','.',' ','D','o','n','e','c',' ','n','e','q','u','e',' ','d','u','i',',',' ',
|
||||
'u','l','l','a','m','c','o','r','p','e','r',' ','i','d',' ','m','o','l','e','s','t','i','e',' ','q','u','i','s',',',' ',
|
||||
'c','o','n','s','e','q','u','a','t',' ','s','i','t',' ','a','m','e','t',' ','l','i','g','u','l','a','.',
|
||||
0xf7,
|
||||
};
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(frameLength + 2);
|
||||
|
||||
midi.begin();
|
||||
midi.sendSysEx(frameLength, reinterpret_cast<const byte*>(frame), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), frameLength + 2);
|
||||
serial.mTxBuffer.read(&buffer[0], frameLength + 2);
|
||||
EXPECT_THAT(buffer, ElementsAreArray(expected));
|
||||
}
|
||||
// With boundaries included
|
||||
{
|
||||
static const byte frame[] = {
|
||||
0xf0, 12, 17, 42, 47, 0xf7
|
||||
};
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendSysEx(6, frame, true);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray(frame));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendTimeCodeQuarterFrame)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
// Separate Nibbles
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(4);
|
||||
|
||||
midi.begin();
|
||||
midi.sendTimeCodeQuarterFrame(0x05, 0x0a);
|
||||
midi.sendTimeCodeQuarterFrame(0xff, 0xff);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 4);
|
||||
serial.mTxBuffer.read(&buffer[0], 4);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xf1, 0x5a,
|
||||
0xf1, 0x7f}));
|
||||
}
|
||||
// Pre-encoded nibbles
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(4);
|
||||
|
||||
midi.begin();
|
||||
midi.sendTimeCodeQuarterFrame(12);
|
||||
midi.sendTimeCodeQuarterFrame(42);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 4);
|
||||
serial.mTxBuffer.read(&buffer[0], 4);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xf1, 0x0c,
|
||||
0xf1, 0x2a}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendSongPosition)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendSongPosition(1234);
|
||||
midi.sendSongPosition(4321);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xf2, 0x52, 0x09,
|
||||
0xf2, 0x61, 0x21}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendSongSelect)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(4);
|
||||
|
||||
midi.begin();
|
||||
midi.sendSongSelect(12);
|
||||
midi.sendSongSelect(42);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 4);
|
||||
serial.mTxBuffer.read(&buffer[0], 4);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xf3, 12, 0xf3, 42}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendTuneRequest)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
buffer.resize(1);
|
||||
|
||||
midi.begin();
|
||||
midi.sendTuneRequest();
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 1);
|
||||
serial.mTxBuffer.read(&buffer[0], 1);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xf6}));
|
||||
}
|
||||
|
||||
TEST(MidiOutput, sendRealTime)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
// Test valid RealTime messages
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(6);
|
||||
|
||||
midi.begin();
|
||||
midi.sendRealTime(midi::Clock);
|
||||
midi.sendRealTime(midi::Start);
|
||||
midi.sendRealTime(midi::Continue);
|
||||
midi.sendRealTime(midi::Stop);
|
||||
midi.sendRealTime(midi::ActiveSensing);
|
||||
midi.sendRealTime(midi::SystemReset);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0xf8, 0xfa, 0xfb, 0xfc, 0xfe, 0xff
|
||||
}));
|
||||
}
|
||||
// Test invalid messages
|
||||
{
|
||||
midi.begin();
|
||||
midi.sendRealTime(midi::InvalidType);
|
||||
midi.sendRealTime(midi::NoteOff);
|
||||
midi.sendRealTime(midi::NoteOn);
|
||||
midi.sendRealTime(midi::AfterTouchPoly);
|
||||
midi.sendRealTime(midi::ControlChange);
|
||||
midi.sendRealTime(midi::ProgramChange);
|
||||
midi.sendRealTime(midi::AfterTouchChannel);
|
||||
midi.sendRealTime(midi::PitchBend);
|
||||
midi.sendRealTime(midi::SystemExclusive);
|
||||
midi.sendRealTime(midi::TimeCodeQuarterFrame);
|
||||
midi.sendRealTime(midi::SongPosition);
|
||||
midi.sendRealTime(midi::SongSelect);
|
||||
midi.sendRealTime(midi::TuneRequest);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiOutput, RPN)
|
||||
{
|
||||
typedef VariableSettings<true, true> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
RsMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
// 14-bit Value Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(13);
|
||||
|
||||
midi.begin();
|
||||
midi.beginRpn(1242, 12);
|
||||
midi.sendRpnValue(12345, 12);
|
||||
midi.endRpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 13);
|
||||
serial.mTxBuffer.read(&buffer[0], 13);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x64, 0x5a,
|
||||
0x65, 0x09,
|
||||
0x06, 0x60,
|
||||
0x26, 0x39,
|
||||
0x64, 0x7f,
|
||||
0x65, 0x7f}));
|
||||
}
|
||||
// MSB/LSB Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(13);
|
||||
|
||||
midi.begin();
|
||||
midi.beginRpn(1242, 12);
|
||||
midi.sendRpnValue(12, 42, 12);
|
||||
midi.endRpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 13);
|
||||
serial.mTxBuffer.read(&buffer[0], 13);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x64, 0x5a,
|
||||
0x65, 0x09,
|
||||
0x06, 0x0c,
|
||||
0x26, 0x2a,
|
||||
0x64, 0x7f,
|
||||
0x65, 0x7f}));
|
||||
}
|
||||
// Increment Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(11);
|
||||
|
||||
midi.begin();
|
||||
midi.beginRpn(1242, 12);
|
||||
midi.sendRpnIncrement(42, 12);
|
||||
midi.endRpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 11);
|
||||
serial.mTxBuffer.read(&buffer[0], 11);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x64, 0x5a,
|
||||
0x65, 0x09,
|
||||
0x60, 0x2a,
|
||||
0x64, 0x7f,
|
||||
0x65, 0x7f}));
|
||||
}
|
||||
// Decrement Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(11);
|
||||
|
||||
midi.begin();
|
||||
midi.beginRpn(1242, 12);
|
||||
midi.sendRpnDecrement(42, 12);
|
||||
midi.endRpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 11);
|
||||
serial.mTxBuffer.read(&buffer[0], 11);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x64, 0x5a,
|
||||
0x65, 0x09,
|
||||
0x61, 0x2a,
|
||||
0x64, 0x7f,
|
||||
0x65, 0x7f}));
|
||||
}
|
||||
// Multi Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(21);
|
||||
|
||||
midi.begin();
|
||||
midi.beginRpn(1242, 12);
|
||||
midi.sendRpnValue(12345, 12);
|
||||
midi.sendRpnValue(12, 42, 12);
|
||||
midi.sendRpnIncrement(42, 12);
|
||||
midi.sendRpnDecrement(42, 12);
|
||||
midi.endRpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 21);
|
||||
serial.mTxBuffer.read(&buffer[0], 21);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x64, 0x5a,
|
||||
0x65, 0x09,
|
||||
0x06, 0x60,
|
||||
0x26, 0x39,
|
||||
0x06, 0x0c,
|
||||
0x26, 0x2a,
|
||||
0x60, 0x2a,
|
||||
0x61, 0x2a,
|
||||
0x64, 0x7f,
|
||||
0x65, 0x7f}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiOutput, NRPN)
|
||||
{
|
||||
typedef VariableSettings<true, true> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
RsMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
// 14-bit Value Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(13);
|
||||
|
||||
midi.begin();
|
||||
midi.beginNrpn(1242, 12);
|
||||
midi.sendNrpnValue(12345, 12);
|
||||
midi.endNrpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 13);
|
||||
serial.mTxBuffer.read(&buffer[0], 13);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x62, 0x5a,
|
||||
0x63, 0x09,
|
||||
0x06, 0x60,
|
||||
0x26, 0x39,
|
||||
0x62, 0x7f,
|
||||
0x63, 0x7f}));
|
||||
}
|
||||
// MSB/LSB Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(13);
|
||||
|
||||
midi.begin();
|
||||
midi.beginNrpn(1242, 12);
|
||||
midi.sendNrpnValue(12, 42, 12);
|
||||
midi.endNrpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 13);
|
||||
serial.mTxBuffer.read(&buffer[0], 13);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x62, 0x5a,
|
||||
0x63, 0x09,
|
||||
0x06, 0x0c,
|
||||
0x26, 0x2a,
|
||||
0x62, 0x7f,
|
||||
0x63, 0x7f}));
|
||||
}
|
||||
// Increment Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(11);
|
||||
|
||||
midi.begin();
|
||||
midi.beginNrpn(1242, 12);
|
||||
midi.sendNrpnIncrement(42, 12);
|
||||
midi.endNrpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 11);
|
||||
serial.mTxBuffer.read(&buffer[0], 11);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x62, 0x5a,
|
||||
0x63, 0x09,
|
||||
0x60, 0x2a,
|
||||
0x62, 0x7f,
|
||||
0x63, 0x7f}));
|
||||
}
|
||||
// Decrement Single Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(11);
|
||||
|
||||
midi.begin();
|
||||
midi.beginNrpn(1242, 12);
|
||||
midi.sendNrpnDecrement(42, 12);
|
||||
midi.endNrpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 11);
|
||||
serial.mTxBuffer.read(&buffer[0], 11);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x62, 0x5a,
|
||||
0x63, 0x09,
|
||||
0x61, 0x2a,
|
||||
0x62, 0x7f,
|
||||
0x63, 0x7f}));
|
||||
}
|
||||
// Multi Frame
|
||||
{
|
||||
buffer.clear();
|
||||
buffer.resize(21);
|
||||
|
||||
midi.begin();
|
||||
midi.beginNrpn(1242, 12);
|
||||
midi.sendNrpnValue(12345, 12);
|
||||
midi.sendNrpnValue(12, 42, 12);
|
||||
midi.sendNrpnIncrement(42, 12);
|
||||
midi.sendNrpnDecrement(42, 12);
|
||||
midi.endNrpn(12);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 21);
|
||||
serial.mTxBuffer.read(&buffer[0], 21);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({0xbb,
|
||||
0x62, 0x5a,
|
||||
0x63, 0x09,
|
||||
0x06, 0x60,
|
||||
0x26, 0x39,
|
||||
0x06, 0x0c,
|
||||
0x26, 0x2a,
|
||||
0x60, 0x2a,
|
||||
0x61, 0x2a,
|
||||
0x62, 0x7f,
|
||||
0x63, 0x7f}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MidiOutput, runningStatusCancellation)
|
||||
{
|
||||
typedef VariableSettings<true, false> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
RsMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
static const unsigned sysExLength = 13;
|
||||
static const byte sysEx[sysExLength] = {
|
||||
'H','e','l','l','o',',',' ','W','o','r','l','d','!'
|
||||
};
|
||||
|
||||
midi.begin();
|
||||
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
midi.sendNoteOn(56, 78, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 5);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(5);
|
||||
serial.mTxBuffer.read(&buffer[0], 5);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x90, 12, 34, 56, 78
|
||||
}));
|
||||
|
||||
midi.sendRealTime(midi::Clock); // Should not reset running status.
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0xf8, 12, 34
|
||||
}));
|
||||
|
||||
midi.sendSysEx(sysExLength, sysEx); // Should reset running status.
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 18);
|
||||
buffer.clear();
|
||||
buffer.resize(18);
|
||||
serial.mTxBuffer.read(&buffer[0], 18);
|
||||
{
|
||||
static const byte expected[] = {
|
||||
0xf0, 'H','e','l','l','o',',',' ','W','o','r','l','d','!', 0xf7,
|
||||
0x90, 12, 34
|
||||
};
|
||||
EXPECT_THAT(buffer, ElementsAreArray(expected));
|
||||
}
|
||||
|
||||
midi.sendTimeCodeQuarterFrame(42); // Should reset running status.
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 5);
|
||||
buffer.clear();
|
||||
buffer.resize(5);
|
||||
serial.mTxBuffer.read(&buffer[0], 5);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0xf1, 42,
|
||||
0x90, 12, 34
|
||||
}));
|
||||
|
||||
midi.sendSongPosition(42); // Should reset running status.
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
buffer.clear();
|
||||
buffer.resize(6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0xf2, 42, 0,
|
||||
0x90, 12, 34
|
||||
}));
|
||||
|
||||
midi.sendSongSelect(42); // Should reset running status.
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 5);
|
||||
buffer.clear();
|
||||
buffer.resize(5);
|
||||
serial.mTxBuffer.read(&buffer[0], 5);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0xf3, 42,
|
||||
0x90, 12, 34
|
||||
}));
|
||||
|
||||
midi.sendTuneRequest(); // Should reset running status.
|
||||
midi.sendNoteOn(12, 34, 1);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 4);
|
||||
buffer.clear();
|
||||
buffer.resize(4);
|
||||
serial.mTxBuffer.read(&buffer[0], 4);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0xf6,
|
||||
0x90, 12, 34
|
||||
}));
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
#include "unit-tests.h"
|
||||
#include "unit-tests_Settings.h"
|
||||
#include <src/MIDI.h>
|
||||
#include <test/mocks/test-mocks_SerialMock.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
using namespace testing;
|
||||
USING_NAMESPACE_UNIT_TESTS
|
||||
typedef test_mocks::SerialMock<32> SerialMock;
|
||||
typedef midi::MidiInterface<SerialMock> MidiInterface;
|
||||
typedef std::vector<byte> Buffer;
|
||||
|
||||
template<unsigned Size>
|
||||
struct VariableSysExSettings : midi::DefaultSettings
|
||||
{
|
||||
static const unsigned SysExMaxSize = Size;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
TEST(MidiThru, defaultValues)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
||||
midi.begin(); // Should not change the state
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
||||
}
|
||||
|
||||
TEST(MidiThru, beginEnablesThru)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
midi.turnThruOff();
|
||||
EXPECT_EQ(midi.getThruState(), false);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Off);
|
||||
midi.begin();
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
||||
}
|
||||
|
||||
TEST(MidiThru, setGet)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
midi.turnThruOff();
|
||||
EXPECT_EQ(midi.getThruState(), false);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Off);
|
||||
|
||||
midi.turnThruOn();
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
||||
midi.turnThruOn(midi::Thru::SameChannel);
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::SameChannel);
|
||||
midi.turnThruOn(midi::Thru::DifferentChannel);
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::DifferentChannel);
|
||||
|
||||
midi.setThruFilterMode(midi::Thru::Full);
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
||||
midi.setThruFilterMode(midi::Thru::SameChannel);
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::SameChannel);
|
||||
midi.setThruFilterMode(midi::Thru::DifferentChannel);
|
||||
EXPECT_EQ(midi.getThruState(), true);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::DifferentChannel);
|
||||
midi.setThruFilterMode(midi::Thru::Off);
|
||||
EXPECT_EQ(midi.getThruState(), false);
|
||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Off);
|
||||
}
|
||||
|
||||
TEST(MidiThru, off)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.turnThruOff();
|
||||
|
||||
static const unsigned rxSize = 5;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiThru, full)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.setThruFilterMode(midi::Thru::Full);
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9b, 12, 34
|
||||
}));
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9c, 56, 78
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(MidiThru, sameChannel)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(12);
|
||||
midi.setThruFilterMode(midi::Thru::SameChannel);
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9b, 12, 34
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(MidiThru, sameChannelOmni) // Acts like full
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.setThruFilterMode(midi::Thru::SameChannel);
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9b, 12, 34
|
||||
}));
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3); // Not using TX running status
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9c, 56, 78
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(MidiThru, differentChannel)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(12);
|
||||
midi.setThruFilterMode(midi::Thru::DifferentChannel);
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9c, 56, 78
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(MidiThru, differentChannelOmni) // Acts like off
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.setThruFilterMode(midi::Thru::DifferentChannel);
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
}
|
||||
|
||||
TEST(MidiThru, multiByteThru)
|
||||
{
|
||||
typedef VariableSettings<false, false> MultiByteParsing;
|
||||
typedef midi::MidiInterface<SerialMock, MultiByteParsing> MultiByteMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
MultiByteMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.setThruFilterMode(midi::Thru::Full);
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 6);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(6);
|
||||
serial.mTxBuffer.read(&buffer[0], 6);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9b, 12, 34, 0x9b, 56, 78
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(MidiThru, withTxRunningStatus)
|
||||
{
|
||||
typedef VariableSettings<true, true> Settings;
|
||||
typedef midi::MidiInterface<SerialMock, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
RsMidiInterface midi(serial);
|
||||
Buffer buffer;
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.setThruFilterMode(midi::Thru::Full);
|
||||
|
||||
static const unsigned rxSize = 5;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(3);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||
serial.mTxBuffer.read(&buffer[0], 3);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
0x9b, 12, 34
|
||||
}));
|
||||
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
buffer.clear();
|
||||
buffer.resize(2);
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 2);
|
||||
serial.mTxBuffer.read(&buffer[0], 2);
|
||||
EXPECT_THAT(buffer, ElementsAreArray({
|
||||
56, 78
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(MidiThru, invalidMode)
|
||||
{
|
||||
SerialMock serial;
|
||||
MidiInterface midi(serial);
|
||||
|
||||
midi.begin(MIDI_CHANNEL_OMNI);
|
||||
midi.setThruFilterMode(midi::Thru::Mode(42));
|
||||
|
||||
static const unsigned rxSize = 6;
|
||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||
serial.mRxBuffer.write(rxData, rxSize);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
EXPECT_EQ(midi.read(), true);
|
||||
|
||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#include "unit-tests.h"
|
||||
#include <src/midi_UsbDefs.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
TEST(MidiUsb, codeIndexNumberSizes)
|
||||
{
|
||||
typedef midi::CodeIndexNumbers CIN;
|
||||
EXPECT_EQ(CIN::getSize(CIN::reserved), 0);
|
||||
EXPECT_EQ(CIN::getSize(CIN::misc), 0);
|
||||
EXPECT_EQ(CIN::getSize(CIN::cableEvent), 0);
|
||||
EXPECT_EQ(CIN::getSize(CIN::systemCommon2Bytes), 2);
|
||||
EXPECT_EQ(CIN::getSize(CIN::systemCommon3Bytes), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::sysExStart), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::sysExContinue), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::systemCommon1Byte), 1);
|
||||
EXPECT_EQ(CIN::getSize(CIN::sysExEnds1Byte), 1);
|
||||
EXPECT_EQ(CIN::getSize(CIN::sysExEnds2Bytes), 2);
|
||||
EXPECT_EQ(CIN::getSize(CIN::sysExEnds3Bytes), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::noteOff), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::noteOn), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::polyPressure), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::controlChange), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::programChange), 2);
|
||||
EXPECT_EQ(CIN::getSize(CIN::channelPressure), 2);
|
||||
EXPECT_EQ(CIN::getSize(CIN::pitchBend), 3);
|
||||
EXPECT_EQ(CIN::getSize(CIN::singleByte), 1);
|
||||
}
|
||||
|
||||
TEST(MidiUsb, UsbMidiEventPacket)
|
||||
{
|
||||
midi::UsbMidiEventPacket packet;
|
||||
EXPECT_EQ(packet.mData[0], 0);
|
||||
EXPECT_EQ(packet.mData[1], 0);
|
||||
EXPECT_EQ(packet.mData[2], 0);
|
||||
EXPECT_EQ(packet.mData[3], 0);
|
||||
EXPECT_EQ(packet.getCableNumber(), 0);
|
||||
EXPECT_EQ(packet.getCodeIndexNumber(), 0);
|
||||
|
||||
packet.setHeader(12, 7);
|
||||
EXPECT_EQ(packet.mData[0], 0xc7);
|
||||
EXPECT_EQ(packet.getCableNumber(), 12);
|
||||
EXPECT_EQ(packet.getCodeIndexNumber(), 7);
|
||||
|
||||
const byte midiData[3] = { 12, 42, 47 };
|
||||
packet.setMidiData(midiData);
|
||||
EXPECT_EQ(packet.mData[0], 0xc7);
|
||||
EXPECT_EQ(packet.mData[1], 12);
|
||||
EXPECT_EQ(packet.mData[2], 42);
|
||||
EXPECT_EQ(packet.mData[3], 47);
|
||||
|
||||
const byte fullData[4] = { 12, 34, 56, 78 };
|
||||
packet = fullData;
|
||||
EXPECT_EQ(packet.mData[0], 12);
|
||||
EXPECT_EQ(packet.mData[1], 34);
|
||||
EXPECT_EQ(packet.mData[2], 56);
|
||||
EXPECT_EQ(packet.mData[3], 78);
|
||||
|
||||
const byte* midiDataConst = packet.getMidiData();
|
||||
EXPECT_EQ(midiDataConst[0], 34);
|
||||
EXPECT_EQ(midiDataConst[1], 56);
|
||||
EXPECT_EQ(midiDataConst[2], 78);
|
||||
byte* midiDataMutable = packet.getMidiData();
|
||||
EXPECT_EQ(midiDataMutable[0], 34);
|
||||
EXPECT_EQ(midiDataMutable[1], 56);
|
||||
EXPECT_EQ(midiDataMutable[2], 78);
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#include "unit-tests.h"
|
||||
#include <test/mocks/test-mocks_SerialMock.h>
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
USING_NAMESPACE_TEST_MOCKS
|
||||
using namespace testing;
|
||||
|
||||
TEST(RingBuffer, initialState)
|
||||
{
|
||||
typedef RingBuffer<uint8, 32> Buffer;
|
||||
Buffer buffer;
|
||||
EXPECT_EQ(buffer.getLength(), 0);
|
||||
EXPECT_EQ(buffer.isEmpty(), true);
|
||||
buffer.clear();
|
||||
EXPECT_EQ(buffer.getLength(), 0);
|
||||
EXPECT_EQ(buffer.isEmpty(), true);
|
||||
}
|
||||
|
||||
TEST(RingBuffer, uint8)
|
||||
{
|
||||
typedef RingBuffer<uint8, 8> Buffer;
|
||||
Buffer buffer;
|
||||
|
||||
buffer.write(42);
|
||||
EXPECT_EQ(buffer.getLength(), 1);
|
||||
EXPECT_EQ(buffer.isEmpty(), false);
|
||||
|
||||
const uint8 read = buffer.read();
|
||||
EXPECT_EQ(read, 42);
|
||||
EXPECT_EQ(buffer.getLength(), 0);
|
||||
EXPECT_EQ(buffer.isEmpty(), true);
|
||||
|
||||
const uint8 data[] = "Hello, World!";
|
||||
buffer.write(data, 13);
|
||||
EXPECT_EQ(buffer.getLength(), 5); // 13 % 8
|
||||
EXPECT_EQ(buffer.isEmpty(), false);
|
||||
|
||||
uint8 output[8] = { 0 };
|
||||
buffer.read(output, 8);
|
||||
const uint8 expected[8] = {
|
||||
'o', 'r', 'l', 'd', '!', ',', ' ', 'W',
|
||||
};
|
||||
EXPECT_THAT(output, ContainerEq(expected));
|
||||
|
||||
buffer.clear();
|
||||
EXPECT_EQ(buffer.getLength(), 0);
|
||||
EXPECT_EQ(buffer.isEmpty(), true);
|
||||
}
|
||||
|
||||
TEST(RingBuffer, uint32)
|
||||
{
|
||||
typedef RingBuffer<uint32_t, 32> Buffer;
|
||||
Buffer buffer;
|
||||
buffer.write(42);
|
||||
EXPECT_EQ(buffer.getLength(), 1);
|
||||
EXPECT_EQ(buffer.isEmpty(), false);
|
||||
const uint8 read = buffer.read();
|
||||
EXPECT_EQ(read, 42);
|
||||
EXPECT_EQ(buffer.getLength(), 0);
|
||||
EXPECT_EQ(buffer.isEmpty(), true);
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#include "unit-tests_Settings.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
const bool DefaultSettings::UseRunningStatus;
|
||||
const bool DefaultSettings::HandleNullVelocityNoteOnAsNoteOff;
|
||||
const bool DefaultSettings::Use1ByteParsing;
|
||||
const long DefaultSettings::BaudRate;
|
||||
const unsigned DefaultSettings::SysExMaxSize;
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
TEST(Settings, hasTheRightDefaultValues)
|
||||
{
|
||||
EXPECT_EQ(midi::DefaultSettings::UseRunningStatus, false);
|
||||
EXPECT_EQ(midi::DefaultSettings::HandleNullVelocityNoteOnAsNoteOff, true);
|
||||
EXPECT_EQ(midi::DefaultSettings::Use1ByteParsing, true);
|
||||
EXPECT_EQ(midi::DefaultSettings::BaudRate, 31250);
|
||||
EXPECT_EQ(midi::DefaultSettings::SysExMaxSize, unsigned(128));
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "unit-tests.h"
|
||||
#include <src/midi_Settings.h>
|
||||
|
||||
BEGIN_UNIT_TESTS_NAMESPACE
|
||||
|
||||
template<bool RunningStatus, bool OneByteParsing>
|
||||
struct VariableSettings : public midi::DefaultSettings
|
||||
{
|
||||
static const bool UseRunningStatus = RunningStatus;
|
||||
static const bool Use1ByteParsing = OneByteParsing;
|
||||
};
|
||||
|
||||
template<bool A, bool B>
|
||||
const bool VariableSettings<A, B>::UseRunningStatus;
|
||||
template<bool A, bool B>
|
||||
const bool VariableSettings<A, B>::Use1ByteParsing;
|
||||
|
||||
END_UNIT_TESTS_NAMESPACE
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
#include "unit-tests.h"
|
||||
#include <src/MIDI.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
using namespace testing;
|
||||
|
||||
TEST(SysExCodec, Encoder)
|
||||
{
|
||||
// ASCII content
|
||||
{
|
||||
const byte input[] = "Hello, World!";
|
||||
byte buffer[16];
|
||||
memset(buffer, 0, 16 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::encodeSysEx(input, buffer, 13);
|
||||
EXPECT_EQ(encodedSize, unsigned(15));
|
||||
const byte expected[16] = {
|
||||
0, 'H', 'e', 'l', 'l', 'o', ',', ' ',
|
||||
0, 'W', 'o', 'r', 'l', 'd', '!', 0,
|
||||
};
|
||||
EXPECT_THAT(buffer, Each(Le(0x7f))); // All elements are <= 127
|
||||
EXPECT_THAT(buffer, ContainerEq(expected));
|
||||
}
|
||||
// Non-ASCII content
|
||||
{
|
||||
const byte input[] = {
|
||||
182, 236, 167, 177, 61, 91, 120, // 01111000 -> 120
|
||||
107, 94, 209, 87, 94 // 000100xx -> 16
|
||||
};
|
||||
byte buffer[16];
|
||||
memset(buffer, 0, 16 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::encodeSysEx(input, buffer, 12);
|
||||
EXPECT_EQ(encodedSize, unsigned(14));
|
||||
const byte expected[16] = {
|
||||
// MSB Data
|
||||
120, 54, 108, 39, 49, 61, 91, 120,
|
||||
16, 107, 94, 81, 87, 94, 0, 0,
|
||||
};
|
||||
EXPECT_THAT(buffer, Each(Le(0x7f))); // All elements are <= 127
|
||||
EXPECT_THAT(buffer, ContainerEq(expected));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SysExCodec, Decoder)
|
||||
{
|
||||
// ASCII content
|
||||
{
|
||||
const byte input[] = {
|
||||
0, 'H', 'e', 'l', 'l', 'o', ',', ' ',
|
||||
0, 'W', 'o', 'r', 'l', 'd', '!',
|
||||
};
|
||||
byte buffer[16];
|
||||
memset(buffer, 0, 16 * sizeof(byte));
|
||||
const unsigned decodedSize = midi::decodeSysEx(input, buffer, 15);
|
||||
EXPECT_EQ(decodedSize, unsigned(13));
|
||||
const byte expected[16] = {
|
||||
'H', 'e', 'l', 'l', 'o', ',', ' ', 'W',
|
||||
'o', 'r', 'l', 'd', '!', 0, 0, 0,
|
||||
};
|
||||
EXPECT_THAT(buffer, Each(Le(0x7f))); // All elements are <= 127
|
||||
EXPECT_THAT(buffer, ContainerEq(expected));
|
||||
}
|
||||
// Non-ASCII content
|
||||
{
|
||||
const byte input[] = {
|
||||
// MSB Data
|
||||
120, 54, 108, 39, 49, 61, 91, 120,
|
||||
16, 107, 94, 81, 87, 94,
|
||||
};
|
||||
byte buffer[16];
|
||||
memset(buffer, 0, 16 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::decodeSysEx(input, buffer, 14);
|
||||
EXPECT_EQ(encodedSize, unsigned(12));
|
||||
const byte expected[16] = {
|
||||
182, 236, 167, 177, 61, 91, 120,
|
||||
107, 94, 209, 87, 94, 0, 0,
|
||||
0, 0,
|
||||
};
|
||||
EXPECT_THAT(input, Each(Le(0x7f))); // All elements are <= 127
|
||||
EXPECT_THAT(buffer, ContainerEq(expected));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SysExCodec, Codec)
|
||||
{
|
||||
// ASCII content
|
||||
{
|
||||
const byte input[] = "Hello, World!";
|
||||
byte buffer1[16];
|
||||
byte buffer2[16];
|
||||
memset(buffer1, 0, 16 * sizeof(byte));
|
||||
memset(buffer2, 0, 16 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::encodeSysEx(input, buffer1, 13);
|
||||
EXPECT_EQ(encodedSize, unsigned(15));
|
||||
const unsigned decodedSize = midi::decodeSysEx(buffer1, buffer2, encodedSize);
|
||||
EXPECT_EQ(decodedSize, unsigned(13));
|
||||
EXPECT_STREQ(reinterpret_cast<const char*>(buffer2),
|
||||
reinterpret_cast<const char*>(input));
|
||||
}
|
||||
// Non-ASCII content
|
||||
{
|
||||
const byte input[] = {
|
||||
// MSB Data
|
||||
182, 236, 167, 177, 61, 91, 120,
|
||||
107, 94, 209, 87, 94
|
||||
};
|
||||
byte buffer1[14];
|
||||
byte buffer2[12];
|
||||
memset(buffer1, 0, 14 * sizeof(byte));
|
||||
memset(buffer2, 0, 12 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::encodeSysEx(input, buffer1, 12);
|
||||
EXPECT_EQ(encodedSize, unsigned(14));
|
||||
const unsigned decodedSize = midi::decodeSysEx(buffer1, buffer2, encodedSize);
|
||||
EXPECT_EQ(decodedSize, unsigned(12));
|
||||
EXPECT_THAT(buffer2, ContainerEq(input));
|
||||
}
|
||||
}
|
||||
|
||||
END_UNNAMED_NAMESPACE
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#include "unit-tests.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "unit-tests_Namespace.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
BEGIN_UNIT_TESTS_NAMESPACE
|
||||
|
||||
END_UNIT_TESTS_NAMESPACE
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#define UNIT_TESTS_NAMESPACE unit_tests
|
||||
#define BEGIN_UNIT_TESTS_NAMESPACE namespace UNIT_TESTS_NAMESPACE {
|
||||
#define END_UNIT_TESTS_NAMESPACE }
|
||||
#define BEGIN_UNNAMED_NAMESPACE namespace {
|
||||
#define END_UNNAMED_NAMESPACE }
|
||||
|
||||
#define USING_NAMESPACE_UNIT_TESTS using namespace UNIT_TESTS_NAMESPACE;
|
||||
|
||||
BEGIN_UNIT_TESTS_NAMESPACE
|
||||
|
||||
END_UNIT_TESTS_NAMESPACE
|
||||
Loading…
Reference in New Issue