Compare commits

..

34 Commits

Author SHA1 Message Date
lathoub 2d64cc3c2f
Update CustomBaudRate.ino
override DefaultSerialSettings to override BaudRate (and not DefaultSettings )
2022-01-11 08:00:48 +01:00
lathoub 7c0d716641
added CustomBaudRate example (#262)
* added CustomBaudRate example

- added customer baudrate example
- fixed old refs to examples in Doxygen

* fixed compile error for boards that have no Serial
2022-01-11 07:24:58 +01:00
François Best 8f8c7cfcc6
doc: Add @muxa to the contributors list 2021-11-30 09:22:26 +01:00
Mikhail Diatchenko 8e9805fc92
feat: Added missing MidiControlChangeNumber definitions (#260)
* Added missing MidiControlChangeNumber definitions
* Fixed duplicate enum
* Added keywords for new definitions
2021-11-29 10:54:16 +01:00
François Best 1944aca891
chore: Add @paul-emile-element as contributor 2021-11-03 15:06:11 +01:00
Paul 663b24b0b5
fix: Updated send(midiMessage&) to better handle system real time (#250)
* updated send(midiMessage&) to better handle system real time,
sysex and other system common messages

* use fully qualified function names as requested

Co-authored-by: Element <paul-emile.element@windriver.com>
2021-11-03 15:04:52 +01:00
lathoub be557c1f4b Update MIDI.h
added return value (missed by PR)
2021-10-10 17:04:46 +02:00
lathoub 50e33c8b4a
Merge pull request #247 from FortySevenEffects/chaining
chaining for all commands, callbacks and setters
2021-10-10 16:47:07 +02:00
lathoub 417beca1c7 Update platformio.yml
added Chaining example
2021-10-10 15:48:29 +02:00
lathoub c9d3b9b592 no chaining for private functions, added example 2021-10-10 15:33:28 +02:00
François Best c7922927e5
chore: Fix PlatformIO checks
See platformio/platformio-core#4078
2021-10-10 14:01:47 +02:00
lathoub 892f65a9ee Update MIDI.hpp 2021-10-10 12:16:15 +02:00
lathoub e1035a03f7 Update MIDI.hpp
fixed errors (return -> return *this where needed)
2021-10-10 12:13:30 +02:00
lathoub 2012e2a84a chaining for all commands, callbacks and setters 2021-10-10 12:04:08 +02:00
Francois Best b4daa697a9 doc: Update badges with CI status 2021-08-10 16:26:57 +02:00
Francois Best d9873560ad chore: Improve coverage blind spots 2021-08-10 16:21:18 +02:00
François Best bf80e99d54
chore: Move CI to GitHub Actions
- Build and run unit tests with CMake
- Track code coverage with Coveralls
- Build examples on officially supported boards
- Removed TravisCI configuration

Closes #233.
2021-08-10 16:12:51 +02:00
François Best 2f03d9b29d
chore: Checkout submodules in CI 2021-08-09 22:06:09 +02:00
François Best d2f11d02e2
chore: Add CMake CI 2021-08-09 22:03:59 +02:00
Francois Best 9f5317f299 chore: Config 2021-08-06 09:35:54 +02:00
Francois Best f356c99ca2 chore: Allow copying Messages
This is ground work for the `map` Thru function
in PR #232.
2021-08-06 09:35:54 +02:00
Francois Best 7ca289d84e chore: Fix internal method casing 2021-08-06 09:35:54 +02:00
François Best 0d605dc8a7
doc: Update contributors
Added @kant for PR #230
2021-08-01 20:02:40 +02:00
Darío Hereñú 3f0a2d3fdb Minor fix (line 65) 2021-08-01 20:00:28 +02:00
lathoub d501f4bb3d end method for Serial Transport
The Transport layer is started, but could never be stopped.
This change will allow the underlying library to call the end() method in the Transport layers (all other Transport layer have the end() method implemented)

Next step is to call the Transport end() method, when ending the MIDI instance
2021-06-08 20:36:38 +02:00
François Best d9149d19df
chore: Better description for Discussions 2020-12-09 14:04:49 +01:00
François Best a93fb27a1c
chore: Add link to Discussions in issue templates 2020-12-09 11:39:42 +01:00
François Best d967c0c389
chore: Disable generic issues
General community-related Q&A is moved to Discussions
2020-12-09 11:34:22 +01:00
François Best d3c361033d
chore: Suggest using Discussions first 2020-12-09 11:28:26 +01:00
François Best 33b294624a chore: Add Feature Request template
Also redirect issues to discussions for project help.
2020-12-09 11:26:09 +01:00
François Best 10ef10bfde feat: Add issue template for bug report 2020-12-09 11:20:27 +01:00
Felix Uhl da8ca5e69a Update search term for library manager 2020-09-16 16:39:29 +02:00
Felix Uhl e292cf61b0 Update library manager screenshot 2020-09-16 16:39:29 +02:00
stfufane 150cecd450 Fix sendPitchBend multiplication 2020-06-08 11:39:16 +02:00
25 changed files with 615 additions and 461 deletions

66
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,66 @@
---
name: Bug report
about: Report something that does not work as intended
title: ''
labels: bug
assignees: ''
---
<!--
Before opening an issue, check if your problem already has been solved by looking in:
- The existing issues: https://github.com/FortySevenEffects/arduino_midi_library/issues
- The discussions: https://github.com/FortySevenEffects/arduino_midi_library/discussions
Consider opening a discussion instead of an issue if you need help with your project:
https://github.com/FortySevenEffects/arduino_midi_library/discussions/new
-->
## Context
Please answer a few questions to help us understand your problem better and guide you to a solution:
<!-- Tip: place the letter x in the checkboxes to tick them:
- [ ] Unticked checkbox
- [x] Ticked checkbox
You can also tick them by clicking after you've submitted your issue.
-->
- What board are you using ?
- `example: Arduino Leonardo`
- _Please list any shields or other **relevant** hardware you're using_
- What version of the Arduino IDE are you using ?
- `example: 1.8.5`
- How are you using MIDI ?
- [ ] Hardware Serial (DIN plugs)
- [ ] USB
- [ ] Other (please specify)
- Is your problem related to:
- [ ] MIDI Input (reading messages from other devices)
- [ ] MIDI Output (sending messages to other devices)
- How comfortable are you with code ?
- [ ] Complete beginner
- [ ] I've done basic projects
- [ ] I know my way around C/C++
- [ ] Advanced / professional
## Describe your project and what you expect to happen:
<!--
Example: When I press a switch on my pedalboard, it sends a SysEx message that I'd like to receive on my Arduino.
Note: Attachments (circuit diagrams, code examples) are most welcome and will help us understand your needs better and find a suitable solution for your issue.
-->
## Describe your problem (what does not work):
<!--
Example: I cannot receive SysEx messages coming from my AxeFX 2
-->
## Steps to reproduce
<!--
Please list the steps you took to hit the problem, so we can try and reproduce it.
-->

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Discussions
url: https://github.com/FortySevenEffects/arduino_midi_library/discussions
about: Not a bug or a feature request ? Discuss your problem, ask for help or show what you've built in Discussions.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: new feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

59
.github/workflows/cmake.yml vendored Normal file
View File

@ -0,0 +1,59 @@
name: CMake
on:
push:
pull_request:
branches: [ master ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Debug
GENERATE_COVERAGE: true
LCOV_ROOT: ${{github.workspace}}/lcov
jobs:
build:
# The CMake configure and build commands are platform agnostic and should work equally
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install lcov
run: |
mkdir -p "$LCOV_ROOT"
wget https://github.com/linux-test-project/lcov/releases/download/v1.15/lcov-1.15.tar.gz --output-document="$LCOV_ROOT/lcov.tar.gz"
tar -xf "$LCOV_ROOT/lcov.tar.gz" --strip-components=1 -C "$LCOV_ROOT"
echo "$LCOV_ROOT/bin" >> $GITHUB_PATH
shell: bash
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILDER_ENABLE_PROFILING=true
- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Run Unit Tests
working-directory: ${{github.workspace}}/build
run: ctest --verbose
- name: Generate code coverage report
working-directory: ${{github.workspace}}/build
run: |
lcov --directory . --capture --output-file coverage.info
lcov --remove coverage.info '/usr/*' "${{github.workspace}}/test/*" "${{github.workspace}}/external/*" --output-file coverage.info
lcov --list coverage.info
- uses: coverallsapp/github-action@9ba913c152ae4be1327bfb9085dc806cedb44057
name: Upload code coverage report to Coveralls
with:
path-to-lcov: ${{github.workspace}}/build/coverage.info
github-token: ${{ secrets.GITHUB_TOKEN }}

62
.github/workflows/platformio.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: PlatformIO
on:
push:
branches: [master]
pull_request:
branches: [ master ]
jobs:
platformio:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- AltPinSerial
- Basic_IO
- Bench
- Callbacks
- Chaining
- DualMerger
- ErrorCallback
- Input
- RPN_NRPN
- SimpleSynth
- CustomBaudRate
board:
- uno
- due
- zero
- leonardo
- micro
- nanoatmega328
- megaatmega2560
- teensy2
- teensy30
- teensy31
- teensylc
steps:
- uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: ${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
pip install "click!=8.0.2" # See platformio/platformio-core#4078
- name: Run PlatformIO
run: pio ci --lib="." --board="${{matrix.board}}"
env:
PLATFORMIO_CI_SRC: examples/${{ matrix.example }}

View File

@ -1,104 +0,0 @@
# 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/ErrorCallback
- PLATFORMIO_CI_SRC=examples/Input
- PLATFORMIO_CI_SRC=examples/RPN_NRPN
- PLATFORMIO_CI_SRC=examples/SimpleSynth
- PLATFORMIO_CI_SRC=examples/AltPinSerial
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.14.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
platformio ci --lib="." \
--board="uno" \
--board="due" \
--board="zero" \
--board="leonardo" \
--board="micro" \
--board="nanoatmega328" \
--board="megaatmega2560" \
--board="teensy2" \
--board="teensy30" \
--board="teensy31" \
--board="teensylc"
fi
after_success:
- |
if [ "${GENERATE_COVERAGE}" ]; then
# Generate code coverage information & send to Coveralls
rm -rf ./external/google-test/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.gcda
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

View File

@ -8,6 +8,7 @@
"string": "cpp", "string": "cpp",
"string_view": "cpp", "string_view": "cpp",
"vector": "cpp", "vector": "cpp",
"istream": "cpp" "istream": "cpp",
"system_error": "cpp"
} }
} }

View File

@ -1,9 +1,10 @@
# Arduino MIDI Library # Arduino MIDI Library
[![Build Status](https://travis-ci.org/FortySevenEffects/arduino_midi_library.svg?branch=master)](https://travis-ci.org/FortySevenEffects/arduino_midi_library)
[![Coveralls](https://img.shields.io/coveralls/FortySevenEffects/arduino_midi_library.svg?maxAge=3600)](https://coveralls.io/github/FortySevenEffects/arduino_midi_library)
[![GitHub release](https://img.shields.io/github/release/FortySevenEffects/arduino_midi_library.svg?maxAge=3600)](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest) [![GitHub release](https://img.shields.io/github/release/FortySevenEffects/arduino_midi_library.svg?maxAge=3600)](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest)
[![License](https://img.shields.io/github/license/FortySevenEffects/arduino_midi_library.svg?maxAge=3600)](LICENSE) [![License](https://img.shields.io/github/license/FortySevenEffects/arduino_midi_library.svg?maxAge=3600)](LICENSE)
[![Build](https://github.com/FortySevenEffects/arduino_midi_library/actions/workflows/cmake.yml/badge.svg?branch=master)](https://github.com/FortySevenEffects/arduino_midi_library/actions/workflows/cmake.yml)
[![Examples](https://github.com/FortySevenEffects/arduino_midi_library/actions/workflows/platformio.yml/badge.svg?branch=master)](https://github.com/FortySevenEffects/arduino_midi_library/actions/workflows/platformio.yml)
[![Coveralls](https://img.shields.io/coveralls/FortySevenEffects/arduino_midi_library.svg?maxAge=3600)](https://coveralls.io/github/FortySevenEffects/arduino_midi_library)
This library adds MIDI I/O communications to an Arduino board. This library adds MIDI I/O communications to an Arduino board.
@ -24,7 +25,7 @@ This library adds MIDI I/O communications to an Arduino board.
### Getting Started ### Getting Started
1. Use the Arduino Library Manager to install the library. 1. Use the Arduino Library Manager to install the library.
![Type "MIDI" in the Arduino IDE Library Manager](res/library-manager.jpg) ![Type "MIDI I/Os for Arduino" in the Arduino IDE Library Manager](res/library-manager.png)
2. Start coding: 2. Start coding:
@ -111,9 +112,9 @@ protocol bridges.
- Software Thru is enabled by default on Serial, but not on other transports. - Software Thru is enabled by default on Serial, but not on other transports.
## Contact ## Contact & Contribution
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). To report a bug, contribute, discuss on usage, or request support, please [discuss it here](https://github.com/FortySevenEffects/arduino_midi_library/discussions/new).
You can also contact me on Twitter: [@fortysevenfx](https://twitter.com/fortysevenfx). You can also contact me on Twitter: [@fortysevenfx](https://twitter.com/fortysevenfx).
@ -130,6 +131,9 @@ Special thanks to all who have contributed to this open-source project !
- [@LnnrtS](https://github.com/LnnrtS) - [@LnnrtS](https://github.com/LnnrtS)
- [@DavidMenting](https://github.com/DavidMenting) - [@DavidMenting](https://github.com/DavidMenting)
- [@Rolel](https://github.com/Rolel) - [@Rolel](https://github.com/Rolel)
- [@kant](https://github.com/kant)
- [@paul-emile-element](https://github.com/paul-emile-element)
- [@muxa](https://github.com/muxa)
You want to help ? Check out the [contribution guidelines](./CONTRIBUTING.md). You want to help ? Check out the [contribution guidelines](./CONTRIBUTING.md).

View File

@ -12,7 +12,7 @@
// Examples // Examples
/*! /*!
\example MIDI_Basic_IO.ino \example Basic_IO.ino
This example shows how to perform simple input and output MIDI. \n This example shows how to perform simple input and output MIDI. \n
\n \n
When any message arrives to the Arduino, the LED is turned on, When any message arrives to the Arduino, the LED is turned on,
@ -29,15 +29,15 @@
*/ */
/*! /*!
\example MIDI_Callbacks.ino \example Callbacks.ino
This example shows how to use callbacks for easier MIDI input handling. \n This example shows how to use callbacks for easier MIDI input handling. \n
*/ */
/*! /*!
\example MIDI_Bench.ino \example Bench.ino
\example MIDI_DualMerger.ino \example DualMerger.ino
\example MIDI_Input.ino \example Input.ino
\example MIDI_SimpleSynth.ino \example SimpleSynth.ino
*/ */
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -62,7 +62,7 @@ becomes:
``` ```
The order of the bits in the "header" byte is reversed. The order of the bits in the "header" byte is reversed.
To follow this beheaviour, set the inFlipHeaderBits argument to true. To follow this behaviour, set the inFlipHeaderBits argument to true.
Example: Example:
```c++ ```c++

View File

@ -5,7 +5,7 @@
// Here, when receiving any message on channel 4, the Arduino // Here, when receiving any message on channel 4, the Arduino
// will blink a led and play back a note for 1 second. // will blink a led and play back a note for 1 second.
#if defined(ARDUINO_SAM_DUE) || defined(SAMD_SERIES) #if defined(ARDUINO_SAM_DUE) || defined(SAMD_SERIES) || defined(_VARIANT_ARDUINO_ZERO_)
/* example not relevant for this hardware (SoftwareSerial not supported) */ /* example not relevant for this hardware (SoftwareSerial not supported) */
MIDI_CREATE_DEFAULT_INSTANCE(); MIDI_CREATE_DEFAULT_INSTANCE();
#else #else

View File

@ -0,0 +1,37 @@
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
void setup()
{
pinMode(2, INPUT);
MIDI // chaining MIDI commands - order is from top to bottom (turnThruOff,... begin)
.turnThruOff()
// using a lamdba function for this callbacks
.setHandleNoteOn([](byte channel, byte note, byte velocity)
{
// Do whatever you want when a note is pressed.
// Try to keep your callbacks short (no delays ect)
// otherwise it would slow down the loop() and have a bad impact
// on real-time performance.
})
.setHandleNoteOff([](byte channel, byte note, byte velocity)
{
// Do something when the note is released.
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
})
.begin(MIDI_CHANNEL_OMNI); // Initiate MIDI communications, listen to all channels
}
void loop()
{
// Call MIDI.read the fastest you can for real-time performance.
MIDI.read();
if (digitalRead(2))
MIDI // chained sendNoteOn commands
.sendNoteOn(42, 127, 1)
.sendNoteOn(40, 54, 1);
}

View File

@ -0,0 +1,32 @@
#include <MIDI.h>
// Override the default MIDI baudrate to
// a decoding program such as Hairless MIDI (set baudrate to 115200)
struct CustomBaudRateSettings : public MIDI_NAMESPACE::DefaultSerialSettings {
static const long BaudRate = 115200;
};
#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
// Leonardo, Due and other USB boards use Serial1 by default.
MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings> serialMIDI(Serial1);
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>> MIDI((MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>&)serialMIDI);
#else
MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings> serialMIDI(Serial);
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>> MIDI((MIDI_NAMESPACE::SerialMIDI<HardwareSerial, CustomBaudRateSettings>&)serialMIDI);
#endif
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
MIDI.begin(MIDI_CHANNEL_OMNI);
}
void loop() {
if (MIDI.read()) // If we have received a message
{
digitalWrite(LED_BUILTIN, 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_BUILTIN, LOW);
}
}

View File

@ -1,30 +0,0 @@
#include <MIDI.h>
USING_NAMESPACE_MIDI
struct MySerialSettings : public MIDI_NAMESPACE::DefaultSerialSettings
{
static const long BaudRate = 115200;
};
unsigned long t1 = millis();
MIDI_NAMESPACE::SerialMIDI<HardwareSerial, MySerialSettings> serialMIDI(Serial1);
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<HardwareSerial, MySerialSettings>> MIDI((MIDI_NAMESPACE::SerialMIDI<HardwareSerial, MySerialSettings>&)serialMIDI);
void setup()
{
MIDI.begin(1);
}
void loop()
{
MIDI.read();
// send a note every second
if ((millis() - t1) > 1000)
{
t1 = millis();
MIDI.sendNoteOn(random(1, 127), 55, 1);
}
}

View File

@ -1,51 +0,0 @@
#include <MIDI.h>
USING_NAMESPACE_MIDI
struct MyMIDISettings : public MIDI_NAMESPACE::DefaultSettings
{
// When setting UseReceiverActiveSensing to true, MIDI.read() *must* be called
// as often as possible (1000 / SenderActiveSensingPeriodicity per second).
//
// setting UseReceiverActiveSensing to true, adds 174 bytes of code.
//
// (Taken from a Roland MIDI Implementation Owner's manual)
// Once an Active Sensing message is received, the unit will begin monitoring
// the interval between all subsequent messages. If there is an interval of 420 ms
// or longer between messages while monitoring is active, the same processing
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
// received will be carried out. The unit will then stopmonitoring the message interval.
static const bool UseReceiverActiveSensing = true;
static const uint16_t ReceiverActiveSensingTimeout = 420;
};
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, MIDI, MyMIDISettings);
void activeSensingTimeoutExceptionHandler(bool active)
{
if (!active)
{
MIDI.sendControlChange(AllSoundOff, 0, 1);
MIDI.sendControlChange(AllNotesOff, 0, 1);
MIDI.sendControlChange(ResetAllControllers, 0, 1);
digitalWrite(LED_BUILTIN, HIGH);
}
else
digitalWrite(LED_BUILTIN, LOW);
}
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
MIDI.setHandleActiveSensingTimeout(activeSensingTimeoutExceptionHandler);
MIDI.begin(1);
}
void loop()
{
MIDI.read();
}

View File

@ -1,58 +0,0 @@
#include <MIDI.h>
USING_NAMESPACE_MIDI
struct MyMIDISettings : public MIDI_NAMESPACE::DefaultSettings
{
// When setting UseSenderActiveSensing to true, MIDI.read() *must* be called
// as often as possible (1000 / SenderActiveSensingPeriodicity per second).
//
// setting UseSenderActiveSensing to true, adds 34 bytes of code.
//
// When using Active Sensing, call MIDI.read(); in the Arduino loop()
//
// from 'a' MIDI implementation manual: "Sent periodically"
// In the example here, a NoteOn is send every 1000ms (1s), ActiveSensing is
// send every 250ms after the last command.
// Logging the command will look like this:
//
// ...
// A.Sense FE
// A.Sense FE
// A.Sense FE
// NoteOn 90 04 37 [E-2]
// A.Sense FE
// A.Sense FE
// A.Sense FE
// NoteOn 90 04 37 [E-2]
// A.Sense FE
// A.Sense FE
// A.Sense FE
// NoteOn 90 04 37 [E-2]
// ...
static const bool UseSenderActiveSensing = true;
static const uint16_t SenderActiveSensingPeriodicity = 250;
};
unsigned long t1 = millis();
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, MIDI, MyMIDISettings);
void setup()
{
MIDI.begin(1);
}
void loop()
{
MIDI.read();
// send a note every second
if ((millis() - t1) > 1000)
{
t1 = millis();
MIDI.sendNoteOn(random(1, 127), 55, 1);
}
}

View File

@ -146,7 +146,18 @@ GeneralPurposeController1 LITERAL1
GeneralPurposeController2 LITERAL1 GeneralPurposeController2 LITERAL1
GeneralPurposeController3 LITERAL1 GeneralPurposeController3 LITERAL1
GeneralPurposeController4 LITERAL1 GeneralPurposeController4 LITERAL1
BankSelectLSB LITERAL1
ModulationWheelLSB LITERAL1
BreathControllerLSB LITERAL1
FootControllerLSB LITERAL1
PortamentoTimeLSB LITERAL1
DataEntryLSB LITERAL1 DataEntryLSB LITERAL1
ChannelVolumeLSB LITERAL1
BalanceLSB LITERAL1
PanLSB LITERAL1
ExpressionControllerLSB LITERAL1
EffectControl1LSB LITERAL1
EffectControl2LSB LITERAL1
Sustain LITERAL1 Sustain LITERAL1
Portamento LITERAL1 Portamento LITERAL1
Sostenuto LITERAL1 Sostenuto LITERAL1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

BIN
res/library-manager.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -62,94 +62,94 @@ public:
inline ~MidiInterface(); inline ~MidiInterface();
public: public:
void begin(Channel inChannel = 1); MidiInterface& begin(Channel inChannel = 1);
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// MIDI Output // MIDI Output
public: public:
inline void sendNoteOn(DataByte inNoteNumber, inline MidiInterface& sendNoteOn(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
Channel inChannel); Channel inChannel);
inline void sendNoteOff(DataByte inNoteNumber, inline MidiInterface& sendNoteOff(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
Channel inChannel); Channel inChannel);
inline void sendProgramChange(DataByte inProgramNumber, inline MidiInterface& sendProgramChange(DataByte inProgramNumber,
Channel inChannel); Channel inChannel);
inline void sendControlChange(DataByte inControlNumber, inline MidiInterface& sendControlChange(DataByte inControlNumber,
DataByte inControlValue, DataByte inControlValue,
Channel inChannel); Channel inChannel);
inline void sendPitchBend(int inPitchValue, Channel inChannel); inline MidiInterface& sendPitchBend(int inPitchValue, Channel inChannel);
inline void sendPitchBend(double inPitchValue, Channel inChannel); inline MidiInterface& sendPitchBend(double inPitchValue, Channel inChannel);
inline void sendPolyPressure(DataByte inNoteNumber, inline MidiInterface& sendPolyPressure(DataByte inNoteNumber,
DataByte inPressure, DataByte inPressure,
Channel inChannel) __attribute__ ((deprecated)); Channel inChannel) __attribute__ ((deprecated));
inline void sendAfterTouch(DataByte inPressure, inline MidiInterface& sendAfterTouch(DataByte inPressure,
Channel inChannel); Channel inChannel);
inline void sendAfterTouch(DataByte inNoteNumber, inline MidiInterface& sendAfterTouch(DataByte inNoteNumber,
DataByte inPressure, DataByte inPressure,
Channel inChannel); Channel inChannel);
inline void sendSysEx(unsigned inLength, inline MidiInterface& sendSysEx(unsigned inLength,
const byte* inArray, const byte* inArray,
bool inArrayContainsBoundaries = false); bool inArrayContainsBoundaries = false);
inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble, inline MidiInterface& sendTimeCodeQuarterFrame(DataByte inTypeNibble,
DataByte inValuesNibble); DataByte inValuesNibble);
inline void sendTimeCodeQuarterFrame(DataByte inData); inline MidiInterface& sendTimeCodeQuarterFrame(DataByte inData);
inline void sendSongPosition(unsigned inBeats); inline MidiInterface& sendSongPosition(unsigned inBeats);
inline void sendSongSelect(DataByte inSongNumber); inline MidiInterface& sendSongSelect(DataByte inSongNumber);
inline void sendTuneRequest(); inline MidiInterface& sendTuneRequest();
inline void sendCommon(MidiType inType, unsigned = 0); inline MidiInterface& sendCommon(MidiType inType, unsigned = 0);
inline void sendClock() { sendRealTime(Clock); }; inline MidiInterface& sendClock() { return sendRealTime(Clock); };
inline void sendStart() { sendRealTime(Start); }; inline MidiInterface& sendStart() { return sendRealTime(Start); };
inline void sendStop() { sendRealTime(Stop); }; inline MidiInterface& sendStop() { return sendRealTime(Stop); };
inline void sendTick() { sendRealTime(Tick); }; inline MidiInterface& sendTick() { return sendRealTime(Tick); };
inline void sendContinue() { sendRealTime(Continue); }; inline MidiInterface& sendContinue() { return sendRealTime(Continue); };
inline void sendActiveSensing() { sendRealTime(ActiveSensing); }; inline MidiInterface& sendActiveSensing() { return sendRealTime(ActiveSensing); };
inline void sendSystemReset() { sendRealTime(SystemReset); }; inline MidiInterface& sendSystemReset() { return sendRealTime(SystemReset); };
inline void sendRealTime(MidiType inType); inline MidiInterface& sendRealTime(MidiType inType);
inline void beginRpn(unsigned inNumber, inline MidiInterface& beginRpn(unsigned inNumber,
Channel inChannel); Channel inChannel);
inline void sendRpnValue(unsigned inValue, inline MidiInterface& sendRpnValue(unsigned inValue,
Channel inChannel); Channel inChannel);
inline void sendRpnValue(byte inMsb, inline MidiInterface& sendRpnValue(byte inMsb,
byte inLsb, byte inLsb,
Channel inChannel); Channel inChannel);
inline void sendRpnIncrement(byte inAmount, inline MidiInterface& sendRpnIncrement(byte inAmount,
Channel inChannel); Channel inChannel);
inline void sendRpnDecrement(byte inAmount, inline MidiInterface& sendRpnDecrement(byte inAmount,
Channel inChannel); Channel inChannel);
inline void endRpn(Channel inChannel); inline MidiInterface& endRpn(Channel inChannel);
inline void beginNrpn(unsigned inNumber, inline MidiInterface& beginNrpn(unsigned inNumber,
Channel inChannel); Channel inChannel);
inline void sendNrpnValue(unsigned inValue, inline MidiInterface& sendNrpnValue(unsigned inValue,
Channel inChannel); Channel inChannel);
inline void sendNrpnValue(byte inMsb, inline MidiInterface& sendNrpnValue(byte inMsb,
byte inLsb, byte inLsb,
Channel inChannel); Channel inChannel);
inline void sendNrpnIncrement(byte inAmount, inline MidiInterface& sendNrpnIncrement(byte inAmount,
Channel inChannel); Channel inChannel);
inline void sendNrpnDecrement(byte inAmount, inline MidiInterface& sendNrpnDecrement(byte inAmount,
Channel inChannel); Channel inChannel);
inline void endNrpn(Channel inChannel); inline MidiInterface& endNrpn(Channel inChannel);
inline void send(const MidiMessage&); inline MidiInterface& send(const MidiMessage&);
public: public:
void send(MidiType inType, MidiInterface& send(MidiType inType,
DataByte inData1, DataByte inData1,
DataByte inData2, DataByte inData2,
Channel inChannel); Channel inChannel);
@ -172,7 +172,7 @@ public:
public: public:
inline Channel getInputChannel() const; inline Channel getInputChannel() const;
inline void setInputChannel(Channel inChannel); inline MidiInterface& setInputChannel(Channel inChannel);
public: public:
static inline MidiType getTypeFromStatusByte(byte inStatus); static inline MidiType getTypeFromStatusByte(byte inStatus);
@ -183,37 +183,35 @@ public:
// Input Callbacks // Input Callbacks
public: public:
inline void setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; }; inline MidiInterface& setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; return *this; };
inline void setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; } inline MidiInterface& setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; return *this; };
inline void setHandleActiveSensingTimeout(ActiveSensingTimeoutCallback fptr) { mActiveSensingTimeoutCallback = fptr; } inline MidiInterface& setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; return *this; };
inline void setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; } inline MidiInterface& setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; return *this; };
inline void setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; } inline MidiInterface& setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; return *this; };
inline void setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; } inline MidiInterface& setHandleControlChange(ControlChangeCallback fptr) { mControlChangeCallback = fptr; return *this; };
inline void setHandleControlChange(ControlChangeCallback fptr) { mControlChangeCallback = fptr; } inline MidiInterface& setHandleProgramChange(ProgramChangeCallback fptr) { mProgramChangeCallback = fptr; return *this; };
inline void setHandleProgramChange(ProgramChangeCallback fptr) { mProgramChangeCallback = fptr; } inline MidiInterface& setHandleAfterTouchChannel(AfterTouchChannelCallback fptr) { mAfterTouchChannelCallback = fptr; return *this; };
inline void setHandleAfterTouchChannel(AfterTouchChannelCallback fptr) { mAfterTouchChannelCallback = fptr; } inline MidiInterface& setHandlePitchBend(PitchBendCallback fptr) { mPitchBendCallback = fptr; return *this; };
inline void setHandlePitchBend(PitchBendCallback fptr) { mPitchBendCallback = fptr; } inline MidiInterface& setHandleSystemExclusive(SystemExclusiveCallback fptr) { mSystemExclusiveCallback = fptr; return *this; };
inline void setHandleSystemExclusive(SystemExclusiveCallback fptr) { mSystemExclusiveCallback = fptr; } inline MidiInterface& setHandleTimeCodeQuarterFrame(TimeCodeQuarterFrameCallback fptr) { mTimeCodeQuarterFrameCallback = fptr; return *this; };
inline void setHandleTimeCodeQuarterFrame(TimeCodeQuarterFrameCallback fptr) { mTimeCodeQuarterFrameCallback = fptr; } inline MidiInterface& setHandleSongPosition(SongPositionCallback fptr) { mSongPositionCallback = fptr; return *this; };
inline void setHandleSongPosition(SongPositionCallback fptr) { mSongPositionCallback = fptr; } inline MidiInterface& setHandleSongSelect(SongSelectCallback fptr) { mSongSelectCallback = fptr; return *this; };
inline void setHandleSongSelect(SongSelectCallback fptr) { mSongSelectCallback = fptr; } inline MidiInterface& setHandleTuneRequest(TuneRequestCallback fptr) { mTuneRequestCallback = fptr; return *this; };
inline void setHandleTuneRequest(TuneRequestCallback fptr) { mTuneRequestCallback = fptr; } inline MidiInterface& setHandleClock(ClockCallback fptr) { mClockCallback = fptr; return *this; };
inline void setHandleClock(ClockCallback fptr) { mClockCallback = fptr; } inline MidiInterface& setHandleStart(StartCallback fptr) { mStartCallback = fptr; return *this; };
inline void setHandleStart(StartCallback fptr) { mStartCallback = fptr; } inline MidiInterface& setHandleTick(TickCallback fptr) { mTickCallback = fptr; return *this; };
inline void setHandleTick(TickCallback fptr) { mTickCallback = fptr; } inline MidiInterface& setHandleContinue(ContinueCallback fptr) { mContinueCallback = fptr; return *this; };
inline void setHandleContinue(ContinueCallback fptr) { mContinueCallback = fptr; } inline MidiInterface& setHandleStop(StopCallback fptr) { mStopCallback = fptr; return *this; };
inline void setHandleStop(StopCallback fptr) { mStopCallback = fptr; } inline MidiInterface& setHandleActiveSensing(ActiveSensingCallback fptr) { mActiveSensingCallback = fptr; return *this; };
inline void setHandleActiveSensing(ActiveSensingCallback fptr) { mActiveSensingCallback = fptr; } inline MidiInterface& setHandleSystemReset(SystemResetCallback fptr) { mSystemResetCallback = fptr; return *this; };
inline void setHandleSystemReset(SystemResetCallback fptr) { mSystemResetCallback = fptr; }
inline void disconnectCallbackFromType(MidiType inType); inline MidiInterface& disconnectCallbackFromType(MidiType inType);
private: private:
void launchCallback(); void launchCallback();
void (*mMessageCallback)(const MidiMessage& message) = nullptr; void (*mMessageCallback)(const MidiMessage& message) = nullptr;
ErrorCallback mErrorCallback = nullptr; ErrorCallback mErrorCallback = nullptr;
ActiveSensingTimeoutCallback mActiveSensingTimeoutCallback = nullptr;
NoteOffCallback mNoteOffCallback = nullptr; NoteOffCallback mNoteOffCallback = nullptr;
NoteOnCallback mNoteOnCallback = nullptr; NoteOnCallback mNoteOnCallback = nullptr;
AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr; AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr;
@ -241,9 +239,9 @@ public:
inline Thru::Mode getFilterMode() const; inline Thru::Mode getFilterMode() const;
inline bool getThruState() const; inline bool getThruState() const;
inline void turnThruOn(Thru::Mode inThruFilterMode = Thru::Full); inline MidiInterface& turnThruOn(Thru::Mode inThruFilterMode = Thru::Full);
inline void turnThruOff(); inline MidiInterface& turnThruOff();
inline void setThruFilterMode(Thru::Mode inThruFilterMode); inline MidiInterface& setThruFilterMode(Thru::Mode inThruFilterMode);
private: private:
void thruFilter(byte inChannel); void thruFilter(byte inChannel);
@ -256,7 +254,7 @@ private:
inline void handleNullVelocityNoteOnAsNoteOff(); inline void handleNullVelocityNoteOnAsNoteOff();
inline bool inputFilter(Channel inChannel); inline bool inputFilter(Channel inChannel);
inline void resetInput(); inline void resetInput();
inline void UpdateLastSentTime(); inline void updateLastSentTime();
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Transport // Transport
@ -284,7 +282,8 @@ private:
MidiMessage mMessage; MidiMessage mMessage;
unsigned long mLastMessageSentTime; unsigned long mLastMessageSentTime;
unsigned long mLastMessageReceivedTime; unsigned long mLastMessageReceivedTime;
bool mReceiverActiveSensingActive; unsigned long mSenderActiveSensingPeriodicity;
bool mReceiverActiveSensingActivated;
int8_t mLastError; int8_t mLastError;
private: private:

View File

@ -44,10 +44,11 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
, mThruFilterMode(Thru::Full) , mThruFilterMode(Thru::Full)
, mLastMessageSentTime(0) , mLastMessageSentTime(0)
, mLastMessageReceivedTime(0) , mLastMessageReceivedTime(0)
, mReceiverActiveSensingActive(false) , mSenderActiveSensingPeriodicity(0)
, mReceiverActiveSensingActivated(false)
, mLastError(0) , mLastError(0)
{ {
static_assert(!(Settings::UseSenderActiveSensing && Settings::UseReceiverActiveSensing), "UseSenderActiveSensing and UseReceiverActiveSensing can't be both set to true."); mSenderActiveSensingPeriodicity = Settings::SenderActiveSensingPeriodicity;
} }
/*! \brief Destructor for MidiInterface. /*! \brief Destructor for MidiInterface.
@ -68,7 +69,7 @@ inline MidiInterface<Transport, Settings, Platform>::~MidiInterface()
- Full thru mirroring - Full thru mirroring
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
{ {
// Initialise the Transport layer // Initialise the Transport layer
mTransport.begin(); mTransport.begin();
@ -83,8 +84,7 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
mCurrentRpnNumber = 0xffff; mCurrentRpnNumber = 0xffff;
mCurrentNrpnNumber = 0xffff; mCurrentNrpnNumber = 0xffff;
mLastMessageSentTime = mLastMessageSentTime = Platform::now();
mLastMessageReceivedTime = Platform::now();
mMessage.valid = false; mMessage.valid = false;
mMessage.type = InvalidType; mMessage.type = InvalidType;
@ -95,6 +95,8 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
mThruFilterMode = Thru::Full; mThruFilterMode = Thru::Full;
mThruActivated = mTransport.thruActivated; mThruActivated = mTransport.thruActivated;
return *this;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -115,33 +117,38 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
them thru. them thru.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::send(const MidiMessage& inMessage) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::send(const MidiMessage& inMessage)
{ {
if (!inMessage.valid) if (!inMessage.valid)
return; return *this;
if (mTransport.beginTransmission(inMessage.type)) if (mTransport.beginTransmission(inMessage.type))
{
if (inMessage.isSystemRealTime())
{
mTransport.write(inMessage.type);
} else if (inMessage.isChannelMessage())
{ {
const StatusByte status = getStatus(inMessage.type, inMessage.channel); const StatusByte status = getStatus(inMessage.type, inMessage.channel);
mTransport.write(status); mTransport.write(status);
if (inMessage.type != MidiType::SystemExclusive)
{
if (inMessage.length > 1) mTransport.write(inMessage.data1); if (inMessage.length > 1) mTransport.write(inMessage.data1);
if (inMessage.length > 2) mTransport.write(inMessage.data2); if (inMessage.length > 2) mTransport.write(inMessage.data2);
} else } else if (inMessage.type == MidiType::SystemExclusive)
{ {
// sysexArray does not contain the start and end tags const unsigned size = inMessage.getSysExSize();
mTransport.write(MidiType::SystemExclusiveStart); for (size_t i = 0; i < size; i++)
for (size_t i = 0; i < inMessage.getSysExSize(); i++)
mTransport.write(inMessage.sysexArray[i]); mTransport.write(inMessage.sysexArray[i]);
} else // at this point, it it assumed to be a system common message
mTransport.write(MidiType::SystemExclusiveEnd); {
mTransport.write(inMessage.type);
if (inMessage.length > 1) mTransport.write(inMessage.data1);
if (inMessage.length > 2) mTransport.write(inMessage.data2);
} }
} }
mTransport.endTransmission(); mTransport.endTransmission();
UpdateLastSentTime(); updateLastSentTime();
return *this;
} }
@ -157,7 +164,7 @@ void MidiInterface<Transport, Settings, Platform>::send(const MidiMessage& inMes
from your code, at your own risks. from your code, at your own risks.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::send(MidiType inType, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::send(MidiType inType,
DataByte inData1, DataByte inData1,
DataByte inData2, DataByte inData2,
Channel inChannel) Channel inChannel)
@ -169,7 +176,7 @@ void MidiInterface<Transport, Settings, Platform>::send(MidiType inType,
inChannel == MIDI_CHANNEL_OMNI || inChannel == MIDI_CHANNEL_OMNI ||
inType < 0x80) inType < 0x80)
{ {
return; // Don't send anything return *this; // Don't send anything
} }
// Protection: remove MSBs on data // Protection: remove MSBs on data
inData1 &= 0x7f; inData1 &= 0x7f;
@ -202,13 +209,15 @@ void MidiInterface<Transport, Settings, Platform>::send(MidiType inType,
} }
mTransport.endTransmission(); mTransport.endTransmission();
UpdateLastSentTime(); updateLastSentTime();
} }
} }
else if (inType >= Clock && inType <= SystemReset) else if (inType >= Clock && inType <= SystemReset)
{ {
sendRealTime(inType); // System Real-time and 1 byte. sendRealTime(inType); // System Real-time and 1 byte.
} }
return *this;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -223,11 +232,11 @@ void MidiInterface<Transport, Settings, Platform>::send(MidiType inType,
http://www.phys.unsw.edu.au/jw/notes.html http://www.phys.unsw.edu.au/jw/notes.html
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendNoteOn(DataByte inNoteNumber, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendNoteOn(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
Channel inChannel) Channel inChannel)
{ {
send(NoteOn, inNoteNumber, inVelocity, inChannel); return send(NoteOn, inNoteNumber, inVelocity, inChannel);
} }
/*! \brief Send a Note Off message /*! \brief Send a Note Off message
@ -242,11 +251,11 @@ void MidiInterface<Transport, Settings, Platform>::sendNoteOn(DataByte inNoteNum
http://www.phys.unsw.edu.au/jw/notes.html http://www.phys.unsw.edu.au/jw/notes.html
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendNoteOff(DataByte inNoteNumber, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendNoteOff(DataByte inNoteNumber,
DataByte inVelocity, DataByte inVelocity,
Channel inChannel) Channel inChannel)
{ {
send(NoteOff, inNoteNumber, inVelocity, inChannel); return send(NoteOff, inNoteNumber, inVelocity, inChannel);
} }
/*! \brief Send a Program Change message /*! \brief Send a Program Change message
@ -254,10 +263,10 @@ void MidiInterface<Transport, Settings, Platform>::sendNoteOff(DataByte inNoteNu
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendProgramChange(DataByte inProgramNumber, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendProgramChange(DataByte inProgramNumber,
Channel inChannel) Channel inChannel)
{ {
send(ProgramChange, inProgramNumber, 0, inChannel); return send(ProgramChange, inProgramNumber, 0, inChannel);
} }
/*! \brief Send a Control Change message /*! \brief Send a Control Change message
@ -267,11 +276,11 @@ void MidiInterface<Transport, Settings, Platform>::sendProgramChange(DataByte in
@see MidiControlChangeNumber @see MidiControlChangeNumber
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendControlChange(DataByte inControlNumber, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendControlChange(DataByte inControlNumber,
DataByte inControlValue, DataByte inControlValue,
Channel inChannel) Channel inChannel)
{ {
send(ControlChange, inControlNumber, inControlValue, inChannel); return send(ControlChange, inControlNumber, inControlValue, inChannel);
} }
/*! \brief Send a Polyphonic AfterTouch message (applies to a specified note) /*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
@ -282,11 +291,11 @@ void MidiInterface<Transport, Settings, Platform>::sendControlChange(DataByte in
library, @see sendAfterTouch to send polyphonic and monophonic AfterTouch messages. library, @see sendAfterTouch to send polyphonic and monophonic AfterTouch messages.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendPolyPressure(DataByte inNoteNumber, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendPolyPressure(DataByte inNoteNumber,
DataByte inPressure, DataByte inPressure,
Channel inChannel) Channel inChannel)
{ {
send(AfterTouchPoly, inNoteNumber, inPressure, inChannel); return send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
} }
/*! \brief Send a MonoPhonic AfterTouch message (applies to all notes) /*! \brief Send a MonoPhonic AfterTouch message (applies to all notes)
@ -294,10 +303,10 @@ void MidiInterface<Transport, Settings, Platform>::sendPolyPressure(DataByte inN
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendAfterTouch(DataByte inPressure, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendAfterTouch(DataByte inPressure,
Channel inChannel) Channel inChannel)
{ {
send(AfterTouchChannel, inPressure, 0, inChannel); return send(AfterTouchChannel, inPressure, 0, inChannel);
} }
/*! \brief Send a Polyphonic AfterTouch message (applies to a specified note) /*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
@ -307,11 +316,11 @@ void MidiInterface<Transport, Settings, Platform>::sendAfterTouch(DataByte inPre
@see Replaces sendPolyPressure (which is now deprecated). @see Replaces sendPolyPressure (which is now deprecated).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendAfterTouch(DataByte inNoteNumber, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendAfterTouch(DataByte inNoteNumber,
DataByte inPressure, DataByte inPressure,
Channel inChannel) Channel inChannel)
{ {
send(AfterTouchPoly, inNoteNumber, inPressure, inChannel); return send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
} }
/*! \brief Send a Pitch Bend message using a signed integer value. /*! \brief Send a Pitch Bend message using a signed integer value.
@ -321,11 +330,11 @@ void MidiInterface<Transport, Settings, Platform>::sendAfterTouch(DataByte inNot
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendPitchBend(int inPitchValue, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendPitchBend(int inPitchValue,
Channel inChannel) Channel inChannel)
{ {
const unsigned bend = unsigned(inPitchValue - int(MIDI_PITCHBEND_MIN)); const unsigned bend = unsigned(inPitchValue - int(MIDI_PITCHBEND_MIN));
send(PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel); return send(PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel);
} }
@ -336,12 +345,12 @@ void MidiInterface<Transport, Settings, Platform>::sendPitchBend(int inPitchValu
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendPitchBend(double inPitchValue, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendPitchBend(double inPitchValue,
Channel inChannel) Channel inChannel)
{ {
const int scale = inPitchValue > 0.0 ? MIDI_PITCHBEND_MAX : MIDI_PITCHBEND_MIN; const int scale = inPitchValue > 0.0 ? MIDI_PITCHBEND_MAX : - MIDI_PITCHBEND_MIN;
const int value = int(inPitchValue * double(scale)); const int value = int(inPitchValue * double(scale));
sendPitchBend(value, inChannel); return sendPitchBend(value, inChannel);
} }
/*! \brief Generate and send a System Exclusive frame. /*! \brief Generate and send a System Exclusive frame.
@ -354,7 +363,7 @@ void MidiInterface<Transport, Settings, Platform>::sendPitchBend(double inPitchV
with previous versions of the library. with previous versions of the library.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendSysEx(unsigned inLength, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendSysEx(unsigned inLength,
const byte* inArray, const byte* inArray,
bool inArrayContainsBoundaries) bool inArrayContainsBoundaries)
{ {
@ -372,11 +381,13 @@ void MidiInterface<Transport, Settings, Platform>::sendSysEx(unsigned inLength,
mTransport.write(MidiType::SystemExclusiveEnd); mTransport.write(MidiType::SystemExclusiveEnd);
mTransport.endTransmission(); mTransport.endTransmission();
UpdateLastSentTime(); updateLastSentTime();
} }
if (Settings::UseRunningStatus) if (Settings::UseRunningStatus)
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
return *this;
} }
/*! \brief Send a Tune Request message. /*! \brief Send a Tune Request message.
@ -385,9 +396,9 @@ void MidiInterface<Transport, Settings, Platform>::sendSysEx(unsigned inLength,
it should tune its oscillators (if equipped with any). it should tune its oscillators (if equipped with any).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendTuneRequest() MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendTuneRequest()
{ {
sendCommon(TuneRequest); return sendCommon(TuneRequest);
} }
/*! \brief Send a MIDI Time Code Quarter Frame. /*! \brief Send a MIDI Time Code Quarter Frame.
@ -397,11 +408,11 @@ void MidiInterface<Transport, Settings, Platform>::sendTuneRequest()
See MIDI Specification for more information. See MIDI Specification for more information.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendTimeCodeQuarterFrame(DataByte inTypeNibble, MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendTimeCodeQuarterFrame(DataByte inTypeNibble,
DataByte inValuesNibble) DataByte inValuesNibble)
{ {
const byte data = byte((((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f))); const byte data = byte((((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f)));
sendTimeCodeQuarterFrame(data); return sendTimeCodeQuarterFrame(data);
} }
/*! \brief Send a MIDI Time Code Quarter Frame. /*! \brief Send a MIDI Time Code Quarter Frame.
@ -411,25 +422,25 @@ void MidiInterface<Transport, Settings, Platform>::sendTimeCodeQuarterFrame(Data
you can send the byte here. you can send the byte here.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendTimeCodeQuarterFrame(DataByte inData) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendTimeCodeQuarterFrame(DataByte inData)
{ {
sendCommon(TimeCodeQuarterFrame, inData); return sendCommon(TimeCodeQuarterFrame, inData);
} }
/*! \brief Send a Song Position Pointer message. /*! \brief Send a Song Position Pointer message.
\param inBeats The number of beats since the start of the song. \param inBeats The number of beats since the start of the song.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendSongPosition(unsigned inBeats) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendSongPosition(unsigned inBeats)
{ {
sendCommon(SongPosition, inBeats); return sendCommon(SongPosition, inBeats);
} }
/*! \brief Send a Song Select message */ /*! \brief Send a Song Select message */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendSongSelect(DataByte inSongNumber) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendSongSelect(DataByte inSongNumber)
{ {
sendCommon(SongSelect, inSongNumber); return sendCommon(SongSelect, inSongNumber);
} }
/*! \brief Send a Common message. Common messages reset the running status. /*! \brief Send a Common message. Common messages reset the running status.
@ -440,7 +451,7 @@ void MidiInterface<Transport, Settings, Platform>::sendSongSelect(DataByte inSon
\param inData1 The byte that goes with the common message. \param inData1 The byte that goes with the common message.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendCommon(MidiType inType, unsigned inData1) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendCommon(MidiType inType, unsigned inData1)
{ {
switch (inType) switch (inType)
{ {
@ -451,7 +462,7 @@ void MidiInterface<Transport, Settings, Platform>::sendCommon(MidiType inType, u
break; break;
default: default:
// Invalid Common marker // Invalid Common marker
return; return *this;
} }
if (mTransport.beginTransmission(inType)) if (mTransport.beginTransmission(inType))
@ -471,15 +482,19 @@ void MidiInterface<Transport, Settings, Platform>::sendCommon(MidiType inType, u
break; break;
case TuneRequest: case TuneRequest:
break; break;
// LCOV_EXCL_START - Coverage blind spot
default: default:
break; // LCOV_EXCL_LINE - Coverage blind spot break;
// LCOV_EXCL_STOP
} }
mTransport.endTransmission(); mTransport.endTransmission();
UpdateLastSentTime(); updateLastSentTime();
} }
if (Settings::UseRunningStatus) if (Settings::UseRunningStatus)
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
return *this;
} }
/*! \brief Send a Real Time (one byte) message. /*! \brief Send a Real Time (one byte) message.
@ -489,7 +504,7 @@ void MidiInterface<Transport, Settings, Platform>::sendCommon(MidiType inType, u
@see MidiType @see MidiType
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::sendRealTime(MidiType inType) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendRealTime(MidiType inType)
{ {
// Do not invalidate Running Status for real-time messages // Do not invalidate Running Status for real-time messages
// as they can be interleaved within any message. // as they can be interleaved within any message.
@ -506,13 +521,15 @@ void MidiInterface<Transport, Settings, Platform>::sendRealTime(MidiType inType)
{ {
mTransport.write((byte)inType); mTransport.write((byte)inType);
mTransport.endTransmission(); mTransport.endTransmission();
UpdateLastSentTime(); updateLastSentTime();
} }
break; break;
default: default:
// Invalid Real Time marker // Invalid Real Time marker
break; break;
} }
return *this;
} }
/*! \brief Start a Registered Parameter Number frame. /*! \brief Start a Registered Parameter Number frame.
@ -520,7 +537,7 @@ void MidiInterface<Transport, Settings, Platform>::sendRealTime(MidiType inType)
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::beginRpn(unsigned inNumber, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::beginRpn(unsigned inNumber,
Channel inChannel) Channel inChannel)
{ {
if (mCurrentRpnNumber != inNumber) if (mCurrentRpnNumber != inNumber)
@ -531,6 +548,8 @@ inline void MidiInterface<Transport, Settings, Platform>::beginRpn(unsigned inNu
sendControlChange(RPNMSB, numMsb, inChannel); sendControlChange(RPNMSB, numMsb, inChannel);
mCurrentRpnNumber = inNumber; mCurrentRpnNumber = inNumber;
} }
return *this;
} }
/*! \brief Send a 14-bit value for the currently selected RPN number. /*! \brief Send a 14-bit value for the currently selected RPN number.
@ -538,13 +557,15 @@ inline void MidiInterface<Transport, Settings, Platform>::beginRpn(unsigned inNu
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendRpnValue(unsigned inValue, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendRpnValue(unsigned inValue,
Channel inChannel) Channel inChannel)
{; {;
const byte valMsb = 0x7f & (inValue >> 7); const byte valMsb = 0x7f & (inValue >> 7);
const byte valLsb = 0x7f & inValue; const byte valLsb = 0x7f & inValue;
sendControlChange(DataEntryMSB, valMsb, inChannel); sendControlChange(DataEntryMSB, valMsb, inChannel);
sendControlChange(DataEntryLSB, valLsb, inChannel); sendControlChange(DataEntryLSB, valLsb, inChannel);
return *this;
} }
/*! \brief Send separate MSB/LSB values for the currently selected RPN number. /*! \brief Send separate MSB/LSB values for the currently selected RPN number.
@ -553,32 +574,38 @@ inline void MidiInterface<Transport, Settings, Platform>::sendRpnValue(unsigned
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendRpnValue(byte inMsb, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendRpnValue(byte inMsb,
byte inLsb, byte inLsb,
Channel inChannel) Channel inChannel)
{ {
sendControlChange(DataEntryMSB, inMsb, inChannel); sendControlChange(DataEntryMSB, inMsb, inChannel);
sendControlChange(DataEntryLSB, inLsb, inChannel); sendControlChange(DataEntryLSB, inLsb, inChannel);
return *this;
} }
/* \brief Increment the value of the currently selected RPN number by the specified amount. /* \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. \param inAmount The amount to add to the currently selected RPN value.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendRpnIncrement(byte inAmount, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendRpnIncrement(byte inAmount,
Channel inChannel) Channel inChannel)
{ {
sendControlChange(DataIncrement, inAmount, inChannel); sendControlChange(DataIncrement, inAmount, inChannel);
return *this;
} }
/* \brief Decrement the value of the currently selected RPN number by the specified amount. /* \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. \param inAmount The amount to subtract to the currently selected RPN value.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendRpnDecrement(byte inAmount, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendRpnDecrement(byte inAmount,
Channel inChannel) Channel inChannel)
{ {
sendControlChange(DataDecrement, inAmount, inChannel); sendControlChange(DataDecrement, inAmount, inChannel);
return *this;
} }
/*! \brief Terminate an RPN frame. /*! \brief Terminate an RPN frame.
@ -586,11 +613,13 @@ 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). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::endRpn(Channel inChannel) inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::endRpn(Channel inChannel)
{ {
sendControlChange(RPNLSB, 0x7f, inChannel); sendControlChange(RPNLSB, 0x7f, inChannel);
sendControlChange(RPNMSB, 0x7f, inChannel); sendControlChange(RPNMSB, 0x7f, inChannel);
mCurrentRpnNumber = 0xffff; mCurrentRpnNumber = 0xffff;
return *this;
} }
@ -600,7 +629,7 @@ inline void MidiInterface<Transport, Settings, Platform>::endRpn(Channel inChann
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::beginNrpn(unsigned inNumber, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::beginNrpn(unsigned inNumber,
Channel inChannel) Channel inChannel)
{ {
if (mCurrentNrpnNumber != inNumber) if (mCurrentNrpnNumber != inNumber)
@ -611,6 +640,8 @@ inline void MidiInterface<Transport, Settings, Platform>::beginNrpn(unsigned inN
sendControlChange(NRPNMSB, numMsb, inChannel); sendControlChange(NRPNMSB, numMsb, inChannel);
mCurrentNrpnNumber = inNumber; mCurrentNrpnNumber = inNumber;
} }
return *this;
} }
/*! \brief Send a 14-bit value for the currently selected NRPN number. /*! \brief Send a 14-bit value for the currently selected NRPN number.
@ -618,13 +649,15 @@ inline void MidiInterface<Transport, Settings, Platform>::beginNrpn(unsigned inN
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendNrpnValue(unsigned inValue, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendNrpnValue(unsigned inValue,
Channel inChannel) Channel inChannel)
{; {
const byte valMsb = 0x7f & (inValue >> 7); const byte valMsb = 0x7f & (inValue >> 7);
const byte valLsb = 0x7f & inValue; const byte valLsb = 0x7f & inValue;
sendControlChange(DataEntryMSB, valMsb, inChannel); sendControlChange(DataEntryMSB, valMsb, inChannel);
sendControlChange(DataEntryLSB, valLsb, inChannel); sendControlChange(DataEntryLSB, valLsb, inChannel);
return *this;
} }
/*! \brief Send separate MSB/LSB values for the currently selected NRPN number. /*! \brief Send separate MSB/LSB values for the currently selected NRPN number.
@ -633,32 +666,38 @@ inline void MidiInterface<Transport, Settings, Platform>::sendNrpnValue(unsigned
\param inChannel The channel on which the message will be sent (1 to 16). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendNrpnValue(byte inMsb, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendNrpnValue(byte inMsb,
byte inLsb, byte inLsb,
Channel inChannel) Channel inChannel)
{ {
sendControlChange(DataEntryMSB, inMsb, inChannel); sendControlChange(DataEntryMSB, inMsb, inChannel);
sendControlChange(DataEntryLSB, inLsb, inChannel); sendControlChange(DataEntryLSB, inLsb, inChannel);
return *this;
} }
/* \brief Increment the value of the currently selected NRPN number by the specified amount. /* \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. \param inAmount The amount to add to the currently selected NRPN value.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendNrpnIncrement(byte inAmount, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendNrpnIncrement(byte inAmount,
Channel inChannel) Channel inChannel)
{ {
sendControlChange(DataIncrement, inAmount, inChannel); sendControlChange(DataIncrement, inAmount, inChannel);
return *this;
} }
/* \brief Decrement the value of the currently selected NRPN number by the specified amount. /* \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. \param inAmount The amount to subtract to the currently selected NRPN value.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::sendNrpnDecrement(byte inAmount, inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::sendNrpnDecrement(byte inAmount,
Channel inChannel) Channel inChannel)
{ {
sendControlChange(DataDecrement, inAmount, inChannel); sendControlChange(DataDecrement, inAmount, inChannel);
return *this;
} }
/*! \brief Terminate an NRPN frame. /*! \brief Terminate an NRPN frame.
@ -666,11 +705,20 @@ 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). \param inChannel The channel on which the message will be sent (1 to 16).
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::endNrpn(Channel inChannel) inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::endNrpn(Channel inChannel)
{ {
sendControlChange(NRPNLSB, 0x7f, inChannel); sendControlChange(NRPNLSB, 0x7f, inChannel);
sendControlChange(NRPNMSB, 0x7f, inChannel); sendControlChange(NRPNMSB, 0x7f, inChannel);
mCurrentNrpnNumber = 0xffff; mCurrentNrpnNumber = 0xffff;
return *this;
}
template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::updateLastSentTime()
{
if (Settings::UseSenderActiveSensing && mSenderActiveSensingPeriodicity)
mLastMessageSentTime = Platform::now();
} }
/*! @} */ // End of doc group MIDI Output /*! @} */ // End of doc group MIDI Output
@ -712,32 +760,27 @@ template<class Transport, class Settings, class Platform>
inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel) inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel)
{ {
#ifndef RegionActiveSending #ifndef RegionActiveSending
// Active Sensing. This message is intended to be sent // Active Sensing. This message is intended to be sent
// repeatedly to tell the receiver that a connection is alive. Use // repeatedly to tell the receiver that a connection is alive. Use
// of this message is optional. // of this message is optional. When initially received, the
if (Settings::UseSenderActiveSensing) // receiver will expect to receive another Active Sensing
// message each 300ms (max), and if it does not then it will
// assume that the connection has been terminated. At
// termination, the receiver will turn off all voices and return to
// normal (non- active sensing) operation.
if (Settings::UseSenderActiveSensing && (mSenderActiveSensingPeriodicity > 0) && (Platform::now() - mLastMessageSentTime) > mSenderActiveSensingPeriodicity)
{ {
// Send ActiveSensing <Settings::ActiveSensingPeriodicity> ms after the last command
if ((Platform::now() - mLastMessageSentTime) > Settings::SenderActiveSensingPeriodicity)
sendActiveSensing(); sendActiveSensing();
mLastMessageSentTime = Platform::now();
} }
// Once an Active Sensing message is received, the unit will begin monitoring if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated && (mLastMessageReceivedTime + ActiveSensingTimeout < Platform::now()))
// the intervalbetween all subsequent messages. If there is an interval of 420 ms
// or longer betweenmessages while monitoring is active, the same processing
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
// received will be carried out. The unit will then stopmonitoring the message interval.
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActive)
{ {
if ((Platform::now() - mLastMessageReceivedTime > Settings::ReceiverActiveSensingTimeout)) mReceiverActiveSensingActivated = false;
{
mReceiverActiveSensingActive = false;
// its up to the handler to send the stop processing messages mLastError |= 1UL << ErrorActiveSensingTimeout; // set the ErrorActiveSensingTimeout bit
// (also, no clue what the channel is on which to send them) if (mErrorCallback)
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive); mErrorCallback(mLastError);
}
} }
#endif #endif
@ -749,18 +792,25 @@ inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel
#ifndef RegionActiveSending #ifndef RegionActiveSending
if (Settings::UseReceiverActiveSensing) if (Settings::UseReceiverActiveSensing && mMessage.type == ActiveSensing)
{ {
// When an ActiveSensing message is received, the time keeping is activated.
// When a timeout occurs, an error message is send and time keeping ends.
mReceiverActiveSensingActivated = true;
// is ErrorActiveSensingTimeout bit in mLastError on
if (mLastError & (1 << (ErrorActiveSensingTimeout - 1)))
{
mLastError &= ~(1UL << ErrorActiveSensingTimeout); // clear the ErrorActiveSensingTimeout bit
if (mErrorCallback)
mErrorCallback(mLastError);
}
}
// Keep the time of the last received message, so we can check for the timeout
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated)
mLastMessageReceivedTime = Platform::now(); mLastMessageReceivedTime = Platform::now();
if (mMessage.type == ActiveSensing && !mReceiverActiveSensingActive)
{
mReceiverActiveSensingActive = true;
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive);
}
}
#endif #endif
handleNullVelocityNoteOnAsNoteOff(); handleNullVelocityNoteOnAsNoteOff();
@ -978,9 +1028,10 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
resetInput(); resetInput();
return false; return false;
} }
// LCOV_EXCL_START - Coverage blind spot
default: default:
break; // LCOV_EXCL_LINE - Coverage blind spot break;
// LCOV_EXCL_STOP
} }
} }
@ -1035,6 +1086,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mMessage.data1 = mPendingMessage[1]; mMessage.data1 = mPendingMessage[1];
// Save data2 only if applicable // Save data2 only if applicable
mMessage.data2 = mPendingMessageExpectedLength == 3 ? mPendingMessage[2] : 0; mMessage.data2 = mPendingMessageExpectedLength == 3 ? mPendingMessage[2] : 0;
mMessage.length = mPendingMessageExpectedLength;
// Reset local variables // Reset local variables
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
@ -1200,9 +1252,11 @@ inline Channel MidiInterface<Transport, Settings, Platform>::getInputChannel() c
if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input. if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::setInputChannel(Channel inChannel) inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::setInputChannel(Channel inChannel)
{ {
mInputChannel = inChannel; mInputChannel = inChannel;
return *this;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -1257,7 +1311,7 @@ bool MidiInterface<Transport, Settings, Platform>::isChannelMessage(MidiType inT
When a message of this type is received, no function will be called. When a message of this type is received, no function will be called.
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::disconnectCallbackFromType(MidiType inType) MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::disconnectCallbackFromType(MidiType inType)
{ {
switch (inType) switch (inType)
{ {
@ -1283,6 +1337,8 @@ void MidiInterface<Transport, Settings, Platform>::disconnectCallbackFromType(Mi
default: default:
break; break;
} }
return *this;
} }
/*! @} */ // End of doc group MIDI Callbacks /*! @} */ // End of doc group MIDI Callbacks
@ -1325,9 +1381,11 @@ void MidiInterface<Transport, Settings, Platform>::launchCallback()
case SystemReset: if (mSystemResetCallback != nullptr) mSystemResetCallback(); break; case SystemReset: if (mSystemResetCallback != nullptr) mSystemResetCallback(); break;
// LCOV_EXCL_START - Unreacheable code, but prevents unhandled case warning.
case InvalidType: case InvalidType:
default: default:
break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning. break;
// LCOV_EXCL_STOP
} }
} }
@ -1347,10 +1405,12 @@ void MidiInterface<Transport, Settings, Platform>::launchCallback()
@see Thru::Mode @see Thru::Mode
*/ */
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::setThruFilterMode(Thru::Mode inThruFilterMode) inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::setThruFilterMode(Thru::Mode inThruFilterMode)
{ {
mThruFilterMode = inThruFilterMode; mThruFilterMode = inThruFilterMode;
mThruActivated = mThruFilterMode != Thru::Off; mThruActivated = mThruFilterMode != Thru::Off;
return *this;
} }
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
@ -1366,25 +1426,23 @@ inline bool MidiInterface<Transport, Settings, Platform>::getThruState() const
} }
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::turnThruOn(Thru::Mode inThruFilterMode) inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::turnThruOn(Thru::Mode inThruFilterMode)
{ {
mThruActivated = true; mThruActivated = true;
mThruFilterMode = inThruFilterMode; mThruFilterMode = inThruFilterMode;
return *this;
} }
template<class Transport, class Settings, class Platform> template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::turnThruOff() inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::turnThruOff()
{ {
mThruActivated = false; mThruActivated = false;
mThruFilterMode = Thru::Off; mThruFilterMode = Thru::Off;
return *this;
} }
template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::UpdateLastSentTime()
{
if (Settings::UseSenderActiveSensing)
mLastMessageSentTime = Platform::now();
}
/*! @} */ // End of doc group MIDI Thru /*! @} */ // End of doc group MIDI Thru

