Compare commits
	
		
			No commits in common. "master" and "init_gitflow" have entirely different histories.
		
	
	
		
			master
			...
			init_gitfl
		
	
		|  | @ -1,9 +0,0 @@ | ||||||
| root = true |  | ||||||
| 
 |  | ||||||
| [*] |  | ||||||
| indent_style = space |  | ||||||
| indent_size = 4 |  | ||||||
| end_of_line = lf |  | ||||||
| charset = utf-8 |  | ||||||
| trim_trailing_whitespace = true |  | ||||||
| insert_final_newline = true |  | ||||||
|  | @ -1,66 +0,0 @@ | ||||||
| --- |  | ||||||
| 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. |  | ||||||
| --> |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| 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. |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| --- |  | ||||||
| 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. |  | ||||||
|  | @ -1,59 +0,0 @@ | ||||||
| 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 }} |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| 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 }} |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| *.sublime-workspace |  | ||||||
| *.pyc |  | ||||||
| logs/ |  | ||||||
| build/ |  | ||||||
| .vscode/.cmaketools.json |  | ||||||
| src/.DS_Store |  | ||||||
| examples/.DS_Store |  | ||||||
| .DS_Store |  | ||||||
| test/xcode |  | ||||||
| .development |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| [submodule "external/google-test"] |  | ||||||
| 	path = external/google-test |  | ||||||
| 	url = https://github.com/google/googletest.git |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| { |  | ||||||
|     "board": "arduino:avr:leonardo" |  | ||||||
| } |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| { |  | ||||||
|     "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 |  | ||||||
| } |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| { |  | ||||||
|     "files.associations": { |  | ||||||
|         "cstddef": "cpp", |  | ||||||
|         "ostream": "cpp", |  | ||||||
|         "__locale": "cpp", |  | ||||||
|         "functional": "cpp", |  | ||||||
|         "iterator": "cpp", |  | ||||||
|         "string": "cpp", |  | ||||||
|         "string_view": "cpp", |  | ||||||
|         "vector": "cpp", |  | ||||||
|         "istream": "cpp", |  | ||||||
|         "system_error": "cpp" |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| { |  | ||||||
|     // 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"] |  | ||||||
|         } |  | ||||||
|     ] |  | ||||||
| } |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| 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) |  | ||||||
|  | @ -1,32 +0,0 @@ | ||||||
| # 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 |  | ||||||
| ``` |  | ||||||
							
								
								
									
										21
									
								
								LICENSE
								
								
								
								
							
							
						
						
									
										21
									
								
								LICENSE
								
								
								
								
							|  | @ -1,21 +0,0 @@ | ||||||
| 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. |  | ||||||
							
								
								
									
										142
									
								
								README.md
								
								
								
								
							
							
						
						
									
										142
									
								
								README.md
								
								
								
								
							|  | @ -1,142 +0,0 @@ | ||||||
| # Arduino MIDI Library |  | ||||||
| 
 |  | ||||||
| [](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) |  | ||||||
| 
 |  | ||||||
| This library adds MIDI I/O communications to an Arduino board. |  | ||||||
| 
 |  | ||||||
| ### Features |  | ||||||
| 
 |  | ||||||
| -   **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. |  | ||||||
| 
 |  | ||||||
| ### Getting Started |  | ||||||
| 
 |  | ||||||
| 1. Use the Arduino Library Manager to install the library. |  | ||||||
|     |  | ||||||
| 
 |  | ||||||
| 2. Start coding: |  | ||||||
| 
 |  | ||||||
| ```c++ |  | ||||||
| #include <MIDI.h> |  | ||||||
| 
 |  | ||||||
| // Create and bind the MIDI interface to the default hardware Serial port |  | ||||||
| MIDI_CREATE_DEFAULT_INSTANCE(); |  | ||||||
| 
 |  | ||||||
