Compare commits
289 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2d64cc3c2f | |
|
|
7c0d716641 | |
|
|
8f8c7cfcc6 | |
|
|
8e9805fc92 | |
|
|
1944aca891 | |
|
|
663b24b0b5 | |
|
|
be557c1f4b | |
|
|
50e33c8b4a | |
|
|
417beca1c7 | |
|
|
c9d3b9b592 | |
|
|
c7922927e5 | |
|
|
892f65a9ee | |
|
|
e1035a03f7 | |
|
|
2012e2a84a | |
|
|
b4daa697a9 | |
|
|
d9873560ad | |
|
|
bf80e99d54 | |
|
|
2f03d9b29d | |
|
|
d2f11d02e2 | |
|
|
9f5317f299 | |
|
|
f356c99ca2 | |
|
|
7ca289d84e | |
|
|
0d605dc8a7 | |
|
|
3f0a2d3fdb | |
|
|
d501f4bb3d | |
|
|
d9149d19df | |
|
|
a93fb27a1c | |
|
|
d967c0c389 | |
|
|
d3c361033d | |
|
|
33b294624a | |
|
|
10ef10bfde | |
|
|
da8ca5e69a | |
|
|
e292cf61b0 | |
|
|
150cecd450 | |
|
|
ff3052ceb4 | |
|
|
e5ee620139 | |
|
|
0d9f6f4731 | |
|
|
80dfb8333c | |
|
|
d0110e4866 | |
|
|
ce57d26d2c | |
|
|
47ce2acc16 | |
|
|
1375b0b6aa | |
|
|
116dba12eb | |
|
|
aa4912b587 | |
|
|
c0a66516f6 | |
|
|
a5e31f15a4 | |
|
|
99a981be5e | |
|
|
419c74adfe | |
|
|
5e9a0165d9 | |
|
|
d600656f04 | |
|
|
804e59669e | |
|
|
fc360d60b3 | |
|
|
b384278b29 | |
|
|
ae49d3037d | |
|
|
2d6bdd6dc2 | |
|
|
c2d17ec429 | |
|
|
c1d2f476b8 | |
|
|
45fb2df88f | |
|
|
05487c5415 | |
|
|
e06e3677f0 | |
|
|
c5330945ee | |
|
|
1e14162bf4 | |
|
|
9d43a8370e | |
|
|
b457f6db7c | |
|
|
be85c7b3e0 | |
|
|
23c53cafe1 | |
|
|
f8707ed49a | |
|
|
a9664ef4a4 | |
|
|
7a96fec0b5 | |
|
|
2c657a38d1 | |
|
|
cab564b995 | |
|
|
2ffbdef8da | |
|
|
843a9e5d8d | |
|
|
f067883dd8 | |
|
|
dac0862657 | |
|
|
d688030c14 | |
|
|
96b7fd4537 | |
|
|
b3ab309781 | |
|
|
faa7bade0b | |
|
|
a9e0db5b73 | |
|
|
39d13e8ac4 | |
|
|
5835806832 | |
|
|
347b67a877 | |
|
|
0e9e505543 | |
|
|
67a13be6c6 | |
|
|
5ccf0159a2 | |
|
|
1ccd7c3ce8 | |
|
|
5b96487362 | |
|
|
3f15b733e4 | |
|
|
a34087c92c | |
|
|
9a0907ea7f | |
|
|
b9397337d2 | |
|
|
db0f65a46e | |
|
|
42b98a78cf | |
|
|
04ae12d9ad | |
|
|
83ad031ee6 | |
|
|
a44d1e7c1c | |
|
|
e486667d8a | |
|
|
58474673b4 | |
|
|
2e8be813c4 | |
|
|
98f7bb8831 | |
|
|
ba6e16442b | |
|
|
1cd638361e | |
|
|
561def7c06 | |
|
|
33bd77dd13 | |
|
|
c5833214a1 | |
|
|
d9cad6034f | |
|
|
064c9d9568 | |
|
|
1c01effbd9 | |
|
|
9b9905fb2a | |
|
|
4f9e629471 | |
|
|
63caf9a564 | |
|
|
ad652d3191 | |
|
|
ed72e06755 | |
|
|
d292b68eaa | |
|
|
43fcb370ed | |
|
|
7809235676 | |
|
|
2a1e4a86b3 | |
|
|
3396ec9429 | |
|
|
724a1caecd | |
|
|
03d73c2b09 | |
|
|
3a31a366e4 | |
|
|
9a4b8b6c86 | |
|
|
77bb518f19 | |
|
|
de51bcfa34 | |
|
|
e615065dc2 | |
|
|
de608f874f | |
|
|
32489bc0ea | |
|
|
ce42efc7c6 | |
|
|
82f5dca265 | |
|
|
ce659116b7 | |
|
|
6b295e6a47 | |
|
|
2a1e86dbaf | |
|
|
e1e6e98ea9 | |
|
|
5aa283f9c5 | |
|
|
3d06d8fde5 | |
|
|
2b31380ba8 | |
|
|
250e512ef7 | |
|
|
7ee313ebb7 | |
|
|
a381c62667 | |
|
|
cfffdbea26 | |
|
|
7d6cd37ec3 | |
|
|
b20a40ed9a | |
|
|
253e41acc6 | |
|
|
29c705fbfa | |
|
|
251cf54ba8 | |
|
|
afbb2f296d | |
|
|
aa35573ca6 | |
|
|
437d5ebce0 | |
|
|
0fabf63d49 | |
|
|
4eee744b35 | |
|
|
4ab8728c67 | |
|
|
2df247489a | |
|
|
53fdcc79b0 | |
|
|
f653d21ca1 | |
|
|
2e816e3e39 | |
|
|
597a75e638 | |
|
|
7b36662a7f | |
|
|
037b9fb9f2 | |
|
|
5d3cbd1f9f | |
|
|
f1f0ef41ce | |
|
|
6abbdd7963 | |
|
|
7117671b16 | |
|
|
37e72bc7fa | |
|
|
58880bb647 | |
|
|
6fc3e38512 | |
|
|
e92c5fc6df | |
|
|
fdb68ab785 | |
|
|
189288c03f | |
|
|
8ebd4f9161 | |
|
|
8eb881f39e | |
|
|
ff94a77521 | |
|
|
5f7fa5d083 | |
|
|
208a79e5f0 | |
|
|
4cebcbf915 | |
|
|
58e1ea2a65 | |
|
|
8badd2bbfa | |
|
|
a2677c3a33 | |
|
|
21a8cdea89 | |
|
|
338eea10ab | |
|
|
7317243c36 | |
|
|
e4bd7f1c18 | |
|
|
5a477fda4f | |
|
|
cf7885d79b | |
|
|
3da8bb013e | |
|
|
2699fc80d6 | |
|
|
0252ce7ebe | |
|
|
e84d12d9ed | |
|
|
63e8e515ad | |
|
|
c17f2396cd | |
|
|
cf8761379c | |
|
|
ebd10f034a | |
|
|
73dfc79815 | |
|
|
ff6bf0111b | |
|
|
203a26ea3a | |
|
|
305c263afb | |
|
|
47b206c279 | |
|
|
606e732490 | |
|
|
2b4b30b32a | |
|
|
300cb139e9 | |
|
|
ac1925a74f | |
|
|
68444227b2 | |
|
|
d6d3f9edb0 | |
|
|
80b46ec903 | |
|
|
b21500e8e6 | |
|
|
f4d103ea36 | |
|
|
63f5eba41b | |
|
|
016f18df4b | |
|
|
4009b2a15b | |
|
|
766961b2f3 | |
|
|
9cbce11523 | |
|
|
cb34be646c | |
|
|
8372ee92d7 | |
|
|
530e983dea | |
|
|
34b1b11ad0 | |
|
|
f26071fb8a | |
|
|
dd700fd49f | |
|
|
886658c6a4 | |
|
|
527a7c8e9d | |
|
|
e65d5b6b4c | |
|
|
34b37f75e8 | |
|
|
a7d6d803a1 | |
|
|
bc0308223c | |
|
|
dc478a3656 | |
|
|
154b87f8d3 | |
|
|
489b49b862 | |
|
|
a8e5b0ba15 | |
|
|
96374a1fa1 | |
|
|
c29dfd84d7 | |
|
|
419606785d | |
|
|
cb6c428913 | |
|
|
b25d111391 | |
|
|
aeffb7d7c5 | |
|
|
8b5e81ed86 | |
|
|
0b0bd7eda5 | |
|
|
3822bc2e33 | |
|
|
6705d0ed81 | |
|
|
993f12ce30 | |
|
|
7268429235 | |
|
|
f483eb86e9 | |
|
|
371d1adba9 | |
|
|
51d25a831f | |
|
|
d42acc0cbb | |
|
|
22d6bb8cf2 | |
|
|
618f71658b | |
|
|
92c50d612a | |
|
|
c86d6f2e26 | |
|
|
b5c2d8c12c | |
|
|
f5b51bd211 | |
|
|
4bd4015f9d | |
|
|
3740624176 | |
|
|
a24d195f93 | |
|
|
c600287129 | |
|
|
3c1c65fd69 | |
|
|
210d3bd2ae | |
|
|
3a07cd0fce | |
|
|
885bc6e870 | |
|
|
0275824e86 | |
|
|
69db820896 | |
|
|
9bab7bd0df | |
|
|
e4e5d8bfcc | |
|
|
d96b886bd5 | |
|
|
533f467299 | |
|
|
40d20e3042 | |
|
|
55f91d54aa | |
|
|
d48f371b96 | |
|
|
7aaa7d4e24 | |
|
|
f6b4282821 | |
|
|
5b3e4ac097 | |
|
|
79c01a27a4 | |
|
|
219797524d | |
|
|
fb693e7245 | |
|
|
c7d2ced229 | |
|
|
8a856e1f9d | |
|
|
3eb65db1a4 | |
|
|
352e3d10fb | |
|
|
96cdd33662 | |
|
|
0d436da5fc | |
|
|
cc79d38e69 | |
|
|
d57acdb635 | |
|
|
33bd451ca7 | |
|
|
c594de85dc | |
|
|
dc4eceec86 | |
|
|
c9e468b327 | |
|
|
dd5b39a686 | |
|
|
1d09cf3a0a | |
|
|
76c5846edd | |
|
|
bbbb6b7e31 | |
|
|
2ad5b7c459 |
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
|
@ -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.
|
||||
-->
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -2,3 +2,9 @@
|
|||
*.pyc
|
||||
logs/
|
||||
build/
|
||||
.vscode/.cmaketools.json
|
||||
src/.DS_Store
|
||||
examples/.DS_Store
|
||||
.DS_Store
|
||||
test/xcode
|
||||
.development
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "external/google-test"]
|
||||
path = external/google-test
|
||||
url = https://github.com/google/googletest.git
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"board": "arduino:avr:leonardo"
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"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": "",
|
||||
"path": [
|
||||
"/Applications/Arduino.app/Contents/Java/hardware/tools/avr/include",
|
||||
"/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino"
|
||||
]
|
||||
},
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"macFrameworkPath": [
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Frameworks"
|
||||
],
|
||||
"compilerPath": "/usr/bin/clang",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/usr/include"
|
||||
],
|
||||
"browse": {
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": "",
|
||||
"path": [
|
||||
"/usr/include"
|
||||
]
|
||||
},
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"compilerPath": "/usr/bin/clang",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"c:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
|
||||
],
|
||||
"browse": {
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": "",
|
||||
"path": [
|
||||
"c:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
|
||||
]
|
||||
},
|
||||
"intelliSenseMode": "msvc-x64",
|
||||
"compilerPath": "/usr/bin/clang",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"cstddef": "cpp",
|
||||
"ostream": "cpp",
|
||||
"__locale": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"vector": "cpp",
|
||||
"istream": "cpp",
|
||||
"system_error": "cpp"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build",
|
||||
"command": "make",
|
||||
"args": ["all"],
|
||||
"options": {
|
||||
"cwd": "${workspaceRoot}/build"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Run Tests",
|
||||
"command": "${workspaceRoot}/build/test/unit-tests/unit-tests",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"dependsOn": ["Build"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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,32 @@
|
|||
# Contributing Guidelines
|
||||
|
||||
First, thanks for your help ! :+1:
|
||||
|
||||
## Branches
|
||||
|
||||
Please base your Pull Requests off the `master` branch.
|
||||
|
||||
## Requirements
|
||||
|
||||
Requirements to build and run the unit tests:
|
||||
|
||||
- CMake 2.8 or later
|
||||
- GCC / Clang with C++11 support (GCC 4.8 or higher)
|
||||
|
||||
## Setup
|
||||
|
||||
Pull Google Test / Google Mock subrepository:
|
||||
|
||||
```
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
```
|
||||
|
||||
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.
|
||||
159
README.md
159
README.md
|
|
@ -1,79 +1,142 @@
|
|||
#Arduino MIDI Library v4.2
|
||||
# Arduino MIDI Library
|
||||
|
||||
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.
|
||||
[](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest)
|
||||
[](LICENSE)
|
||||
[](https://github.com/FortySevenEffects/arduino_midi_library/actions/workflows/cmake.yml)
|
||||
[](https://github.com/FortySevenEffects/arduino_midi_library/actions/workflows/platformio.yml)
|
||||
[](https://coveralls.io/github/FortySevenEffects/arduino_midi_library)
|
||||
|
||||
Download the latest version [here](https://github.com/FortySevenEffects/arduino_midi_library/releases/latest).
|
||||
This library adds MIDI I/O communications to an Arduino board.
|
||||
|
||||
### 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.
|
||||
* [Callbacks](http://playground.arduino.cc/Main/MIDILibraryCallbacks) to handle input messages more easily.
|
||||
* Last received message is saved until a new one arrives.
|
||||
* Configurable: disable input or output if you don't need it, and get the pin back for IO use (and save some flash space).
|
||||
* Create more than one MIDI port for mergers/splitters applications.
|
||||
* Use any serial port, hardware or software.
|
||||
|
||||
- **New** : MIDI over USB, Bluetooth, IP & AppleMIDI (see [Transports](#other-transport-mechanisms)).
|
||||
- **New** : Active Sensing support
|
||||
- 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](https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks) to handle input messages more easily.
|
||||
- Last received message is saved until a new one arrives.
|
||||
- Configurable: [overridable template-based settings](https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-custom-Settings).
|
||||
- Create more than one MIDI interface for mergers/splitters applications.
|
||||
- Use any serial port, hardware or software.
|
||||
|
||||
#### Changelog
|
||||
* 11/06/2014 : Version 4.2 released. Bug fix for SysEx, overridable template settings.
|
||||
* 16/04/2014 : Version 4.1 released. Bug fixes regarding running status.
|
||||
* 13/02/2014 : Version 4.0 released. Moved to GitHub, added multiple instances & software serial support, and a few bug fixes.
|
||||
* 29/01/2012 : Version 3.2 released. Release notes are [here](http://sourceforge.net/news/?group_id=265194)
|
||||
* 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.
|
||||
### Getting Started
|
||||
|
||||
1. Use the Arduino Library Manager to install the library.
|
||||

|
||||
|
||||
2. Start coding:
|
||||
|
||||
**__Warning: this library requires Arduino 1.0 or more recent versions.__**
|
||||
```c++
|
||||
#include <MIDI.h>
|
||||
|
||||
// Create and bind the MIDI interface to the default hardware Serial port
|
||||
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||
|
||||
### What do I need to do?
|
||||
void setup()
|
||||
{
|
||||
MIDI.begin(MIDI_CHANNEL_OMNI); // Listen to all incoming messages
|
||||
}
|
||||
|
||||
* 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..
|
||||
void loop()
|
||||
{
|
||||
// Send note 42 with velocity 127 on channel 1
|
||||
MIDI.sendNoteOn(42, 127, 1);
|
||||
|
||||
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...
|
||||
// Read incoming messages
|
||||
MIDI.read();
|
||||
}
|
||||
```
|
||||
|
||||
3. Read the [documentation](#documentation) or watch the awesome video tutorials from [Notes & Volts](https://www.youtube.com/playlist?list=PL4_gPbvyebyH2xfPXePHtx8gK5zPBrVkg).
|
||||
|
||||
##Reference
|
||||
## Documentation
|
||||
|
||||
See the extended reference [here](http://arduinomidilib.fortyseveneffects.com) ([Mirror](http://fortyseveneffects.github.io/arduino_midi_library/)).
|
||||
- [Doxygen Extended Documentation](https://fortyseveneffects.github.io/arduino_midi_library/).
|
||||
- [GitHub wiki](https://github.com/FortySevenEffects/arduino_midi_library/wiki).
|
||||
|
||||
### Using MIDI.begin
|
||||
## USB Migration (4.x to 5.x)
|
||||
|
||||
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).
|
||||
All USB related code has been moved into a separate repository [Arduino-USB-MIDI](https://github.com/lathoub/Arduino-USBMIDI), USB MIDI Device support with [`MIDIUSB`](https://github.com/arduino-libraries/MIDIUSB), still using this library to do all the MIDI heavy-lifting.
|
||||
|
||||
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)
|
||||
Migration has been made as easy as possible: only the declaration of the MIDI object has been modified, the rest of your code remains identical.
|
||||
|
||||
`4.3.1` code:
|
||||
|
||||
```c++
|
||||
#include <MIDI.h>
|
||||
#include <midi_UsbTransport.h>
|
||||
|
||||
### MIDI Thru
|
||||
static const unsigned sUsbTransportBufferSize = 16;
|
||||
typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport;
|
||||
|
||||
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.
|
||||
UsbTransport sUsbTransport;
|
||||
|
||||
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.
|
||||
MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, MIDI);
|
||||
|
||||
Thru is enabled by default, you can turn it off using appropriate methods.
|
||||
// ...
|
||||
```
|
||||
|
||||
now becomes in `5.x`:
|
||||
|
||||
### Hardware
|
||||
```c++
|
||||
#include <USB-MIDI.h>
|
||||
USBMIDI_CREATE_DEFAULT_INSTANCE();
|
||||
|
||||
Take a look at [the MIDI.org schematic](http://www.midi.org/techspecs/electrispec.php)
|
||||
// ...
|
||||
```
|
||||
|
||||
Start with the [NoteOnOffEverySec](https://github.com/lathoub/Arduino-USBMIDI/blob/master/examples/NoteOnOffEverySec/NoteOnOffEverySec.ino) example that is based on the original MidiUSB [sketch](https://github.com/lathoub/arduino_midi_library/blob/master/examples/MidiUSB/MidiUSB.ino). Note the only difference is in the declaration.
|
||||
|
||||
## Contact
|
||||
if you have any comment or support request to make, feel free to contact me: francois.best@fortyseveneffects.com
|
||||
The [USB-MIDI](https://github.com/lathoub/Arduino-USBMIDI) Arduino library depends on [this library](https://github.com/FortySevenEffects/arduino_midi_library) and the [MIDIUSB](https://github.com/arduino-libraries/MIDIUSB) library.
|
||||
|
||||
You can also get informations about bug fixes and updates on my twitter account: [@fortysevenfx](http://twitter.com/fortysevenfx).
|
||||
[USB-MIDI](https://github.com/lathoub/Arduino-USBMIDI) uses the latest Arduino IDE `depends` feature in the `library.properties` file installing all the dependencies automatically when installing from the IDE.
|
||||
|
||||
## Other Transport mechanisms
|
||||
|
||||
Version 5 of this library, allows for other Transport layers than the
|
||||
original MIDI 1.0 Electrical Specification (hardware serial).
|
||||
|
||||
- [USB-MIDI](https://github.com/lathoub/Arduino-USBMIDI)
|
||||
- [AppleMIDI or rtpMIDI](https://github.com/lathoub/Arduino-AppleMIDI-Library)
|
||||
- [ipMIDI](https://github.com/lathoub/Arduino-ipMIDI)
|
||||
- [BLE-MIDI](https://github.com/lathoub/Arduino-BLE-MIDI)
|
||||
|
||||
All these Transport layers use this library for all the underlying MIDI
|
||||
work, making it easy to switch transport protocols or making transport
|
||||
protocol bridges.
|
||||
|
||||
### Differences between Serial & other transports
|
||||
|
||||
- Software Thru is enabled by default on Serial, but not on other transports.
|
||||
|
||||
## Contact & Contribution
|
||||
|
||||
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).
|
||||
|
||||
## Contributors
|
||||
|
||||
Special thanks to all who have contributed to this open-source project !
|
||||
|
||||
- [@lathoub](https://github.com/lathoub)
|
||||
- [@jarosz](https://github.com/jarosz)
|
||||
- [@ivankravets](https://github.com/ivankravets)
|
||||
- [@insolace](https://github.com/insolace)
|
||||
- [@softegg](https://github.com/softegg)
|
||||
- [@per1234](https://github.com/per1234)
|
||||
- [@LnnrtS](https://github.com/LnnrtS)
|
||||
- [@DavidMenting](https://github.com/DavidMenting)
|
||||
- [@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).
|
||||
|
||||
## License
|
||||
|
||||
MIT © 2009 - present [Francois Best](https://francoisbest.com)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
#### Changelog
|
||||
|
||||
- 20/04/2020 : Version 5.0 released. Separation of transports by [@lathoub](https://github.com/lathoub), adds Active Sensing.
|
||||
- 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,33 @@
|
|||
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()
|
||||
|
||||
macro(increase_warning_level)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wconversion -Wsign-conversion")
|
||||
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 5.0.2"
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// Examples
|
||||
|
||||
/*!
|
||||
\example MIDI_Basic_IO.ino
|
||||
\example Basic_IO.ino
|
||||
This example shows how to perform simple input and output MIDI. \n
|
||||
\n
|
||||
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
|
||||
*/
|
||||
|
||||
/*!
|
||||
\example MIDI_Bench.ino
|
||||
\example MIDI_DualMerger.ino
|
||||
\example MIDI_Input.ino
|
||||
\example MIDI_SimpleSynth.ino
|
||||
\example Bench.ino
|
||||
\example DualMerger.ino
|
||||
\example Input.ino
|
||||
\example SimpleSynth.ino
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
# SysEx Encoding & Decoding
|
||||
|
||||
There are various ways of encoding & decoding arbitrary 8-bit wide data into
|
||||
SysEx, which is 7-bit wide.
|
||||
|
||||
The [official documentation](http://www.somascape.org/midi/tech/spec.html#nusx_fd)
|
||||
for FileDump data exchanges states the following:
|
||||
|
||||
> The 8-bit file data needs to be converted to 7-bit form,
|
||||
> with the result that every 7 bytes of file data translates
|
||||
> to 8 bytes in the MIDI stream.
|
||||
>
|
||||
> For each group of 7 bytes (of file data) the top bit from each
|
||||
> is used to construct an eigth byte, which is sent first.
|
||||
> So:
|
||||
> ```
|
||||
> AAAAaaaa BBBBbbbb CCCCcccc DDDDdddd EEEEeeee FFFFffff GGGGgggg
|
||||
> ```
|
||||
> becomes:
|
||||
> ```
|
||||
> 0ABCDEFG 0AAAaaaa 0BBBbbbb 0CCCcccc 0DDDdddd 0EEEeeee 0FFFffff 0GGGgggg
|
||||
> ```
|
||||
>
|
||||
> The final group may have less than 7 bytes, and is coded as follows
|
||||
> (e.g. with 3 bytes in the final group):
|
||||
> ```
|
||||
> 0ABC0000 0AAAaaaa 0BBBbbbb 0CCCcccc
|
||||
> ```
|
||||
|
||||
## SysEx encoding / decoding functions
|
||||
|
||||
The MIDI library supplies two functions to do this, `encodeSysEx` and `decodeSysEx`.
|
||||
|
||||
Example usage:
|
||||
```c++
|
||||
#include <MIDI.h>
|
||||
|
||||
static const byte myData[12] = {
|
||||
// Hex dump: CAFEBABE BAADF00D FACADE42
|
||||
0xca, 0xfe, 0xba, 0xbe, 0xba, 0xad, 0xf0, 0x0d,
|
||||
0xfa, 0xca, 0xde, 0x42
|
||||
};
|
||||
|
||||
byte encoded[16];
|
||||
const unsigned encodedSize = midi::encodeSysEx(myData, encoded, 12);
|
||||
// Encoded hex dump: 07 4a 7e 3a 3e 3a 2d 70 07 0d 7a 4a 5e 42
|
||||
|
||||
byte decoded[12];
|
||||
const unsigned decoded = midi::decodeSysEx(encoded, decoded, encodedSize);
|
||||
```
|
||||
|
||||
## Special case for Korg devices
|
||||
|
||||
Korg apparently uses another convention for their SysEx encoding / decoding,
|
||||
where:
|
||||
```
|
||||
AAAAaaaa BBBBbbbb CCCCcccc DDDDdddd EEEEeeee FFFFffff GGGGgggg
|
||||
```
|
||||
becomes:
|
||||
```
|
||||
0GFEDCBA 0AAAaaaa 0BBBbbbb 0CCCcccc 0DDDdddd 0EEEeeee 0FFFffff 0GGGgggg
|
||||
```
|
||||
|
||||
The order of the bits in the "header" byte is reversed.
|
||||
To follow this behaviour, set the inFlipHeaderBits argument to true.
|
||||
|
||||
Example:
|
||||
```c++
|
||||
void handleSysEx(byte* inData, unsigned inSize)
|
||||
{
|
||||
// SysEx body data starts at 3rd byte: F0 42 aa bb cc dd F7
|
||||
// 42 being the hex value of the Korg SysEx ID.
|
||||
const unsigned dataStartOffset = 2;
|
||||
const unsigned encodedDataLength = inSize - 3; // Remove F0 42 & F7
|
||||
|
||||
// Create a large enough buffer where to decode the message
|
||||
byte decodedData[64];
|
||||
|
||||
const unsigned decodedSize = decodeSysEx(inData + dataStartOffset,
|
||||
decodedData,
|
||||
encodedDataLength,
|
||||
true); // flip header bits
|
||||
// Do stuff with your message
|
||||
}
|
||||
```
|
||||
|
||||
See original discussion in issue [#92](FortySevenEffects/arduino_midi_library#92).
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include <MIDI.h>
|
||||
|
||||
// Simple tutorial on how to receive and send MIDI messages
|
||||
// on a different serial port, using SoftwareSerial.
|
||||
// Here, when receiving any message on channel 4, the Arduino
|
||||
// will blink a led and play back a note for 1 second.
|
||||
|
||||
#if defined(ARDUINO_SAM_DUE) || defined(SAMD_SERIES) || defined(_VARIANT_ARDUINO_ZERO_)
|
||||
/* example not relevant for this hardware (SoftwareSerial not supported) */
|
||||
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||
#else
|
||||
#include <SoftwareSerial.h>
|
||||
using Transport = MIDI_NAMESPACE::SerialMIDI<SoftwareSerial>;
|
||||
int rxPin = 18;
|
||||
int txPin = 19;
|
||||
SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);
|
||||
Transport serialMIDI(mySerial);
|
||||
MIDI_NAMESPACE::MidiInterface<Transport> MIDI((Transport&)serialMIDI);
|
||||
#endif
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
MIDI.begin(4); // Launch MIDI and listen to channel 4
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#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();
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
MIDI.begin(4); // Launch MIDI and listen to channel 4
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,8 +71,8 @@ void setup()
|
|||
midiBench.setHandleNoteOn(handleNoteOn);
|
||||
midiBench.begin();
|
||||
|
||||
while(!Serial);
|
||||
Serial.begin(115200);
|
||||
while(!Serial);
|
||||
Serial.println("Arduino Ready");
|
||||
|
||||
midiBench.sendNoteOn(69,127,1);
|
||||
|
|
@ -7,7 +7,7 @@ MIDI_CREATE_DEFAULT_INSTANCE();
|
|||
// This function will be automatically called when a NoteOn is received.
|
||||
// It must be a void-returning function with the correct parameters,
|
||||
// see documentation here:
|
||||
// http://arduinomidilib.fortyseveneffects.com/a00022.html
|
||||
// https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks
|
||||
|
||||
void handleNoteOn(byte channel, byte pitch, byte velocity)
|
||||
{
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,17 @@
|
|||
// A out = A in + B in
|
||||
// B out = B in + A in
|
||||
|
||||
#ifdef ARDUINO_SAM_DUE
|
||||
#if defined(ARDUINO_SAM_DUE)
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, midiA);
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiB);
|
||||
#elif defined(ARDUINO_SAMD_ZERO)
|
||||
MIDI_CREATE_INSTANCE(Serial_, SerialUSB, midiA);
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiB);
|
||||
#elif defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial softSerial(2,3);
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiA);
|
||||
MIDI_CREATE_INSTANCE(SoftwareSerial, softSerial, midiB);
|
||||
#else
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial softSerial(2,3);
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#include <MIDI.h>
|
||||
|
||||
// Before running the program below, make sure you set
|
||||
// UseReceiverActiveSensing (optionally UseSenderActiveSensing) in Settings.h to true
|
||||
|
||||
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||
|
||||
void handleError(int8_t err)
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, (err == 0)? LOW : HIGH);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
||||
MIDI.setHandleError(handleError);
|
||||
MIDI.begin(1);
|
||||
}
|
||||
|
||||
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 703bd9caab50b139428cea1aaff9974ebee5742e
|
||||
|
|
@ -29,6 +29,24 @@ sendSongPosition KEYWORD2
|
|||
sendSongSelect KEYWORD2
|
||||
sendTuneRequest KEYWORD2
|
||||
sendRealTime KEYWORD2
|
||||
sendCommon KEYWORD2
|
||||
sendClock KEYWORD2
|
||||
sendStart KEYWORD2
|
||||
sendStop KEYWORD2
|
||||
sendTick KEYWORD2
|
||||
sendContinue KEYWORD2
|
||||
sendActiveSensing KEYWORD2
|
||||
sendSystemReset 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 +54,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,8 +83,10 @@ setHandleStop KEYWORD2
|
|||
setHandleActiveSensing KEYWORD2
|
||||
setHandleSystemReset KEYWORD2
|
||||
getTypeFromStatusByte KEYWORD2
|
||||
encodeSysEx KEYWORD2
|
||||
decodeSysEx KEYWORD2
|
||||
getChannelFromStatusByte KEYWORD2
|
||||
isChannelMessage KEYWORD2
|
||||
encodeSysEx KEYWORD2
|
||||
decodeSysEx KEYWORD2
|
||||
|
||||
|
||||
#######################################
|
||||
|
|
@ -101,6 +119,7 @@ Continue LITERAL1
|
|||
ActiveSensing LITERAL1
|
||||
SystemReset LITERAL1
|
||||
InvalidType LITERAL1
|
||||
Thru LITERAL1
|
||||
Off LITERAL1
|
||||
Full LITERAL1
|
||||
SameChannel LITERAL1
|
||||
|
|
@ -110,3 +129,79 @@ 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
|
||||
BankSelectLSB LITERAL1
|
||||
ModulationWheelLSB LITERAL1
|
||||
BreathControllerLSB LITERAL1
|
||||
FootControllerLSB LITERAL1
|
||||
PortamentoTimeLSB LITERAL1
|
||||
DataEntryLSB LITERAL1
|
||||
ChannelVolumeLSB LITERAL1
|
||||
BalanceLSB LITERAL1
|
||||
PanLSB LITERAL1
|
||||
ExpressionControllerLSB LITERAL1
|
||||
EffectControl1LSB LITERAL1
|
||||
EffectControl2LSB 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
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "MIDI Library",
|
||||
"version": "5.0.2",
|
||||
"keywords": "midi",
|
||||
"description": "Enables MIDI I/O communications on the Arduino serial ports",
|
||||
"license": "MIT",
|
||||
"authors": {
|
||||
"name": "Francois Best",
|
||||
"email": "contact@francoisbest.com",
|
||||
"url": "https://github.com/Franky47",
|
||||
"maintainer": true
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/FortySevenEffects/arduino_midi_library.git",
|
||||
"branch": "master"
|
||||
},
|
||||
"export": {
|
||||
"include": ["src", "examples"]
|
||||
},
|
||||
"frameworks": "arduino",
|
||||
"platforms": ["atmelavr", "atmelsam", "teensy"]
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
name=MIDI Library
|
||||
version=5.0.2
|
||||
author=Francois Best, lathoub
|
||||
maintainer=Francois Best <contact@francoisbest.com>
|
||||
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);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -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 @@
|
|||
increase_warning_level()
|
||||
|
||||
project(midi)
|
||||
|
||||
add_library(midi STATIC
|
||||
midi_Namespace.h
|
||||
midi_Defs.h
|
||||
midi_Message.h
|
||||
midi_Platform.h
|
||||
midi_Settings.h
|
||||
MIDI.cpp
|
||||
MIDI.hpp
|
||||
MIDI.h
|
||||
serialMIDI.h
|
||||
)
|
||||
61
src/MIDI.cpp
61
src/MIDI.cpp
|
|
@ -2,23 +2,27 @@
|
|||
* @file MIDI.cpp
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino
|
||||
* @version 4.2
|
||||
* @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"
|
||||
|
|
@ -33,12 +37,16 @@ BEGIN_MIDI_NAMESPACE
|
|||
data you want to send.
|
||||
\param inData The data to encode.
|
||||
\param outSysEx The output buffer where to store the encoded message.
|
||||
\param inLength The lenght of the input buffer.
|
||||
\return The lenght of the encoded output buffer.
|
||||
\param inLength The length of the input buffer.
|
||||
\param inFlipHeaderBits True for Korg and other who store MSB in reverse order
|
||||
\return The length of the encoded output buffer.
|
||||
@see decodeSysEx
|
||||
Code inspired from Ruin & Wesen's SysEx encoder/decoder - http://ruinwesen.com
|
||||
*/
|
||||
unsigned encodeSysEx(const byte* inData, byte* outSysEx, unsigned inLength)
|
||||
unsigned encodeSysEx(const byte* inData,
|
||||
byte* outSysEx,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits)
|
||||
{
|
||||
unsigned outLength = 0; // Num bytes in output array.
|
||||
byte count = 0; // Num 7bytes in a block.
|
||||
|
|
@ -50,7 +58,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 << (inFlipHeaderBits ? count : (6 - count)));
|
||||
outSysEx[1 + count] = body;
|
||||
|
||||
if (count++ == 6)
|
||||
|
|
@ -69,27 +77,36 @@ unsigned encodeSysEx(const byte* inData, byte* outSysEx, unsigned inLength)
|
|||
127 without breaking the MIDI protocol. Use this static method to reassemble
|
||||
your received message.
|
||||
\param inSysEx The SysEx data received from MIDI in.
|
||||
\param outData The output buffer where to store the decrypted message.
|
||||
\param inLength The lenght of the input buffer.
|
||||
\return The lenght of the output buffer.
|
||||
\param outData The output buffer where to store the decrypted message.
|
||||
\param inLength The length of the input buffer.
|
||||
\param inFlipHeaderBits True for Korg and other who store MSB in reverse order
|
||||
\return The length of the output buffer.
|
||||
@see encodeSysEx @see getSysExArrayLength
|
||||
Code inspired from Ruin & Wesen's SysEx encoder/decoder - http://ruinwesen.com
|
||||
*/
|
||||
unsigned decodeSysEx(const byte* inSysEx, byte* outData, unsigned inLength)
|
||||
unsigned decodeSysEx(const byte* inSysEx,
|
||||
byte* outData,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits)
|
||||
{
|
||||
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 shift = inFlipHeaderBits ? 6 - byteIndex : byteIndex;
|
||||
const byte msb = byte(((msbStorage >> shift) & 1) << 7);
|
||||
byteIndex--;
|
||||
outData[count++] = msb | body;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
|
|
|
|||
277
src/MIDI.h
277
src/MIDI.h
|
|
@ -2,95 +2,154 @@
|
|||
* @file MIDI.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino
|
||||
* @version 4.2
|
||||
* @author Francois Best
|
||||
* @author Francois Best, lathoub
|
||||
* @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
|
||||
|
||||
#include "midi_Defs.h"
|
||||
#include "midi_Platform.h"
|
||||
#include "midi_Settings.h"
|
||||
#include "midi_Message.h"
|
||||
|
||||
#include "serialMIDI.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
#define MIDI_LIBRARY_VERSION 0x050000
|
||||
#define MIDI_LIBRARY_VERSION_MAJOR 5
|
||||
#define MIDI_LIBRARY_VERSION_MINOR 0
|
||||
#define MIDI_LIBRARY_VERSION_PATCH 0
|
||||
|
||||
/*! \brief The main class for MIDI handling.
|
||||
It is templated over the type of serial port to provide abstraction from
|
||||
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 Transport, class _Settings = DefaultSettings, class _Platform = DefaultPlatform>
|
||||
class MidiInterface
|
||||
{
|
||||
public:
|
||||
inline MidiInterface(SerialPort& inSerial);
|
||||
typedef _Settings Settings;
|
||||
typedef _Platform Platform;
|
||||
typedef Message<Settings::SysExMaxSize> MidiMessage;
|
||||
|
||||
public:
|
||||
inline MidiInterface(Transport&);
|
||||
inline ~MidiInterface();
|
||||
|
||||
public:
|
||||
void begin(Channel inChannel = 1);
|
||||
MidiInterface& begin(Channel inChannel = 1);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MIDI Output
|
||||
|
||||
public:
|
||||
inline void sendNoteOn(DataByte inNoteNumber,
|
||||
inline MidiInterface& sendNoteOn(DataByte inNoteNumber,
|
||||
DataByte inVelocity,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendNoteOff(DataByte inNoteNumber,
|
||||
inline MidiInterface& sendNoteOff(DataByte inNoteNumber,
|
||||
DataByte inVelocity,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendProgramChange(DataByte inProgramNumber,
|
||||
inline MidiInterface& sendProgramChange(DataByte inProgramNumber,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendControlChange(DataByte inControlNumber,
|
||||
inline MidiInterface& sendControlChange(DataByte inControlNumber,
|
||||
DataByte inControlValue,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendPitchBend(int inPitchValue, Channel inChannel);
|
||||
inline void sendPitchBend(double inPitchValue, Channel inChannel);
|
||||
inline MidiInterface& sendPitchBend(int inPitchValue, Channel inChannel);
|
||||
inline MidiInterface& sendPitchBend(double inPitchValue, Channel inChannel);
|
||||
|
||||
inline void sendPolyPressure(DataByte inNoteNumber,
|
||||
inline MidiInterface& sendPolyPressure(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel);
|
||||
Channel inChannel) __attribute__ ((deprecated));
|
||||
|
||||
inline void sendAfterTouch(DataByte inPressure,
|
||||
inline MidiInterface& sendAfterTouch(DataByte inPressure,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendAfterTouch(DataByte inNoteNumber,
|
||||
DataByte inPressure,
|
||||
Channel inChannel);
|
||||
|
||||
inline void sendSysEx(unsigned inLength,
|
||||
inline MidiInterface& sendSysEx(unsigned inLength,
|
||||
const byte* inArray,
|
||||
bool inArrayContainsBoundaries = false);
|
||||
|
||||
inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble,
|
||||
inline MidiInterface& sendTimeCodeQuarterFrame(DataByte inTypeNibble,
|
||||
DataByte inValuesNibble);
|
||||
inline void sendTimeCodeQuarterFrame(DataByte inData);
|
||||
inline MidiInterface& sendTimeCodeQuarterFrame(DataByte inData);
|
||||
|
||||
inline void sendSongPosition(unsigned inBeats);
|
||||
inline void sendSongSelect(DataByte inSongNumber);
|
||||
inline void sendTuneRequest();
|
||||
inline void sendRealTime(MidiType inType);
|
||||
inline MidiInterface& sendSongPosition(unsigned inBeats);
|
||||
inline MidiInterface& sendSongSelect(DataByte inSongNumber);
|
||||
inline MidiInterface& sendTuneRequest();
|
||||
|
||||
inline MidiInterface& sendCommon(MidiType inType, unsigned = 0);
|
||||
|
||||
inline MidiInterface& sendClock() { return sendRealTime(Clock); };
|
||||
inline MidiInterface& sendStart() { return sendRealTime(Start); };
|
||||
inline MidiInterface& sendStop() { return sendRealTime(Stop); };
|
||||
inline MidiInterface& sendTick() { return sendRealTime(Tick); };
|
||||
inline MidiInterface& sendContinue() { return sendRealTime(Continue); };
|
||||
inline MidiInterface& sendActiveSensing() { return sendRealTime(ActiveSensing); };
|
||||
inline MidiInterface& sendSystemReset() { return sendRealTime(SystemReset); };
|
||||
|
||||
inline MidiInterface& sendRealTime(MidiType inType);
|
||||
|
||||
inline MidiInterface& beginRpn(unsigned inNumber,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendRpnValue(unsigned inValue,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendRpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendRpnIncrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendRpnDecrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& endRpn(Channel inChannel);
|
||||
|
||||
inline MidiInterface& beginNrpn(unsigned inNumber,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendNrpnValue(unsigned inValue,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendNrpnValue(byte inMsb,
|
||||
byte inLsb,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendNrpnIncrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& sendNrpnDecrement(byte inAmount,
|
||||
Channel inChannel);
|
||||
inline MidiInterface& endNrpn(Channel inChannel);
|
||||
|
||||
inline MidiInterface& send(const MidiMessage&);
|
||||
|
||||
public:
|
||||
void send(MidiType inType,
|
||||
MidiInterface& send(MidiType inType,
|
||||
DataByte inData1,
|
||||
DataByte inData2,
|
||||
Channel inChannel);
|
||||
|
|
@ -113,112 +172,136 @@ public:
|
|||
|
||||
public:
|
||||
inline Channel getInputChannel() const;
|
||||
inline void setInputChannel(Channel inChannel);
|
||||
inline MidiInterface& setInputChannel(Channel inChannel);
|
||||
|
||||
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
|
||||
|
||||
public:
|
||||
inline void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity));
|
||||
inline void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity));
|
||||
inline void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure));
|
||||
inline void setHandleControlChange(void (*fptr)(byte channel, byte number, byte value));
|
||||
inline void setHandleProgramChange(void (*fptr)(byte channel, byte number));
|
||||
inline void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure));
|
||||
inline void setHandlePitchBend(void (*fptr)(byte channel, int bend));
|
||||
inline void setHandleSystemExclusive(void (*fptr)(byte * array, unsigned size));
|
||||
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
|
||||
inline void setHandleSongPosition(void (*fptr)(unsigned beats));
|
||||
inline void setHandleSongSelect(void (*fptr)(byte songnumber));
|
||||
inline void setHandleTuneRequest(void (*fptr)(void));
|
||||
inline void setHandleClock(void (*fptr)(void));
|
||||
inline void setHandleStart(void (*fptr)(void));
|
||||
inline void setHandleContinue(void (*fptr)(void));
|
||||
inline void setHandleStop(void (*fptr)(void));
|
||||
inline void setHandleActiveSensing(void (*fptr)(void));
|
||||
inline void setHandleSystemReset(void (*fptr)(void));
|
||||
inline MidiInterface& setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleControlChange(ControlChangeCallback fptr) { mControlChangeCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleProgramChange(ProgramChangeCallback fptr) { mProgramChangeCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleAfterTouchChannel(AfterTouchChannelCallback fptr) { mAfterTouchChannelCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandlePitchBend(PitchBendCallback fptr) { mPitchBendCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleSystemExclusive(SystemExclusiveCallback fptr) { mSystemExclusiveCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleTimeCodeQuarterFrame(TimeCodeQuarterFrameCallback fptr) { mTimeCodeQuarterFrameCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleSongPosition(SongPositionCallback fptr) { mSongPositionCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleSongSelect(SongSelectCallback fptr) { mSongSelectCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleTuneRequest(TuneRequestCallback fptr) { mTuneRequestCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleClock(ClockCallback fptr) { mClockCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleStart(StartCallback fptr) { mStartCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleTick(TickCallback fptr) { mTickCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleContinue(ContinueCallback fptr) { mContinueCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleStop(StopCallback fptr) { mStopCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleActiveSensing(ActiveSensingCallback fptr) { mActiveSensingCallback = fptr; return *this; };
|
||||
inline MidiInterface& setHandleSystemReset(SystemResetCallback fptr) { mSystemResetCallback = fptr; return *this; };
|
||||
|
||||
inline void disconnectCallbackFromType(MidiType inType);
|
||||
inline MidiInterface& disconnectCallbackFromType(MidiType inType);
|
||||
|
||||
private:
|
||||
void launchCallback();
|
||||
|
||||
void (*mNoteOffCallback)(byte channel, byte note, byte velocity);
|
||||
void (*mNoteOnCallback)(byte channel, byte note, byte velocity);
|
||||
void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity);
|
||||
void (*mControlChangeCallback)(byte channel, byte, byte);
|
||||
void (*mProgramChangeCallback)(byte channel, byte);
|
||||
void (*mAfterTouchChannelCallback)(byte channel, byte);
|
||||
void (*mPitchBendCallback)(byte channel, int);
|
||||
void (*mSystemExclusiveCallback)(byte * array, unsigned size);
|
||||
void (*mTimeCodeQuarterFrameCallback)(byte data);
|
||||
void (*mSongPositionCallback)(unsigned beats);
|
||||
void (*mSongSelectCallback)(byte songnumber);
|
||||
void (*mTuneRequestCallback)(void);
|
||||
void (*mClockCallback)(void);
|
||||
void (*mStartCallback)(void);
|
||||
void (*mContinueCallback)(void);
|
||||
void (*mStopCallback)(void);
|
||||
void (*mActiveSensingCallback)(void);
|
||||
void (*mSystemResetCallback)(void);
|
||||
void (*mMessageCallback)(const MidiMessage& message) = nullptr;
|
||||
ErrorCallback mErrorCallback = nullptr;
|
||||
NoteOffCallback mNoteOffCallback = nullptr;
|
||||
NoteOnCallback mNoteOnCallback = nullptr;
|
||||
AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr;
|
||||
ControlChangeCallback mControlChangeCallback = nullptr;
|
||||
ProgramChangeCallback mProgramChangeCallback = nullptr;
|
||||
AfterTouchChannelCallback mAfterTouchChannelCallback = nullptr;
|
||||
PitchBendCallback mPitchBendCallback = nullptr;
|
||||
SystemExclusiveCallback mSystemExclusiveCallback = nullptr;
|
||||
TimeCodeQuarterFrameCallback mTimeCodeQuarterFrameCallback = nullptr;
|
||||
SongPositionCallback mSongPositionCallback = nullptr;
|
||||
SongSelectCallback mSongSelectCallback = nullptr;
|
||||
TuneRequestCallback mTuneRequestCallback = nullptr;
|
||||
ClockCallback mClockCallback = nullptr;
|
||||
StartCallback mStartCallback = nullptr;
|
||||
TickCallback mTickCallback = nullptr;
|
||||
ContinueCallback mContinueCallback = nullptr;
|
||||
StopCallback mStopCallback = nullptr;
|
||||
ActiveSensingCallback mActiveSensingCallback = nullptr;
|
||||
SystemResetCallback mSystemResetCallback = nullptr;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 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 turnThruOff();
|
||||
inline void setThruFilterMode(MidiFilterMode inThruFilterMode);
|
||||
inline MidiInterface& turnThruOn(Thru::Mode inThruFilterMode = Thru::Full);
|
||||
inline MidiInterface& turnThruOff();
|
||||
inline MidiInterface& setThruFilterMode(Thru::Mode inThruFilterMode);
|
||||
|
||||
private:
|
||||
void thruFilter(byte inChannel);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// MIDI Parsing
|
||||
|
||||
private:
|
||||
bool parse();
|
||||
inline void handleNullVelocityNoteOnAsNoteOff();
|
||||
inline bool inputFilter(Channel inChannel);
|
||||
inline void resetInput();
|
||||
inline void updateLastSentTime();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Transport
|
||||
|
||||
public:
|
||||
Transport* getTransport() { return &mTransport; };
|
||||
|
||||
private:
|
||||
Transport& mTransport;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Internal variables
|
||||
|
||||
private:
|
||||
Channel mInputChannel;
|
||||
StatusByte mRunningStatus_RX;
|
||||
StatusByte mRunningStatus_TX;
|
||||
byte mPendingMessage[3];
|
||||
unsigned mPendingMessageExpectedLength;
|
||||
unsigned mPendingMessageIndex;
|
||||
unsigned mCurrentRpnNumber;
|
||||
unsigned mCurrentNrpnNumber;
|
||||
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;
|
||||
Thru::Mode mThruFilterMode : 7;
|
||||
MidiMessage mMessage;
|
||||
unsigned long mLastMessageSentTime;
|
||||
unsigned long mLastMessageReceivedTime;
|
||||
unsigned long mSenderActiveSensingPeriodicity;
|
||||
bool mReceiverActiveSensingActivated;
|
||||
int8_t mLastError;
|
||||
|
||||
private:
|
||||
inline StatusByte getStatus(MidiType inType,
|
||||
Channel inChannel) const;
|
||||
|
||||
private:
|
||||
SerialPort& mSerial;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
unsigned encodeSysEx(const byte* inData, byte* outSysEx, unsigned inLenght);
|
||||
unsigned decodeSysEx(const byte* inSysEx, byte* outData, unsigned inLenght);
|
||||
unsigned encodeSysEx(const byte* inData,
|
||||
byte* outSysEx,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits = false);
|
||||
unsigned decodeSysEx(const byte* inSysEx,
|
||||
byte* outData,
|
||||
unsigned inLength,
|
||||
bool inFlipHeaderBits = false);
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "MIDI.hpp"
|
||||
|
|
|
|||
1111
src/MIDI.hpp
1111
src/MIDI.hpp
File diff suppressed because it is too large
Load Diff
170
src/midi_Defs.h
170
src/midi_Defs.h
|
|
@ -2,23 +2,27 @@
|
|||
* @file midi_Defs.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Definitions
|
||||
* @version 4.2
|
||||
* @author Francois Best
|
||||
* @author Francois Best, lathoub
|
||||
* @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
|
||||
|
|
@ -42,6 +46,10 @@ BEGIN_MIDI_NAMESPACE
|
|||
#define MIDI_PITCHBEND_MIN -8192
|
||||
#define MIDI_PITCHBEND_MAX 8191
|
||||
|
||||
/*! Receiving Active Sensing
|
||||
*/
|
||||
static const uint16_t ActiveSensingTimeout = 300;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
|
||||
|
|
@ -50,28 +58,65 @@ typedef byte DataByte;
|
|||
typedef byte Channel;
|
||||
typedef byte FilterMode;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Errors
|
||||
static const uint8_t ErrorParse = 0;
|
||||
static const uint8_t ErrorActiveSensingTimeout = 1;
|
||||
static const uint8_t WarningSplitSysEx = 2;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Aliasing
|
||||
|
||||
using ErrorCallback = void (*)(int8_t);
|
||||
using NoteOffCallback = 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 ControlChangeCallback = void (*)(Channel channel, byte, byte);
|
||||
using ProgramChangeCallback = void (*)(Channel channel, byte);
|
||||
using AfterTouchChannelCallback = void (*)(Channel channel, byte);
|
||||
using PitchBendCallback = void (*)(Channel channel, int);
|
||||
using SystemExclusiveCallback = void (*)(byte * array, unsigned size);
|
||||
using TimeCodeQuarterFrameCallback = void (*)(byte data);
|
||||
using SongPositionCallback = void (*)(unsigned beats);
|
||||
using SongSelectCallback = void (*)(byte songnumber);
|
||||
using TuneRequestCallback = void (*)(void);
|
||||
using ClockCallback = void (*)(void);
|
||||
using StartCallback = void (*)(void);
|
||||
using TickCallback = void (*)(void);
|
||||
using ContinueCallback = void (*)(void);
|
||||
using StopCallback = void (*)(void);
|
||||
using ActiveSensingCallback = void (*)(void);
|
||||
using SystemResetCallback = void (*)(void);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! Enumeration of MIDI types */
|
||||
enum MidiType
|
||||
enum MidiType: uint8_t
|
||||
{
|
||||
InvalidType = 0x00, ///< For notifying errors
|
||||
NoteOff = 0x80, ///< Note Off
|
||||
NoteOn = 0x90, ///< Note On
|
||||
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
|
||||
ControlChange = 0xB0, ///< Control Change / Channel Mode
|
||||
ProgramChange = 0xC0, ///< Program Change
|
||||
AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch
|
||||
PitchBend = 0xE0, ///< Pitch Bend
|
||||
NoteOff = 0x80, ///< Channel Message - Note Off
|
||||
NoteOn = 0x90, ///< Channel Message - Note On
|
||||
AfterTouchPoly = 0xA0, ///< Channel Message - Polyphonic AfterTouch
|
||||
ControlChange = 0xB0, ///< Channel Message - Control Change / Channel Mode
|
||||
ProgramChange = 0xC0, ///< Channel Message - Program Change
|
||||
AfterTouchChannel = 0xD0, ///< Channel Message - Channel (monophonic) AfterTouch
|
||||
PitchBend = 0xE0, ///< Channel Message - Pitch Bend
|
||||
SystemExclusive = 0xF0, ///< System Exclusive
|
||||
SystemExclusiveStart = SystemExclusive, ///< System Exclusive Start
|
||||
TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
|
||||
SongPosition = 0xF2, ///< System Common - Song Position Pointer
|
||||
SongSelect = 0xF3, ///< System Common - Song Select
|
||||
Undefined_F4 = 0xF4,
|
||||
Undefined_F5 = 0xF5,
|
||||
TuneRequest = 0xF6, ///< System Common - Tune Request
|
||||
SystemExclusiveEnd = 0xF7, ///< System Exclusive End
|
||||
Clock = 0xF8, ///< System Real Time - Timing Clock
|
||||
Undefined_F9 = 0xF9,
|
||||
Tick = Undefined_F9, ///< System Real Time - Timing Tick (1 tick = 10 milliseconds)
|
||||
Start = 0xFA, ///< System Real Time - Start
|
||||
Continue = 0xFB, ///< System Real Time - Continue
|
||||
Stop = 0xFC, ///< System Real Time - Stop
|
||||
Undefined_FD = 0xFD,
|
||||
ActiveSensing = 0xFE, ///< System Real Time - Active Sensing
|
||||
SystemReset = 0xFF, ///< System Real Time - System Reset
|
||||
};
|
||||
|
|
@ -79,12 +124,15 @@ 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.
|
||||
};
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -93,7 +141,7 @@ enum MidiFilterMode
|
|||
See the detailed controllers numbers & description here:
|
||||
http://www.somascape.org/midi/tech/spec.html#ctrlnums
|
||||
*/
|
||||
enum MidiControlChangeNumber
|
||||
enum MidiControlChangeNumber: uint8_t
|
||||
{
|
||||
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
|
||||
BankSelect = 0,
|
||||
|
|
@ -102,7 +150,7 @@ enum MidiControlChangeNumber
|
|||
// CC3 undefined
|
||||
FootController = 4,
|
||||
PortamentoTime = 5,
|
||||
DataEntry = 6,
|
||||
DataEntryMSB = 6,
|
||||
ChannelVolume = 7,
|
||||
Balance = 8,
|
||||
// CC9 undefined
|
||||
|
|
@ -116,6 +164,22 @@ enum MidiControlChangeNumber
|
|||
GeneralPurposeController2 = 17,
|
||||
GeneralPurposeController3 = 18,
|
||||
GeneralPurposeController4 = 19,
|
||||
// CC20 to CC31 undefined
|
||||
BankSelectLSB = 32,
|
||||
ModulationWheelLSB = 33,
|
||||
BreathControllerLSB = 34,
|
||||
// CC35 undefined
|
||||
FootControllerLSB = 36,
|
||||
PortamentoTimeLSB = 37,
|
||||
DataEntryLSB = 38,
|
||||
ChannelVolumeLSB = 39,
|
||||
BalanceLSB = 40,
|
||||
// CC41 undefined
|
||||
PanLSB = 42,
|
||||
ExpressionControllerLSB = 43,
|
||||
EffectControl1LSB = 44,
|
||||
EffectControl2LSB = 45,
|
||||
// CC46 to CC63 undefined
|
||||
|
||||
// Switches ----------------------------------------------------------------
|
||||
Sustain = 64,
|
||||
|
|
@ -147,6 +211,13 @@ 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)
|
||||
// CC102 to CC119 undefined
|
||||
|
||||
// Channel Mode messages ---------------------------------------------------
|
||||
AllSoundOff = 120,
|
||||
|
|
@ -159,35 +230,18 @@ enum MidiControlChangeNumber
|
|||
PolyModeOn = 127
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port.
|
||||
You can use HardwareSerial or SoftwareSerial for the serial port.
|
||||
Example: MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, midi2);
|
||||
Then call midi2.begin(), midi2.read() etc..
|
||||
*/
|
||||
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
|
||||
midi::MidiInterface<Type> Name((Type&)SerialPort);
|
||||
|
||||
#if defined(ARDUINO_SAM_DUE) || defined(USBCON)
|
||||
// Leonardo, Due and other USB boards use Serial1 by default.
|
||||
#define MIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
|
||||
#else
|
||||
/*! \brief Create an instance of the library with default name, serial port
|
||||
and settings, for compatibility with sketches written with pre-v4.2 MIDI Lib,
|
||||
or if you don't bother using custom names, serial port or settings.
|
||||
*/
|
||||
#define MIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
|
||||
#endif
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port with
|
||||
custom settings.
|
||||
@see DefaultSettings
|
||||
@see MIDI_CREATE_INSTANCE
|
||||
*/
|
||||
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
|
||||
midi::MidiInterface<Type, Settings> Name((Type&)SerialPort);
|
||||
struct RPN
|
||||
{
|
||||
enum RegisteredParameterNumbers: uint16_t
|
||||
{
|
||||
PitchBendSensitivity = 0x0000,
|
||||
ChannelFineTuning = 0x0001,
|
||||
ChannelCoarseTuning = 0x0002,
|
||||
SelectTuningProgram = 0x0003,
|
||||
SelectTuningBank = 0x0004,
|
||||
ModulationDepthRange = 0x0005,
|
||||
NullFunction = (0x7f << 7) + 0x7f,
|
||||
};
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -2,29 +2,36 @@
|
|||
* @file midi_Message.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Message struct definition
|
||||
* @version 4.2
|
||||
* @author Francois Best
|
||||
* @date 11/06/14
|
||||
* @license GPL v3.0 - Copyright Forty Seven Effects 2014
|
||||
* @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
|
||||
|
||||
#include "midi_Namespace.h"
|
||||
#include "midi_Defs.h"
|
||||
#ifndef ARDUINO
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
|
|
@ -34,6 +41,33 @@ BEGIN_MIDI_NAMESPACE
|
|||
template<unsigned SysExMaxSize>
|
||||
struct Message
|
||||
{
|
||||
/*! Default constructor
|
||||
\n Initializes the attributes with their default values.
|
||||
*/
|
||||
inline Message()
|
||||
: channel(0)
|
||||
, type(MIDI_NAMESPACE::InvalidType)
|
||||
, data1(0)
|
||||
, data2(0)
|
||||
, valid(false)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
static const unsigned sSysExMaxSize = SysExMaxSize;
|
||||
|
|
@ -71,11 +105,27 @@ struct Message
|
|||
*/
|
||||
bool valid;
|
||||
|
||||
/*! Total Length of the message.
|
||||
*/
|
||||
unsigned length;
|
||||
|
||||
inline unsigned getSysExSize() const
|
||||
{
|
||||
const unsigned size = unsigned(data2) << 8 | data1;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,23 +2,27 @@
|
|||
* @file midi_Namespace.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Namespace declaration
|
||||
* @version 4.2
|
||||
* @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,51 @@
|
|||
/*!
|
||||
* @file midi_Platform.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Platform
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
* @author lathoub
|
||||
* @date 22/03/20
|
||||
*
|
||||
* 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
|
||||
|
||||
#if ARDUINO
|
||||
|
||||
// DefaultPlatform is the Arduino Platform
|
||||
struct DefaultPlatform
|
||||
{
|
||||
static unsigned long now() { return ::millis(); };
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct DefaultPlatform
|
||||
{
|
||||
static unsigned long now() { return 0; };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
@ -2,23 +2,27 @@
|
|||
* @file midi_Settings.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Settings
|
||||
* @version 4.2
|
||||
* @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,43 +38,67 @@ BEGIN_MIDI_NAMESPACE
|
|||
macro to create your instance. The settings you don't override will keep their
|
||||
default value. Eg:
|
||||
\code{.cpp}
|
||||
struct MySettings : public midi::DefaultSettings
|
||||
struct MySettings : public MIDI_NAMESPACE::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);
|
||||
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial2, MIDI, MySettings);
|
||||
\endcode
|
||||
*/
|
||||
struct DefaultSettings
|
||||
{
|
||||
/*! Running status enables short messages when sending multiple values
|
||||
of the same type and channel.\n
|
||||
Set to 0 if you have troubles controlling your hardware.
|
||||
*/
|
||||
static const bool UseRunningStatus = true;
|
||||
of the same type and channel.\n
|
||||
Must be disabled to send USB MIDI messages to a computer
|
||||
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.
|
||||
static const bool Use1ByteParsing = true;
|
||||
|
||||
/*! Override the default MIDI baudrate to transmit over USB serial, to
|
||||
a decoding program such as Hairless MIDI (set baudrate to 115200)\n
|
||||
http://projectgus.github.io/hairless-midiserial/
|
||||
/*! 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 long BaudRate = 31250;
|
||||
static const bool Use1ByteParsing = true;
|
||||
|
||||
/*! Maximum size of SysEx receivable. Decrease to save RAM if you don't expect
|
||||
to receive SysEx, or adjust accordingly.
|
||||
*/
|
||||
static const unsigned SysExMaxSize = 128;
|
||||
|
||||
/*! Global switch to turn on/off sender ActiveSensing
|
||||
Set to true to send ActiveSensing
|
||||
Set to false will not send ActiveSensing message (will also save memory)
|
||||
*/
|
||||
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
|
||||
repeatedly by the sender to tell the receiver that a connection is alive. Use
|
||||
of this message is optional. When initially received, the
|
||||
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.
|
||||
|
||||
Typical value is 250 (ms) - an Active Sensing command is send 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 = 0;
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
/*!
|
||||
* @file serialMIDI.h
|
||||
* Project Arduino MIDI Library
|
||||
* @brief MIDI Library for the Arduino - Platform
|
||||
* @license MIT - Copyright (c) 2015 Francois Best
|
||||
* @author lathoub, Francois Best
|
||||
* @date 22/03/20
|
||||
*
|
||||
* 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
|
||||
|
||||
struct DefaultSerialSettings
|
||||
{
|
||||
/*! Override the default MIDI baudrate to transmit over USB serial, to
|
||||
a decoding program such as Hairless MIDI (set baudrate to 115200)\n
|
||||
http://projectgus.github.io/hairless-midiserial/
|
||||
*/
|
||||
static const long BaudRate = 31250;
|
||||
};
|
||||
|
||||
template <class SerialPort, class _Settings = DefaultSerialSettings>
|
||||
class SerialMIDI
|
||||
{
|
||||
typedef _Settings Settings;
|
||||
|
||||
public:
|
||||
SerialMIDI(SerialPort& inSerial)
|
||||
: mSerial(inSerial)
|
||||
{
|
||||
};
|
||||
|
||||
public:
|
||||
static const bool thruActivated = true;
|
||||
|
||||
void begin()
|
||||
{
|
||||
// Initialise the Serial port
|
||||
#if defined(AVR_CAKE)
|
||||
mSerial. template open<Settings::BaudRate>();
|
||||
#else
|
||||
mSerial.begin(Settings::BaudRate);
|
||||
#endif
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
mSerial.end();
|
||||
}
|
||||
|
||||
bool beginTransmission(MidiType)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
void write(byte value)
|
||||
{
|
||||
mSerial.write(value);
|
||||
};
|
||||
|
||||
void endTransmission()
|
||||
{
|
||||
};
|
||||
|
||||
byte read()
|
||||
{
|
||||
return mSerial.read();
|
||||
};
|
||||
|
||||
unsigned available()
|
||||
{
|
||||
return mSerial.available();
|
||||
};
|
||||
|
||||
private:
|
||||
SerialPort& mSerial;
|
||||
};
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port.
|
||||
You can use HardwareSerial or SoftwareSerial for the serial port.
|
||||
Example: MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, midi2);
|
||||
Then call midi2.begin(), midi2.read() etc..
|
||||
*/
|
||||
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
|
||||
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
||||
|
||||
#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
|
||||
// Leonardo, Due and other USB boards use Serial1 by default.
|
||||
#define MIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
|
||||
#else
|
||||
/*! \brief Create an instance of the library with default name, serial port
|
||||
and settings, for compatibility with sketches written with pre-v4.2 MIDI Lib,
|
||||
or if you don't bother using custom names, serial port or settings.
|
||||
*/
|
||||
#define MIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
|
||||
#endif
|
||||
|
||||
/*! \brief Create an instance of the library attached to a serial port with
|
||||
custom settings.
|
||||
@see DefaultSettings
|
||||
@see MIDI_CREATE_INSTANCE
|
||||
*/
|
||||
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
|
||||
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>, Settings> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
add_subdirectory(mocks)
|
||||
add_subdirectory(unit-tests)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
project(test-mocks)
|
||||
|
||||
add_library(test-mocks STATIC
|
||||
test-mocks.cpp
|
||||
test-mocks.h
|
||||
test-mocks_Namespace.h
|
||||
test-mocks_SerialMock.cpp
|
||||
test-mocks_SerialMock.hpp
|
||||
test-mocks_SerialMock.h
|
||||
)
|
||||
|
||||
target_link_libraries(test-mocks
|
||||
midi
|
||||
)
|
||||
|
|
@ -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,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,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "test-mocks.h"
|
||||
#include <inttypes.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_t inData);
|
||||
uint8_t read();
|
||||
|
||||
public: // Test Helpers API
|
||||
void moveTxToRx(); // Simulate loopback
|
||||
|
||||
public:
|
||||
typedef RingBuffer<uint8_t, 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_t inData)
|
||||
{
|
||||
mTxBuffer.write(inData);
|
||||
}
|
||||
|
||||
template<int BufferSize>
|
||||
uint8_t 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,37 @@
|
|||
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_MidiInput.cpp
|
||||
tests/unit-tests_MidiInputCallbacks.cpp
|
||||
tests/unit-tests_MidiOutput.cpp
|
||||
tests/unit-tests_MidiThru.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,991 @@
|
|||
#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::SerialMIDI<SerialMock> Transport;
|
||||
typedef midi::MidiInterface<Transport> 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(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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
// 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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
EXPECT_EQ(midi.getInputChannel(), 0);
|
||||
midi.setInputChannel(12);
|
||||
EXPECT_EQ(midi.getInputChannel(), 12);
|
||||
}
|
||||
|
||||
TEST(MidiInput, initMessage)
|
||||
{
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
midi.begin();
|
||||
EXPECT_EQ(midi.read(), false);
|
||||
}
|
||||
|
||||
TEST(MidiInput, inputDisabled)
|
||||
{
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> MultiByteMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MultiByteMidiInterface midi(transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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::SerialMIDI<LargerSerialMock> LargerTransport;
|
||||
typedef midi::MidiInterface<LargerTransport, Settings> LargerMidiInterface;
|
||||
|
||||
LargerSerialMock serial;
|
||||
LargerTransport transport(serial);
|
||||
LargerMidiInterface midi(transport);
|
||||
|
||||
// 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<Transport, Settings> SmallMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
SmallMidiInterface midi(transport);
|
||||
|
||||
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);
|
||||
|
||||
EXPECT_EQ(midi.read(), false); // start sysex f0
|
||||
EXPECT_EQ(midi.read(), false); // H
|
||||
EXPECT_EQ(midi.read(), false); // e
|
||||
EXPECT_EQ(midi.read(), false); // l
|
||||
EXPECT_EQ(midi.read(), false); // l
|
||||
EXPECT_EQ(midi.read(), false); // o
|
||||
EXPECT_EQ(midi.read(), false); // , message send and buffer cleared.
|
||||
EXPECT_EQ(midi.read(), false); // start sysex
|
||||
EXPECT_EQ(midi.read(), false); // (space)
|
||||
EXPECT_EQ(midi.read(), false); // W
|
||||
EXPECT_EQ(midi.read(), false); // o
|
||||
EXPECT_EQ(midi.read(), false); // r
|
||||
EXPECT_EQ(midi.read(), false); // l
|
||||
EXPECT_EQ(midi.read(), false); // d
|
||||
EXPECT_EQ(midi.read(), true); // end sysex
|
||||
}
|
||||
|
||||
TEST(MidiInput, mtcQuarterFrame)
|
||||
{
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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(), true);
|
||||
EXPECT_EQ(midi.getType(), midi::Tick);
|
||||
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::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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
// 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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
static const unsigned rxSize = 13;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xbb, 12, 0xfd, 34,
|
||||
12, 0,
|
||||
42, 0xfd, 127,
|
||||
0xfd,
|
||||
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<Transport, Settings> MultiByteMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MultiByteMidiInterface midi(transport);
|
||||
|
||||
static const unsigned rxSize = 4;
|
||||
static const byte rxData[rxSize] = {
|
||||
0xbb, 12, 0xfd, 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,594 @@
|
|||
#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 midi::SerialMIDI<SerialMock> Transport;
|
||||
|
||||
typedef VariableSysExSettings<256> Settings;
|
||||
typedef midi::MidiInterface<Transport, Settings> MidiInterface;
|
||||
|
||||
MidiInterface* midi;
|
||||
|
||||
class MidiInputCallbacks : public Test
|
||||
{
|
||||
public:
|
||||
MidiInputCallbacks()
|
||||
: mTransport(mSerial)
|
||||
, mMidi(mTransport)
|
||||
{
|
||||
}
|
||||
virtual ~MidiInputCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
midi = &mMidi;
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
midi = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
SerialMock mSerial;
|
||||
Transport mTransport;
|
||||
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,884 @@
|
|||
#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::SerialMIDI<SerialMock> Transport;
|
||||
typedef midi::MidiInterface<Transport> MidiInterface;
|
||||
|
||||
typedef std::vector<uint8_t> Buffer;
|
||||
|
||||
// --
|
||||
|
||||
TEST(MidiOutput, sendInvalid)
|
||||
{
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
RsMidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> NoRsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
NoRsMidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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::SerialMIDI<LargeSerialMock> LargeTransport;
|
||||
typedef midi::MidiInterface<LargeTransport> LargeMidiInterface;
|
||||
|
||||
LargeSerialMock serial;
|
||||
LargeTransport transport(serial);
|
||||
LargeMidiInterface midi((LargeTransport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
RsMidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
RsMidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
RsMidiInterface midi((Transport&)transport);
|
||||
|
||||
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,389 @@
|
|||
#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::SerialMIDI<SerialMock> Transport;
|
||||
typedef midi::MidiInterface<Transport> MidiInterface;
|
||||
typedef std::vector<byte> Buffer;
|
||||
|
||||
template<unsigned Size>
|
||||
struct VariableSysExSettings : midi::DefaultSettings
|
||||
{
|
||||
static const unsigned SysExMaxSize = Size;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
TEST(MidiThru, defaultValues)
|
||||
{
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, MultiByteParsing> MultiByteMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
MultiByteMidiInterface midi((Transport&)transport);
|
||||
|
||||
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<Transport, Settings> RsMidiInterface;
|
||||
|
||||
SerialMock serial;
|
||||
Transport transport(serial);
|
||||
RsMidiInterface midi((Transport&)transport);
|
||||
|
||||
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;
|
||||
Transport transport(serial);
|
||||
MidiInterface midi((Transport&)transport);
|
||||
|
||||
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,24 @@
|
|||
#include "unit-tests_Settings.h"
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
const bool DefaultSettings::UseRunningStatus;
|
||||
const bool DefaultSettings::HandleNullVelocityNoteOnAsNoteOff;
|
||||
const bool DefaultSettings::Use1ByteParsing;
|
||||
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::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,181 @@
|
|||
#include "unit-tests.h"
|
||||
#include <src/MIDI.h>
|
||||
|
||||
BEGIN_MIDI_NAMESPACE
|
||||
|
||||
END_MIDI_NAMESPACE
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_UNNAMED_NAMESPACE
|
||||
|
||||
using namespace testing;
|
||||
|
||||
TEST(SysExCodec, EncoderAscii)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(SysExCodec, EncoderNonAscii)
|
||||
{
|
||||
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, EncoderNonAsciiFlipHeader)
|
||||
{
|
||||
const byte input[] = {
|
||||
182, 236, 167, 177, 61, 91, 120, // 00011111 -> 15
|
||||
107, 94, 209, 87, 94 // 0xx00100 -> 4
|
||||
};
|
||||
byte buffer[16];
|
||||
memset(buffer, 0, 16 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::encodeSysEx(input, buffer, 12, true);
|
||||
EXPECT_EQ(encodedSize, unsigned(14));
|
||||
const byte expected[16] = {
|
||||
// MSB Data
|
||||
15, 54, 108, 39, 49, 61, 91, 120,
|
||||
4, 107, 94, 81, 87, 94, 0, 0,
|
||||
};
|
||||
EXPECT_THAT(buffer, Each(Le(0x7f))); // All elements are <= 127
|
||||
EXPECT_THAT(buffer, ContainerEq(expected));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
TEST(SysExCodec, DecoderAscii)
|
||||
{
|
||||
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
|
||||
TEST(SysExCodec, DecoderNonAscii)
|
||||
{
|
||||
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, DecoderNonAsciiFlipHeader)
|
||||
{
|
||||
const byte input[] = {
|
||||
// MSB Data
|
||||
15, 54, 108, 39, 49, 61, 91, 120,
|
||||
4, 107, 94, 81, 87, 94,
|
||||
};
|
||||
byte buffer[16];
|
||||
memset(buffer, 0, 16 * sizeof(byte));
|
||||
const unsigned encodedSize = midi::decodeSysEx(input, buffer, 14, true);
|
||||
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, CodecAscii)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(SysExCodec, CodecNonAscii)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(SysExCodec, CodecNonAsciiFlipHeader)
|
||||
{
|
||||
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, true);
|
||||
EXPECT_EQ(encodedSize, unsigned(14));
|
||||
const unsigned decodedSize = midi::decodeSysEx(buffer1, buffer2, encodedSize, true);
|
||||
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