View File

@ -46,6 +46,10 @@ BEGIN_MIDI_NAMESPACE
#define MIDI_PITCHBEND_MIN -8192 #define MIDI_PITCHBEND_MIN -8192
#define MIDI_PITCHBEND_MAX 8191 #define MIDI_PITCHBEND_MAX 8191
/*! Receiving Active Sensing
*/
static const uint16_t ActiveSensingTimeout = 300;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Type definitions // Type definitions
@ -57,13 +61,13 @@ typedef byte FilterMode;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Errors // Errors
static const uint8_t ErrorParse = 0; static const uint8_t ErrorParse = 0;
static const uint8_t ErrorActiveSensingTimeout = 1;
static const uint8_t WarningSplitSysEx = 2; static const uint8_t WarningSplitSysEx = 2;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Aliasing // Aliasing
using ErrorCallback = void (*)(int8_t); using ErrorCallback = void (*)(int8_t);
using ActiveSensingTimeoutCallback = void (*)(bool);
using NoteOffCallback = void (*)(Channel channel, byte note, byte velocity); using NoteOffCallback = void (*)(Channel channel, byte note, byte velocity);
using NoteOnCallback = void (*)(Channel channel, byte note, byte velocity); using NoteOnCallback = void (*)(Channel channel, byte note, byte velocity);
using AfterTouchPolyCallback = void (*)(Channel channel, byte note, byte velocity); using AfterTouchPolyCallback = void (*)(Channel channel, byte note, byte velocity);
@ -160,8 +164,22 @@ enum MidiControlChangeNumber: uint8_t
GeneralPurposeController2 = 17, GeneralPurposeController2 = 17,
GeneralPurposeController3 = 18, GeneralPurposeController3 = 18,
GeneralPurposeController4 = 19, GeneralPurposeController4 = 19,
// CC20 to CC31 undefined
BankSelectLSB = 32,
ModulationWheelLSB = 33,
BreathControllerLSB = 34,
// CC35 undefined
FootControllerLSB = 36,
PortamentoTimeLSB = 37,
DataEntryLSB = 38, DataEntryLSB = 38,
ChannelVolumeLSB = 39,
BalanceLSB = 40,
// CC41 undefined
PanLSB = 42,
ExpressionControllerLSB = 43,
EffectControl1LSB = 44,
EffectControl2LSB = 45,
// CC46 to CC63 undefined
// Switches ---------------------------------------------------------------- // Switches ----------------------------------------------------------------
Sustain = 64, Sustain = 64,
@ -199,6 +217,7 @@ enum MidiControlChangeNumber: uint8_t
NRPNMSB = 99, ///< Non-Registered Parameter Number (MSB) NRPNMSB = 99, ///< Non-Registered Parameter Number (MSB)
RPNLSB = 100, ///< Registered Parameter Number (LSB) RPNLSB = 100, ///< Registered Parameter Number (LSB)
RPNMSB = 101, ///< Registered Parameter Number (MSB) RPNMSB = 101, ///< Registered Parameter Number (MSB)
// CC102 to CC119 undefined
// Channel Mode messages --------------------------------------------------- // Channel Mode messages ---------------------------------------------------
AllSoundOff = 120, AllSoundOff = 120,