| void setup() |  | ||||||
| { |  | ||||||
|     MIDI.begin(MIDI_CHANNEL_OMNI);  // Listen to all incoming messages |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loop() |  | ||||||
| { |  | ||||||
|     // Send note 42 with velocity 127 on channel 1 |  | ||||||
|     MIDI.sendNoteOn(42, 127, 1); |  | ||||||
| 
 |  | ||||||
|     // 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). |  | ||||||
| 
 |  | ||||||
| ## Documentation |  | ||||||
| 
 |  | ||||||
| -   [Doxygen Extended Documentation](https://fortyseveneffects.github.io/arduino_midi_library/). |  | ||||||
| -   [GitHub wiki](https://github.com/FortySevenEffects/arduino_midi_library/wiki). |  | ||||||
| 
 |  | ||||||
| ## USB Migration (4.x to 5.x) |  | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
| 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> |  | ||||||
| 
 |  | ||||||
| static const unsigned sUsbTransportBufferSize = 16; |  | ||||||
| typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport; |  | ||||||
| 
 |  | ||||||
| UsbTransport sUsbTransport; |  | ||||||
| 
 |  | ||||||
| MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, MIDI); |  | ||||||
| 
 |  | ||||||
| // ... |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| now becomes in `5.x`: |  | ||||||
| 
 |  | ||||||
| ```c++ |  | ||||||
| #include <USB-MIDI.h> |  | ||||||
| USBMIDI_CREATE_DEFAULT_INSTANCE(); |  | ||||||
| 
 |  | ||||||
| // ... |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
| [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,4 @@ | ||||||
|  | There is nothing in this branch. | ||||||
|  | 
 | ||||||
|  | Please checkout the branch dedicated to your target board to get the code. | ||||||
|  | 
 | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| #### 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,33 +0,0 @@ | ||||||
| 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() |  | ||||||
							
								
								
									
										2428
									
								
								doc/Doxyfile
								
								
								
								
							
							
						
						
									
										2428
									
								
								doc/Doxyfile
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,54 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  \mainpage Arduino MIDI Library |  | ||||||
| 
 |  | ||||||
|  See the documentation of the main class, MidiInterface, or browse the modules |  | ||||||
|  and examples using the sidebar on the left. |  | ||||||
|  \n |  | ||||||
|  The latest development version is available on GitHub: |  | ||||||
|  https://github.com/FortySevenEffects/arduino_midi_library/tree/dev
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| // Examples
 |  | ||||||
| 
 |  | ||||||
| /*!
 |  | ||||||
|  \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, |  | ||||||
|  then we send a Note On message, wait for a second, then send |  | ||||||
|  the Note Off and turn off the LED. |  | ||||||
|  \n |  | ||||||
|  \n |  | ||||||
|  <em> |  | ||||||
|  Note that instead of sending a Note Off, we could have sent a |  | ||||||
|  Note On with velocity 0 to shorten the message. This is called Running |  | ||||||
|  Status. |  | ||||||
|  </em> |  | ||||||
|  \n |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /*!
 |  | ||||||
|  \example Callbacks.ino |  | ||||||
|  This example shows how to use callbacks for easier MIDI input handling. \n |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /*!
 |  | ||||||
|  \example Bench.ino |  | ||||||
|  \example DualMerger.ino |  | ||||||
|  \example Input.ino |  | ||||||
|  \example SimpleSynth.ino |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| /*! \defgroup output   MIDI Output
 |  | ||||||
|  */ |  | ||||||
| /*! \defgroup input    MIDI Input
 |  | ||||||
|  */ |  | ||||||
| /*! \defgroup callbacks Callbacks
 |  | ||||||
|  \ingroup input |  | ||||||
|  */ |  | ||||||
| /*! \defgroup thru     MIDI Thru
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
|  | @ -1,87 +0,0 @@ | ||||||
| # 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). |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| #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); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,25 +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(); |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,85 +0,0 @@ | ||||||
| #include <MIDI.h> |  | ||||||
| 
 |  | ||||||
| // This program will measure the time needed to receive, parse and process a
 |  | ||||||
| // NoteOn message.
 |  | ||||||
| // For it to work, please connect RX and TX on the MIDI port:
 |  | ||||||
| // Due, Leonardo and other USB-native Arduinos:     Serial1
 |  | ||||||
| // All other Arduinos:                              Connect pins 2 and 3.
 |  | ||||||
| // The program will then wait for 100 loops and print the results.
 |  | ||||||
| 
 |  | ||||||
| #if defined(ARDUINO_SAM_DUE) || defined(USBCON) |  | ||||||
|     // Print through USB and bench with Hardware serial
 |  | ||||||
|     MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midiBench); |  | ||||||
| #else |  | ||||||
|     #include <SoftwareSerial.h> |  | ||||||
|     SoftwareSerial midiSerial(2,3); |  | ||||||
|     MIDI_CREATE_INSTANCE(SoftwareSerial, midiSerial, midiBench); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| unsigned long gTime_start = 0; |  | ||||||
| unsigned long gTime_stop  = 0; |  | ||||||
| unsigned gCounter = 0; |  | ||||||
| unsigned long gTime_sum   = 0; |  | ||||||
| unsigned long gTime_min   = -1; |  | ||||||
| unsigned long gTime_max   = 0; |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void handleNoteOn(byte inChannel, byte inNote, byte inVelocity) |  | ||||||
| { |  | ||||||
|     gTime_stop = micros(); |  | ||||||
| 
 |  | ||||||
|     const unsigned long diff = gTime_stop - gTime_start; |  | ||||||
|     gTime_sum += diff; |  | ||||||
| 
 |  | ||||||
|     if (diff > gTime_max) gTime_max = diff; |  | ||||||
|     if (diff < gTime_min) gTime_min = diff; |  | ||||||
| 
 |  | ||||||
|     if (gCounter++ >= 1000) |  | ||||||
|     { |  | ||||||
|         const unsigned long average = gTime_sum / (float)gCounter; |  | ||||||
| 
 |  | ||||||
|         Serial.println("Time to receive NoteOn: "); |  | ||||||
| 
 |  | ||||||
|         Serial.print("Average: "); |  | ||||||
|         Serial.print(average); |  | ||||||
|         Serial.println(" microsecs"); |  | ||||||
| 
 |  | ||||||
|         Serial.print("Min:     "); |  | ||||||
|         Serial.print(gTime_min); |  | ||||||
|         Serial.println(" microsecs"); |  | ||||||
| 
 |  | ||||||
|         Serial.print("Max:     "); |  | ||||||
|         Serial.print(gTime_max); |  | ||||||
|         Serial.println(" microsecs"); |  | ||||||
| 
 |  | ||||||
|         gCounter = 0; |  | ||||||
|         gTime_sum = 0; |  | ||||||
|         gTime_max = 0; |  | ||||||
|         gTime_min = -1; |  | ||||||
| 
 |  | ||||||
|         midiBench.turnThruOff(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void setup() |  | ||||||
| { |  | ||||||
|     midiBench.setHandleNoteOn(handleNoteOn); |  | ||||||
|     midiBench.begin(); |  | ||||||
| 
 |  | ||||||
|     Serial.begin(115200); |  | ||||||
|     while(!Serial); |  | ||||||
|     Serial.println("Arduino Ready"); |  | ||||||
| 
 |  | ||||||
|     midiBench.sendNoteOn(69,127,1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loop() |  | ||||||
| { |  | ||||||
|     gTime_start = micros(); |  | ||||||
|     midiBench.read(); |  | ||||||
| } |  | ||||||
|  | @ -1,51 +0,0 @@ | ||||||
| #include <MIDI.h> |  | ||||||
| 
 |  | ||||||
| MIDI_CREATE_DEFAULT_INSTANCE(); |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| // This function will be automatically called when a NoteOn is received.
 |  | ||||||
| // It must be a void-returning function with the correct parameters,
 |  | ||||||
| // see documentation here:
 |  | ||||||
| // https://github.com/FortySevenEffects/arduino_midi_library/wiki/Using-Callbacks
 |  | ||||||
| 
 |  | ||||||
| void handleNoteOn(byte channel, byte pitch, 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.
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void handleNoteOff(byte channel, byte pitch, byte velocity) |  | ||||||
| { |  | ||||||
|     // Do something when the note is released.
 |  | ||||||
|     // Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void setup() |  | ||||||
| { |  | ||||||
|     // Connect the handleNoteOn function to the library,
 |  | ||||||
|     // so it is called upon reception of a NoteOn.
 |  | ||||||
|     MIDI.setHandleNoteOn(handleNoteOn);  // Put only the name of the function
 |  | ||||||
| 
 |  | ||||||
|     // Do the same for NoteOffs
 |  | ||||||
|     MIDI.setHandleNoteOff(handleNoteOff); |  | ||||||
| 
 |  | ||||||
|     // Initiate MIDI communications, listen to all channels
 |  | ||||||
|     MIDI.begin(MIDI_CHANNEL_OMNI); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loop() |  | ||||||
| { |  | ||||||
|     // Call MIDI.read the fastest you can for real-time performance.
 |  | ||||||
|     MIDI.read(); |  | ||||||
| 
 |  | ||||||
|     // There is no need to check if there are messages incoming
 |  | ||||||
|     // if they are bound to a Callback function.
 |  | ||||||
|     // The attached method will be called automatically
 |  | ||||||
|     // when the corresponding message has been received.
 |  | ||||||
| } |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| #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); |  | ||||||
| } |  | ||||||
|  | @ -1,32 +0,0 @@ | ||||||
| #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); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,55 +0,0 @@ | ||||||
| #include <MIDI.h> |  | ||||||
| 
 |  | ||||||
| // This example shows how to create two instances of the library to create a merger.
 |  | ||||||
| // There are two MIDI couples of IO, A and B, each using thru and merging with the
 |  | ||||||
| // input from the other node. The result is the following:
 |  | ||||||
| // A out = A in + B in
 |  | ||||||
| // B out = B in + A in
 |  | ||||||
| 
 |  | ||||||
| #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); |  | ||||||
|     MIDI_CREATE_INSTANCE(HardwareSerial, Serial,     midiA); |  | ||||||
|     MIDI_CREATE_INSTANCE(SoftwareSerial, softSerial, midiB); |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| void setup() |  | ||||||
| { |  | ||||||
|     // Initiate MIDI communications, listen to all channels
 |  | ||||||
|     midiA.begin(MIDI_CHANNEL_OMNI); |  | ||||||
|     midiB.begin(MIDI_CHANNEL_OMNI); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loop() |  | ||||||
| { |  | ||||||
|     if (midiA.read()) |  | ||||||
|     { |  | ||||||
|         // Thru on A has already pushed the input message to out A.
 |  | ||||||
|         // Forward the message to out B as well.
 |  | ||||||
|         midiB.send(midiA.getType(), |  | ||||||
|                    midiA.getData1(), |  | ||||||
|                    midiA.getData2(), |  | ||||||
|                    midiA.getChannel()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (midiB.read()) |  | ||||||
|     { |  | ||||||
|         // Thru on B has already pushed the input message to out B.
 |  | ||||||
|         // Forward the message to out A as well.
 |  | ||||||
|         midiA.send(midiB.getType(), |  | ||||||
|                    midiB.getData1(), |  | ||||||
|                    midiB.getData2(), |  | ||||||
|                    midiB.getChannel()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| #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(); |  | ||||||
| } |  | ||||||
|  | @ -1,49 +0,0 @@ | ||||||
| #include <MIDI.h> |  | ||||||
| 
 |  | ||||||
| MIDI_CREATE_DEFAULT_INSTANCE(); |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| // This example shows the old way of checking for input messages.
 |  | ||||||
| // It's simpler to use the callbacks now, check out the dedicated example.
 |  | ||||||
| 
 |  | ||||||
| #define LED 13                   // LED pin on Arduino Uno
 |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void BlinkLed(byte num)         // Basic blink function
 |  | ||||||
| { |  | ||||||
|     for (byte i=0;i<num;i++) |  | ||||||
|     { |  | ||||||
|         digitalWrite(LED,HIGH); |  | ||||||
|         delay(50); |  | ||||||
|         digitalWrite(LED,LOW); |  | ||||||
|         delay(50); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void setup() |  | ||||||
| { |  | ||||||
|     pinMode(LED, OUTPUT); |  | ||||||
|     MIDI.begin();           // Launch MIDI, by default listening to channel 1.
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loop() |  | ||||||
| { |  | ||||||
|     if (MIDI.read())                // Is there a MIDI message incoming ?
 |  | ||||||
|     { |  | ||||||
|         switch(MIDI.getType())      // Get the type of the message we caught
 |  | ||||||
|         { |  | ||||||
|             case midi::ProgramChange:       // If it is a Program Change,
 |  | ||||||
|                 BlinkLed(MIDI.getData1());  // blink the LED a number of times
 |  | ||||||
|                                             // correponding to the program number
 |  | ||||||
|                                             // (0 to 127, it can last a while..)
 |  | ||||||
|                 break; |  | ||||||
|             // See the online reference for other message types
 |  | ||||||
|             default: |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,208 +0,0 @@ | ||||||
| #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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,166 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  *  \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; |  | ||||||
| }; |  | ||||||
|  | @ -1,97 +0,0 @@ | ||||||
| #include <MIDI.h> |  | ||||||
| #include "noteList.h" |  | ||||||
| #include "pitches.h" |  | ||||||
| 
 |  | ||||||
| MIDI_CREATE_DEFAULT_INSTANCE(); |  | ||||||
| 
 |  | ||||||
| #ifdef ARDUINO_SAM_DUE // Due has no tone function (yet), overriden to prevent build errors.
 |  | ||||||
| #define tone(...) |  | ||||||
| #define noTone(...) |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| // This example shows how to make a simple synth out of an Arduino, using the
 |  | ||||||
| // tone() function. It also outputs a gate signal for controlling external
 |  | ||||||
| // analog synth components (like envelopes).
 |  | ||||||
| 
 |  | ||||||
| static const unsigned sGatePin     = 13; |  | ||||||
| static const unsigned sAudioOutPin = 10; |  | ||||||
| static const unsigned sMaxNumNotes = 16; |  | ||||||
| MidiNoteList<sMaxNumNotes> midiNotes; |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| inline void handleGateChanged(bool inGateActive) |  | ||||||
| { |  | ||||||
|     digitalWrite(sGatePin, inGateActive ? HIGH : LOW); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline void pulseGate() |  | ||||||
| { |  | ||||||
|     handleGateChanged(false); |  | ||||||
|     delay(1); |  | ||||||
|     handleGateChanged(true); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void handleNotesChanged(bool isFirstNote = false) |  | ||||||
| { |  | ||||||
|     if (midiNotes.empty()) |  | ||||||
|     { |  | ||||||
|         handleGateChanged(false); |  | ||||||
|         noTone(sAudioOutPin); // Remove to keep oscillator running during envelope release.
 |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         // Possible playing modes:
 |  | ||||||
|         // Mono Low:  use midiNotes.getLow
 |  | ||||||
|         // Mono High: use midiNotes.getHigh
 |  | ||||||
|         // Mono Last: use midiNotes.getLast
 |  | ||||||
| 
 |  | ||||||
|         byte currentNote = 0; |  | ||||||
|         if (midiNotes.getLast(currentNote)) |  | ||||||
|         { |  | ||||||
|             tone(sAudioOutPin, sNotePitches[currentNote]); |  | ||||||
| 
 |  | ||||||
|             if (isFirstNote) |  | ||||||
|             { |  | ||||||
|                 handleGateChanged(true); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 pulseGate(); // Retrigger envelopes. Remove for legato effect.
 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void handleNoteOn(byte inChannel, byte inNote, byte inVelocity) |  | ||||||
| { |  | ||||||
|     const bool firstNote = midiNotes.empty(); |  | ||||||
|     midiNotes.add(MidiNote(inNote, inVelocity)); |  | ||||||
|     handleNotesChanged(firstNote); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void handleNoteOff(byte inChannel, byte inNote, byte inVelocity) |  | ||||||
| { |  | ||||||
|     midiNotes.remove(inNote); |  | ||||||
|     handleNotesChanged(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| void setup() |  | ||||||
| { |  | ||||||
|     pinMode(sGatePin,     OUTPUT); |  | ||||||
|     pinMode(sAudioOutPin, OUTPUT); |  | ||||||
|     MIDI.setHandleNoteOn(handleNoteOn); |  | ||||||
|     MIDI.setHandleNoteOff(handleNoteOff); |  | ||||||
|     MIDI.begin(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void loop() |  | ||||||
| { |  | ||||||
|     MIDI.read(); |  | ||||||
| } |  | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  *  \file       synth-core_NoteList.h |  | ||||||
|  *  \author     Francois Best |  | ||||||
|  *  \date       24/05/2013 |  | ||||||
|  *  \license    GPL v3.0 - Copyright Forty Seven Effects 2013 |  | ||||||
|  * |  | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  *  the Free Software Foundation, either version 3 of the License, or |  | ||||||
|  *  (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  *  This program is distributed in the hope that it will be useful, |  | ||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  *  GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  *  You should have received a copy of the GNU General Public License |  | ||||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| #include "noteList.h" |  | ||||||
|  | @ -1,391 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  *  \file       noteList.h |  | ||||||
|  *  \author     Francois Best |  | ||||||
|  *  \date       24/05/2013 |  | ||||||
|  *  \brief      Linked list of notes, for Low, Last & High playing modes. |  | ||||||
|  *  \license    GPL v3.0 - Copyright Forty Seven Effects 2013 |  | ||||||
|  * |  | ||||||
|  *  This program is free software: you can redistribute it and/or modify |  | ||||||
|  *  it under the terms of the GNU General Public License as published by |  | ||||||
|  *  the Free Software Foundation, either version 3 of the License, or |  | ||||||
|  *  (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  *  This program is distributed in the hope that it will be useful, |  | ||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  *  GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  *  You should have received a copy of the GNU General Public License |  | ||||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <inttypes.h> |  | ||||||
| 
 |  | ||||||
| typedef uint8_t byte; |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| struct MidiNote |  | ||||||
| { |  | ||||||
|     inline MidiNote(); |  | ||||||
|     inline MidiNote(byte inPitch, byte inVelocity); |  | ||||||
|     inline MidiNote(const MidiNote& inOther); |  | ||||||
|     inline MidiNote& operator= (const MidiNote& inOther); |  | ||||||
| 
 |  | ||||||
|     byte pitch; |  | ||||||
|     byte velocity; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| class MidiNoteList |  | ||||||
| { |  | ||||||
| private: |  | ||||||
|     struct Cell |  | ||||||
|     { |  | ||||||
|         inline Cell(); |  | ||||||
|         inline Cell(const Cell& inOther); |  | ||||||
|         inline Cell& operator= (const Cell& inOther); |  | ||||||
| 
 |  | ||||||
|         MidiNote note; |  | ||||||
|         bool active; |  | ||||||
|         Cell* next; |  | ||||||
|         Cell* prev; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     inline  MidiNoteList(); |  | ||||||
|     inline ~MidiNoteList(); |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     inline void add(const MidiNote& inNote); |  | ||||||
|     inline void remove(byte inPitch); |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     inline bool get(byte inIndex, byte& outPitch) const; |  | ||||||
|     inline bool getLast(byte& outPitch) const; |  | ||||||
|     inline bool getHigh(byte& outPitch) const; |  | ||||||
|     inline bool getLow(byte& outPitch) const; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     inline bool empty() const; |  | ||||||
|     inline byte size() const; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     inline Cell* getFirstEmptyCell(); |  | ||||||
|     inline void print() const; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     Cell mArray[Size]; |  | ||||||
|     Cell* mHead; |  | ||||||
|     Cell* mTail; |  | ||||||
|     byte mSize; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // ########################################################################## //
 |  | ||||||
| // Inline implementation
 |  | ||||||
| 
 |  | ||||||
| inline MidiNote::MidiNote() |  | ||||||
|     : pitch(0) |  | ||||||
|     , velocity(0) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline MidiNote::MidiNote(byte inPitch, byte inVelocity) |  | ||||||
|     : pitch(inPitch) |  | ||||||
|     , velocity(inVelocity) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline MidiNote::MidiNote(const MidiNote& inOther) |  | ||||||
|     : pitch(inOther.pitch) |  | ||||||
|     , velocity(inOther.velocity) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline MidiNote& MidiNote::operator= (const MidiNote& inOther) |  | ||||||
| { |  | ||||||
|     pitch    = inOther.pitch; |  | ||||||
|     velocity = inOther.velocity; |  | ||||||
|     return *this; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ########################################################################## //
 |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline MidiNoteList<Size>::Cell::Cell() |  | ||||||
|     : note() |  | ||||||
|     , active(false) |  | ||||||
|     , next(0) |  | ||||||
|     , prev(0) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline MidiNoteList<Size>::Cell::Cell(const Cell& inOther) |  | ||||||
|     : note(inOther.note) |  | ||||||
|     , active(inOther.active) |  | ||||||
|     , next(inOther.next) |  | ||||||
|     , prev(inOther.prev) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline typename MidiNoteList<Size>::Cell& MidiNoteList<Size>::Cell::operator= (const Cell& inOther) |  | ||||||
| { |  | ||||||
|     note = inOther.note; |  | ||||||
|     active = inOther.active; |  | ||||||
|     next = inOther.next; |  | ||||||
|     prev = inOther.prev; |  | ||||||
|     return *this; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ########################################################################## //
 |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline MidiNoteList<Size>::MidiNoteList() |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline MidiNoteList<Size>::~MidiNoteList() |  | ||||||
| { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| /*! \brief Add a note, sorting it by time.
 |  | ||||||
|  Call this when receiving a NoteOn event. This will add the new note as the tail |  | ||||||
|  of the list. |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline void MidiNoteList<Size>::add(const MidiNote& inNote) |  | ||||||
| { |  | ||||||
|     if (mHead == 0) |  | ||||||
|     { |  | ||||||
|         mArray[0].note   = inNote; |  | ||||||
|         mArray[0].active = true; |  | ||||||
|         mArray[0].next   = 0; |  | ||||||
|         mArray[0].prev   = 0; |  | ||||||
|         mHead = mArray; |  | ||||||
|         mTail = mArray; |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         // Find the first inactive cell, and use it as tail.
 |  | ||||||
|         Cell* const oldTail = mTail; |  | ||||||
|         Cell* const newTail = getFirstEmptyCell(); |  | ||||||
| 
 |  | ||||||
|         newTail->active = true; |  | ||||||
|         newTail->note = inNote; |  | ||||||
| 
 |  | ||||||
|         oldTail->next = newTail; |  | ||||||
|         newTail->prev = oldTail; |  | ||||||
|         newTail->next = 0; |  | ||||||
|         mTail = newTail; |  | ||||||
|     } |  | ||||||
|     mSize++; |  | ||||||
|     print(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*! \brief Remove a note
 |  | ||||||
|  Call this when receiving a NoteOff event. |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline void MidiNoteList<Size>::remove(byte inPitch) |  | ||||||
| { |  | ||||||
|     if (mTail != 0) |  | ||||||
|     { |  | ||||||
|         for (Cell* it = mTail; it != 0; it = it->prev) |  | ||||||
|         { |  | ||||||
|             if (it->note.pitch == inPitch) |  | ||||||
|             { |  | ||||||
|                 Cell* const prev = it->prev; |  | ||||||
|                 Cell* const next = it->next; |  | ||||||
| 
 |  | ||||||
|                 it->active = false; |  | ||||||
|                 it->next = 0; |  | ||||||
|                 it->prev = 0; |  | ||||||
| 
 |  | ||||||
|                 // Reconnect both ends
 |  | ||||||
|                 if (it == mHead) |  | ||||||
|                 { |  | ||||||
|                     //AVR_ASSERT(prev == 0);
 |  | ||||||
|                     mHead = next; |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     //AVR_ASSERT(prev != 0);
 |  | ||||||
|                     prev->next = next; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (it == mTail) |  | ||||||
|                 { |  | ||||||
|                     //AVR_ASSERT(next == 0);
 |  | ||||||
|                     mTail = prev; |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     //AVR_ASSERT(next != 0);
 |  | ||||||
|                     next->prev = prev; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 mSize--; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     print(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| /*! \brief Get a note at an arbitrary position
 |  | ||||||
|  This can be interesting for duo/multi/polyphony operations. |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline bool MidiNoteList<Size>::get(byte inIndex, byte& outPitch) const |  | ||||||
| { |  | ||||||
|     if (mTail) |  | ||||||
|     { |  | ||||||
|         const Cell* it = mTail; |  | ||||||
|         for (byte i = 0; i < inIndex; ++i) |  | ||||||
|         { |  | ||||||
|             if (it->prev) |  | ||||||
|             { |  | ||||||
|                 it = it->prev; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         print(); |  | ||||||
|         //AVR_LOG("Index " << inIndex << ": " << it->note.pitch);
 |  | ||||||
| 
 |  | ||||||
|         outPitch = it->note.pitch; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*! \brief Get the last active note played
 |  | ||||||
|  This implements the Mono Last playing mode. |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline bool MidiNoteList<Size>::getLast(byte& outPitch) const |  | ||||||
| { |  | ||||||
|     if (!mTail) |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     outPitch = mTail->note.pitch; |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*! \brief Get the highest pitched active note
 |  | ||||||
|  This implements the Mono High playing mode. |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline bool MidiNoteList<Size>::getHigh(byte& outPitch) const |  | ||||||
| { |  | ||||||
|     if (!mTail) |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     outPitch = 0; |  | ||||||
|     const Cell* it = mTail; |  | ||||||
|     for (byte i = 0; i < mSize; ++i) |  | ||||||
|     { |  | ||||||
|         if (it->note.pitch > outPitch) |  | ||||||
|         { |  | ||||||
|             outPitch = it->note.pitch; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (it->prev) |  | ||||||
|         { |  | ||||||
|             it = it->prev; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*! \brief Get the lowest pitched active note
 |  | ||||||
|  This implements the Mono Low playing mode. |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline bool MidiNoteList<Size>::getLow(byte& outPitch) const |  | ||||||
| { |  | ||||||
|     if (!mTail) |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     outPitch = 0xff; |  | ||||||
|     const Cell* it = mTail; |  | ||||||
|     for (byte i = 0; i < mSize; ++i) |  | ||||||
|     { |  | ||||||
|         if (it->note.pitch < outPitch) |  | ||||||
|         { |  | ||||||
|             outPitch = it->note.pitch; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (it->prev) |  | ||||||
|         { |  | ||||||
|             it = it->prev; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline bool MidiNoteList<Size>::empty() const |  | ||||||
| { |  | ||||||
|     return mSize == 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*! \brief Get the number of active notes.
 |  | ||||||
|  */ |  | ||||||
| template<byte Size> |  | ||||||
| inline byte MidiNoteList<Size>::size() const |  | ||||||
| { |  | ||||||
|     return mSize; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| // Private implementations, for internal use only.
 |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline typename MidiNoteList<Size>::Cell* MidiNoteList<Size>::getFirstEmptyCell() |  | ||||||
| { |  | ||||||
|     for (byte i = 0; i < Size; ++i) |  | ||||||
|     { |  | ||||||
|         if (mArray[i].active == false) |  | ||||||
|         { |  | ||||||
|             return mArray + i; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template<byte Size> |  | ||||||
| inline void MidiNoteList<Size>::print() const |  | ||||||
| { |  | ||||||
| //#ifndef NDEBUG
 |  | ||||||
| //    AVR_DBG("Note List: [ ");
 |  | ||||||
| //    if (mHead)
 |  | ||||||
| //    {
 |  | ||||||
| //        for (const Cell* it = mHead; it != 0; it = it->next)
 |  | ||||||
| //        {
 |  | ||||||
| //            AVR_DBG(it->note.pitch);
 |  | ||||||
| //            if (it->next)
 |  | ||||||
| //                AVR_DBG(" -> ");
 |  | ||||||
| //        }
 |  | ||||||
| //    }
 |  | ||||||
| //    AVR_LOG(" ]");
 |  | ||||||
| //#endif
 |  | ||||||
| } |  | ||||||
|  | @ -1,108 +0,0 @@ | ||||||
| /*************************************************
 |  | ||||||
|  * Public Constants |  | ||||||
|  *************************************************/ |  | ||||||
| #include <inttypes.h> |  | ||||||
| 
 |  | ||||||
| #define NOTE_B0  31 |  | ||||||
| #define NOTE_C1  33 |  | ||||||
| #define NOTE_CS1 35 |  | ||||||
| #define NOTE_D1  37 |  | ||||||
| #define NOTE_DS1 39 |  | ||||||
| #define NOTE_E1  41 |  | ||||||
| #define NOTE_F1  44 |  | ||||||
| #define NOTE_FS1 46 |  | ||||||
| #define NOTE_G1  49 |  | ||||||
| #define NOTE_GS1 52 |  | ||||||
| #define NOTE_A1  55 |  | ||||||
| #define NOTE_AS1 58 |  | ||||||
| #define NOTE_B1  62 |  | ||||||
| #define NOTE_C2  65 |  | ||||||
| #define NOTE_CS2 69 |  | ||||||
| #define NOTE_D2  73 |  | ||||||
| #define NOTE_DS2 78 |  | ||||||
| #define NOTE_E2  82 |  | ||||||
| #define NOTE_F2  87 |  | ||||||
| #define NOTE_FS2 93 |  | ||||||
| #define NOTE_G2  98 |  | ||||||
| #define NOTE_GS2 104 |  | ||||||
| #define NOTE_A2  110 |  | ||||||
| #define NOTE_AS2 117 |  | ||||||
| #define NOTE_B2  123 |  | ||||||
| #define NOTE_C3  131 |  | ||||||
| #define NOTE_CS3 139 |  | ||||||
| #define NOTE_D3  147 |  | ||||||
| #define NOTE_DS3 156 |  | ||||||
| #define NOTE_E3  165 |  | ||||||
| #define NOTE_F3  175 |  | ||||||
| #define NOTE_FS3 185 |  | ||||||
| #define NOTE_G3  196 |  | ||||||
| #define NOTE_GS3 208 |  | ||||||
| #define NOTE_A3  220 |  | ||||||
| #define NOTE_AS3 233 |  | ||||||
| #define NOTE_B3  247 |  | ||||||
| #define NOTE_C4  262 |  | ||||||
| #define NOTE_CS4 277 |  | ||||||
| #define NOTE_D4  294 |  | ||||||
| #define NOTE_DS4 311 |  | ||||||
| #define NOTE_E4  330 |  | ||||||
| #define NOTE_F4  349 |  | ||||||
| #define NOTE_FS4 370 |  | ||||||
| #define NOTE_G4  392 |  | ||||||
| #define NOTE_GS4 415 |  | ||||||
| #define NOTE_A4  440 |  | ||||||
| #define NOTE_AS4 466 |  | ||||||
| #define NOTE_B4  494 |  | ||||||
| #define NOTE_C5  523 |  | ||||||
| #define NOTE_CS5 554 |  | ||||||
| #define NOTE_D5  587 |  | ||||||
| #define NOTE_DS5 622 |  | ||||||
| #define NOTE_E5  659 |  | ||||||
| #define NOTE_F5  698 |  | ||||||
| #define NOTE_FS5 740 |  | ||||||
| #define NOTE_G5  784 |  | ||||||
| #define NOTE_GS5 831 |  | ||||||
| #define NOTE_A5  880 |  | ||||||
| #define NOTE_AS5 932 |  | ||||||
| #define NOTE_B5  988 |  | ||||||
| #define NOTE_C6  1047 |  | ||||||
| #define NOTE_CS6 1109 |  | ||||||
| #define NOTE_D6  1175 |  | ||||||
| #define NOTE_DS6 1245 |  | ||||||
| #define NOTE_E6  1319 |  | ||||||
| #define NOTE_F6  1397 |  | ||||||
| #define NOTE_FS6 1480 |  | ||||||
| #define NOTE_G6  1568 |  | ||||||
| #define NOTE_GS6 1661 |  | ||||||
| #define NOTE_A6  1760 |  | ||||||
| #define NOTE_AS6 1865 |  | ||||||
| #define NOTE_B6  1976 |  | ||||||
| #define NOTE_C7  2093 |  | ||||||
| #define NOTE_CS7 2217 |  | ||||||
| #define NOTE_D7  2349 |  | ||||||
| #define NOTE_DS7 2489 |  | ||||||
| #define NOTE_E7  2637 |  | ||||||
| #define NOTE_F7  2794 |  | ||||||
| #define NOTE_FS7 2960 |  | ||||||
| #define NOTE_G7  3136 |  | ||||||
| #define NOTE_GS7 3322 |  | ||||||
| #define NOTE_A7  3520 |  | ||||||
| #define NOTE_AS7 3729 |  | ||||||
| #define NOTE_B7  3951 |  | ||||||
| #define NOTE_C8  4186 |  | ||||||
| #define NOTE_CS8 4435 |  | ||||||
| #define NOTE_D8  4699 |  | ||||||
| #define NOTE_DS8 4978 |  | ||||||
| 
 |  | ||||||
| static const uint16_t sNotePitches[] = { |  | ||||||
|     NOTE_B0, NOTE_C1, NOTE_CS1, NOTE_D1, NOTE_DS1, NOTE_E1, NOTE_F1, NOTE_FS1, |  | ||||||
|     NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1, NOTE_B1, NOTE_C2, NOTE_CS2, NOTE_D2, |  | ||||||
|     NOTE_DS2, NOTE_E2, NOTE_F2, NOTE_FS2, NOTE_G2, NOTE_GS2, NOTE_A2, NOTE_AS2, |  | ||||||
|     NOTE_B2, NOTE_C3, NOTE_CS3, NOTE_D3, NOTE_DS3, NOTE_E3, NOTE_F3, NOTE_FS3, |  | ||||||
|     NOTE_G3, NOTE_GS3, NOTE_A3, NOTE_AS3, NOTE_B3, NOTE_C4, NOTE_CS4, NOTE_D4, |  | ||||||
|     NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, |  | ||||||
|     NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, |  | ||||||
|     NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6, |  | ||||||
|     NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, |  | ||||||
|     NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, |  | ||||||
|     NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7, NOTE_C8, NOTE_CS8, NOTE_D8, NOTE_DS8, |  | ||||||
| }; |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| add_subdirectory(google-test) |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| Subproject commit 703bd9caab50b139428cea1aaff9974ebee5742e |  | ||||||
							
								
								
									
										207
									
								
								keywords.txt
								
								
								
								
							
							
						
						
									
										207
									
								
								keywords.txt
								
								
								
								
							|  | @ -1,207 +0,0 @@ | ||||||
| ####################################### |  | ||||||
| # Syntax Coloring Map For Test |  | ||||||
| ####################################### |  | ||||||
| 
 |  | ||||||
| ####################################### |  | ||||||
| # Datatypes (KEYWORD1) |  | ||||||
| ####################################### |  | ||||||
| 
 |  | ||||||
| MIDI	KEYWORD1 |  | ||||||
| MIDI.h	KEYWORD1 |  | ||||||
| MidiInterface	KEYWORD1 |  | ||||||
| DefaultSettings	KEYWORD1 |  | ||||||
| 
 |  | ||||||
| ####################################### |  | ||||||
| # Methods and Functions (KEYWORD2) |  | ||||||
| ####################################### |  | ||||||
| 
 |  | ||||||
| send	KEYWORD2 |  | ||||||
| sendNoteOn	KEYWORD2 |  | ||||||
| sendNoteOff	KEYWORD2 |  | ||||||
| sendProgramChange	KEYWORD2 |  | ||||||
| sendControlChange	KEYWORD2 |  | ||||||
| sendPitchBend	KEYWORD2 |  | ||||||
| sendPolyPressure	KEYWORD2 |  | ||||||
| sendAfterTouch	KEYWORD2 |  | ||||||
| sendSysEx	KEYWORD2 |  | ||||||
| sendTimeCodeQuarterFrame	KEYWORD2 |  | ||||||
| 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 |  | ||||||
| getChannel	KEYWORD2 |  | ||||||
| getData1	KEYWORD2 |  | ||||||
| getData2	KEYWORD2 |  | ||||||
| getSysExArray	KEYWORD2 |  | ||||||
| getSysExArrayLength	KEYWORD2 |  | ||||||
| getFilterMode	KEYWORD2 |  | ||||||
| getThruState	KEYWORD2 |  | ||||||
| getInputChannel	KEYWORD2 |  | ||||||
| check	KEYWORD2 |  | ||||||
| setInputChannel	KEYWORD2 |  | ||||||
| turnThruOn	KEYWORD2 |  | ||||||
| turnThruOff	KEYWORD2 |  | ||||||
| setThruFilterMode	KEYWORD2 |  | ||||||
| disconnectCallbackFromType	KEYWORD2 |  | ||||||
| setHandleNoteOff	KEYWORD2 |  | ||||||
| setHandleNoteOn	KEYWORD2 |  | ||||||
| setHandleAfterTouchPoly	KEYWORD2 |  | ||||||
| setHandleControlChange	KEYWORD2 |  | ||||||
| setHandleProgramChange	KEYWORD2 |  | ||||||
| setHandleAfterTouchChannel	KEYWORD2 |  | ||||||
| setHandlePitchBend	KEYWORD2 |  | ||||||
| setHandleSystemExclusive	KEYWORD2 |  | ||||||
| setHandleTimeCodeQuarterFrame	KEYWORD2 |  | ||||||
| setHandleSongPosition	KEYWORD2 |  | ||||||
| setHandleSongSelect	KEYWORD2 |  | ||||||
| setHandleTuneRequest	KEYWORD2 |  | ||||||
| setHandleClock	KEYWORD2 |  | ||||||
| setHandleStart	KEYWORD2 |  | ||||||
| setHandleContinue	KEYWORD2 |  | ||||||
| setHandleStop	KEYWORD2 |  | ||||||
| setHandleActiveSensing	KEYWORD2 |  | ||||||
| setHandleSystemReset	KEYWORD2 |  | ||||||
| getTypeFromStatusByte	KEYWORD2 |  | ||||||
| getChannelFromStatusByte	KEYWORD2 |  | ||||||
| isChannelMessage	KEYWORD2 |  | ||||||
| encodeSysEx	KEYWORD2 |  | ||||||
| decodeSysEx	KEYWORD2 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ####################################### |  | ||||||
| # Instances (KEYWORD2) |  | ||||||
| ####################################### |  | ||||||
| 
 |  | ||||||
| ####################################### |  | ||||||
| # Constants (LITERAL1) |  | ||||||
| ####################################### |  | ||||||
| 
 |  | ||||||
| # Namespace, considering it as a literal |  | ||||||
| midi	LITERAL1 |  | ||||||
| 
 |  | ||||||
| NoteOff	LITERAL1 |  | ||||||
| NoteOn	LITERAL1 |  | ||||||
| AfterTouchPoly	LITERAL1 |  | ||||||
| ControlChange	LITERAL1 |  | ||||||
| ProgramChange	LITERAL1 |  | ||||||
| AfterTouchChannel	LITERAL1 |  | ||||||
| PitchBend	LITERAL1 |  | ||||||
| SystemExclusive	LITERAL1 |  | ||||||
| TimeCodeQuarterFrame	LITERAL1 |  | ||||||
| SongPosition	LITERAL1 |  | ||||||
| SongSelect	LITERAL1 |  | ||||||
| TuneRequest	LITERAL1 |  | ||||||
| Clock	LITERAL1 |  | ||||||
| Start	LITERAL1 |  | ||||||
| Stop	LITERAL1 |  | ||||||
| Continue	LITERAL1 |  | ||||||
| ActiveSensing	LITERAL1 |  | ||||||
| SystemReset	LITERAL1 |  | ||||||
| InvalidType	LITERAL1 |  | ||||||
| Thru	LITERAL1 |  | ||||||
| Off	LITERAL1 |  | ||||||
| Full	LITERAL1 |  | ||||||
| SameChannel	LITERAL1 |  | ||||||
| DifferentChannel	LITERAL1 |  | ||||||
| MIDI_CHANNEL_OMNI	LITERAL1 |  | ||||||
| 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 |  | ||||||
							
								
								
									
										23
									
								
								library.json
								
								
								
								
							
							
						
						
									
										23
									
								
								library.json
								
								
								
								
							|  | @ -1,23 +0,0 @@ | ||||||
| { |  | ||||||
|     "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"] |  | ||||||
| } |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| 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 |  | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | #include <MIDI.h> | ||||||
|  | /*
 | ||||||
|  |   Basic I/O MIDI tutorial | ||||||
|  |   by Franky | ||||||
|  |   28/07/2009 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define LED 13   		// LED pin on Arduino board
 | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  |   pinMode(LED, OUTPUT); | ||||||
|  |   MIDI.begin(4);            	// Launch MIDI with default options
 | ||||||
|  | 				// input channel is set to 4
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  |   if (MIDI.read()) { | ||||||
|  |     digitalWrite(LED,HIGH);     // Blink the LED
 | ||||||
|  |     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);    	 | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,75 @@ | ||||||
|  | #include <MIDI.h> | ||||||
|  | 
 | ||||||
|  | unsigned long gTime_start = 0; | ||||||
|  | unsigned long gTime_stop  = 0; | ||||||
|  | unsigned gCounter = 0; | ||||||
|  | unsigned long gTime_sum   = 0; | ||||||
|  | unsigned long gTime_min   = -1; | ||||||
|  | unsigned long gTime_max   = 0; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void handleNoteOn(byte inChannel,byte inNote,byte inVelocity) | ||||||
|  | { | ||||||
|  |    | ||||||
|  |   gTime_stop = micros(); | ||||||
|  |    | ||||||
|  |   const unsigned long diff = gTime_stop - gTime_start; | ||||||
|  |   gTime_sum += diff; | ||||||
|  |    | ||||||
|  |   if (diff > gTime_max) gTime_max = diff; | ||||||
|  |   if (diff < gTime_min) gTime_min = diff; | ||||||
|  |    | ||||||
|  |   gCounter++; | ||||||
|  |    | ||||||
|  |   if (gCounter >= 100) { | ||||||
|  |      | ||||||
|  |     const unsigned long average = gTime_sum / (float)gCounter; | ||||||
|  |      | ||||||
|  |     Serial.println("Time to receive NoteOn: "); | ||||||
|  |      | ||||||
|  |     Serial.print("Average: "); | ||||||
|  |     Serial.print(average); | ||||||
|  |     Serial.println(" microsecs"); | ||||||
|  |      | ||||||
|  |     Serial.print("Min:     "); | ||||||
|  |     Serial.print(gTime_min); | ||||||
|  |     Serial.println(" microsecs"); | ||||||
|  |      | ||||||
|  |     Serial.print("Max:     "); | ||||||
|  |     Serial.print(gTime_max); | ||||||
|  |     Serial.println(" microsecs"); | ||||||
|  |      | ||||||
|  |     gCounter = 0; | ||||||
|  |     gTime_sum = 0; | ||||||
|  |     gTime_max = 0; | ||||||
|  |     gTime_min = -1; | ||||||
|  |      | ||||||
|  |     MIDI.turnThruOff(); | ||||||
|  |      | ||||||
|  |   } | ||||||
|  |    | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void setup() | ||||||
|  | { | ||||||
|  |    | ||||||
|  |   MIDI.begin(); | ||||||
|  |    | ||||||
|  |   Serial.begin(38400); | ||||||
|  |   Serial.print("MCU Ready"); | ||||||
|  |    | ||||||
|  |   MIDI.sendNoteOn(69,127,1); | ||||||
|  |    | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void loop() | ||||||
|  | { | ||||||
|  |    | ||||||
|  |   gTime_start = micros(); | ||||||
|  |    | ||||||
|  |   MIDI.read(); | ||||||
|  |    | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | #include <MIDI.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // 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.sourceforge.net/class_m_i_d_i___class.html
 | ||||||
|  | 
 | ||||||
|  | void HandleNoteOn(byte channel, byte pitch, byte velocity) | ||||||
|  | { | ||||||
|  |   // Do whatever you want when you receive a Note On.
 | ||||||
|  |    | ||||||
|  |   if (velocity == 0) { | ||||||
|  |     // This acts like a NoteOff.
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Try to keep your callbacks short (no delays ect) as the contrary would slow down the loop()
 | ||||||
|  |   // and have a bad impact on real-time performance.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  |   // Initiate MIDI communications, listen to all channels
 | ||||||
|  |   MIDI.begin(MIDI_CHANNEL_OMNI);     | ||||||
|  |    | ||||||
|  |   // Connect the HandleNoteOn function to the library, so it is called upon reception of a NoteOn.
 | ||||||
|  |   MIDI.setHandleNoteOn(HandleNoteOn);  // Put only the name of the function
 | ||||||
|  |    | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  |   // Call MIDI.read the fastest you can for real-time performance.
 | ||||||
|  |   MIDI.read(); | ||||||
|  |    | ||||||
|  |   // There is no need to check if there are messages incoming if they are bound to a Callback function.
 | ||||||
|  |   // The attached method will be called automatically when the corresponding message has been received.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | #include <MIDI.h> | ||||||
|  | /*
 | ||||||
|  |   MIDI Input tutorial | ||||||
|  |   by Franky | ||||||
|  |   28/07/2009 | ||||||
|  |    | ||||||
|  |   NOTE: for easier MIDI input reading,  | ||||||
|  |   take a look a the Callbacks example. | ||||||
|  |    | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define LED 13   		// LED pin on Arduino board
 | ||||||
|  | 
 | ||||||
|  | void BlinkLed(byte num) { 	// Basic blink function
 | ||||||
|  |   for (byte i=0;i<num;i++) { | ||||||
|  |     digitalWrite(LED,HIGH); | ||||||
|  |     delay(50); | ||||||
|  |     digitalWrite(LED,LOW); | ||||||
|  |     delay(50); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  |   pinMode(LED, OUTPUT); | ||||||
|  |   MIDI.begin();            	// Launch MIDI with default options
 | ||||||
|  | 				// (input channel is default set to 1)
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  |   if (MIDI.read()) {                    // Is there a MIDI message incoming ?
 | ||||||
|  |     switch(MIDI.getType()) {		// Get the type of the message we caught
 | ||||||
|  |       case midi::ProgramChange:         // If it is a Program Change
 | ||||||
|  | 	BlinkLed(MIDI.getData1());	// Blink the LED a number of times 
 | ||||||
|  | 					// correponding to the program number 
 | ||||||
|  | 					// (0 to 127, it can last a while..)
 | ||||||
|  |         break; | ||||||
|  |       // See the online reference for other message types
 | ||||||
|  |       default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,41 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | # Use this script to install the library directy from the git clone. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if [[ -d /Applications/Arduino.app ]] | ||||||
|  | then | ||||||
|  |      | ||||||
|  |     # Define locations | ||||||
|  |      | ||||||
|  |     lib_path=/Applications/Arduino.app/Contents/Resources/Java/libraries/MIDI | ||||||
|  |      | ||||||
|  |     if [[ -d $lib_path ]] | ||||||
|  |     then | ||||||
|  |         # Remove old lib | ||||||
|  |         rm -rf $lib_path | ||||||
|  |     fi | ||||||
|  |      | ||||||
|  |     # Create folder | ||||||
|  |     mkdir $lib_path | ||||||
|  |      | ||||||
|  |     # Copy sources | ||||||
|  |     cp ../src/MIDI.cpp  $lib_path | ||||||
|  |     cp ../src/MIDI.h    $lib_path | ||||||
|  |     cp ../src/midi_*    $lib_path | ||||||
|  |      | ||||||
|  |     # Copy resources | ||||||
|  |     cp ../res/keywords.txt $lib_path | ||||||
|  |      | ||||||
|  |     # Copy examples | ||||||
|  |     mkdir $lib_path/examples | ||||||
|  |      | ||||||
|  |     cp -r examples/* $lib_path/examples | ||||||
|  |      | ||||||
|  |     # Copy doc | ||||||
|  |     mkdir $lib_path/doc | ||||||
|  |      | ||||||
|  |     cp -r ../doc/* $lib_path/doc | ||||||
|  |      | ||||||
|  | else | ||||||
|  |     echo "Arduino application not found." | ||||||
|  | fi | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | # This script installs the Arduino MIDI Library into the Arduino application,  | ||||||
|  | # so that every sketch can include it directly, without having to copy anything. | ||||||
|  | # | ||||||
|  | # To install the library, run this script by double-clicking it, | ||||||
|  | # it should be directly executable and seen as such by Mac OS X. | ||||||
|  | # If not, open a terminal, cd to the script location, and run ./install_mac | ||||||
|  | #  | ||||||
|  | # Open the Arduino IDE, and you're ready to go! | ||||||
|  | 
 | ||||||
|  | # The script assumes the Arduino application | ||||||
|  | # is installed in the default location. | ||||||
|  | 
 | ||||||
|  | if [[ -d /Applications/Arduino.app ]] | ||||||
|  | then | ||||||
|  |      | ||||||
|  |     # Define locations | ||||||
|  |     lib_path=/Applications/Arduino.app/Contents/Resources/Java/libraries/MIDI | ||||||
|  |      | ||||||
|  |     if [[ -d $lib_path ]] | ||||||
|  |     then | ||||||
|  |         # Remove old lib | ||||||
|  |         rm -rf $lib_path | ||||||
|  |     fi | ||||||
|  |      | ||||||
|  |     # Create folder | ||||||
|  |     mkdir $lib_path | ||||||
|  |      | ||||||
|  |     # Install contents | ||||||
|  |     cp -r * $lib_path | ||||||
|  |      | ||||||
|  |     # Cleanup | ||||||
|  |     rm $lib_path/install_mac | ||||||
|  |      | ||||||
|  | else | ||||||
|  |     echo "Arduino application not found." | ||||||
|  | fi | ||||||
|  | @ -0,0 +1,108 @@ | ||||||
|  | ####################################### | ||||||
|  | # Syntax Coloring Map For Test | ||||||
|  | ####################################### | ||||||
|  | 
 | ||||||
|  | ####################################### | ||||||
|  | # Datatypes (KEYWORD1) | ||||||
|  | ####################################### | ||||||
|  | 
 | ||||||
|  | MIDI	KEYWORD1 | ||||||
|  | MIDI.h	KEYWORD1 | ||||||
|  | MidiInterface	KEYWORD1 | ||||||
|  | 
 | ||||||
|  | ####################################### | ||||||
|  | # Methods and Functions (KEYWORD2) | ||||||
|  | ####################################### | ||||||
|  | 
 | ||||||
|  | send	KEYWORD2 | ||||||
|  | sendNoteOn	KEYWORD2 | ||||||
|  | sendNoteOff	KEYWORD2 | ||||||
|  | sendProgramChange	KEYWORD2 | ||||||
|  | sendControlChange	KEYWORD2 | ||||||
|  | sendPitchBend	KEYWORD2 | ||||||
|  | sendPolyPressure	KEYWORD2 | ||||||
|  | sendAfterTouch	KEYWORD2 | ||||||
|  | sendSysEx	KEYWORD2 | ||||||
|  | sendTimeCodeQuarterFrame	KEYWORD2 | ||||||
|  | sendSongPosition	KEYWORD2 | ||||||
|  | sendSongSelect	KEYWORD2 | ||||||
|  | sendTuneRequest	KEYWORD2 | ||||||
|  | sendRealTime	KEYWORD2 | ||||||
|  | begin	KEYWORD2 | ||||||
|  | read	KEYWORD2 | ||||||
|  | getType	KEYWORD2 | ||||||
|  | getChannel	KEYWORD2 | ||||||
|  | getData1	KEYWORD2 | ||||||
|  | getData2	KEYWORD2 | ||||||
|  | getSysExArray	KEYWORD2 | ||||||
|  | getFilterMode	KEYWORD2 | ||||||
|  | getThruState	KEYWORD2 | ||||||
|  | getInputChannel	KEYWORD2 | ||||||
|  | check	KEYWORD2 | ||||||
|  | delMsg	KEYWORD2 | ||||||
|  | delSysEx	KEYWORD2 | ||||||
|  | setInputChannel	KEYWORD2 | ||||||
|  | setStatus	KEYWORD2 | ||||||
|  | turnThruOn	KEYWORD2 | ||||||
|  | turnThruOff	KEYWORD2 | ||||||
|  | setThruFilterMode	KEYWORD2 | ||||||
|  | disconnectCallbackFromType	KEYWORD2 | ||||||
|  | setHandleNoteOff	KEYWORD2 | ||||||
|  | setHandleNoteOn	KEYWORD2 | ||||||
|  | setHandleAfterTouchPoly	KEYWORD2 | ||||||
|  | setHandleControlChange	KEYWORD2 | ||||||
|  | setHandleProgramChange	KEYWORD2 | ||||||
|  | setHandleAfterTouchChannel	KEYWORD2 | ||||||
|  | setHandlePitchBend	KEYWORD2 | ||||||
|  | setHandleSystemExclusive	KEYWORD2 | ||||||
|  | setHandleTimeCodeQuarterFrame	KEYWORD2 | ||||||
|  | setHandleSongPosition	KEYWORD2 | ||||||
|  | setHandleSongSelect	KEYWORD2 | ||||||
|  | setHandleTuneRequest	KEYWORD2 | ||||||
|  | setHandleClock	KEYWORD2 | ||||||
|  | setHandleStart	KEYWORD2 | ||||||
|  | setHandleContinue	KEYWORD2 | ||||||
|  | setHandleStop	KEYWORD2 | ||||||
|  | setHandleActiveSensing	KEYWORD2 | ||||||
|  | setHandleSystemReset	KEYWORD2 | ||||||
|  | getTypeFromStatusByte	KEYWORD2 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ####################################### | ||||||
|  | # Instances (KEYWORD2) | ||||||
|  | ####################################### | ||||||
|  | 
 | ||||||
|  | ####################################### | ||||||
|  | # Constants (LITERAL1) | ||||||
|  | ####################################### | ||||||
|  | 
 | ||||||
|  | # Namespace, considering it as a literal | ||||||
|  | midi	LITERAL1 | ||||||
|  | 
 | ||||||
|  | NoteOff	LITERAL1 | ||||||
|  | NoteOn	LITERAL1 | ||||||
|  | AfterTouchPoly	LITERAL1 | ||||||
|  | ControlChange	LITERAL1 | ||||||
|  | ProgramChange	LITERAL1 | ||||||
|  | AfterTouchChannel	LITERAL1 | ||||||
|  | PitchBend	LITERAL1 | ||||||
|  | SystemExclusive	LITERAL1 | ||||||
|  | TimeCodeQuarterFrame	LITERAL1 | ||||||
|  | SongPosition	LITERAL1 | ||||||
|  | SongSelect	LITERAL1 | ||||||
|  | TuneRequest	LITERAL1 | ||||||
|  | Clock	LITERAL1 | ||||||
|  | Start	LITERAL1 | ||||||
|  | Stop	LITERAL1 | ||||||
|  | Continue	LITERAL1 | ||||||
|  | ActiveSensing	LITERAL1 | ||||||
|  | SystemReset	LITERAL1 | ||||||
|  | InvalidType	LITERAL1 | ||||||
|  | Off	LITERAL1 | ||||||
|  | Full	LITERAL1 | ||||||
|  | SameChannel	LITERAL1 | ||||||
|  | DifferentChannel	LITERAL1 | ||||||
|  | MIDI_CHANNEL_OMNI	LITERAL1 | ||||||
|  | MIDI_CHANNEL_OFF	LITERAL1 | ||||||
|  | MIDI_BAUDRATE	LITERAL1 | ||||||
|  | MIDI_SYSEX_ARRAY_SIZE	LITERAL1 | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 18 KiB | 
|  | @ -0,0 +1,50 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | #  | ||||||
|  | # Generate an archive with packaged content for easier delivery. | ||||||
|  | # The generated archive contains: | ||||||
|  | #   - Source files (MIDI.cpp / MIDI.h) | ||||||
|  | #   - Resources (keywords.txt) | ||||||
|  | #   - Documentation (Doxygen) | ||||||
|  | #   - Examples for Arduino IDE | ||||||
|  | #   - Installation scripts | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Create a temporary destination folder | ||||||
|  | mkdir -p temp/doc | ||||||
|  | mkdir -p temp/examples | ||||||
|  | 
 | ||||||
|  | # Copy sources | ||||||
|  | cp ../src/* temp | ||||||
|  | 
 | ||||||
|  | # Copy resources | ||||||
|  | cp keywords.txt temp | ||||||
|  | cp install_* temp | ||||||
|  | rm temp/install_local_* | ||||||
|  | 
 | ||||||
|  | # Copy examples | ||||||
|  | cp -r examples/* temp/examples | ||||||
|  | 
 | ||||||
|  | # Generate & copy doc | ||||||
|  | cd ../doc | ||||||
|  | /Applications/Doxygen.app/Contents/Resources/doxygen Doxyfile | ||||||
|  | rm -rf latex | ||||||
|  | cd ../res | ||||||
|  | 
 | ||||||
|  | cp -r ../doc/* temp/doc | ||||||
|  | 
 | ||||||
|  | # Generate package | ||||||
|  | mv temp MIDI | ||||||
|  | zip -r MIDI.zip MIDI | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Remove temp folder | ||||||
|  | rm -rf MIDI | ||||||
|  | 
 | ||||||
|  | # Archive generated packaged | ||||||
|  | 
 | ||||||
|  | if [[ !( -d ../bin ) ]] | ||||||
|  | then | ||||||
|  |     mkdir ../bin    # Create archives location | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | mv MIDI.zip ../bin/Arduino_MIDI_Library.zip | ||||||
|  | @ -1,40 +0,0 @@ | ||||||
| #!/bin/sh |  | ||||||
| # |  | ||||||
| # Generate an archive with packaged content for easier delivery. |  | ||||||
| # The generated archive contains: |  | ||||||
| #   - Source files (MIDI.cpp / MIDI.h) |  | ||||||
| #   - Resources (keywords.txt) |  | ||||||
| #   - Examples for Arduino IDE |  | ||||||
| #   - Installation scripts |  | ||||||
| 
 |  | ||||||
| cd "`dirname "${0}"`" |  | ||||||
| 
 |  | ||||||
| root="${PWD}/.." |  | ||||||
| build="$root/build/dist/MIDI" |  | ||||||
| 
 |  | ||||||
| echo "root:  $root" |  | ||||||
| echo "build: $build" |  | ||||||
| 
 |  | ||||||
| # Create a destination directory structure |  | ||||||
| mkdir -p "$build/examples" |  | ||||||
| mkdir -p "$build/src" |  | ||||||
| 
 |  | ||||||
| # Copy sources |  | ||||||
| cp -rf "$root/src" "$build" |  | ||||||
| 
 |  | ||||||
| # Copy resources |  | ||||||
| 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 |  | ||||||
| cp -rf "$root/examples" "$build" |  | ||||||
| 
 |  | ||||||
| # Generate package |  | ||||||
| cd "$build/.." |  | ||||||
| zip -r Arduino_MIDI_Library.zip MIDI |  | ||||||
| 
 |  | ||||||
| # Generate doc |  | ||||||
| cd "$root/doc" |  | ||||||
| doxygen |  | ||||||
|  | @ -1,114 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| 
 |  | ||||||
| import rtmidi |  | ||||||
| import random |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| class Midi: |  | ||||||
|     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 |  | ||||||
|     SystemExclusive       = 0xF0    # System Exclusive |  | ||||||
|     TimeCodeQuarterFrame  = 0xF1    # System Common - MIDI Time Code Quarter Frame |  | ||||||
|     SongPosition          = 0xF2    # System Common - Song Position Pointer |  | ||||||
|     SongSelect            = 0xF3    # System Common - Song Select |  | ||||||
|     TuneRequest           = 0xF6    # System Common - Tune Request |  | ||||||
|     Clock                 = 0xF8    # System Real Time - Timing Clock |  | ||||||
|     Start                 = 0xFA    # System Real Time - Start |  | ||||||
|     Continue              = 0xFB    # System Real Time - Continue |  | ||||||
|     Stop                  = 0xFC    # System Real Time - Stop |  | ||||||
|     ActiveSensing         = 0xFE    # System Real Time - Active Sensing |  | ||||||
|     SystemReset           = 0xFF    # System Real Time - System Reset |  | ||||||
| 
 |  | ||||||
|     @staticmethod |  | ||||||
|     def getChannel(statusByte): |  | ||||||
|         return statusByte & 0x0f; |  | ||||||
| 
 |  | ||||||
|     @staticmethod |  | ||||||
|     def getType(statusByte): |  | ||||||
|         if statusByte >= 0xf0: |  | ||||||
|             # System messages |  | ||||||
|             return statusByte |  | ||||||
|         else: |  | ||||||
|             # Channel messages |  | ||||||
|             return statusByte & 0xf0; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| class MidiInterface: |  | ||||||
|     def __init__(self, listenerCallback = None): |  | ||||||
|         self.input              = rtmidi.MidiIn() |  | ||||||
|         self.output             = rtmidi.MidiOut() |  | ||||||
|         self.listenerCallback   = listenerCallback |  | ||||||
|         self.ports              = self.getAvailablePorts() |  | ||||||
|         self.port               = self.connect(self.choosePorts()) |  | ||||||
| 
 |  | ||||||
|     # -------------------------------------------------------------------------- |  | ||||||
| 
 |  | ||||||
|     def handleMidiInput(self, message, timestamp): |  | ||||||
|         midiData = message[0] |  | ||||||
|         if self.listenerCallback: |  | ||||||
|             self.listenerCallback(midiData) |  | ||||||
| 
 |  | ||||||
|     def send(self, message): |  | ||||||
|         print('Sending', message) |  | ||||||
|         self.output.send_message(message) |  | ||||||
| 
 |  | ||||||
|     # -------------------------------------------------------------------------- |  | ||||||
| 
 |  | ||||||
|     def getAvailablePorts(self): |  | ||||||
|         return { |  | ||||||
|             'input' : self.input.get_ports(), |  | ||||||
|             'output': self.output.get_ports(), |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def choosePorts(self): |  | ||||||
|         return { |  | ||||||
|             'input' : self.choosePort(self.ports['input'],  'input'), |  | ||||||
|             'output': self.choosePort(self.ports['output'], 'output') |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def choosePort(self, ports, direction): |  | ||||||
|         if not ports: |  | ||||||
|             print('No MIDI ports available, bailing out.') |  | ||||||
|             return None |  | ||||||
| 
 |  | ||||||
|         if len(ports) == 1: |  | ||||||
|             return { |  | ||||||
|                 'id':   0, |  | ||||||
|                 'name': ports[0] |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         else: |  | ||||||
|             # Give a choice |  | ||||||
|             print('Multiple %s ports available, please make a choice:' % direction) |  | ||||||
|             choices = dict() |  | ||||||
|             for port, i in zip(ports, range(0, len(ports))): |  | ||||||
|                 choices[i] = port |  | ||||||
|                 print('  [%d]' % i, port) |  | ||||||
|             choiceIndex = int(input('-> ')) |  | ||||||
|             return { |  | ||||||
|                 'id': choiceIndex, |  | ||||||
|                 'name': choices[choiceIndex] |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|     # -------------------------------------------------------------------------- |  | ||||||
| 
 |  | ||||||
|     def connect(self, ports): |  | ||||||
|         if not ports: |  | ||||||
|             return None |  | ||||||
| 
 |  | ||||||
|         print('Connecting input to %s'  % ports['input']['name']) |  | ||||||
|         print('Connecting output to %s' % ports['output']['name']) |  | ||||||
| 
 |  | ||||||
|         self.input.set_callback(self.handleMidiInput) |  | ||||||
|         self.input.open_port(ports['input']['id']) |  | ||||||
|         self.output.open_port(ports['output']['id']) |  | ||||||
|         return ports |  | ||||||
|  | @ -1,24 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| class Tester: |  | ||||||
|     def __init__(self, interface): |  | ||||||
|         self.interface  = interface |  | ||||||
|         self.sent       = None |  | ||||||
|         self.expected   = None |  | ||||||
|         self.received   = None |  | ||||||
| 
 |  | ||||||
|     def handleMidiInput(self, data): |  | ||||||
|         print('Recived data:', data) |  | ||||||
|         self.received = data |  | ||||||
| 
 |  | ||||||
|     def checkThru(self, message): |  | ||||||
|         self.interface.send(message) |  | ||||||
|         self.sent     = message |  | ||||||
|         self.expected = message |  | ||||||
|         self.received = None |  | ||||||
|         while not self.received: |  | ||||||
|             pass |  | ||||||
|         return self.expected == self.received |  | ||||||
|  | @ -1,182 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| 
 |  | ||||||
| import sys |  | ||||||
| import os |  | ||||||
| import shutil |  | ||||||
| import subprocess |  | ||||||
| import argparse |  | ||||||
| from pprint     import pprint |  | ||||||
| from midi       import * |  | ||||||
| from tester     import * |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| rootDir = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../..')) |  | ||||||
| logsDir = os.path.join(rootDir, 'logs') |  | ||||||
| resDir  = os.path.join(rootDir, 'res') |  | ||||||
| srcDir  = os.path.join(rootDir, 'src') |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| class Dict(dict): |  | ||||||
|     def __init__(self, *args, **kwargs): |  | ||||||
|         super().__init__(*args, **kwargs) |  | ||||||
|         self.__dict__ = self |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| class Arduino: |  | ||||||
|     if sys.platform == 'darwin': |  | ||||||
|         binary  = '/Applications/Arduino.app/Contents/MacOS/JavaApplicationStub' |  | ||||||
|         home    = os.path.expanduser('~/Documents/Arduino') |  | ||||||
|     elif sys.platform == 'win32': |  | ||||||
|         binary  = 'arduino.exe' |  | ||||||
|         home    = os.path.expanduser('~/My Documents/Arduino') |  | ||||||
|     elif sys.platform == 'linux': |  | ||||||
|         binary  = 'arduino' |  | ||||||
|         home    = os.path.expanduser('~/Arduino') |  | ||||||
|     else: |  | ||||||
|         print('Unsupported platform %s' % str(sys.platform)) |  | ||||||
|         sys.exit(1) |  | ||||||
| 
 |  | ||||||
|     libraryDir = os.path.join(home, 'libraries') |  | ||||||
| 
 |  | ||||||
|     boards = [ |  | ||||||
|         Dict({ |  | ||||||
|             'name':   'Uno', |  | ||||||
|             'id':     'arduino:avr:uno', |  | ||||||
|             'port':   None, |  | ||||||
|         }), |  | ||||||
|         Dict({ |  | ||||||
|             'name':   'Leonardo', |  | ||||||
|             'id':     'arduino:avr:leonardo', |  | ||||||
|             'port':   None, |  | ||||||
|         }), |  | ||||||
|         Dict({ |  | ||||||
|             'name':   'Mega', |  | ||||||
|             'id':     'arduino:avr:mega', |  | ||||||
|             'port':   None, |  | ||||||
|         }), |  | ||||||
|         Dict({ |  | ||||||
|             'name':   'Due', |  | ||||||
|             'id':     'arduino:sam:due', |  | ||||||
|             'port':   None, |  | ||||||
|         }), |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
|     def checkReturnCode(code): |  | ||||||
|         if code == 0: |  | ||||||
|             return True |  | ||||||
|         if code == 1: |  | ||||||
|             print('Operation failed.') |  | ||||||
|         if code == 2: |  | ||||||
|             print('File not found') |  | ||||||
|         if code == 3: |  | ||||||
|             print('Invalid argument') |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     def verify(sketch, boardId): |  | ||||||
|         return  Arduino.checkReturnCode(subprocess.call([ |  | ||||||
|                 Arduino.binary, |  | ||||||
|                 '--verify', sketch, |  | ||||||
|                 '--board',  boardId, |  | ||||||
|                 '--verbose-build', |  | ||||||
|             ])) |  | ||||||
|             #], stdout = open(os.devnull, 'wb'))) |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| class ArduinoMidiLibrary: |  | ||||||
|     def __init__(self): |  | ||||||
|         self.path       = os.path.join(Arduino.libraryDir, 'MIDI') |  | ||||||
|         self.sources    = self.getSources() |  | ||||||
|         self.resources  = self.getResources() |  | ||||||
| 
 |  | ||||||
|     def getSources(self): |  | ||||||
|         sources = dict() |  | ||||||
|         for root, dirs, files in os.walk(srcDir): |  | ||||||
|             for name, ext in [os.path.splitext(f) for f in files]: |  | ||||||
|                 if ext in ('.cpp', '.hpp', '.h'): |  | ||||||
|                     source  = os.path.join(root, name + ext) |  | ||||||
|                     dest    = os.path.join(self.path, os.path.relpath(source, srcDir)) |  | ||||||
|                     sources[source] = dest |  | ||||||
|         return sources |  | ||||||
| 
 |  | ||||||
|     def getResources(self): |  | ||||||
|         return { |  | ||||||
|             os.path.join(resDir, 'keywords.txt'): os.path.join(self.path, 'keywords.txt'), |  | ||||||
|             os.path.join(resDir, 'examples/'): os.path.join(self.path, 'examples/'), |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def install(self): |  | ||||||
|         payloads = dict(list(self.sources.items()) + list(self.resources.items())) |  | ||||||
|         for s,d in payloads.items(): |  | ||||||
|             if not os.path.exists(os.path.dirname(d)): |  | ||||||
|                 os.makedirs(os.path.dirname(d)) |  | ||||||
|             if os.path.isfile(s): |  | ||||||
|                 shutil.copy2(s, d) |  | ||||||
|             elif os.path.isdir(s): |  | ||||||
|                 if os.path.exists(d): |  | ||||||
|                     shutil.rmtree(d) |  | ||||||
|                 shutil.copytree(s, d) |  | ||||||
| 
 |  | ||||||
|     def getInstalledExamples(self): |  | ||||||
|         exDir = os.path.join(self.path, 'examples') |  | ||||||
|         return [os.path.join(exDir, x, x + '.ino') for x in next(os.walk(exDir))[1]] |  | ||||||
| 
 |  | ||||||
|     def validate(self): |  | ||||||
|         for board in Arduino.boards: |  | ||||||
|             # Validate examples |  | ||||||
|             print('Validation for Arduino %s' % board.name) |  | ||||||
|             for example in self.getInstalledExamples(): |  | ||||||
|                 if not Arduino.verify(example, board.id): |  | ||||||
|                     print('{0:40} {1}'.format(os.path.basename(example), 'FAILED')) |  | ||||||
|                     return False |  | ||||||
|                 else: |  | ||||||
|                     print('{0:40} {1}'.format(os.path.basename(example), 'PASSED')) |  | ||||||
|         return True |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| def main(): |  | ||||||
| 
 |  | ||||||
|     info = "Validator script for the Arduino MIDI Library." |  | ||||||
|     arg_parser = argparse.ArgumentParser(description = info) |  | ||||||
| 
 |  | ||||||
|     arg_parser.add_argument('--compile', '-c', |  | ||||||
|                             action="store_true", |  | ||||||
|                             help="Test compilation of the example sketches") |  | ||||||
| 
 |  | ||||||
|     arg_parser.add_argument('--runtime', '-r', |  | ||||||
|                             action="store_true", |  | ||||||
|                             help="Test runtime") |  | ||||||
| 
 |  | ||||||
|     args = arg_parser.parse_args() |  | ||||||
| 
 |  | ||||||
|     if args.compile: |  | ||||||
|         lib = ArduinoMidiLibrary() |  | ||||||
|         lib.install() |  | ||||||
|         if lib.validate(): |  | ||||||
|             print('Compilation test passed') |  | ||||||
|         else: |  | ||||||
|             print('Compilation test failed') |  | ||||||
| 
 |  | ||||||
|     if args.runtime: |  | ||||||
|         midiInterface = MidiInterface() |  | ||||||
|         tester  = Tester(midiInterface) |  | ||||||
|         midiInterface.listenerCallback = tester.handleMidiInput |  | ||||||
| 
 |  | ||||||
|         tester.checkThru([Midi.NoteOn, 64, 80]) |  | ||||||
|         tester.checkThru([Midi.AfterTouchChannel, 1]) |  | ||||||
|         tester.checkThru([2]) |  | ||||||
|         tester.checkThru([3]) |  | ||||||
|         tester.checkThru([Midi.NoteOn, 64, 0]) |  | ||||||
|         tester.checkThru([65, 127]) |  | ||||||
|         tester.checkThru([65, 0]) |  | ||||||
|         tester.checkThru([66, 127]) |  | ||||||
|         tester.checkThru([66, 0]) |  | ||||||
| 
 |  | ||||||
| # ------------------------------------------------------------------------------ |  | ||||||
| 
 |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     main() |  | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| 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 |  | ||||||
| ) |  | ||||||
							
								
								
									
										803
									
								
								src/MIDI.cpp
								
								
								
								
							
							
						
						
									
										803
									
								
								src/MIDI.cpp
								
								
								
								
							|  | @ -2,114 +2,743 @@ | ||||||
|  *  @file       MIDI.cpp |  *  @file       MIDI.cpp | ||||||
|  *  Project     Arduino MIDI Library |  *  Project     Arduino MIDI Library | ||||||
|  *  @brief      MIDI Library for the Arduino |  *  @brief      MIDI Library for the Arduino | ||||||
|  |  *  @version    4.0 | ||||||
|  *  @author     Francois Best  |  *  @author     Francois Best  | ||||||
|  *  @date       24/02/11 |  *  @date       24/02/11 | ||||||
|  *  @license    MIT - Copyright (c) 2015 Francois Best |  *  license     GPL Forty Seven Effects - 2011 | ||||||
|  * |  | ||||||
|  * 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. |  | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include "MIDI.h" | #include "MIDI.h" | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
|  | #if !(MIDI_BUILD_INPUT) && !(MIDI_BUILD_OUTPUT) | ||||||
|  | #   error To use MIDI, you need to enable at least input or output. | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if MIDI_BUILD_THRU && !(MIDI_BUILD_OUTPUT) | ||||||
|  | #   error For thru to work, you need to enable output. | ||||||
|  | #endif | ||||||
|  | #if MIDI_BUILD_THRU && !(MIDI_BUILD_INPUT) | ||||||
|  | #   error For thru to work, you need to enable input. | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_AUTO_INSTANCIATE | ||||||
|  | #   if MIDI_USE_SOFTWARE_SERIAL | ||||||
|  | #       ifndef FSE_AVR | ||||||
|  | #           include "../SoftwareSerial/SoftwareSerial.h" | ||||||
|  |             SoftwareSerial softSerialClass(MIDI_SOFTSERIAL_RX_PIN, | ||||||
|  |                                            MIDI_SOFTSERIAL_TX_PIN); | ||||||
|  | #       else | ||||||
|  | #           error Todo: implement SoftwareSerial for avr core. | ||||||
|  | #       endif | ||||||
|  | #       undef  MIDI_SERIAL_PORT | ||||||
|  | #       define MIDI_SERIAL_PORT softSerialClass | ||||||
|  | #   else | ||||||
|  | #       ifdef FSE_AVR | ||||||
|  | #           include <hardware_Serial.h> | ||||||
|  | #       else | ||||||
|  | #           include "HardwareSerial.h" | ||||||
|  | #       endif | ||||||
|  | #   endif // MIDI_USE_SOFTWARE_SERIAL
 | ||||||
|  | 
 | ||||||
|  |     MIDI_NAMESPACE::MidiInterface MIDI; | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_AUTO_INSTANCIATE
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
| BEGIN_MIDI_NAMESPACE | BEGIN_MIDI_NAMESPACE | ||||||
| 
 | 
 | ||||||
| /*! \brief Encode System Exclusive messages.
 | // -----------------------------------------------------------------------------
 | ||||||
|  SysEx messages are encoded to guarantee transmission of data bytes higher than | 
 | ||||||
|  127 without breaking the MIDI protocol. Use this static method to convert the | /*! \brief Constructor for MidiInterface. */ | ||||||
|  data you want to send. | MidiInterface::MidiInterface() | ||||||
|  \param inData The data to encode. |  | ||||||
|  \param outSysEx The output buffer where to store the encoded 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 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, |  | ||||||
|                      bool inFlipHeaderBits) |  | ||||||
| { | { | ||||||
|     unsigned outLength  = 0;     // Num bytes in output array.
 | #if MIDI_BUILD_INPUT && MIDI_USE_CALLBACKS | ||||||
|     byte count          = 0;     // Num 7bytes in a block.
 |     mNoteOffCallback                = 0; | ||||||
|     outSysEx[0]         = 0; |     mNoteOnCallback                 = 0; | ||||||
| 
 |     mAfterTouchPolyCallback         = 0; | ||||||
|     for (unsigned i = 0; i < inLength; ++i) |     mControlChangeCallback          = 0; | ||||||
|     { |     mProgramChangeCallback          = 0; | ||||||
|         const byte data = inData[i]; |     mAfterTouchChannelCallback      = 0; | ||||||
|         const byte msb  = data >> 7; |     mPitchBendCallback              = 0; | ||||||
|         const byte body = data & 0x7f; |     mSystemExclusiveCallback        = 0; | ||||||
| 
 |     mTimeCodeQuarterFrameCallback   = 0; | ||||||
|         outSysEx[0] |= (msb << (inFlipHeaderBits ? count : (6 - count))); |     mSongPositionCallback           = 0; | ||||||
|         outSysEx[1 + count] = body; |     mSongSelectCallback             = 0; | ||||||
| 
 |     mTuneRequestCallback            = 0; | ||||||
|         if (count++ == 6) |     mClockCallback                  = 0; | ||||||
|         { |     mStartCallback                  = 0; | ||||||
|             outSysEx   += 8; |     mContinueCallback               = 0; | ||||||
|             outLength  += 8; |     mStopCallback                   = 0; | ||||||
|             outSysEx[0] = 0; |     mActiveSensingCallback          = 0; | ||||||
|             count       = 0; |     mSystemResetCallback            = 0; | ||||||
|         } | #endif | ||||||
|     } |  | ||||||
|     return outLength + count + (count != 0 ? 1 : 0); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*! \brief Decode System Exclusive messages.
 | /*! \brief Destructor for MidiInterface.
 | ||||||
|  SysEx messages are encoded to guarantee transmission of data bytes higher than |  | ||||||
|  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 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, |  | ||||||
|                      bool inFlipHeaderBits) |  | ||||||
| { |  | ||||||
|     unsigned count  = 0; |  | ||||||
|     byte msbStorage = 0; |  | ||||||
|     byte byteIndex  = 0; |  | ||||||
|   |   | ||||||
|     for (unsigned i = 0; i < inLength; ++i) |  This is not really useful for the Arduino, as it is never called... | ||||||
|  |  */ | ||||||
|  | MidiInterface::~MidiInterface() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | /*! \brief Call the begin method in the setup() function of the Arduino.
 | ||||||
|  |   | ||||||
|  |  All parameters are set to their default values: | ||||||
|  |  - Input channel set to 1 if no value is specified | ||||||
|  |  - Full thru mirroring | ||||||
|  |  */ | ||||||
|  | void MidiInterface::begin(Channel inChannel) | ||||||
|  | { | ||||||
|  |     // Initialise the Serial port
 | ||||||
|  |     MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE); | ||||||
|  |      | ||||||
|  | #if MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS | ||||||
|  |      | ||||||
|  |     mRunningStatus_TX = InvalidType; | ||||||
|  |      | ||||||
|  | #endif // MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS
 | ||||||
|  |      | ||||||
|  |      | ||||||
|  | #if MIDI_BUILD_INPUT | ||||||
|  |      | ||||||
|  |     mInputChannel = inChannel; | ||||||
|  |     mRunningStatus_RX = InvalidType; | ||||||
|  |     mPendingMessageIndex = 0; | ||||||
|  |     mPendingMessageExpectedLenght = 0; | ||||||
|  |      | ||||||
|  |     mMessage.valid = false; | ||||||
|  |     mMessage.type = InvalidType; | ||||||
|  |     mMessage.channel = 0; | ||||||
|  |     mMessage.data1 = 0; | ||||||
|  |     mMessage.data2 = 0; | ||||||
|  |      | ||||||
|  | #endif // MIDI_BUILD_INPUT
 | ||||||
|  |      | ||||||
|  |      | ||||||
|  | #if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru
 | ||||||
|  |      | ||||||
|  |     mThruFilterMode = Full; | ||||||
|  |     mThruActivated = true; | ||||||
|  |      | ||||||
|  | #endif // Thru
 | ||||||
|  |      | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | //                                 Output
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_BUILD_OUTPUT | ||||||
|  | 
 | ||||||
|  | /*! \brief Generate and send a MIDI message from the values given.
 | ||||||
|  |  \param inType    The message type (see type defines for reference) | ||||||
|  |  \param inData1   The first data byte. | ||||||
|  |  \param inData2   The second data byte (if the message contains only 1 data byte, | ||||||
|  |  set this one to 0). | ||||||
|  |  \param inChannel The output channel on which the message will be sent  | ||||||
|  |  (values from 1 to 16). Note: you cannot send to OMNI. | ||||||
|  |   | ||||||
|  |  This is an internal method, use it only if you need to send raw data | ||||||
|  |  from your code, at your own risks. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::send(MidiType inType, | ||||||
|  |                          DataByte inData1, | ||||||
|  |                          DataByte inData2, | ||||||
|  |                          Channel inChannel) | ||||||
|  | { | ||||||
|  |     // Then test if channel is valid
 | ||||||
|  |     if (inChannel >= MIDI_CHANNEL_OFF  ||  | ||||||
|  |         inChannel == MIDI_CHANNEL_OMNI ||  | ||||||
|  |         inType < NoteOff) | ||||||
|     { |     { | ||||||
|         if ((i % 8) == 0) |          | ||||||
|  | #if MIDI_USE_RUNNING_STATUS     | ||||||
|  |         mRunningStatus_TX = InvalidType; | ||||||
|  | #endif  | ||||||
|  |          | ||||||
|  |         return; // Don't send anything
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     if (inType <= PitchBend)  // Channel messages
 | ||||||
|  |     { | ||||||
|  |         // Protection: remove MSBs on data
 | ||||||
|  |         inData1 &= 0x7F; | ||||||
|  |         inData2 &= 0x7F; | ||||||
|  |          | ||||||
|  |         const StatusByte status = getStatus(inType, inChannel); | ||||||
|  |          | ||||||
|  | #if MIDI_USE_RUNNING_STATUS | ||||||
|  |         // Check Running Status
 | ||||||
|  |         if (mRunningStatus_TX != status) | ||||||
|         { |         { | ||||||
|             msbStorage = inSysEx[i]; |             // New message, memorise and send header
 | ||||||
|             byteIndex  = 6; |             mRunningStatus_TX = status; | ||||||
|  |             MIDI_SERIAL_PORT.write(mRunningStatus_TX); | ||||||
|  |         } | ||||||
|  | #else | ||||||
|  |         // Don't care about running status, send the status byte.
 | ||||||
|  |         MIDI_SERIAL_PORT.write(status); | ||||||
|  | #endif | ||||||
|  |          | ||||||
|  |         // Then send data
 | ||||||
|  |         MIDI_SERIAL_PORT.write(inData1); | ||||||
|  |         if (inType != ProgramChange && inType != AfterTouchChannel) | ||||||
|  |             MIDI_SERIAL_PORT.write(inData2); | ||||||
|  |          | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     else if (inType >= TuneRequest && inType <= SystemReset) | ||||||
|  |         sendRealTime(inType); // System Real-time and 1 byte.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_BUILD_OUTPUT
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | //                                  Input
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_BUILD_INPUT | ||||||
|  | 
 | ||||||
|  | /*! \brief Read a MIDI message from the serial port
 | ||||||
|  |  using the main input channel (see setInputChannel() for reference). | ||||||
|  |   | ||||||
|  |  \return True if a valid message has been stored in the structure, false if not. | ||||||
|  |  A valid message is a message that matches the input channel. \n\n | ||||||
|  |  If the Thru is enabled and the messages matches the filter, | ||||||
|  |  it is sent back on the MIDI output. | ||||||
|  |  */ | ||||||
|  | bool MidiInterface::read() | ||||||
|  | { | ||||||
|  |     return read(mInputChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Reading/thru-ing method, the same as read()
 | ||||||
|  |  with a given input channel to read on.  | ||||||
|  |  */ | ||||||
|  | bool MidiInterface::read(Channel inChannel) | ||||||
|  | { | ||||||
|  |     if (inChannel >= MIDI_CHANNEL_OFF) | ||||||
|  |         return false; // MIDI Input disabled.
 | ||||||
|  |      | ||||||
|  |     if (parse(inChannel)) | ||||||
|  |     { | ||||||
|  |         if (inputFilter(inChannel)) | ||||||
|  |         { | ||||||
|  |              | ||||||
|  | #if (MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) | ||||||
|  |             thruFilter(inChannel); | ||||||
|  | #endif | ||||||
|  |              | ||||||
|  | #if MIDI_USE_CALLBACKS | ||||||
|  |             launchCallback(); | ||||||
|  | #endif | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | // Private method: MIDI parser
 | ||||||
|  | bool MidiInterface::parse(Channel inChannel) | ||||||
|  | {  | ||||||
|  |     const byte bytes_available = MIDI_SERIAL_PORT.available(); | ||||||
|  |      | ||||||
|  |     if (bytes_available == 0) | ||||||
|  |         // No data available.
 | ||||||
|  |         return false; | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     /* Parsing algorithm:
 | ||||||
|  |      Get a byte from the serial buffer. | ||||||
|  |      * If there is no pending message to be recomposed, start a new one. | ||||||
|  |      - Find type and channel (if pertinent) | ||||||
|  |      - Look for other bytes in buffer, call parser recursively,  | ||||||
|  |      until the message is assembled or the buffer is empty. | ||||||
|  |      * Else, add the extracted byte to the pending message, and check validity.  | ||||||
|  |      When the message is done, store it. | ||||||
|  |      */ | ||||||
|  |      | ||||||
|  |     const byte extracted = MIDI_SERIAL_PORT.read(); | ||||||
|  |      | ||||||
|  |     if (mPendingMessageIndex == 0)  | ||||||
|  |     {  | ||||||
|  |         // Start a new pending message
 | ||||||
|  |         mPendingMessage[0] = extracted; | ||||||
|  |          | ||||||
|  |         // Check for running status first
 | ||||||
|  |         switch (getTypeFromStatusByte(mRunningStatus_RX)) | ||||||
|  |         { | ||||||
|  |                 // Only these types allow Running Status:
 | ||||||
|  |             case NoteOff: | ||||||
|  |             case NoteOn: | ||||||
|  |             case AfterTouchPoly: | ||||||
|  |             case ControlChange: | ||||||
|  |             case ProgramChange: | ||||||
|  |             case AfterTouchChannel: | ||||||
|  |             case PitchBend: | ||||||
|  |                  | ||||||
|  |                 // If the status byte is not received, prepend it 
 | ||||||
|  |                 // to the pending message
 | ||||||
|  |                 if (extracted < 0x80) | ||||||
|  |                 { | ||||||
|  |                     mPendingMessage[0] = mRunningStatus_RX; | ||||||
|  |                     mPendingMessage[1] = extracted; | ||||||
|  |                     mPendingMessageIndex = 1; | ||||||
|  |                 } | ||||||
|  |                 // Else: well, we received another status byte,
 | ||||||
|  |                 // so the running status does not apply here.
 | ||||||
|  |                 // It will be updated upon completion of this message.
 | ||||||
|  |                  | ||||||
|  |                 if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) | ||||||
|  |                 { | ||||||
|  |                     mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); | ||||||
|  |                     mMessage.channel = (mPendingMessage[0] & 0x0F)+1; | ||||||
|  |                     mMessage.data1 = mPendingMessage[1]; | ||||||
|  |                      | ||||||
|  |                     // Save data2 only if applicable
 | ||||||
|  |                     if (mPendingMessageExpectedLenght == 3) | ||||||
|  |                         mMessage.data2 = mPendingMessage[2]; | ||||||
|  |                     else  | ||||||
|  |                         mMessage.data2 = 0; | ||||||
|  |                      | ||||||
|  |                     mPendingMessageIndex = 0; | ||||||
|  |                     mPendingMessageExpectedLenght = 0; | ||||||
|  |                     mMessage.valid = true; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 // No running status
 | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         switch (getTypeFromStatusByte(mPendingMessage[0])) | ||||||
|  |         { | ||||||
|  |                 // 1 byte messages
 | ||||||
|  |             case Start: | ||||||
|  |             case Continue: | ||||||
|  |             case Stop: | ||||||
|  |             case Clock: | ||||||
|  |             case ActiveSensing: | ||||||
|  |             case SystemReset: | ||||||
|  |             case TuneRequest: | ||||||
|  |                 // Handle the message type directly here.
 | ||||||
|  |                 mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); | ||||||
|  |                 mMessage.channel = 0; | ||||||
|  |                 mMessage.data1 = 0; | ||||||
|  |                 mMessage.data2 = 0; | ||||||
|  |                 mMessage.valid = true; | ||||||
|  |                  | ||||||
|  |                 // \fix Running Status broken when receiving Clock messages.
 | ||||||
|  |                 // Do not reset all input attributes, Running Status must remain unchanged.
 | ||||||
|  |                 //resetInput(); 
 | ||||||
|  |                  | ||||||
|  |                 // We still need to reset these
 | ||||||
|  |                 mPendingMessageIndex = 0; | ||||||
|  |                 mPendingMessageExpectedLenght = 0; | ||||||
|  |                  | ||||||
|  |                 return true; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |                 // 2 bytes messages
 | ||||||
|  |             case ProgramChange: | ||||||
|  |             case AfterTouchChannel: | ||||||
|  |             case TimeCodeQuarterFrame: | ||||||
|  |             case SongSelect: | ||||||
|  |                 mPendingMessageExpectedLenght = 2; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |                 // 3 bytes messages
 | ||||||
|  |             case NoteOn: | ||||||
|  |             case NoteOff: | ||||||
|  |             case ControlChange: | ||||||
|  |             case PitchBend: | ||||||
|  |             case AfterTouchPoly: | ||||||
|  |             case SongPosition: | ||||||
|  |                 mPendingMessageExpectedLenght = 3; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |             case SystemExclusive: | ||||||
|  |                 // The message can be any lenght 
 | ||||||
|  |                 // between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
 | ||||||
|  |                 mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE;  | ||||||
|  |                 mRunningStatus_RX = InvalidType; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |             case InvalidType: | ||||||
|  |             default: | ||||||
|  |                 // This is obviously wrong. Let's get the hell out'a here.
 | ||||||
|  |                 resetInput(); | ||||||
|  |                 return false; | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Then update the index of the pending message.
 | ||||||
|  |         mPendingMessageIndex++; | ||||||
|  |          | ||||||
|  | #if USE_1BYTE_PARSING | ||||||
|  |         // Message is not complete.
 | ||||||
|  |         return false; | ||||||
|  | #else | ||||||
|  |         // Call the parser recursively
 | ||||||
|  |         // to parse the rest of the message.
 | ||||||
|  |         return parse(inChannel); | ||||||
|  | #endif | ||||||
|  |          | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     {  | ||||||
|  |         // First, test if this is a status byte
 | ||||||
|  |         if (extracted >= 0x80) | ||||||
|  |         { | ||||||
|  |             // Reception of status bytes in the middle of an uncompleted message
 | ||||||
|  |             // are allowed only for interleaved Real Time message or EOX
 | ||||||
|  |             switch (extracted) | ||||||
|  |             { | ||||||
|  |                 case Clock: | ||||||
|  |                 case Start: | ||||||
|  |                 case Continue: | ||||||
|  |                 case Stop: | ||||||
|  |                 case ActiveSensing: | ||||||
|  |                 case SystemReset: | ||||||
|  |                      | ||||||
|  |                     // Here we will have to extract the one-byte message, 
 | ||||||
|  |                     // pass it to the structure for being read outside
 | ||||||
|  |                     // the MIDI class, and recompose the message it was 
 | ||||||
|  |                     // interleaved into. Oh, and without killing the running status.. 
 | ||||||
|  |                     // This is done by leaving the pending message as is, 
 | ||||||
|  |                     // it will be completed on next calls.
 | ||||||
|  |                      | ||||||
|  |                     mMessage.type = (MidiType)extracted; | ||||||
|  |                     mMessage.data1 = 0; | ||||||
|  |                     mMessage.data2 = 0; | ||||||
|  |                     mMessage.channel = 0; | ||||||
|  |                     mMessage.valid = true; | ||||||
|  |                     return true; | ||||||
|  |                      | ||||||
|  |                     break; | ||||||
|  |                      | ||||||
|  |                     // End of Exclusive
 | ||||||
|  |                 case 0xF7: | ||||||
|  |                     if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) | ||||||
|  |                     { | ||||||
|  |                         // Store System Exclusive array in midimsg structure
 | ||||||
|  |                         for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) | ||||||
|  |                             mMessage.sysex_array[i] = mPendingMessage[i]; | ||||||
|  |                          | ||||||
|  |                         mMessage.type = SystemExclusive; | ||||||
|  |                          | ||||||
|  |                         // Get length
 | ||||||
|  |                         mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;     | ||||||
|  |                         mMessage.data2 = (mPendingMessageIndex+1) >> 8; | ||||||
|  |                         mMessage.channel = 0; | ||||||
|  |                         mMessage.valid = true; | ||||||
|  |                          | ||||||
|  |                         resetInput(); | ||||||
|  |                         return true; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         // Well well well.. error.
 | ||||||
|  |                         resetInput(); | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Add extracted data byte to pending message
 | ||||||
|  |         mPendingMessage[mPendingMessageIndex] = extracted; | ||||||
|  |          | ||||||
|  |         // Now we are going to check if we have reached the end of the message
 | ||||||
|  |         if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) | ||||||
|  |         { | ||||||
|  |             // "FML" case: fall down here with an overflown SysEx..
 | ||||||
|  |             // This means we received the last possible data byte that can fit 
 | ||||||
|  |             // the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
 | ||||||
|  |             if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) | ||||||
|  |             { | ||||||
|  |                 resetInput(); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); | ||||||
|  |             // Don't check if it is a Channel Message
 | ||||||
|  |             mMessage.channel = (mPendingMessage[0] & 0x0F)+1; | ||||||
|  |              | ||||||
|  |             mMessage.data1 = mPendingMessage[1]; | ||||||
|  |              | ||||||
|  |             // Save data2 only if applicable
 | ||||||
|  |             if (mPendingMessageExpectedLenght == 3) | ||||||
|  |                 mMessage.data2 = mPendingMessage[2]; | ||||||
|  |             else  | ||||||
|  |                 mMessage.data2 = 0; | ||||||
|  |              | ||||||
|  |             // Reset local variables
 | ||||||
|  |             mPendingMessageIndex = 0; | ||||||
|  |             mPendingMessageExpectedLenght = 0; | ||||||
|  |              | ||||||
|  |             mMessage.valid = true; | ||||||
|  |              | ||||||
|  |             // Activate running status (if enabled for the received type)
 | ||||||
|  |             switch (mMessage.type) | ||||||
|  |             { | ||||||
|  |                 case NoteOff: | ||||||
|  |                 case NoteOn: | ||||||
|  |                 case AfterTouchPoly: | ||||||
|  |                 case ControlChange: | ||||||
|  |                 case ProgramChange: | ||||||
|  |                 case AfterTouchChannel: | ||||||
|  |                 case PitchBend:     | ||||||
|  |                     // Running status enabled: store it from received message
 | ||||||
|  |                     mRunningStatus_RX = mPendingMessage[0]; | ||||||
|  |                     break; | ||||||
|  |                      | ||||||
|  |                 default: | ||||||
|  |                     // No running status
 | ||||||
|  |                     mRunningStatus_RX = InvalidType; | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|             const byte body     = inSysEx[i]; |             // Then update the index of the pending message.
 | ||||||
|             const byte shift    = inFlipHeaderBits ? 6 - byteIndex : byteIndex; |             mPendingMessageIndex++; | ||||||
|             const byte msb      = byte(((msbStorage >> shift) & 1) << 7); |              | ||||||
|             byteIndex--; | #if USE_1BYTE_PARSING | ||||||
|             outData[count++] = msb | body; |             // Message is not complete.
 | ||||||
|  |             return false; | ||||||
|  | #else | ||||||
|  |             // Call the parser recursively
 | ||||||
|  |             // to parse the rest of the message.
 | ||||||
|  |             return parse(inChannel); | ||||||
|  | #endif | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return count; |      | ||||||
|  |     // What are our chances to fall here?
 | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Private method: check if the received message is on the listened channel
 | ||||||
|  | bool MidiInterface::inputFilter(Channel inChannel) | ||||||
|  | { | ||||||
|  |     // This method handles recognition of channel 
 | ||||||
|  |     // (to know if the message is destinated to the Arduino)
 | ||||||
|  |      | ||||||
|  |     if (mMessage.type == InvalidType) | ||||||
|  |         return false; | ||||||
|  |      | ||||||
|  |     // First, check if the received message is Channel
 | ||||||
|  |     if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) | ||||||
|  |     { | ||||||
|  |         // Then we need to know if we listen to it
 | ||||||
|  |         if ((mMessage.channel == mInputChannel) ||  | ||||||
|  |             (mInputChannel == MIDI_CHANNEL_OMNI)) | ||||||
|  |         { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         else  | ||||||
|  |         { | ||||||
|  |             // We don't listen to this channel
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else  | ||||||
|  |     { | ||||||
|  |         // System messages are always received
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Private method: reset input attributes
 | ||||||
|  | void MidiInterface::resetInput() | ||||||
|  | { | ||||||
|  |     mPendingMessageIndex = 0; | ||||||
|  |     mPendingMessageExpectedLenght = 0; | ||||||
|  |     mRunningStatus_RX = InvalidType; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_USE_CALLBACKS | ||||||
|  | 
 | ||||||
|  | // Private - launch callback function based on received type.
 | ||||||
|  | void MidiInterface::launchCallback() | ||||||
|  | { | ||||||
|  |     // The order is mixed to allow frequent messages to trigger their callback faster.
 | ||||||
|  |     switch (mMessage.type) | ||||||
|  |     { | ||||||
|  |             // Notes
 | ||||||
|  |         case NoteOff:               if (mNoteOffCallback != 0)               mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2);   break; | ||||||
|  |         case NoteOn:                if (mNoteOnCallback != 0)                mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break; | ||||||
|  |              | ||||||
|  |             // Real-time messages
 | ||||||
|  |         case Clock:                 if (mClockCallback != 0)                 mClockCallback();           break;             | ||||||
|  |         case Start:                 if (mStartCallback != 0)                 mStartCallback();           break; | ||||||
|  |         case Continue:              if (mContinueCallback != 0)              mContinueCallback();        break; | ||||||
|  |         case Stop:                  if (mStopCallback != 0)                  mStopCallback();            break; | ||||||
|  |         case ActiveSensing:         if (mActiveSensingCallback != 0)         mActiveSensingCallback();   break; | ||||||
|  |              | ||||||
|  |             // Continuous controllers
 | ||||||
|  |         case ControlChange:         if (mControlChangeCallback != 0)         mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break; | ||||||
|  |         case PitchBend:             if (mPitchBendCallback != 0)             mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) + MIDI_PITCHBEND_MIN); break; // TODO: check this
 | ||||||
|  |         case AfterTouchPoly:        if (mAfterTouchPolyCallback != 0)        mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break; | ||||||
|  |         case AfterTouchChannel:     if (mAfterTouchChannelCallback != 0)     mAfterTouchChannelCallback(mMessage.channel,mMessage.data1);    break; | ||||||
|  |              | ||||||
|  |         case ProgramChange:         if (mProgramChangeCallback != 0)         mProgramChangeCallback(mMessage.channel,mMessage.data1);    break; | ||||||
|  |         case SystemExclusive:       if (mSystemExclusiveCallback != 0)       mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1);    break; | ||||||
|  |              | ||||||
|  |             // Occasional messages
 | ||||||
|  |         case TimeCodeQuarterFrame:  if (mTimeCodeQuarterFrameCallback != 0)  mTimeCodeQuarterFrameCallback(mMessage.data1);    break; | ||||||
|  |         case SongPosition:          if (mSongPositionCallback != 0)          mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7));    break; | ||||||
|  |         case SongSelect:            if (mSongSelectCallback != 0)            mSongSelectCallback(mMessage.data1);    break; | ||||||
|  |         case TuneRequest:           if (mTuneRequestCallback != 0)           mTuneRequestCallback();    break; | ||||||
|  |              | ||||||
|  |         case SystemReset:           if (mSystemResetCallback != 0)           mSystemResetCallback();    break; | ||||||
|  |         case InvalidType: | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_USE_CALLBACKS
 | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_BUILD_INPUT
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | //                                  Thru
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_BUILD_THRU | ||||||
|  | 
 | ||||||
|  | // This method is called upon reception of a message 
 | ||||||
|  | // and takes care of Thru filtering and sending.
 | ||||||
|  | 
 | ||||||
|  | void MidiInterface::thruFilter(Channel inChannel) | ||||||
|  | { | ||||||
|  |      | ||||||
|  |     /*
 | ||||||
|  |      This method handles Soft-Thru filtering. | ||||||
|  |       | ||||||
|  |      Soft-Thru filtering: | ||||||
|  |      - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off | ||||||
|  |      - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting | ||||||
|  |       | ||||||
|  |      */ | ||||||
|  |      | ||||||
|  |     // If the feature is disabled, don't do anything.
 | ||||||
|  |     if (!mThruActivated || (mThruFilterMode == Off)) | ||||||
|  |         return; | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     // First, check if the received message is Channel
 | ||||||
|  |     if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) | ||||||
|  |     { | ||||||
|  |         const bool filter_condition = ((mMessage.channel == mInputChannel) ||  | ||||||
|  |                                        (mInputChannel == MIDI_CHANNEL_OMNI)); | ||||||
|  |          | ||||||
|  |         // Now let's pass it to the output
 | ||||||
|  |         switch (mThruFilterMode) | ||||||
|  |         { | ||||||
|  |             case Full: | ||||||
|  |                 send(mMessage.type, | ||||||
|  |                      mMessage.data1, | ||||||
|  |                      mMessage.data2, | ||||||
|  |                      mMessage.channel); | ||||||
|  |                 return; | ||||||
|  |                 break; | ||||||
|  |             case SameChannel: | ||||||
|  |                 if (filter_condition) | ||||||
|  |                 { | ||||||
|  |                     send(mMessage.type, | ||||||
|  |                          mMessage.data1, | ||||||
|  |                          mMessage.data2, | ||||||
|  |                          mMessage.channel); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case DifferentChannel: | ||||||
|  |                 if (!filter_condition) | ||||||
|  |                 { | ||||||
|  |                     send(mMessage.type, | ||||||
|  |                          mMessage.data1, | ||||||
|  |                          mMessage.data2, | ||||||
|  |                          mMessage.channel); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case Off: | ||||||
|  |                 // Do nothing. 
 | ||||||
|  |                 // Technically it's impossible to get there because 
 | ||||||
|  |                 // the case was already tested earlier.
 | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         // Send the message to the output
 | ||||||
|  |         switch (mMessage.type) | ||||||
|  |         { | ||||||
|  |                 // Real Time and 1 byte
 | ||||||
|  |             case Clock: | ||||||
|  |             case Start: | ||||||
|  |             case Stop: | ||||||
|  |             case Continue: | ||||||
|  |             case ActiveSensing: | ||||||
|  |             case SystemReset: | ||||||
|  |             case TuneRequest:     | ||||||
|  |                 sendRealTime(mMessage.type); | ||||||
|  |                 return; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |             case SystemExclusive: | ||||||
|  |                 // Send SysEx (0xF0 and 0xF7 are included in the buffer)
 | ||||||
|  |                 sendSysEx(mMessage.data1,mMessage.sysex_array,true);  | ||||||
|  |                 return; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |             case SongSelect: | ||||||
|  |                 sendSongSelect(mMessage.data1); | ||||||
|  |                 return; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |             case SongPosition: | ||||||
|  |                 sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7)); | ||||||
|  |                 return; | ||||||
|  |                 break; | ||||||
|  |                  | ||||||
|  |             case TimeCodeQuarterFrame: | ||||||
|  |                 sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2); | ||||||
|  |                 return; | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_BUILD_THRU
 | ||||||
|  | 
 | ||||||
| END_MIDI_NAMESPACE | END_MIDI_NAMESPACE | ||||||
|  |  | ||||||
							
								
								
									
										339
									
								
								src/MIDI.h
								
								
								
								
							
							
						
						
									
										339
									
								
								src/MIDI.h
								
								
								
								
							|  | @ -2,164 +2,103 @@ | ||||||
|  *  @file       MIDI.h |  *  @file       MIDI.h | ||||||
|  *  Project     Arduino MIDI Library |  *  Project     Arduino MIDI Library | ||||||
|  *  @brief      MIDI Library for the Arduino |  *  @brief      MIDI Library for the Arduino | ||||||
|  *  @author     Francois Best, lathoub |  *  @version    4.0 | ||||||
|  |  *  @author     Francois Best  | ||||||
|  *  @date       24/02/11 |  *  @date       24/02/11 | ||||||
|  *  @license    MIT - Copyright (c) 2015 Francois Best |  *  license     GPL Forty Seven Effects - 2011 | ||||||
|  * |  | ||||||
|  * 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 | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "midi_Defs.h" |  | ||||||
| #include "midi_Platform.h" |  | ||||||
| #include "midi_Settings.h" | #include "midi_Settings.h" | ||||||
| #include "midi_Message.h" | #include "midi_Defs.h" | ||||||
| 
 | 
 | ||||||
| #include "serialMIDI.h" | #ifdef FSE_AVR | ||||||
|  | #   include "hardware_Serial.h" | ||||||
|  | #else | ||||||
|  | #   include "Arduino.h" | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| BEGIN_MIDI_NAMESPACE | 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.
 | /*! \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 Transport, class _Settings = DefaultSettings, class _Platform = DefaultPlatform> |  | ||||||
| class MidiInterface | class MidiInterface | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     typedef _Settings Settings; |     MidiInterface(); | ||||||
|     typedef _Platform Platform; |     ~MidiInterface(); | ||||||
|     typedef Message<Settings::SysExMaxSize> MidiMessage; |  | ||||||
|      |      | ||||||
| public: | public: | ||||||
|     inline  MidiInterface(Transport&); |     void begin(Channel inChannel = 1); | ||||||
|     inline ~MidiInterface(); |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     MidiInterface& begin(Channel inChannel = 1); |  | ||||||
|      |      | ||||||
|     // -------------------------------------------------------------------------
 |     // -------------------------------------------------------------------------
 | ||||||
|     // MIDI Output
 |     // MIDI Output
 | ||||||
|      |      | ||||||
|  | #if MIDI_BUILD_OUTPUT | ||||||
|  |      | ||||||
| public: | public: | ||||||
|     inline MidiInterface& sendNoteOn(DataByte inNoteNumber, |     inline void sendNoteOn(DataByte inNoteNumber, | ||||||
|                            DataByte inVelocity, |                            DataByte inVelocity, | ||||||
|                            Channel inChannel); |                            Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendNoteOff(DataByte inNoteNumber, |     inline void sendNoteOff(DataByte inNoteNumber, | ||||||
|                             DataByte inVelocity, |                             DataByte inVelocity, | ||||||
|                             Channel inChannel); |                             Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendProgramChange(DataByte inProgramNumber, |     inline void sendProgramChange(DataByte inProgramNumber, | ||||||
|                                   Channel inChannel); |                                   Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendControlChange(DataByte inControlNumber, |     inline void sendControlChange(DataByte inControlNumber, | ||||||
|                                   DataByte inControlValue,  |                                   DataByte inControlValue,  | ||||||
|                                   Channel inChannel); |                                   Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendPitchBend(int inPitchValue,    Channel inChannel); |     inline void sendPitchBend(int inPitchValue,    Channel inChannel); | ||||||
|     inline MidiInterface& sendPitchBend(double inPitchValue, Channel inChannel); |     inline void sendPitchBend(double inPitchValue, Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendPolyPressure(DataByte inNoteNumber, |     inline void sendPolyPressure(DataByte inNoteNumber, | ||||||
|                                  DataByte inPressure, |                                  DataByte inPressure, | ||||||
|                                  Channel inChannel) __attribute__ ((deprecated)); |                                  Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendAfterTouch(DataByte inPressure, |     inline void sendAfterTouch(DataByte inPressure, | ||||||
|                                Channel inChannel); |  | ||||||
|     inline MidiInterface& sendAfterTouch(DataByte inNoteNumber, |  | ||||||
|                                DataByte inPressure, |  | ||||||
|                                Channel inChannel); |                                Channel inChannel); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendSysEx(unsigned inLength, |     inline void sendSysEx(unsigned int inLength,  | ||||||
|                           const byte* inArray, |                           const byte* inArray, | ||||||
|                           bool inArrayContainsBoundaries = false);     |                           bool inArrayContainsBoundaries = false);     | ||||||
|      |      | ||||||
|     inline MidiInterface& sendTimeCodeQuarterFrame(DataByte inTypeNibble, |     inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble,  | ||||||
|                                          DataByte inValuesNibble); |                                          DataByte inValuesNibble); | ||||||
|     inline MidiInterface& sendTimeCodeQuarterFrame(DataByte inData); |     inline void sendTimeCodeQuarterFrame(DataByte inData); | ||||||
|      |      | ||||||
|     inline MidiInterface& sendSongPosition(unsigned inBeats); |     inline void sendSongPosition(unsigned int inBeats); | ||||||
|     inline MidiInterface& sendSongSelect(DataByte inSongNumber); |     inline void sendSongSelect(DataByte inSongNumber); | ||||||
|     inline MidiInterface& sendTuneRequest(); |     inline void sendTuneRequest(); | ||||||
| 
 |     inline void sendRealTime(MidiType inType); | ||||||
|     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: | public: | ||||||
|     MidiInterface& send(MidiType inType, |     void send(MidiType inType, | ||||||
|               DataByte inData1, |               DataByte inData1, | ||||||
|               DataByte inData2, |               DataByte inData2, | ||||||
|               Channel inChannel); |               Channel inChannel); | ||||||
|      |      | ||||||
|  | private: | ||||||
|  |     inline StatusByte getStatus(MidiType inType, | ||||||
|  |                                 Channel inChannel) const; | ||||||
|  |      | ||||||
|  | #endif // MIDI_BUILD_OUTPUT
 | ||||||
|  |      | ||||||
|  |      | ||||||
|     // -------------------------------------------------------------------------
 |     // -------------------------------------------------------------------------
 | ||||||
|     // MIDI Input
 |     // MIDI Input
 | ||||||
|      |      | ||||||
|  | #if MIDI_BUILD_INPUT | ||||||
|  |      | ||||||
| public: | public: | ||||||
|     inline bool read(); |     bool read(); | ||||||
|     inline bool read(Channel inChannel); |     bool read(Channel inChannel); | ||||||
|      |      | ||||||
| public: | public: | ||||||
|     inline MidiType getType() const; |     inline MidiType getType() const; | ||||||
|  | @ -167,141 +106,127 @@ public: | ||||||
|     inline DataByte getData1() const; |     inline DataByte getData1() const; | ||||||
|     inline DataByte getData2() const; |     inline DataByte getData2() const; | ||||||
|     inline const byte* getSysExArray() const; |     inline const byte* getSysExArray() const; | ||||||
|     inline unsigned getSysExArrayLength() const; |     inline unsigned int getSysExArrayLength() const; | ||||||
|     inline bool check() const; |     inline bool check() const; | ||||||
|      |      | ||||||
| public: | public: | ||||||
|     inline Channel getInputChannel() const; |     inline Channel getInputChannel() const; | ||||||
|     inline MidiInterface& setInputChannel(Channel inChannel); |     inline void setInputChannel(Channel inChannel); | ||||||
|      |      | ||||||
| public: | public: | ||||||
|     static inline MidiType getTypeFromStatusByte(byte inStatus); |     static inline MidiType getTypeFromStatusByte(const byte inStatus); | ||||||
|     static inline Channel getChannelFromStatusByte(byte inStatus); |      | ||||||
|     static inline bool isChannelMessage(MidiType inType); | private: | ||||||
|  |     bool inputFilter(Channel inChannel); | ||||||
|  |     bool parse(Channel inChannel); | ||||||
|  |     void resetInput(); | ||||||
|  |      | ||||||
|  | private: | ||||||
|  |     StatusByte mRunningStatus_RX; | ||||||
|  |     Channel    mInputChannel; | ||||||
|  |      | ||||||
|  |     byte         mPendingMessage[3];             // SysEx are dumped into mMessage directly.
 | ||||||
|  |     unsigned int mPendingMessageExpectedLenght; | ||||||
|  |     unsigned int mPendingMessageIndex;           // Extended to unsigned int for larger SysEx payloads.
 | ||||||
|  |     Message mMessage; | ||||||
|  |      | ||||||
|      |      | ||||||
|     // -------------------------------------------------------------------------
 |     // -------------------------------------------------------------------------
 | ||||||
|     // Input Callbacks
 |     // Input Callbacks
 | ||||||
|      |      | ||||||
| public: | #if MIDI_USE_CALLBACKS | ||||||
|     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 MidiInterface& disconnectCallbackFromType(MidiType inType); | 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, byte size)); | ||||||
|  |     inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)); | ||||||
|  |     inline void setHandleSongPosition(void (*fptr)(unsigned int 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 void disconnectCallbackFromType(MidiType inType); | ||||||
|      |      | ||||||
| private: | private: | ||||||
|  |      | ||||||
|     void launchCallback(); |     void launchCallback(); | ||||||
|      |      | ||||||
|     void (*mMessageCallback)(const MidiMessage& message) = nullptr; |     void (*mNoteOffCallback)(byte channel, byte note, byte velocity); | ||||||
|     ErrorCallback mErrorCallback = nullptr; |     void (*mNoteOnCallback)(byte channel, byte note, byte velocity); | ||||||
|     NoteOffCallback mNoteOffCallback = nullptr; |     void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity); | ||||||
|     NoteOnCallback mNoteOnCallback = nullptr; |     void (*mControlChangeCallback)(byte channel, byte, byte); | ||||||
|     AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr; |     void (*mProgramChangeCallback)(byte channel, byte); | ||||||
|     ControlChangeCallback mControlChangeCallback = nullptr; |     void (*mAfterTouchChannelCallback)(byte channel, byte); | ||||||
|     ProgramChangeCallback mProgramChangeCallback = nullptr; |     void (*mPitchBendCallback)(byte channel, int); | ||||||
|     AfterTouchChannelCallback mAfterTouchChannelCallback = nullptr; |     void (*mSystemExclusiveCallback)(byte * array, byte size); | ||||||
|     PitchBendCallback mPitchBendCallback = nullptr; |     void (*mTimeCodeQuarterFrameCallback)(byte data); | ||||||
|     SystemExclusiveCallback mSystemExclusiveCallback = nullptr; |     void (*mSongPositionCallback)(unsigned int beats); | ||||||
|     TimeCodeQuarterFrameCallback mTimeCodeQuarterFrameCallback = nullptr; |     void (*mSongSelectCallback)(byte songnumber); | ||||||
|     SongPositionCallback mSongPositionCallback = nullptr; |     void (*mTuneRequestCallback)(void); | ||||||
|     SongSelectCallback mSongSelectCallback = nullptr; |     void (*mClockCallback)(void); | ||||||
|     TuneRequestCallback mTuneRequestCallback = nullptr; |     void (*mStartCallback)(void); | ||||||
|     ClockCallback mClockCallback = nullptr; |     void (*mContinueCallback)(void); | ||||||
|     StartCallback mStartCallback = nullptr; |     void (*mStopCallback)(void); | ||||||
|     TickCallback mTickCallback = nullptr; |     void (*mActiveSensingCallback)(void); | ||||||
|     ContinueCallback mContinueCallback = nullptr; |     void (*mSystemResetCallback)(void); | ||||||
|     StopCallback mStopCallback = nullptr; |      | ||||||
|     ActiveSensingCallback mActiveSensingCallback = nullptr; | #endif // MIDI_USE_CALLBACKS    
 | ||||||
|     SystemResetCallback mSystemResetCallback = nullptr; |      | ||||||
|  | #endif // MIDI_BUILD_INPUT
 | ||||||
|  |      | ||||||
|      |      | ||||||
|     // -------------------------------------------------------------------------
 |     // -------------------------------------------------------------------------
 | ||||||
|     // MIDI Soft Thru
 |     // MIDI Soft Thru
 | ||||||
|      |      | ||||||
|  | #if MIDI_BUILD_THRU | ||||||
|  |      | ||||||
| public: | public: | ||||||
|     inline Thru::Mode getFilterMode() const; |     inline MidiFilterMode getFilterMode() const; | ||||||
|     inline bool getThruState() const; |     inline bool getThruState() const; | ||||||
|      |      | ||||||
|     inline MidiInterface& turnThruOn(Thru::Mode inThruFilterMode = Thru::Full); |     inline void turnThruOn(MidiFilterMode inThruFilterMode = Full); | ||||||
|     inline MidiInterface& turnThruOff(); |     inline void turnThruOff(); | ||||||
|     inline MidiInterface& setThruFilterMode(Thru::Mode inThruFilterMode); |     inline void setThruFilterMode(MidiFilterMode inThruFilterMode); | ||||||
|  |      | ||||||
|      |      | ||||||
| private: | private: | ||||||
|     void thruFilter(byte inChannel); |     void thruFilter(byte inChannel); | ||||||
|      |      | ||||||
|     // -------------------------------------------------------------------------
 |  | ||||||
|     // MIDI Parsing
 |  | ||||||
| 
 |  | ||||||
| private: | 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; |     bool            mThruActivated  : 1; | ||||||
|     Thru::Mode      mThruFilterMode : 7; |     MidiFilterMode  mThruFilterMode : 7; | ||||||
|     MidiMessage     mMessage; |      | ||||||
|     unsigned long   mLastMessageSentTime; | #endif // MIDI_BUILD_THRU
 | ||||||
|     unsigned long   mLastMessageReceivedTime; |      | ||||||
|     unsigned long   mSenderActiveSensingPeriodicity; | 
 | ||||||
|     bool            mReceiverActiveSensingActivated; | #if MIDI_USE_RUNNING_STATUS | ||||||
|     int8_t          mLastError; |  | ||||||
|      |      | ||||||
| private: | private: | ||||||
|     inline StatusByte getStatus(MidiType inType, |     StatusByte mRunningStatus_TX; | ||||||
|                                 Channel inChannel) const; |      | ||||||
|  | #endif // MIDI_USE_RUNNING_STATUS
 | ||||||
|  |      | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 |  | ||||||
| 
 |  | ||||||
| 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 | END_MIDI_NAMESPACE | ||||||
| 
 | 
 | ||||||
| #include "MIDI.hpp" | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_AUTO_INSTANCIATE | ||||||
|  |     extern MIDI_NAMESPACE::MidiInterface MIDI; | ||||||
|  | #endif // MIDI_AUTO_INSTANCIATE
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #include "midi_Inline.hpp" | ||||||
|  |  | ||||||
							
								
								
									
										1541
									
								
								src/MIDI.hpp
								
								
								
								
							
							
						
						
									
										1541
									
								
								src/MIDI.hpp
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										192
									
								
								src/midi_Defs.h
								
								
								
								
							
							
						
						
									
										192
									
								
								src/midi_Defs.h
								
								
								
								
							|  | @ -2,40 +2,26 @@ | ||||||
|  *  @file       midi_Defs.h |  *  @file       midi_Defs.h | ||||||
|  *  Project     Arduino MIDI Library |  *  Project     Arduino MIDI Library | ||||||
|  *  @brief      MIDI Library for the Arduino - Definitions |  *  @brief      MIDI Library for the Arduino - Definitions | ||||||
|  *  @author     Francois Best, lathoub |  *  @version    4.0 | ||||||
|  |  *  @author     Francois Best  | ||||||
|  *  @date       24/02/11 |  *  @date       24/02/11 | ||||||
|  *  @license    MIT - Copyright (c) 2015 Francois Best |  *  license     GPL Forty Seven Effects - 2011 | ||||||
|  * |  | ||||||
|  * 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 | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "midi_Namespace.h" | #include "midi_Namespace.h" | ||||||
| 
 | 
 | ||||||
| #if ARDUINO | // -----------------------------------------------------------------------------
 | ||||||
| #include <Arduino.h> | 
 | ||||||
|  | #ifdef FSE_AVR | ||||||
|  | #   include <core_Types.h> | ||||||
| #else | #else | ||||||
| #include <inttypes.h> | #   include <inttypes.h> | ||||||
| typedef uint8_t byte; |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
| BEGIN_MIDI_NAMESPACE | BEGIN_MIDI_NAMESPACE | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
|  | @ -46,77 +32,40 @@ BEGIN_MIDI_NAMESPACE | ||||||
| #define MIDI_PITCHBEND_MIN      -8192 | #define MIDI_PITCHBEND_MIN      -8192 | ||||||
| #define MIDI_PITCHBEND_MAX      8191 | #define MIDI_PITCHBEND_MAX      8191 | ||||||
| 
 | 
 | ||||||
| /*! Receiving Active Sensing 
 |  | ||||||
| */ |  | ||||||
| static const uint16_t ActiveSensingTimeout = 300; |  | ||||||
| 
 |  | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| // Type definitions
 | // Type definitions
 | ||||||
| 
 | 
 | ||||||
|  | #ifndef FSE_AVR | ||||||
|  | typedef uint8_t  byte; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| typedef byte StatusByte; | typedef byte StatusByte; | ||||||
| typedef byte DataByte; | typedef byte DataByte; | ||||||
| typedef byte Channel; | typedef byte Channel; | ||||||
| typedef byte FilterMode; | 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 */ | /*! Enumeration of MIDI types */ | ||||||
| enum MidiType: uint8_t | enum MidiType  | ||||||
| { | { | ||||||
|     InvalidType           = 0x00,    ///< For notifying errors
 |     InvalidType           = 0x00,    ///< For notifying errors
 | ||||||
|     NoteOff               = 0x80,    ///< Channel Message - Note Off
 |     NoteOff               = 0x80,    ///< Note Off
 | ||||||
|     NoteOn                = 0x90,    ///< Channel Message - Note On
 |     NoteOn                = 0x90,    ///< Note On
 | ||||||
|     AfterTouchPoly        = 0xA0,    ///< Channel Message - Polyphonic AfterTouch
 |     AfterTouchPoly        = 0xA0,    ///< Polyphonic AfterTouch
 | ||||||
|     ControlChange         = 0xB0,    ///< Channel Message - Control Change / Channel Mode
 |     ControlChange         = 0xB0,    ///< Control Change / Channel Mode
 | ||||||
|     ProgramChange         = 0xC0,    ///< Channel Message - Program Change
 |     ProgramChange         = 0xC0,    ///< Program Change
 | ||||||
|     AfterTouchChannel     = 0xD0,    ///< Channel Message - Channel (monophonic) AfterTouch
 |     AfterTouchChannel     = 0xD0,    ///< Channel (monophonic) AfterTouch
 | ||||||
|     PitchBend             = 0xE0,    ///< Channel Message - Pitch Bend
 |     PitchBend             = 0xE0,    ///< Pitch Bend
 | ||||||
|     SystemExclusive       = 0xF0,    ///< System Exclusive
 |     SystemExclusive       = 0xF0,    ///< System Exclusive
 | ||||||
| 	SystemExclusiveStart  = SystemExclusive,   ///< System Exclusive Start
 |  | ||||||
|     TimeCodeQuarterFrame  = 0xF1,    ///< System Common - MIDI Time Code Quarter Frame
 |     TimeCodeQuarterFrame  = 0xF1,    ///< System Common - MIDI Time Code Quarter Frame
 | ||||||
|     SongPosition          = 0xF2,    ///< System Common - Song Position Pointer
 |     SongPosition          = 0xF2,    ///< System Common - Song Position Pointer
 | ||||||
|     SongSelect            = 0xF3,    ///< System Common - Song Select
 |     SongSelect            = 0xF3,    ///< System Common - Song Select
 | ||||||
|     Undefined_F4          = 0xF4, |  | ||||||
|     Undefined_F5          = 0xF5, |  | ||||||
|     TuneRequest           = 0xF6,    ///< System Common - Tune Request
 |     TuneRequest           = 0xF6,    ///< System Common - Tune Request
 | ||||||
| 	SystemExclusiveEnd    = 0xF7,    ///< System Exclusive End
 |  | ||||||
|     Clock                 = 0xF8,    ///< System Real Time - Timing Clock
 |     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
 |     Start                 = 0xFA,    ///< System Real Time - Start
 | ||||||
|     Continue              = 0xFB,    ///< System Real Time - Continue
 |     Continue              = 0xFB,    ///< System Real Time - Continue
 | ||||||
|     Stop                  = 0xFC,    ///< System Real Time - Stop
 |     Stop                  = 0xFC,    ///< System Real Time - Stop
 | ||||||
|     Undefined_FD          = 0xFD, |  | ||||||
|     ActiveSensing         = 0xFE,    ///< System Real Time - Active Sensing
 |     ActiveSensing         = 0xFE,    ///< System Real Time - Active Sensing
 | ||||||
|     SystemReset           = 0xFF,    ///< System Real Time - System Reset
 |     SystemReset           = 0xFF,    ///< System Real Time - System Reset
 | ||||||
| }; | }; | ||||||
|  | @ -124,24 +73,17 @@ enum MidiType: uint8_t | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| /*! Enumeration of Thru filter modes */ | /*! Enumeration of Thru filter modes */ | ||||||
| struct Thru | enum MidiFilterMode  | ||||||
| { | { | ||||||
|     enum Mode |     Off                   = 0,  ///< Thru disabled (nothing passes through).
 | ||||||
|     { |     Full                  = 1,  ///< Fully enabled Thru (every incoming message is sent back).
 | ||||||
|         Off                   = 0,  ///< Thru disabled (nothing passes through).
 |     SameChannel           = 2,  ///< Only the messages on the Input Channel will be sent back.
 | ||||||
|         Full                  = 1,  ///< Fully enabled Thru (every incoming message is sent back).
 |     DifferentChannel      = 3,  ///< All the messages but the ones on the Input Channel will be 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.
 |  | ||||||
|     }; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // -----------------------------------------------------------------------------
 | // -----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| /*! \brief Enumeration of Control Change command numbers.
 | enum MidiControlChangeNumber | ||||||
|  See the detailed controllers numbers & description here: |  | ||||||
|  http://www.somascape.org/midi/tech/spec.html#ctrlnums
 |  | ||||||
|  */ |  | ||||||
| enum MidiControlChangeNumber: uint8_t |  | ||||||
| { | { | ||||||
|     // High resolution Continuous Controllers MSB (+32 for LSB) ----------------
 |     // High resolution Continuous Controllers MSB (+32 for LSB) ----------------
 | ||||||
|     BankSelect                  = 0, |     BankSelect                  = 0, | ||||||
|  | @ -150,7 +92,7 @@ enum MidiControlChangeNumber: uint8_t | ||||||
|     // CC3 undefined
 |     // CC3 undefined
 | ||||||
|     FootController              = 4, |     FootController              = 4, | ||||||
|     PortamentoTime              = 5, |     PortamentoTime              = 5, | ||||||
|     DataEntryMSB                = 6, |     DataEntry                   = 6, | ||||||
|     ChannelVolume               = 7, |     ChannelVolume               = 7, | ||||||
|     Balance                     = 8, |     Balance                     = 8, | ||||||
|     // CC9 undefined
 |     // CC9 undefined
 | ||||||
|  | @ -164,22 +106,6 @@ enum MidiControlChangeNumber: uint8_t | ||||||
|     GeneralPurposeController2   = 17, |     GeneralPurposeController2   = 17, | ||||||
|     GeneralPurposeController3   = 18, |     GeneralPurposeController3   = 18, | ||||||
|     GeneralPurposeController4   = 19, |     GeneralPurposeController4   = 19, | ||||||
|     // CC20 to CC31 undefined
 |  | ||||||
|     BankSelectLSB               = 32, |  | ||||||
|     ModulationWheelLSB          = 33, |  | ||||||
|     BreathControllerLSB         = 34, |  | ||||||
|     // CC35 undefined
 |  | ||||||
|     FootControllerLSB           = 36, |  | ||||||
|     PortamentoTimeLSB           = 37, |  | ||||||
|     DataEntryLSB                = 38, |  | ||||||
|     ChannelVolumeLSB            = 39, |  | ||||||
|     BalanceLSB                  = 40, |  | ||||||
|     // CC41 undefined
 |  | ||||||
|     PanLSB                      = 42, |  | ||||||
|     ExpressionControllerLSB     = 43, |  | ||||||
|     EffectControl1LSB           = 44, |  | ||||||
|     EffectControl2LSB           = 45, |  | ||||||
|     // CC46 to CC63 undefined
 |  | ||||||
|      |      | ||||||
|     // Switches ----------------------------------------------------------------
 |     // Switches ----------------------------------------------------------------
 | ||||||
|     Sustain                     = 64, |     Sustain                     = 64, | ||||||
|  | @ -211,13 +137,6 @@ enum MidiControlChangeNumber: uint8_t | ||||||
|     Effects3                    = 93,   ///< Chorus send level
 |     Effects3                    = 93,   ///< Chorus send level
 | ||||||
|     Effects4                    = 94,   ///< Celeste depth
 |     Effects4                    = 94,   ///< Celeste depth
 | ||||||
|     Effects5                    = 95,   ///< Phaser 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 ---------------------------------------------------
 |     // Channel Mode messages ---------------------------------------------------
 | ||||||
|     AllSoundOff                 = 120, |     AllSoundOff                 = 120, | ||||||
|  | @ -230,18 +149,49 @@ enum MidiControlChangeNumber: uint8_t | ||||||
|     PolyModeOn                  = 127 |     PolyModeOn                  = 127 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct RPN | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | /*! The midimsg structure contains decoded data 
 | ||||||
|  |     of a MIDI message read from the serial port  | ||||||
|  |     with read() or thru(). | ||||||
|  |  */ | ||||||
|  | struct Message | ||||||
| { | { | ||||||
|     enum RegisteredParameterNumbers: uint16_t |      | ||||||
|     { |     /*! The MIDI channel on which the message was recieved.
 | ||||||
|         PitchBendSensitivity    = 0x0000, |      \n Value goes from 1 to 16.  | ||||||
|         ChannelFineTuning       = 0x0001, |      */ | ||||||
|         ChannelCoarseTuning     = 0x0002, |     Channel channel;  | ||||||
|         SelectTuningProgram     = 0x0003, |      | ||||||
|         SelectTuningBank        = 0x0004, |     /*! The type of the message 
 | ||||||
|         ModulationDepthRange    = 0x0005, |      (see the MidiType enum for types reference) | ||||||
|         NullFunction            = (0x7f << 7) + 0x7f, |      */ | ||||||
|     }; |     MidiType type; | ||||||
|  |      | ||||||
|  |     /*! The first data byte.
 | ||||||
|  |      \n Value goes from 0 to 127. | ||||||
|  |      */ | ||||||
|  |     DataByte data1; | ||||||
|  |      | ||||||
|  |     /*! The second data byte. 
 | ||||||
|  |      If the message is only 2 bytes long, this one is null. | ||||||
|  |      \n Value goes from 0 to 127. | ||||||
|  |      */ | ||||||
|  |     DataByte data2; | ||||||
|  |      | ||||||
|  |     /*! System Exclusive dedicated byte array.
 | ||||||
|  |      \n Array length is stocked on 16 bits,  | ||||||
|  |      in data1 (LSB) and data2 (MSB) | ||||||
|  |      */ | ||||||
|  |     DataByte sysex_array[MIDI_SYSEX_ARRAY_SIZE]; | ||||||
|  |      | ||||||
|  |     /*! This boolean indicates if the message is valid or not.
 | ||||||
|  |      There is no channel consideration here,  | ||||||
|  |      validity means the message respects the MIDI norm. | ||||||
|  |      */ | ||||||
|  |     bool valid; | ||||||
|  |      | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| END_MIDI_NAMESPACE | END_MIDI_NAMESPACE | ||||||
|  |  | ||||||
|  | @ -0,0 +1,477 @@ | ||||||
|  | /*!
 | ||||||
|  |  *  @file       midi_Inline.hpp | ||||||
|  |  *  Project     Arduino MIDI Library | ||||||
|  |  *  @brief      MIDI Library for the Arduino - Inline implementations | ||||||
|  |  *  @version    4.0 | ||||||
|  |  *  @author     Francois Best  | ||||||
|  |  *  @date       24/02/11 | ||||||
|  |  *  license     GPL Forty Seven Effects - 2011 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | BEGIN_MIDI_NAMESPACE | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | //                                 Output
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_BUILD_OUTPUT | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Note On message 
 | ||||||
|  |  \param inNoteNumber  Pitch value in the MIDI format (0 to 127).  | ||||||
|  |  \param inVelocity    Note attack velocity (0 to 127). A NoteOn with 0 velocity  | ||||||
|  |  is considered as a NoteOff. | ||||||
|  |  \param inChannel     The channel on which the message will be sent (1 to 16).  | ||||||
|  |   | ||||||
|  |  Take a look at the values, names and frequencies of notes here:  | ||||||
|  |  http://www.phys.unsw.edu.au/jw/notes.html
 | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendNoteOn(DataByte inNoteNumber, | ||||||
|  |                                DataByte inVelocity, | ||||||
|  |                                Channel inChannel) | ||||||
|  | {  | ||||||
|  |     send(NoteOn, inNoteNumber, inVelocity, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Note Off message
 | ||||||
|  |  \param inNoteNumber  Pitch value in the MIDI format (0 to 127).  | ||||||
|  |  \param inVelocity    Release velocity (0 to 127). | ||||||
|  |  \param inChannel     The channel on which the message will be sent (1 to 16). | ||||||
|  |   | ||||||
|  |  Note: you can send NoteOn with zero velocity to make a NoteOff, this is based | ||||||
|  |  on the Running Status principle, to avoid sending status messages and thus | ||||||
|  |  sending only NoteOn data. This method will always send a real NoteOff message. | ||||||
|  |  Take a look at the values, names and frequencies of notes here:  | ||||||
|  |  http://www.phys.unsw.edu.au/jw/notes.html
 | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendNoteOff(DataByte inNoteNumber, | ||||||
|  |                                 DataByte inVelocity, | ||||||
|  |                                 Channel inChannel) | ||||||
|  | { | ||||||
|  |     send(NoteOff, inNoteNumber, inVelocity, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Program Change message 
 | ||||||
|  |  \param inProgramNumber The Program to select (0 to 127). | ||||||
|  |  \param inChannel       The channel on which the message will be sent (1 to 16). | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendProgramChange(DataByte inProgramNumber, | ||||||
|  |                                       Channel inChannel) | ||||||
|  | { | ||||||
|  |     send(ProgramChange, inProgramNumber, 0, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Control Change message 
 | ||||||
|  |  \param ControlNumber   The controller number (0 to 127).  | ||||||
|  |  \param ControlValue    The value for the specified controller (0 to 127). | ||||||
|  |  \param Channel         The channel on which the message will be sent (1 to 16).  | ||||||
|  |   | ||||||
|  |  See the detailed controllers numbers & description here:  | ||||||
|  |  http://www.somascape.org/midi/tech/spec.html#ctrlnums
 | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendControlChange(DataByte inControlNumber, | ||||||
|  |                                       DataByte inControlValue, | ||||||
|  |                                       Channel inChannel) | ||||||
|  | { | ||||||
|  |     send(ControlChange, inControlNumber, inControlValue, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
 | ||||||
|  |  \param NoteNumber  The note to apply AfterTouch to (0 to 127). | ||||||
|  |  \param Pressure    The amount of AfterTouch to apply (0 to 127). | ||||||
|  |  \param Channel     The channel on which the message will be sent (1 to 16).  | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendPolyPressure(DataByte inNoteNumber, | ||||||
|  |                                      DataByte inPressure, | ||||||
|  |                                      Channel inChannel) | ||||||
|  | { | ||||||
|  |     send(AfterTouchPoly, inNoteNumber, inPressure, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a MonoPhonic AfterTouch message (applies to all notes)
 | ||||||
|  |  \param Pressure    The amount of AfterTouch to apply to all notes. | ||||||
|  |  \param Channel     The channel on which the message will be sent (1 to 16).  | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendAfterTouch(DataByte inPressure, | ||||||
|  |                                    Channel inChannel) | ||||||
|  | { | ||||||
|  |     send(AfterTouchChannel, inPressure, 0, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Pitch Bend message using a signed integer value.
 | ||||||
|  |  \param PitchValue  The amount of bend to send (in a signed integer format),  | ||||||
|  |  between MIDI_PITCHBEND_MIN and MIDI_PITCHBEND_MAX,  | ||||||
|  |  center value is 0. | ||||||
|  |  \param Channel     The channel on which the message will be sent (1 to 16). | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendPitchBend(int inPitchValue, | ||||||
|  |                                   Channel inChannel) | ||||||
|  | { | ||||||
|  |     const unsigned int bend = inPitchValue - MIDI_PITCHBEND_MIN; | ||||||
|  |     send(PitchBend, (bend & 0x7F), (bend >> 7) & 0x7F, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Pitch Bend message using a floating point value.
 | ||||||
|  |  \param PitchValue  The amount of bend to send (in a floating point format),  | ||||||
|  |  between -1.0f (maximum downwards bend)  | ||||||
|  |  and +1.0f (max upwards bend), center value is 0.0f. | ||||||
|  |  \param Channel     The channel on which the message will be sent (1 to 16). | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendPitchBend(double inPitchValue, | ||||||
|  |                                   Channel inChannel) | ||||||
|  | { | ||||||
|  |     const int value = inPitchValue * MIDI_PITCHBEND_MAX; | ||||||
|  |     sendPitchBend(value, inChannel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Generate and send a System Exclusive frame.
 | ||||||
|  |  \param length  The size of the array to send | ||||||
|  |  \param array   The byte array containing the data to send | ||||||
|  |  \param ArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes | ||||||
|  |  (start & stop SysEx) will NOT be sent | ||||||
|  |  (and therefore must be included in the array). | ||||||
|  |  default value for ArrayContainsBoundaries is set to 'false' for compatibility | ||||||
|  |  with previous versions of the library. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendSysEx(unsigned int inLength, | ||||||
|  |                               const byte* inArray, | ||||||
|  |                               bool inArrayContainsBoundaries) | ||||||
|  | { | ||||||
|  |     if (inArrayContainsBoundaries == false) | ||||||
|  |     { | ||||||
|  |         MIDI_SERIAL_PORT.write(0xF0); | ||||||
|  |          | ||||||
|  |         for (unsigned int i=0;i<inLength;++i) | ||||||
|  |             MIDI_SERIAL_PORT.write(inArray[i]); | ||||||
|  |          | ||||||
|  |         MIDI_SERIAL_PORT.write(0xF7); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         for (unsigned int i=0;i<inLength;++i) | ||||||
|  |             MIDI_SERIAL_PORT.write(inArray[i]); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | #if MIDI_USE_RUNNING_STATUS | ||||||
|  |     mRunningStatus_TX = InvalidType; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Tune Request message. 
 | ||||||
|  |   | ||||||
|  |  When a MIDI unit receives this message, | ||||||
|  |  it should tune its oscillators (if equipped with any). | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendTuneRequest() | ||||||
|  | { | ||||||
|  |     sendRealTime(TuneRequest); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a MIDI Time Code Quarter Frame. 
 | ||||||
|  |   | ||||||
|  |  \param TypeNibble      MTC type | ||||||
|  |  \param ValuesNibble    MTC data | ||||||
|  |  See MIDI Specification for more information. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendTimeCodeQuarterFrame(DataByte inTypeNibble,  | ||||||
|  |                                              DataByte inValuesNibble) | ||||||
|  | { | ||||||
|  |     const byte data = ( ((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0F) ); | ||||||
|  |     sendTimeCodeQuarterFrame(data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a MIDI Time Code Quarter Frame. 
 | ||||||
|  |   | ||||||
|  |  See MIDI Specification for more information. | ||||||
|  |  \param data     if you want to encode directly the nibbles in your program,  | ||||||
|  |  you can send the byte here. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendTimeCodeQuarterFrame(DataByte inData) | ||||||
|  | { | ||||||
|  |     MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame); | ||||||
|  |     MIDI_SERIAL_PORT.write(inData); | ||||||
|  |      | ||||||
|  | #if MIDI_USE_RUNNING_STATUS | ||||||
|  |     mRunningStatus_TX = InvalidType; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Song Position Pointer message.
 | ||||||
|  |  \param Beats    The number of beats since the start of the song. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendSongPosition(unsigned int inBeats) | ||||||
|  | { | ||||||
|  |     MIDI_SERIAL_PORT.write((byte)SongPosition); | ||||||
|  |     MIDI_SERIAL_PORT.write(inBeats & 0x7F); | ||||||
|  |     MIDI_SERIAL_PORT.write((inBeats >> 7) & 0x7F); | ||||||
|  |      | ||||||
|  | #if MIDI_USE_RUNNING_STATUS | ||||||
|  |     mRunningStatus_TX = InvalidType; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Song Select message */ | ||||||
|  | void MidiInterface::sendSongSelect(DataByte inSongNumber) | ||||||
|  | { | ||||||
|  |     MIDI_SERIAL_PORT.write((byte)SongSelect); | ||||||
|  |     MIDI_SERIAL_PORT.write(inSongNumber & 0x7F); | ||||||
|  |      | ||||||
|  | #if MIDI_USE_RUNNING_STATUS | ||||||
|  |     mRunningStatus_TX = InvalidType; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Send a Real Time (one byte) message. 
 | ||||||
|  |   | ||||||
|  |  \param Type    The available Real Time types are:  | ||||||
|  |  Start, Stop, Continue, Clock, ActiveSensing and SystemReset. | ||||||
|  |  You can also send a Tune Request with this method. | ||||||
|  |  @see MidiType | ||||||
|  |  */ | ||||||
|  | void MidiInterface::sendRealTime(MidiType inType) | ||||||
|  | { | ||||||
|  |     switch (inType)  | ||||||
|  |     { | ||||||
|  |         case TuneRequest: // Not really real-time, but one byte anyway.
 | ||||||
|  |         case Clock: | ||||||
|  |         case Start: | ||||||
|  |         case Stop:     | ||||||
|  |         case Continue: | ||||||
|  |         case ActiveSensing: | ||||||
|  |         case SystemReset: | ||||||
|  |             MIDI_SERIAL_PORT.write((byte)inType); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             // Invalid Real Time marker
 | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Do not cancel Running Status for real-time messages as they can be 
 | ||||||
|  |     // interleaved within any message. Though, TuneRequest can be sent here, 
 | ||||||
|  |     // and as it is a System Common message, it must reset Running Status.
 | ||||||
|  | #if MIDI_USE_RUNNING_STATUS | ||||||
|  |     if (inType == TuneRequest) mRunningStatus_TX = InvalidType; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | StatusByte MidiInterface::getStatus(MidiType inType, | ||||||
|  |                                     Channel inChannel) const | ||||||
|  | { | ||||||
|  |     return ((byte)inType | ((inChannel - 1) & 0x0F)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_BUILD_OUTPUT
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | //                                  Input
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_BUILD_INPUT | ||||||
|  | 
 | ||||||
|  | /*! \brief Get the last received message's type
 | ||||||
|  |   | ||||||
|  |  Returns an enumerated type. @see MidiType | ||||||
|  |  */ | ||||||
|  | MidiType MidiInterface::getType() const | ||||||
|  | { | ||||||
|  |     return mMessage.type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Get the channel of the message stored in the structure.
 | ||||||
|  |   | ||||||
|  |  \return Channel range is 1 to 16.  | ||||||
|  |  For non-channel messages, this will return 0. | ||||||
|  |  */ | ||||||
|  | Channel MidiInterface::getChannel() const | ||||||
|  | { | ||||||
|  |     return mMessage.channel; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Get the first data byte of the last received message. */ | ||||||
|  | DataByte MidiInterface::getData1() const | ||||||
|  | { | ||||||
|  |     return mMessage.data1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Get the second data byte of the last received message. */ | ||||||
|  | DataByte MidiInterface::getData2() const | ||||||
|  | {  | ||||||
|  |     return mMessage.data2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Get the System Exclusive byte array. 
 | ||||||
|  |   | ||||||
|  |  @see getSysExArrayLength to get the array's length in bytes. | ||||||
|  |  */ | ||||||
|  | const byte* MidiInterface::getSysExArray() const | ||||||
|  | {  | ||||||
|  |     return mMessage.sysex_array; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Get the lenght of the System Exclusive array.
 | ||||||
|  |   | ||||||
|  |  It is coded using data1 as LSB and data2 as MSB. | ||||||
|  |  \return The array's length, in bytes. | ||||||
|  |  */ | ||||||
|  | unsigned int MidiInterface::getSysExArrayLength() const | ||||||
|  | { | ||||||
|  |     const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1; | ||||||
|  |     return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Check if a valid message is stored in the structure. */ | ||||||
|  | bool MidiInterface::check() const | ||||||
|  | {  | ||||||
|  |     return mMessage.valid; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | Channel MidiInterface::getInputChannel() const  | ||||||
|  | { | ||||||
|  |     return mInputChannel; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Set the value for the input MIDI channel 
 | ||||||
|  |  \param Channel the channel value. Valid values are 1 to 16, MIDI_CHANNEL_OMNI  | ||||||
|  |  if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::setInputChannel(Channel inChannel) | ||||||
|  | {  | ||||||
|  |     mInputChannel = inChannel; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | /*! \brief Extract an enumerated MIDI type from a status byte.
 | ||||||
|  |   | ||||||
|  |  This is a utility static method, used internally,  | ||||||
|  |  made public so you can handle MidiTypes more easily. | ||||||
|  |  */ | ||||||
|  | MidiType MidiInterface::getTypeFromStatusByte(const byte inStatus)  | ||||||
|  | { | ||||||
|  |     if ((inStatus  < 0x80) || | ||||||
|  |         (inStatus == 0xF4) || | ||||||
|  |         (inStatus == 0xF5) || | ||||||
|  |         (inStatus == 0xF9) || | ||||||
|  |         (inStatus == 0xFD)) return InvalidType; // data bytes and undefined.
 | ||||||
|  |     if (inStatus < 0xF0) return (MidiType)(inStatus & 0xF0);    // Channel message, remove channel nibble.
 | ||||||
|  |     else return (MidiType)inStatus; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if MIDI_USE_CALLBACKS | ||||||
|  | 
 | ||||||
|  | void MidiInterface::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity))          { mNoteOffCallback              = fptr; } | ||||||
|  | void MidiInterface::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity))           { mNoteOnCallback               = fptr; } | ||||||
|  | void MidiInterface::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure))   { mAfterTouchPolyCallback       = fptr; } | ||||||
|  | void MidiInterface::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value))     { mControlChangeCallback        = fptr; } | ||||||
|  | void MidiInterface::setHandleProgramChange(void (*fptr)(byte channel, byte number))                 { mProgramChangeCallback        = fptr; } | ||||||
|  | void MidiInterface::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure))           { mAfterTouchChannelCallback    = fptr; } | ||||||
|  | void MidiInterface::setHandlePitchBend(void (*fptr)(byte channel, int bend))                        { mPitchBendCallback            = fptr; } | ||||||
|  | void MidiInterface::setHandleSystemExclusive(void (*fptr)(byte* array, byte size))                  { mSystemExclusiveCallback      = fptr; } | ||||||
|  | void MidiInterface::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data))                          { mTimeCodeQuarterFrameCallback = fptr; } | ||||||
|  | void MidiInterface::setHandleSongPosition(void (*fptr)(unsigned int beats))                         { mSongPositionCallback         = fptr; } | ||||||
|  | void MidiInterface::setHandleSongSelect(void (*fptr)(byte songnumber))                              { mSongSelectCallback           = fptr; } | ||||||
|  | void MidiInterface::setHandleTuneRequest(void (*fptr)(void))                                        { mTuneRequestCallback          = fptr; } | ||||||
|  | void MidiInterface::setHandleClock(void (*fptr)(void))                                              { mClockCallback                = fptr; } | ||||||
|  | void MidiInterface::setHandleStart(void (*fptr)(void))                                              { mStartCallback                = fptr; } | ||||||
|  | void MidiInterface::setHandleContinue(void (*fptr)(void))                                           { mContinueCallback             = fptr; } | ||||||
|  | void MidiInterface::setHandleStop(void (*fptr)(void))                                               { mStopCallback                 = fptr; } | ||||||
|  | void MidiInterface::setHandleActiveSensing(void (*fptr)(void))                                      { mActiveSensingCallback        = fptr; } | ||||||
|  | void MidiInterface::setHandleSystemReset(void (*fptr)(void))                                        { mSystemResetCallback          = fptr; } | ||||||
|  | 
 | ||||||
|  | /*! \brief Detach an external function from the given type.
 | ||||||
|  |   | ||||||
|  |  Use this method to cancel the effects of setHandle********. | ||||||
|  |  \param Type        The type of message to unbind.  | ||||||
|  |  When a message of this type is received, no function will be called. | ||||||
|  |  */ | ||||||
|  | void MidiInterface::disconnectCallbackFromType(MidiType inType) | ||||||
|  | { | ||||||
|  |     switch (inType) | ||||||
|  |     { | ||||||
|  |         case NoteOff:               mNoteOffCallback                = 0; break; | ||||||
|  |         case NoteOn:                mNoteOnCallback                 = 0; break; | ||||||
|  |         case AfterTouchPoly:        mAfterTouchPolyCallback         = 0; break; | ||||||
|  |         case ControlChange:         mControlChangeCallback          = 0; break; | ||||||
|  |         case ProgramChange:         mProgramChangeCallback          = 0; break; | ||||||
|  |         case AfterTouchChannel:     mAfterTouchChannelCallback      = 0; break; | ||||||
|  |         case PitchBend:             mPitchBendCallback              = 0; break; | ||||||
|  |         case SystemExclusive:       mSystemExclusiveCallback        = 0; break; | ||||||
|  |         case TimeCodeQuarterFrame:  mTimeCodeQuarterFrameCallback   = 0; break; | ||||||
|  |         case SongPosition:          mSongPositionCallback           = 0; break; | ||||||
|  |         case SongSelect:            mSongSelectCallback             = 0; break; | ||||||
|  |         case TuneRequest:           mTuneRequestCallback            = 0; break; | ||||||
|  |         case Clock:                 mClockCallback                  = 0; break; | ||||||
|  |         case Start:                 mStartCallback                  = 0; break; | ||||||
|  |         case Continue:              mContinueCallback               = 0; break; | ||||||
|  |         case Stop:                  mStopCallback                   = 0; break; | ||||||
|  |         case ActiveSensing:         mActiveSensingCallback          = 0; break; | ||||||
|  |         case SystemReset:           mSystemResetCallback            = 0; break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_USE_CALLBACKS
 | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_BUILD_INPUT
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | //                                  Thru
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | #if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) | ||||||
|  | 
 | ||||||
|  | MidiFilterMode MidiInterface::getFilterMode() const | ||||||
|  | { | ||||||
|  |     return mThruFilterMode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MidiInterface::getThruState() const | ||||||
|  | { | ||||||
|  |     return mThruActivated; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Setter method: turn message mirroring on. */ | ||||||
|  | void MidiInterface::turnThruOn(MidiFilterMode inThruFilterMode) | ||||||
|  | {  | ||||||
|  |     mThruActivated = true; | ||||||
|  |     mThruFilterMode = inThruFilterMode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*! \brief Setter method: turn message mirroring off. */ | ||||||
|  | void MidiInterface::turnThruOff() | ||||||
|  | { | ||||||
|  |     mThruActivated = false;  | ||||||
|  |     mThruFilterMode = Off; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*! \brief Set the filter for thru mirroring
 | ||||||
|  |  \param inThruFilterMode a filter mode | ||||||
|  |   | ||||||
|  |  @see MidiFilterMode | ||||||
|  |  */ | ||||||
|  | void MidiInterface::setThruFilterMode(MidiFilterMode inThruFilterMode) | ||||||
|  | {  | ||||||
|  |     mThruFilterMode = inThruFilterMode; | ||||||
|  |     if (mThruFilterMode != Off)  | ||||||
|  |         mThruActivated = true; | ||||||
|  |     else  | ||||||
|  |         mThruActivated = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // MIDI_BUILD_THRU
 | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | END_MIDI_NAMESPACE | ||||||
|  | @ -1,131 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  *  @file       midi_Message.h |  | ||||||
|  *  Project     Arduino MIDI Library |  | ||||||
|  *  @brief      MIDI Library for the Arduino - Message struct definition |  | ||||||
|  *  @author     Francois Best |  | ||||||
|  *  @date       11/06/14 |  | ||||||
|  *  @license    MIT - Copyright (c) 2015 Francois Best |  | ||||||
|  * |  | ||||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  * of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  * in the Software without restriction, including without limitation the rights |  | ||||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  * copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  * furnished to do so, subject to the following conditions: |  | ||||||
|  * |  | ||||||
|  * The above copyright notice and this permission notice shall be included in |  | ||||||
|  * all copies or substantial portions of the Software. |  | ||||||
|  * |  | ||||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  * THE SOFTWARE. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "midi_Namespace.h" |  | ||||||
| #include "midi_Defs.h" |  | ||||||
| #ifndef ARDUINO |  | ||||||
| #include <string.h> |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| BEGIN_MIDI_NAMESPACE |  | ||||||
| 
 |  | ||||||
| /*! The Message structure contains decoded data of a MIDI message
 |  | ||||||
|     read from the serial port with read() |  | ||||||
|  */ |  | ||||||
| template<unsigned SysExMaxSize> |  | ||||||
| struct Message |  | ||||||
| { |  | ||||||
|     /*! 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; |  | ||||||
| 
 |  | ||||||
|     /*! The MIDI channel on which the message was recieved.
 |  | ||||||
|      \n Value goes from 1 to 16. |  | ||||||
|      */ |  | ||||||
|     Channel channel; |  | ||||||
| 
 |  | ||||||
|     /*! The type of the message
 |  | ||||||
|      (see the MidiType enum for types reference) |  | ||||||
|      */ |  | ||||||
|     MidiType type; |  | ||||||
| 
 |  | ||||||
|     /*! The first data byte.
 |  | ||||||
|      \n Value goes from 0 to 127. |  | ||||||
|      */ |  | ||||||
|     DataByte data1; |  | ||||||
| 
 |  | ||||||
|     /*! The second data byte.
 |  | ||||||
|      If the message is only 2 bytes long, this one is null. |  | ||||||
|      \n Value goes from 0 to 127. |  | ||||||
|      */ |  | ||||||
|     DataByte data2; |  | ||||||
| 
 |  | ||||||
|     /*! System Exclusive dedicated byte array.
 |  | ||||||
|      \n Array length is stocked on 16 bits, |  | ||||||
|      in data1 (LSB) and data2 (MSB) |  | ||||||
|      */ |  | ||||||
|     DataByte sysexArray[sSysExMaxSize]; |  | ||||||
| 
 |  | ||||||
|     /*! This boolean indicates if the message is valid or not.
 |  | ||||||
|      There is no channel consideration here, |  | ||||||
|      validity means the message respects the MIDI norm. |  | ||||||
|      */ |  | ||||||
|     bool valid; |  | ||||||
| 
 |  | ||||||
|     /*! 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,27 +2,10 @@ | ||||||
|  *  @file       midi_Namespace.h |  *  @file       midi_Namespace.h | ||||||
|  *  Project     Arduino MIDI Library |  *  Project     Arduino MIDI Library | ||||||
|  *  @brief      MIDI Library for the Arduino - Namespace declaration |  *  @brief      MIDI Library for the Arduino - Namespace declaration | ||||||
|  |  *  @version    4.0 | ||||||
|  *  @author     Francois Best  |  *  @author     Francois Best  | ||||||
|  *  @date       24/02/11 |  *  @date       24/02/11 | ||||||
|  *  @license    MIT - Copyright (c) 2015 Francois Best |  *  license     GPL Forty Seven Effects - 2011 | ||||||
|  * |  | ||||||
|  * 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 | #pragma once | ||||||
|  |  | ||||||
|  | @ -1,51 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  *  @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,103 +2,64 @@ | ||||||
|  *  @file       midi_Settings.h |  *  @file       midi_Settings.h | ||||||
|  *  Project     Arduino MIDI Library |  *  Project     Arduino MIDI Library | ||||||
|  *  @brief      MIDI Library for the Arduino - Settings |  *  @brief      MIDI Library for the Arduino - Settings | ||||||
|  |  *  @version    3.5 | ||||||
|  *  @author     Francois Best  |  *  @author     Francois Best  | ||||||
|  *  @date       24/02/11 |  *  @date       24/02/11 | ||||||
|  *  @license    MIT - Copyright (c) 2015 Francois Best |  *  license     GPL Forty Seven Effects - 2011 | ||||||
|  * |  | ||||||
|  * 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 | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "midi_Defs.h" | #include "midi_Namespace.h" | ||||||
| 
 | 
 | ||||||
| BEGIN_MIDI_NAMESPACE | BEGIN_MIDI_NAMESPACE | ||||||
| 
 | 
 | ||||||
| /*! \brief Default Settings for the MIDI Library.
 | // -----------------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
|  To change the default settings, don't edit them there, create a subclass and | // Here are a few settings you can change to customize
 | ||||||
|  override the values in that subclass, then use the MIDI_CREATE_CUSTOM_INSTANCE | // the library for your own project. You can for example
 | ||||||
|  macro to create your instance. The settings you don't override will keep their | // choose to compile only parts of it so you gain flash
 | ||||||
|  default value. Eg: | // space and optimise the speed of your sketch.
 | ||||||
|  \code{.cpp} |  | ||||||
|  struct MySettings : public MIDI_NAMESPACE::DefaultSettings |  | ||||||
|  { |  | ||||||
|     static const unsigned SysExMaxSize = 1024; // Accept SysEx messages up to 1024 bytes long.
 |  | ||||||
|  }; |  | ||||||
| 
 | 
 | ||||||
|  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 |  | ||||||
|     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
 | // Compilation flags. Set them to 1 to build the associated feature
 | ||||||
|     Set to true  to get NoteOff events when receiving null-velocity NoteOn messages.\n | // (MIDI in, out, thru), or to 0 to disable the feature and save space.
 | ||||||
|     Set to false to get NoteOn  events when receiving null-velocity NoteOn messages. | // Note that the Thru can only work if in and out are enabled.
 | ||||||
|     */ |  | ||||||
|     static const bool HandleNullVelocityNoteOnAsNoteOff = true; |  | ||||||
| 
 | 
 | ||||||
|     /*! Setting this to true will make MIDI.read parse only one byte of data for each
 | #define MIDI_BUILD_INPUT                1 | ||||||
|     call when data is available. This can speed up your application if receiving | #define MIDI_BUILD_OUTPUT               1 | ||||||
|     a lot of traffic, but might induce MIDI Thru and treatment latency. | #define MIDI_BUILD_THRU                 1 | ||||||
|     */ |  | ||||||
|     static const bool Use1ByteParsing = true; |  | ||||||
| 
 | 
 | ||||||
|     /*! Maximum size of SysEx receivable. Decrease to save RAM if you don't expect
 | #define MIDI_USE_CALLBACKS              1 | ||||||
|     to receive SysEx, or adjust accordingly. |  | ||||||
|     */ |  | ||||||
|     static const unsigned SysExMaxSize = 128; |  | ||||||
| 
 | 
 | ||||||
|     /*! Global switch to turn on/off sender ActiveSensing
 | // Create a MIDI object automatically on the port defined with MIDI_SERIAL_PORT.
 | ||||||
|     Set to true to send ActiveSensing | #define MIDI_AUTO_INSTANCIATE           1 | ||||||
|     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) | // Serial port configuration
 | ||||||
|     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
 | // Set the default port to use for MIDI.
 | ||||||
|     repeatedly by the sender to tell the receiver that a connection is alive. Use | #define MIDI_SERIAL_PORT                Serial | ||||||
|     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. | // Software serial options
 | ||||||
|     (All Roland devices send Active Sensing every 250ms) | #define MIDI_USE_SOFTWARE_SERIAL        0 | ||||||
| 
 | 
 | ||||||
|     Setting this field to 0 will disable sending MIDI active sensing. | #if MIDI_USE_SOFTWARE_SERIAL | ||||||
|     */ |     #define MIDI_SOFTSERIAL_RX_PIN      1   // Pin number to use for MIDI Input
 | ||||||
|     static const uint16_t SenderActiveSensingPeriodicity = 0; |     #define MIDI_SOFTSERIAL_TX_PIN      2   // Pin number to use for MIDI Output
 | ||||||
| }; | #endif | ||||||
|  | 
 | ||||||
|  | // -----------------------------------------------------------------------------
 | ||||||
|  | // Misc. options
 | ||||||
|  | 
 | ||||||
|  | // Running status enables short messages when sending multiple values
 | ||||||
|  | // of the same type and channel.
 | ||||||
|  | // Set to 0 if you have troubles controlling your hardware.
 | ||||||
|  | #define MIDI_USE_RUNNING_STATUS         1 | ||||||
|  | #define MIDI_USE_1BYTE_PARSING          1 | ||||||
|  | 
 | ||||||
|  | #define MIDI_BAUDRATE                   31250 | ||||||
|  | #define MIDI_SYSEX_ARRAY_SIZE           255     // Maximum size is 65535 bytes.
 | ||||||
| 
 | 
 | ||||||
| END_MIDI_NAMESPACE | END_MIDI_NAMESPACE | ||||||
|  |  | ||||||
							
								
								
									
										130
									
								
								src/serialMIDI.h
								
								
								
								
							
							
						
						
									
										130
									
								
								src/serialMIDI.h
								
								
								
								
							|  | @ -1,130 +0,0 @@ | ||||||
| /*!
 |  | ||||||
|  *  @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); |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| add_subdirectory(mocks) |  | ||||||
| add_subdirectory(unit-tests) |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| 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 |  | ||||||
| ) |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| #include "test-mocks.h" |  | ||||||
| 
 |  | ||||||
| BEGIN_TEST_MOCKS_NAMESPACE |  | ||||||
| 
 |  | ||||||
| END_TEST_MOCKS_NAMESPACE |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "test-mocks_Namespace.h" |  | ||||||
| 
 |  | ||||||
| BEGIN_TEST_MOCKS_NAMESPACE |  | ||||||
| 
 |  | ||||||
| END_TEST_MOCKS_NAMESPACE |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| #include "test-mocks_SerialMock.h" |  | ||||||
| 
 |  | ||||||
| BEGIN_TEST_MOCKS_NAMESPACE |  | ||||||
| 
 |  | ||||||
| END_TEST_MOCKS_NAMESPACE |  | ||||||
|  | @ -1,60 +0,0 @@ | ||||||
| #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" |  | ||||||
|  | @ -1,153 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| 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 |  | ||||||
| ) |  | ||||||
|  | @ -1,991 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,594 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,83 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,884 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,389 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,24 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,181 +0,0 @@ | ||||||
| #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 |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| #include "unit-tests.h" |  | ||||||
| 
 |  | ||||||
| int main(int argc, char **argv) { |  | ||||||
|     ::testing::InitGoogleTest(&argc, argv); |  | ||||||
|     return RUN_ALL_TESTS(); |  | ||||||
| } |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "unit-tests_Namespace.h" |  | ||||||
| #include <gtest/gtest.h> |  | ||||||
| #include <gmock/gmock.h> |  | ||||||
| 
 |  | ||||||
| BEGIN_UNIT_TESTS_NAMESPACE |  | ||||||
| 
 |  | ||||||
| END_UNIT_TESTS_NAMESPACE |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| #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