From 5b96487362f8eec20a6e1c1551c3c14298541e44 Mon Sep 17 00:00:00 2001 From: Francois Best Date: Sat, 3 Nov 2018 18:59:20 +0100 Subject: [PATCH] feat: Compose TX USB MIDI packets from serial buffer --- .vscode/settings.json | 9 +- src/midi_UsbPacketInterface.h | 44 +++ src/midi_UsbPacketInterface.hpp | 65 ++++ test/unit-tests/CMakeLists.txt | 3 +- .../unit-tests_MidiUsbPacketInterface.cpp | 314 ++++++++++++++++++ 5 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 src/midi_UsbPacketInterface.h create mode 100644 src/midi_UsbPacketInterface.hpp create mode 100644 test/unit-tests/tests/unit-tests_MidiUsbPacketInterface.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 73c61a5..007ba05 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,13 @@ { "files.associations": { "cstddef": "cpp", - "ostream": "cpp" + "ostream": "cpp", + "__locale": "cpp", + "functional": "cpp", + "iterator": "cpp", + "string": "cpp", + "string_view": "cpp", + "vector": "cpp", + "istream": "cpp" } } \ No newline at end of file diff --git a/src/midi_UsbPacketInterface.h b/src/midi_UsbPacketInterface.h new file mode 100644 index 0000000..33d5721 --- /dev/null +++ b/src/midi_UsbPacketInterface.h @@ -0,0 +1,44 @@ +/*! + * @file midi_UsbPacketInterface.h + * Project Arduino MIDI Library + * @brief MIDI Library for the Arduino - Transport layer for USB MIDI + * @author Francois Best + * @date 2018-11-03 + * @license MIT - Copyright (c) 2018 Francois Best + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "midi_Defs.h" +#include "midi_UsbDefs.h" +#include + +BEGIN_MIDI_NAMESPACE + +template +bool composeTxPacket(Buffer& inBuffer, midiEventPacket_t& outPacket); + +template +bool parseRxPacket(const midiEventPacket_t& inPacket, Buffer& outBuffer); + +END_MIDI_NAMESPACE + +#include "midi_UsbPacketInterface.hpp" diff --git a/src/midi_UsbPacketInterface.hpp b/src/midi_UsbPacketInterface.hpp new file mode 100644 index 0000000..6d57518 --- /dev/null +++ b/src/midi_UsbPacketInterface.hpp @@ -0,0 +1,65 @@ +/*! + * @file midi_UsbPacketInterface.hpp + * Project Arduino MIDI Library + * @brief MIDI Library for the Arduino - Transport layer for USB MIDI + * @author Francois Best + * @date 2018-11-03 + * @license MIT - Copyright (c) 2018 Francois Best + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +BEGIN_MIDI_NAMESPACE + +template +bool composeTxPacket(Buffer& inBuffer, midiEventPacket_t& outPacket) +{ + if (inBuffer.isEmpty()) { + return false; + } + int bufferLength = inBuffer.getLength(); + const byte status = inBuffer.peek(); + const byte cin = midi::CodeIndexNumbers::fromStatus(status); + const byte messageLength = midi::CodeIndexNumbers::getSize(cin); + + if (bufferLength < messageLength) { + return false; // Not enough data in the buffer to compose a full packet. + } + + outPacket.header = cin; + outPacket.byte1 = status; + outPacket.byte2 = messageLength >= 2 ? inBuffer.peek(1) : 0x00; + outPacket.byte3 = messageLength >= 3 ? inBuffer.peek(2) : 0x00; + + inBuffer.pop(messageLength); + return true; + + // todo: handle interleaved RealTime messages + // todo: handle SysEx +} + +template +bool parseRxPacket(const midiEventPacket_t& /*inPacket*/, Buffer& /*outBuffer*/) +{ + return false; +} + +END_MIDI_NAMESPACE diff --git a/test/unit-tests/CMakeLists.txt b/test/unit-tests/CMakeLists.txt index 06dc4f9..59bfb7b 100644 --- a/test/unit-tests/CMakeLists.txt +++ b/test/unit-tests/CMakeLists.txt @@ -25,7 +25,8 @@ add_executable(unit-tests tests/unit-tests_MidiInputCallbacks.cpp tests/unit-tests_MidiOutput.cpp tests/unit-tests_MidiThru.cpp - tests/unit-tests_MidiUsb.cpp + tests/unit-tests_MidiUsbDefs.cpp + tests/unit-tests_MidiUsbPacketInterface.cpp ) target_link_libraries(unit-tests diff --git a/test/unit-tests/tests/unit-tests_MidiUsbPacketInterface.cpp b/test/unit-tests/tests/unit-tests_MidiUsbPacketInterface.cpp new file mode 100644 index 0000000..3ca614e --- /dev/null +++ b/test/unit-tests/tests/unit-tests_MidiUsbPacketInterface.cpp @@ -0,0 +1,314 @@ +#include "unit-tests.h" +#include +#include +#include + +BEGIN_MIDI_NAMESPACE + +END_MIDI_NAMESPACE + +// ----------------------------------------------------------------------------- + +BEGIN_UNNAMED_NAMESPACE + +using Buffer = midi::RingBuffer; + +TEST(MidiUsbPacketInterfaceTx, EmptyBufferShouldNotComposeAPacket) +{ + midiEventPacket_t packet; + Buffer buffer; + + const bool result = midi::composeTxPacket(buffer, packet); + EXPECT_FALSE(result) << "Empty buffer should not compose a packet"; +} + +TEST(MidiUsbPacketInterfaceTx, IncompleteDataShouldNotComposeAPacket) +{ + midiEventPacket_t packet; + Buffer buffer; + bool result = false; + + buffer.write(0x81); + result = midi::composeTxPacket(buffer, packet); + EXPECT_FALSE(result) << "Insufficient data should not compose a packet"; + EXPECT_EQ(1, buffer.getLength()) << "Partial data should not be read out of the buffer"; + + buffer.write(0x12); + buffer.write(0x42); + result = midi::composeTxPacket(buffer, packet); + EXPECT_TRUE(result) << "Complete data should compose a packet"; + EXPECT_EQ(0, buffer.getLength()) << "Complete packet data should be read out of the buffer"; + EXPECT_EQ(midi::CodeIndexNumbers::noteOff, packet.header); + EXPECT_EQ(0x81, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); +} + +// Channel Voice Messages -- + +TEST(MidiUsbPacketInterfaceTx, NoteOff) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0x80); + buffer.write(0x12); + buffer.write(0x42); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::noteOff, packet.header); + EXPECT_EQ(0x80, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, NoteOn) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0x91); + buffer.write(0x12); + buffer.write(0x42); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::noteOn, packet.header); + EXPECT_EQ(0x91, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, PolyPressure) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0xA2); + buffer.write(0x12); + buffer.write(0x42); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::polyPressure, packet.header); + EXPECT_EQ(0xA2, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, ControlChange) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0xB3); + buffer.write(0x12); + buffer.write(0x42); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::controlChange, packet.header); + EXPECT_EQ(0xB3, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, ProgramChange) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0xC4); + buffer.write(0x12); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::programChange, packet.header); + EXPECT_EQ(0xC4, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, ChannelPressure) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0xD5); + buffer.write(0x12); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::channelPressure, packet.header); + EXPECT_EQ(0xD5, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, PitchBend) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(0xE6); + buffer.write(0x12); + buffer.write(0x42); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::pitchBend, packet.header); + EXPECT_EQ(0xE6, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +// System Real Time Messages -- + +TEST(MidiUsbPacketInterfaceTx, Clock) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::Clock); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::singleByte, packet.header); + EXPECT_EQ(midi::Clock, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, Start) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::Start); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::singleByte, packet.header); + EXPECT_EQ(midi::Start, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, Stop) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::Stop); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::singleByte, packet.header); + EXPECT_EQ(midi::Stop, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, Continue) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::Continue); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::singleByte, packet.header); + EXPECT_EQ(midi::Continue, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, ActiveSensing) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::ActiveSensing); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::singleByte, packet.header); + EXPECT_EQ(midi::ActiveSensing, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, SystemReset) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::SystemReset); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::singleByte, packet.header); + EXPECT_EQ(midi::SystemReset, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +// System Common Messages -- + +TEST(MidiUsbPacketInterfaceTx, TuneRequest) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::TuneRequest); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::systemCommon1Byte, packet.header); + EXPECT_EQ(midi::TuneRequest, packet.byte1); + EXPECT_EQ(0x00, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, SongSelect) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::SongSelect); + buffer.write(0x12); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::systemCommon2Bytes, packet.header); + EXPECT_EQ(midi::SongSelect, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, SongPosition) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::SongPosition); + buffer.write(0x12); + buffer.write(0x42); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::systemCommon3Bytes, packet.header); + EXPECT_EQ(midi::SongPosition, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x42, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +TEST(MidiUsbPacketInterfaceTx, TimeCodeQuarterFrame) +{ + midiEventPacket_t packet; + Buffer buffer; + + buffer.write(midi::TimeCodeQuarterFrame); + buffer.write(0x12); + EXPECT_TRUE(midi::composeTxPacket(buffer, packet)); + EXPECT_EQ(midi::CodeIndexNumbers::systemCommon2Bytes, packet.header); + EXPECT_EQ(midi::TimeCodeQuarterFrame, packet.byte1); + EXPECT_EQ(0x12, packet.byte2); + EXPECT_EQ(0x00, packet.byte3); + EXPECT_EQ(0, buffer.getLength()); +} + +// ----------------------------------------------------------------------------- + +TEST(MidiUsbPacketInterfaceRx, PacketParsing) +{ + +} + +END_UNNAMED_NAMESPACE