View File

@ -54,6 +54,20 @@ struct Message
memset(sysexArray, 0, sSysExMaxSize * sizeof(DataByte)); memset(sysexArray, 0, sSysExMaxSize * sizeof(DataByte));
} }
inline Message(const Message& inOther)
: channel(inOther.channel)
, type(inOther.type)
, data1(inOther.data1)
, data2(inOther.data2)
, valid(inOther.valid)
, length(inOther.length)
{
if (type == midi::SystemExclusive)
{
memcpy(sysexArray, inOther.sysexArray, sSysExMaxSize * sizeof(DataByte));
}
}
/*! The maximum size for the System Exclusive array. /*! The maximum size for the System Exclusive array.
*/ */
static const unsigned sSysExMaxSize = SysExMaxSize; static const unsigned sSysExMaxSize = SysExMaxSize;
@ -100,6 +114,18 @@ struct Message
const unsigned size = unsigned(data2) << 8 | data1; const unsigned size = unsigned(data2) << 8 | data1;
return size > sSysExMaxSize ? sSysExMaxSize : size; return size > sSysExMaxSize ? sSysExMaxSize : size;
} }
inline bool isSystemRealTime () const
{
return (type & 0xf8) == 0xf8;
}
inline bool isSystemCommon () const
{
return (type & 0xf8) == 0xf0;
}
inline bool isChannelMessage () const
{
return (type & 0xf0) != 0xf0;
}
}; };
END_MIDI_NAMESPACE END_MIDI_NAMESPACE

View File

@ -72,15 +72,18 @@ struct DefaultSettings
*/ */
static const unsigned SysExMaxSize = 128; static const unsigned SysExMaxSize = 128;
/*! Global switch to turn on/off sending and receiving ActiveSensing /*! Global switch to turn on/off sender ActiveSensing
Set to true to activate ActiveSensing Set to true to send ActiveSensing
Set to false will not send/receive ActiveSensing message (will also save 236 bytes of memory) Set to false will not send ActiveSensing message (will also save memory)
When setting UseActiveSensing to true, MIDI.read() *must* be called
as often as possible (1000 / ActiveSensingPeriodicity per second).
*/ */
static const bool UseSenderActiveSensing = false; static const bool UseSenderActiveSensing = false;
/*! Global switch to turn on/off receiver ActiveSensing
Set to true to check for message timeouts (via ErrorCallback)
Set to false will not check if chained device are still alive (if they use ActiveSensing) (will also save memory)
*/
static const bool UseReceiverActiveSensing = false;
/*! Active Sensing is intended to be sent /*! Active Sensing is intended to be sent
repeatedly by the sender to tell the receiver that a connection is alive. Use repeatedly by the sender to tell the receiver that a connection is alive. Use
of this message is optional. When initially received, the of this message is optional. When initially received, the
@ -91,20 +94,11 @@ struct DefaultSettings
normal (non- active sensing) operation. normal (non- active sensing) operation.
Typical value is 250 (ms) - an Active Sensing command is send every 250ms. Typical value is 250 (ms) - an Active Sensing command is send every 250ms.
(Most Roland devices send Active Sensing every 250ms) (All Roland devices send Active Sensing every 250ms)
Setting this field to 0 will disable sending MIDI active sensing.
*/ */
static const uint16_t SenderActiveSensingPeriodicity = 300; static const uint16_t SenderActiveSensingPeriodicity = 0;
/*! Once an Active Sensing message is received, the unit will begin monitoring
the intervalbetween all subsequent messages. If there is an interval of ActiveSensingPeriodicity ms
or longer betweenmessages while monitoring is active, the same processing
as when All Sound Off, All Notes Off,and Reset All Controllers messages are
received will be carried out. The unit will then stopmonitoring the message interval.
*/
static const bool UseReceiverActiveSensing = false;
static const uint16_t ReceiverActiveSensingTimeout = 300;
}; };
END_MIDI_NAMESPACE END_MIDI_NAMESPACE

View File

@ -63,6 +63,11 @@ public:
#endif #endif
} }
void end()
{
mSerial.end();
}
bool beginTransmission(MidiType) bool beginTransmission(MidiType)
{ {
return true; return true;
@ -116,11 +121,10 @@ END_MIDI_NAMESPACE
#endif #endif
/*! \brief Create an instance of the library attached to a serial port with /*! \brief Create an instance of the library attached to a serial port with
custom MIDI settings (not to be confused with modified Serial Settings, like BaudRate) custom settings.
@see DefaultSettings @see DefaultSettings
@see MIDI_CREATE_INSTANCE @see MIDI_CREATE_INSTANCE
*/ */
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \ #define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\ MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>, Settings> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name); MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>, Settings> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);