diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h b/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h index 8980e1e7e4..f28cc76560 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h @@ -27,6 +27,44 @@ namespace juce namespace universal_midi_packets { +/** Represents a MIDI message that happened at a particular time. + + Unlike MidiMessage, BytestreamMidiView is non-owning. +*/ +struct BytestreamMidiView +{ + constexpr BytestreamMidiView (Span bytesIn, double timestampIn) + : bytes (bytesIn), timestamp (timestampIn) {} + + /** Creates a view over the provided message. + + Note that the argument is a pointer, not a reference, in order to avoid taking a reference + to a temporary. + */ + explicit BytestreamMidiView (const MidiMessage* msg) + : bytes (unalignedPointerCast (msg->getRawData()), + static_cast (msg->getRawDataSize())), + timestamp (msg->getTimeStamp()) {} + + explicit BytestreamMidiView (const MidiMessageMetadata msg) + : bytes (unalignedPointerCast (msg.data), + static_cast (msg.numBytes)), + timestamp (msg.samplePosition) {} + + MidiMessage getMessage() const + { + return MidiMessage (bytes.data(), (int) bytes.size(), timestamp); + } + + bool isSysEx() const + { + return ! bytes.empty() && bytes.front() == std::byte { 0xf0 }; + } + + Span bytes; + double timestamp = 0.0; +}; + /** Functions to assist conversion of UMP messages to/from other formats, especially older 'bytestream' formatted MidiMessages. @@ -40,13 +78,17 @@ struct Conversion `callback` is a function which accepts a single View argument. */ template - static void toMidi1 (const MidiMessage& m, PacketCallbackFunction&& callback) + static void toMidi1 (const BytestreamMidiView& m, PacketCallbackFunction&& callback) { - const auto* data = m.getRawData(); - const auto firstByte = data[0]; - const auto size = m.getRawDataSize(); + const auto size = m.bytes.size(); - if (firstByte != 0xf0) + if (size <= 0) + return; + + const auto* data = m.bytes.data(); + const auto firstByte = data[0]; + + if (firstByte != std::byte { 0xf0 }) { const auto mask = [size]() -> uint32_t { @@ -61,15 +103,15 @@ struct Conversion return 0x00000000; }(); - const auto extraByte = (uint8_t) ((((firstByte & 0xf0) == 0xf0) ? 0x1 : 0x2) << 0x4); + const auto extraByte = ((((firstByte & std::byte { 0xf0 }) == std::byte { 0xf0 }) ? std::byte { 0x1 } : std::byte { 0x2 }) << 0x4); const PacketX1 packet { mask & Utils::bytesToWord (extraByte, data[0], data[1], data[2]) }; callback (View (packet.data())); return; } - const auto numSysExBytes = m.getSysExDataSize(); + const auto numSysExBytes = (ssize_t) (size - 2); const auto numMessages = SysEx7::getNumPacketsRequiredForDataSize ((uint32_t) numSysExBytes); - auto* dataOffset = m.getSysExData(); + auto* dataOffset = data + 1; if (numMessages <= 1) { @@ -78,9 +120,9 @@ struct Conversion return; } - constexpr auto byteIncrement = 6; + constexpr ssize_t byteIncrement = 6; - for (auto i = numSysExBytes; i > 0; i -= byteIncrement, dataOffset += byteIncrement) + for (auto i = static_cast (numSysExBytes); i > 0; i -= byteIncrement, dataOffset += byteIncrement) { const auto func = [&] { @@ -99,20 +141,6 @@ struct Conversion } } - /** Converts a MidiMessage to one or more messages in UMP format, using - the MIDI 1.0 Protocol. - - `packets` is an out-param to allow the caller to control - allocation/deallocation. Returning a new Packets object would - require every call to toMidi1 to allocate. With this version, no - allocations will occur, provided that `packets` has adequate reserved - space. - */ - static void toMidi1 (const MidiMessage& m, Packets& packets) - { - toMidi1 (m, [&] (const View& view) { packets.add (view); }); - } - /** Widens a 7-bit MIDI 1.0 value to a 8-bit MIDI 2.0 value. */ static uint8_t scaleTo8 (uint8_t word7Bit) { @@ -198,7 +226,7 @@ struct Conversion } const auto status = Utils::getStatus (firstWord); - const auto typeAndGroup = (uint8_t) ((0x2 << 0x4) | Utils::getGroup (firstWord)); + const auto typeAndGroup = ((std::byte { 0x2 } << 0x4) | std::byte { Utils::getGroup (firstWord) }); switch (status) { @@ -207,18 +235,18 @@ struct Conversion case 0xa: // poly pressure case 0xb: // control change { - const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff); - const auto byte2 = (uint8_t) ((firstWord >> 0x08) & 0xff); - const auto byte3 = scaleTo7 (v[1]); + const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); + const auto byte2 = std::byte ((firstWord >> 0x08) & 0xff); + const auto byte3 = std::byte { scaleTo7 (v[1]) }; // If this is a note-on, and the scaled byte is 0, // the scaled velocity should be 1 instead of 0 - const auto needsCorrection = status == 0x9 && byte3 == 0; - const auto correctedByte = (uint8_t) (needsCorrection ? 1 : byte3); + const auto needsCorrection = status == 0x9 && byte3 == std::byte { 0 }; + const auto correctedByte = needsCorrection ? std::byte { 1 } : byte3; const auto shouldIgnore = status == 0xb && [&] { - switch (byte2) + switch (uint8_t (byte2)) { case 0: case 6: @@ -247,13 +275,13 @@ struct Conversion case 0xd: // channel pressure { - const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff); - const auto byte2 = scaleTo7 (v[1]); + const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); + const auto byte2 = std::byte { scaleTo7 (v[1]) }; const PacketX1 packet { Utils::bytesToWord (typeAndGroup, statusAndChannel, byte2, - 0) }; + std::byte { 0 }) }; callback (View (packet.data())); return; } @@ -261,17 +289,17 @@ struct Conversion case 0x2: // rpn case 0x3: // nrpn { - const auto ccX = (uint8_t) (status == 0x2 ? 101 : 99); - const auto ccY = (uint8_t) (status == 0x2 ? 100 : 98); - const auto statusAndChannel = (uint8_t) ((0xb << 0x4) | Utils::getChannel (firstWord)); + const auto ccX = status == 0x2 ? std::byte { 101 } : std::byte { 99 }; + const auto ccY = status == 0x2 ? std::byte { 100 } : std::byte { 98 }; + const auto statusAndChannel = std::byte ((0xb << 0x4) | Utils::getChannel (firstWord)); const auto data = scaleTo14 (v[1]); const PacketX1 packets[] { - PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccX, (uint8_t) ((firstWord >> 0x8) & 0x7f)) }, - PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccY, (uint8_t) ((firstWord >> 0x0) & 0x7f)) }, - PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 6, (uint8_t) ((data >> 0x7) & 0x7f)) }, - PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 38, (uint8_t) ((data >> 0x0) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccX, std::byte ((firstWord >> 0x8) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, ccY, std::byte ((firstWord >> 0x0) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 6 }, std::byte ((data >> 0x7) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 38 }, std::byte ((data >> 0x0) & 0x7f)) }, }; for (const auto& packet : packets) @@ -284,24 +312,24 @@ struct Conversion { if (firstWord & 1) { - const auto statusAndChannel = (uint8_t) ((0xb << 0x4) | Utils::getChannel (firstWord)); + const auto statusAndChannel = std::byte ((0xb << 0x4) | Utils::getChannel (firstWord)); const auto secondWord = v[1]; const PacketX1 packets[] { - PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 0, (uint8_t) ((secondWord >> 0x8) & 0x7f)) }, - PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, 32, (uint8_t) ((secondWord >> 0x0) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 0 }, std::byte ((secondWord >> 0x8) & 0x7f)) }, + PacketX1 { Utils::bytesToWord (typeAndGroup, statusAndChannel, std::byte { 32 }, std::byte ((secondWord >> 0x0) & 0x7f)) }, }; for (const auto& packet : packets) callback (View (packet.data())); } - const auto statusAndChannel = (uint8_t) ((0xc << 0x4) | Utils::getChannel (firstWord)); + const auto statusAndChannel = std::byte ((0xc << 0x4) | Utils::getChannel (firstWord)); const PacketX1 packet { Utils::bytesToWord (typeAndGroup, statusAndChannel, - (uint8_t) ((v[1] >> 0x18) & 0x7f), - 0) }; + std::byte ((v[1] >> 0x18) & 0x7f), + std::byte { 0 }) }; callback (View (packet.data())); return; } @@ -309,11 +337,11 @@ struct Conversion case 0xe: // pitch bend { const auto data = scaleTo14 (v[1]); - const auto statusAndChannel = (uint8_t) ((firstWord >> 0x10) & 0xff); + const auto statusAndChannel = std::byte ((firstWord >> 0x10) & 0xff); const PacketX1 packet { Utils::bytesToWord (typeAndGroup, statusAndChannel, - (uint8_t) (data & 0x7f), - (uint8_t) ((data >> 7) & 0x7f)) }; + std::byte (data & 0x7f), + std::byte ((data >> 7) & 0x7f)) }; callback (View (packet.data())); return; } diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h b/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h index 3639cceb83..22ed3b563a 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h @@ -35,7 +35,7 @@ namespace universal_midi_packets struct ToUMP1Converter { template - void convert (const MidiMessage& m, Fn&& fn) + void convert (const BytestreamMidiView& m, Fn&& fn) { Conversion::toMidi1 (m, std::forward (fn)); } @@ -56,7 +56,7 @@ namespace universal_midi_packets struct ToUMP2Converter { template - void convert (const MidiMessage& m, Fn&& fn) + void convert (const BytestreamMidiView& m, Fn&& fn) { Conversion::toMidi1 (m, [&] (const View& v) { @@ -98,7 +98,7 @@ namespace universal_midi_packets } template - void convert (const MidiMessage& m, Fn&& fn) + void convert (const BytestreamMidiView& m, Fn&& fn) { switch (mode) { diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h b/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h index b2512d2f3e..f5eb8873d0 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h @@ -123,7 +123,7 @@ public: void handleIncomingMidiMessage (void*, const MidiMessage& msg) const { - Conversion::toMidi1 (msg, [&] (const View& view) + Conversion::toMidi1 (BytestreamMidiView (&msg), [&] (const View& view) { dispatch.converter.convert (view, *callbackPtr); }); diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h b/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h index 788d34a41f..9f1e265634 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h @@ -45,7 +45,7 @@ struct Factory static PacketX2 makeSysEx (uint8_t group, uint8_t status, uint8_t numBytes, - const uint8_t* data) + const std::byte* data) { jassert (numBytes <= 6); @@ -250,28 +250,28 @@ struct Factory static PacketX2 makeSysExIn1Packet (uint8_t group, uint8_t numBytes, - const uint8_t* data) + const std::byte* data) { return Detail::makeSysEx (group, 0x0, numBytes, data); } static PacketX2 makeSysExStart (uint8_t group, uint8_t numBytes, - const uint8_t* data) + const std::byte* data) { return Detail::makeSysEx (group, 0x1, numBytes, data); } static PacketX2 makeSysExContinue (uint8_t group, uint8_t numBytes, - const uint8_t* data) + const std::byte* data) { return Detail::makeSysEx (group, 0x2, numBytes, data); } static PacketX2 makeSysExEnd (uint8_t group, uint8_t numBytes, - const uint8_t* data) + const std::byte* data) { return Detail::makeSysEx (group, 0x3, numBytes, data); } diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h b/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h index f3d4e01cc6..e1b268b53f 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h @@ -73,7 +73,10 @@ public: { // Utility messages don't translate to bytestream format if (Utils::getMessageType (firstWord) != 0x00) - callback (fromUmp (PacketX1 { firstWord }, time)); + { + const auto message = fromUmp (PacketX1 { firstWord }, time); + callback (BytestreamMidiView (&message)); + } break; } @@ -162,16 +165,14 @@ private: void startSysExMessage (double time) { pendingSysExTime = time; - pendingSysExData.push_back (0xf0); + pendingSysExData.push_back (std::byte { 0xf0 }); } template void terminateSysExMessage (MessageCallback&& callback) { - pendingSysExData.push_back (0xf7); - callback (MidiMessage (pendingSysExData.data(), - int (pendingSysExData.size()), - pendingSysExTime)); + pendingSysExData.push_back (std::byte { 0xf7 }); + callback (BytestreamMidiView (pendingSysExData, pendingSysExTime)); pendingSysExData.clear(); } @@ -206,7 +207,7 @@ private: return Utils::getMessageType (word) == 0x1 && ((word >> 0x10) & 0xff) >= 0xf8; } - std::vector pendingSysExData; + std::vector pendingSysExData; double pendingSysExTime = 0.0; }; diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp b/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp index d4c5e2757f..6964809263 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp +++ b/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp @@ -28,14 +28,14 @@ namespace universal_midi_packets PacketX2 Midi1ToMidi2DefaultTranslator::processNoteOnOrOff (const HelperValues helpers) { const auto velocity = helpers.byte2; - const auto needsConversion = (helpers.byte0 >> 0x4) == 0x9 && velocity == 0; - const auto firstByte = needsConversion ? (uint8_t) ((0x8 << 0x4) | (helpers.byte0 & 0xf)) + const auto needsConversion = (helpers.byte0 & std::byte { 0xf0 }) == std::byte { 0x90 } && velocity == std::byte { 0 }; + const auto firstByte = needsConversion ? (std::byte { 0x80 } | (helpers.byte0 & std::byte { 0xf })) : helpers.byte0; return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, firstByte, helpers.byte1, 0), - (uint32_t) (Conversion::scaleTo16 (velocity) << 0x10) + Utils::bytesToWord (helpers.typeAndGroup, firstByte, helpers.byte1, std::byte { 0 }), + (uint32_t) (Conversion::scaleTo16 (uint8_t (velocity)) << 0x10) }; } @@ -43,8 +43,8 @@ PacketX2 Midi1ToMidi2DefaultTranslator::processPolyPressure (const HelperValues { return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, helpers.byte1, 0), - Conversion::scaleTo32 (helpers.byte2) + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, helpers.byte1, std::byte { 0 }), + Conversion::scaleTo32 (uint8_t (helpers.byte2)) }; } @@ -52,7 +52,7 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel PacketX2& packet) { const auto statusAndChannel = helpers.byte0; - const auto cc = helpers.byte1; + const auto cc = uint8_t (helpers.byte1); const auto shouldAccumulate = [&] { @@ -70,8 +70,8 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel return false; }(); - const auto group = (uint8_t) (helpers.typeAndGroup & 0xf); - const auto channel = (uint8_t) (statusAndChannel & 0xf); + const auto group = (uint8_t) (helpers.typeAndGroup & std::byte { 0xf }); + const auto channel = (uint8_t) (statusAndChannel & std::byte { 0xf }); const auto byte = helpers.byte2; if (shouldAccumulate) @@ -86,13 +86,13 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel const auto msb = bytes[2]; const auto lsb = bytes[3]; - const auto value = (uint16_t) (((msb & 0x7f) << 7) | (lsb & 0x7f)); + const auto value = uint16_t ((uint16_t (msb & std::byte { 0x7f }) << 7) | uint16_t (lsb & std::byte { 0x7f })); const auto newStatus = (uint8_t) (accumulator.getKind() == PnKind::nrpn ? 0x3 : 0x2); packet = PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, (uint8_t) ((newStatus << 0x4) | channel), bank, index), + Utils::bytesToWord (helpers.typeAndGroup, std::byte ((newStatus << 0x4) | channel), bank, index), Conversion::scaleTo32 (value) }; return true; @@ -103,35 +103,41 @@ bool Midi1ToMidi2DefaultTranslator::processControlChange (const HelperValues hel if (cc == 0) { - groupBanks[group][channel].setMsb (byte); + groupBanks[group][channel].setMsb (uint8_t (byte)); return false; } if (cc == 32) { - groupBanks[group][channel].setLsb (byte); + groupBanks[group][channel].setLsb (uint8_t (byte)); return false; } packet = PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, statusAndChannel, cc, 0), - Conversion::scaleTo32 (helpers.byte2) + Utils::bytesToWord (helpers.typeAndGroup, statusAndChannel, std::byte { cc }, std::byte { 0 }), + Conversion::scaleTo32 (uint8_t (helpers.byte2)) }; return true; } PacketX2 Midi1ToMidi2DefaultTranslator::processProgramChange (const HelperValues helpers) const { - const auto group = (uint8_t) (helpers.typeAndGroup & 0xf); - const auto channel = (uint8_t) (helpers.byte0 & 0xf); + const auto group = (uint8_t) (helpers.typeAndGroup & std::byte { 0xf }); + const auto channel = (uint8_t) (helpers.byte0 & std::byte { 0xf }); const auto bank = groupBanks[group][channel]; const auto valid = bank.isValid(); return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, valid ? 1 : 0), - Utils::bytesToWord (helpers.byte1, 0, valid ? bank.getMsb() : 0, valid ? bank.getLsb() : 0) + Utils::bytesToWord (helpers.typeAndGroup, + helpers.byte0, + std::byte { 0 }, + valid ? std::byte { 1 } : std::byte { 0 }), + Utils::bytesToWord (helpers.byte1, + std::byte { 0 }, + valid ? std::byte { bank.getMsb() } : std::byte { 0 }, + valid ? std::byte { bank.getLsb() } : std::byte { 0 }) }; } @@ -139,8 +145,8 @@ PacketX2 Midi1ToMidi2DefaultTranslator::processChannelPressure (const HelperValu { return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, 0), - Conversion::scaleTo32 (helpers.byte1) + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, std::byte { 0 }, std::byte { 0 }), + Conversion::scaleTo32 (uint8_t (helpers.byte1)) }; } @@ -148,16 +154,16 @@ PacketX2 Midi1ToMidi2DefaultTranslator::processPitchBend (const HelperValues hel { const auto lsb = helpers.byte1; const auto msb = helpers.byte2; - const auto value = (uint16_t) (msb << 7 | lsb); + const auto value = uint16_t (uint16_t (msb) << 7 | uint16_t (lsb)); return PacketX2 { - Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, 0, 0), + Utils::bytesToWord (helpers.typeAndGroup, helpers.byte0, std::byte { 0 }, std::byte { 0 }), Conversion::scaleTo32 (value) }; } -bool Midi1ToMidi2DefaultTranslator::PnAccumulator::addByte (uint8_t cc, uint8_t byte) +bool Midi1ToMidi2DefaultTranslator::PnAccumulator::addByte (uint8_t cc, std::byte byte) { const auto isStart = cc == 99 || cc == 101; diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h b/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h index b2a47836b5..b644b839e6 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h @@ -61,10 +61,10 @@ public: const HelperValues helperValues { - (uint8_t) ((0x4 << 0x4) | Utils::getGroup (firstWord)), - (uint8_t) ((firstWord >> 0x10) & 0xff), - (uint8_t) ((firstWord >> 0x08) & 0x7f), - (uint8_t) ((firstWord >> 0x00) & 0x7f), + std::byte ((0x4 << 0x4) | Utils::getGroup (firstWord)), + std::byte ((firstWord >> 0x10) & 0xff), + std::byte ((firstWord >> 0x08) & 0x7f), + std::byte ((firstWord >> 0x00) & 0x7f), }; switch (Utils::getStatus (firstWord)) @@ -128,10 +128,10 @@ private: struct HelperValues { - uint8_t typeAndGroup; - uint8_t byte0; - uint8_t byte1; - uint8_t byte2; + std::byte typeAndGroup; + std::byte byte0; + std::byte byte1; + std::byte byte2; }; static PacketX2 processNoteOnOrOff (const HelperValues helpers); @@ -147,13 +147,13 @@ private: class PnAccumulator { public: - bool addByte (uint8_t cc, uint8_t byte); + bool addByte (uint8_t cc, std::byte byte); - const std::array& getBytes() const noexcept { return bytes; } + const std::array& getBytes() const noexcept { return bytes; } PnKind getKind() const noexcept { return kind; } private: - std::array bytes; + std::array bytes; uint8_t index = 0; PnKind kind = PnKind::nrpn; }; diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp b/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp index 9f8e2ef866..eaea29f13a 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp +++ b/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp @@ -39,12 +39,12 @@ SysEx7::PacketBytes SysEx7::getDataBytes (const PacketX2& packet) return { - { { packet.getU8<2>(), - packet.getU8<3>(), - packet.getU8<4>(), - packet.getU8<5>(), - packet.getU8<6>(), - packet.getU8<7>() } }, + { { std::byte { packet.getU8<2>() }, + std::byte { packet.getU8<3>() }, + std::byte { packet.getU8<4>() }, + std::byte { packet.getU8<5>() }, + std::byte { packet.getU8<6>() }, + std::byte { packet.getU8<7>() } } }, jmin (numBytes, maxBytes) }; } diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h b/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h index 0d6e800136..c08313c0cf 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h @@ -63,7 +63,7 @@ struct SysEx7 /** Holds the bytes from a single SysEx-7 packet. */ struct PacketBytes { - std::array data; + std::array data; uint8_t size; }; diff --git a/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h b/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h index 2b86a71ff7..53b097b99f 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h +++ b/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h @@ -35,9 +35,12 @@ namespace universal_midi_packets struct Utils { /** Joins 4 bytes into a single 32-bit word. */ - static constexpr uint32_t bytesToWord (uint8_t a, uint8_t b, uint8_t c, uint8_t d) + static constexpr uint32_t bytesToWord (std::byte a, std::byte b, std::byte c, std::byte d) { - return uint32_t (a << 0x18 | b << 0x10 | c << 0x08 | d << 0x00); + return uint32_t (a) << 0x18 + | uint32_t (b) << 0x10 + | uint32_t (c) << 0x08 + | uint32_t (d) << 0x00; } /** Returns the expected number of 32-bit words in a Universal MIDI Packet, given diff --git a/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp b/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp index fd52554bbe..b34ed0fb03 100644 --- a/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp +++ b/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp @@ -48,18 +48,18 @@ public: forEachNonSysExTestMessage (random, [&] (const MidiMessage& m) { - Packets packets; - Conversion::toMidi1 (m, packets); + const auto packets = toMidi1 (m); expect (packets.size() == 1); // Make sure that the message type is correct - expect (Utils::getMessageType (packets.data()[0]) == ((m.getRawData()[0] >> 0x4) == 0xf ? 0x1 : 0x2)); + const auto msgType = Utils::getMessageType (packets.data()[0]); + expect (msgType == ((m.getRawData()[0] >> 0x4) == 0xf ? 0x1 : 0x2)); translator.dispatch (View {packets.data() }, 0, - [&] (const MidiMessage& roundTripped) + [&] (const BytestreamMidiView& roundTripped) { - expect (equal (m, roundTripped)); + expect (equal (m, roundTripped.getMessage())); }); }); } @@ -68,8 +68,7 @@ public: { { // Zero length message - Packets packets; - Conversion::toMidi1 (createRandomSysEx (random, 0), packets); + const auto packets = toMidi1 (createRandomSysEx (random, 0)); expect (packets.size() == 2); expect (packets.data()[0] == 0x30000000); @@ -78,51 +77,50 @@ public: { const auto message = createRandomSysEx (random, 1); - Packets packets; - Conversion::toMidi1 (message, packets); + const auto packets = toMidi1 (message); expect (packets.size() == 2); const auto* sysEx = message.getSysExData(); - expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x01, sysEx[0], 0)); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, + std::byte { 0x01 }, + std::byte { sysEx[0] }, + std::byte { 0 })); expect (packets.data()[1] == 0x00000000); } { const auto message = createRandomSysEx (random, 6); - Packets packets; - Conversion::toMidi1 (message, packets); + const auto packets = toMidi1 (message); expect (packets.size() == 2); const auto* sysEx = message.getSysExData(); - expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x06, sysEx[0], sysEx[1])); - expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5])); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x06 }, std::byte { sysEx[0] }, std::byte { sysEx[1] })); + expect (packets.data()[1] == Utils::bytesToWord (std::byte { sysEx[2] }, std::byte { sysEx[3] }, std::byte { sysEx[4] }, std::byte { sysEx[5] })); } { const auto message = createRandomSysEx (random, 12); - Packets packets; - Conversion::toMidi1 (message, packets); + const auto packets = toMidi1 (message); expect (packets.size() == 4); const auto* sysEx = message.getSysExData(); - expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x16, sysEx[0], sysEx[1])); - expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5])); - expect (packets.data()[2] == Utils::bytesToWord (0x30, 0x36, sysEx[6], sysEx[7])); - expect (packets.data()[3] == Utils::bytesToWord (sysEx[8], sysEx[9], sysEx[10], sysEx[11])); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x16 }, std::byte { sysEx[0] }, std::byte { sysEx[1] })); + expect (packets.data()[1] == Utils::bytesToWord (std::byte { sysEx[2] }, std::byte { sysEx[3] }, std::byte { sysEx[4] }, std::byte { sysEx[5] })); + expect (packets.data()[2] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x36 }, std::byte { sysEx[6] }, std::byte { sysEx[7] })); + expect (packets.data()[3] == Utils::bytesToWord (std::byte { sysEx[8] }, std::byte { sysEx[9] }, std::byte { sysEx[10] }, std::byte { sysEx[11] })); } { const auto message = createRandomSysEx (random, 13); - Packets packets; - Conversion::toMidi1 (message, packets); + const auto packets = toMidi1 (message); expect (packets.size() == 6); const auto* sysEx = message.getSysExData(); - expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x16, sysEx[0], sysEx[1])); - expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5])); - expect (packets.data()[2] == Utils::bytesToWord (0x30, 0x26, sysEx[6], sysEx[7])); - expect (packets.data()[3] == Utils::bytesToWord (sysEx[8], sysEx[9], sysEx[10], sysEx[11])); - expect (packets.data()[4] == Utils::bytesToWord (0x30, 0x31, sysEx[12], 0)); + expect (packets.data()[0] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x16 }, std::byte { sysEx[0] }, std::byte { sysEx[1] })); + expect (packets.data()[1] == Utils::bytesToWord (std::byte { sysEx[2] }, std::byte { sysEx[3] }, std::byte { sysEx[4] }, std::byte { sysEx[5] })); + expect (packets.data()[2] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x26 }, std::byte { sysEx[6] }, std::byte { sysEx[7] })); + expect (packets.data()[3] == Utils::bytesToWord (std::byte { sysEx[8] }, std::byte { sysEx[9] }, std::byte { sysEx[10] }, std::byte { sysEx[11] })); + expect (packets.data()[4] == Utils::bytesToWord (std::byte { 0x30 }, std::byte { 0x31 }, std::byte { sysEx[12] }, std::byte { 0 })); expect (packets.data()[5] == 0x00000000); } } @@ -133,15 +131,15 @@ public: const auto checkRoundTrip = [&] (const MidiBuffer& expected) { for (const auto meta : expected) - Conversion::toMidi1 (meta.getMessage(), packets); + Conversion::toMidi1 (ump::BytestreamMidiView (meta), [&] (const auto p) { packets.add (p); }); MidiBuffer output; converter.dispatch (packets.data(), packets.data() + packets.size(), 0, - [&] (const MidiMessage& roundTripped) + [&] (const BytestreamMidiView& roundTripped) { - output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); }); packets.clear(); @@ -161,8 +159,7 @@ public: beginTest ("UMP SysEx7 messages interspersed with utility messages convert to bytestream"); { const auto sysEx = createRandomSysEx (random, 100); - Packets originalPackets; - Conversion::toMidi1 (sysEx, originalPackets); + const auto originalPackets = toMidi1 (sysEx); Packets modifiedPackets; @@ -183,9 +180,9 @@ public: converter.dispatch (modifiedPackets.data(), modifiedPackets.data() + modifiedPackets.size(), 0, - [&] (const MidiMessage& roundTripped) + [&] (const BytestreamMidiView& roundTripped) { - output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); }); // All Utility messages should have been ignored @@ -198,8 +195,7 @@ public: beginTest ("UMP SysEx7 messages interspersed with System Realtime messages convert to bytestream"); { const auto sysEx = createRandomSysEx (random, 200); - Packets originalPackets; - Conversion::toMidi1 (sysEx, originalPackets); + const auto originalPackets = toMidi1 (sysEx); Packets modifiedPackets; MidiBuffer realtimeMessages; @@ -222,9 +218,9 @@ public: converter.dispatch (modifiedPackets.data(), modifiedPackets.data() + modifiedPackets.size(), 0, - [&] (const MidiMessage& roundTripped) + [&] (const BytestreamMidiView& roundTripped) { - output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); }); const auto numOutputs = output.getNumEvents(); @@ -258,8 +254,7 @@ public: beginTest ("UMP SysEx7 messages interspersed with System Realtime and Utility messages convert to bytestream"); { const auto sysEx = createRandomSysEx (random, 300); - Packets originalPackets; - Conversion::toMidi1 (sysEx, originalPackets); + const auto originalPackets = toMidi1 (sysEx); Packets modifiedPackets; MidiBuffer realtimeMessages; @@ -290,9 +285,9 @@ public: converter.dispatch (modifiedPackets.data(), modifiedPackets.data() + modifiedPackets.size(), 0, - [&] (const MidiMessage& roundTripped) + [&] (const BytestreamMidiView& roundTripped) { - output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); }); const auto numOutputs = output.getNumEvents(); @@ -336,19 +331,14 @@ public: Packets p; for (const auto meta : noteOn) - Conversion::toMidi1 (meta.getMessage(), p); + Conversion::toMidi1 (ump::BytestreamMidiView (meta), [&] (const auto packet) { p.add (packet); }); return p; }(); const auto sysEx = createRandomSysEx (random, 300); - const auto originalPackets = [&] - { - Packets p; - Conversion::toMidi1 (sysEx, p); - return p; - }(); + const auto originalPackets = toMidi1 (sysEx); const auto modifiedPackets = [&] { @@ -378,9 +368,9 @@ public: converter.dispatch (p.data(), p.data() + p.size(), 0, - [&] (const MidiMessage& roundTripped) + [&] (const BytestreamMidiView& roundTripped) { - output.addEvent (roundTripped, int (roundTripped.getTimeStamp())); + output.addEvent (roundTripped.getMessage(), int (roundTripped.timestamp)); }); }; @@ -390,8 +380,7 @@ public: expect (equal (output, noteOn)); const auto newSysEx = createRandomSysEx (random, 300); - Packets newSysExPackets; - Conversion::toMidi1 (newSysEx, newSysExPackets); + const auto newSysExPackets = toMidi1 (newSysEx); // If we push another midi event without interrupting it, // it should get through without being modified, @@ -678,12 +667,17 @@ public: beginTest ("MIDI 2 -> 1 messages which don't convert"); { - const uint8_t opcodes[] { 0x0, 0x1, 0x4, 0x5, 0x6, 0xf }; + const std::byte opcodes[] { std::byte { 0x0 }, + std::byte { 0x1 }, + std::byte { 0x4 }, + std::byte { 0x5 }, + std::byte { 0x6 }, + std::byte { 0xf } }; for (const auto opcode : opcodes) { Packets midi2; - midi2.add (PacketX2 { Utils::bytesToWord (0x40, (uint8_t) (opcode << 0x4), 0, 0), 0x0 }); + midi2.add (PacketX2 { Utils::bytesToWord (std::byte { 0x40 }, std::byte { opcode << 0x4 }, std::byte { 0 }, std::byte { 0 }), 0x0 }); checkMidi2ToMidi1Conversion (midi2, {}); } } @@ -785,7 +779,7 @@ public: for (const auto cc : CCs) { Packets midi1; - midi1.add (PacketX1 { Utils::bytesToWord (0x20, 0xb0, cc, 0x00) }); + midi1.add (PacketX1 { Utils::bytesToWord (std::byte { 0x20 }, std::byte { 0xb0 }, std::byte { cc }, std::byte { 0x00 }) }); checkMidi1ToMidi2Conversion (midi1, {}); } @@ -874,6 +868,13 @@ public: } private: + static Packets toMidi1 (const MidiMessage& msg) + { + Packets packets; + Conversion::toMidi1 (ump::BytestreamMidiView (&msg), [&] (const auto p) { packets.add (p); }); + return packets; + } + static Packets convertMidi2ToMidi1 (const Packets& midi2) { Packets r; @@ -934,10 +935,10 @@ private: { const auto status = random.nextInt (3); - return PacketX1 { Utils::bytesToWord (0, - uint8_t (status << 0x4), - uint8_t (status == 0 ? 0 : random.nextInt (0x100)), - uint8_t (status == 0 ? 0 : random.nextInt (0x100))) }; + return PacketX1 { Utils::bytesToWord (std::byte { 0 }, + std::byte (status << 0x4), + std::byte (status == 0 ? 0 : random.nextInt (0x100)), + std::byte (status == 0 ? 0 : random.nextInt (0x100))) }; } PacketX1 createRandomRealtimeUMP (Random& random) @@ -946,19 +947,19 @@ private: { switch (random.nextInt (6)) { - case 0: return 0xf8; - case 1: return 0xfa; - case 2: return 0xfb; - case 3: return 0xfc; - case 4: return 0xfe; - case 5: return 0xff; + case 0: return std::byte { 0xf8 }; + case 1: return std::byte { 0xfa }; + case 2: return std::byte { 0xfb }; + case 3: return std::byte { 0xfc }; + case 4: return std::byte { 0xfe }; + case 5: return std::byte { 0xff }; } jassertfalse; - return 0x00; + return std::byte { 0x00 }; }(); - return PacketX1 { Utils::bytesToWord (0x10, uint8_t (status), 0x00, 0x00) }; + return PacketX1 { Utils::bytesToWord (std::byte { 0x10 }, status, std::byte { 0x00 }, std::byte { 0x00 }) }; } template diff --git a/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h b/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h index 2733f053ac..15ee58d3ad 100644 --- a/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h +++ b/modules/juce_audio_devices/midi_io/ump/juce_UMPU32InputHandler.h @@ -82,9 +82,9 @@ struct U32ToBytestreamHandler : public U32InputHandler void pushMidiData (const uint32_t* begin, const uint32_t* end, double time) override { - dispatcher.dispatch (begin, end, time, [this] (const MidiMessage& m) + dispatcher.dispatch (begin, end, time, [this] (const BytestreamMidiView& m) { - callback.handleIncomingMidiMessage (&input, m); + callback.handleIncomingMidiMessage (&input, m.getMessage()); }); } diff --git a/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm b/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm index ed40d6719d..fc5ffa050d 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm +++ b/modules/juce_audio_devices/native/juce_mac_CoreMidi.mm @@ -71,7 +71,7 @@ namespace CoreMidiHelpers { virtual ~SenderBase() noexcept = default; - virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) = 0; + virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) = 0; virtual void send (MIDIPortRef port, MIDIEndpointRef endpoint, ump::Iterator b, ump::Iterator e) = 0; virtual ump::MidiProtocol getProtocol() const noexcept = 0; @@ -88,7 +88,7 @@ namespace CoreMidiHelpers : umpConverter (getProtocolForEndpoint (ep)) {} - void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) override + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) override { newSendImpl (port, endpoint, m); } @@ -182,7 +182,7 @@ namespace CoreMidiHelpers { explicit Sender (MIDIEndpointRef) {} - void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) override + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) override { oldSendImpl (port, endpoint, m); } @@ -191,7 +191,7 @@ namespace CoreMidiHelpers { std::for_each (b, e, [&] (const ump::View& v) { - bytestreamConverter.convert (v, 0.0, [&] (const MidiMessage& m) + bytestreamConverter.convert (v, 0.0, [&] (const ump::BytestreamMidiView& m) { send (port, endpoint, m); }); @@ -206,7 +206,7 @@ namespace CoreMidiHelpers private: ump::ToBytestreamConverter bytestreamConverter { 2048 }; - void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& message) + void oldSendImpl (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& message) { #if JUCE_IOS const MIDITimeStamp timeStamp = mach_absolute_time(); @@ -217,7 +217,7 @@ namespace CoreMidiHelpers HeapBlock allocatedPackets; MIDIPacketList stackPacket; auto* packetToSend = &stackPacket; - auto dataSize = (size_t) message.getRawDataSize(); + auto dataSize = message.bytes.size(); if (message.isSysEx()) { @@ -234,7 +234,7 @@ namespace CoreMidiHelpers { p->timeStamp = timeStamp; p->length = (UInt16) jmin (maxPacketSize, bytesLeft); - memcpy (p->data, message.getRawData() + pos, p->length); + memcpy (p->data, message.bytes.data() + pos, p->length); pos += p->length; bytesLeft -= p->length; p = MIDIPacketNext (p); @@ -254,7 +254,7 @@ namespace CoreMidiHelpers auto& p = *(packetToSend->packet); p.timeStamp = timeStamp; p.length = (UInt16) dataSize; - memcpy (p.data, message.getRawData(), dataSize); + memcpy (p.data, message.bytes.data(), dataSize); } else { @@ -278,7 +278,7 @@ namespace CoreMidiHelpers : sender (makeImpl (ep)) {} - void send (MIDIPortRef port, MIDIEndpointRef endpoint, const MidiMessage& m) + void send (MIDIPortRef port, MIDIEndpointRef endpoint, const ump::BytestreamMidiView& m) { sender->send (port, endpoint, m); } @@ -379,7 +379,7 @@ namespace CoreMidiHelpers endpoint.release(); } - void send (const MidiMessage& m) + void send (const ump::BytestreamMidiView& m) { sender.send (*port, *endpoint, m); } @@ -1298,7 +1298,7 @@ MidiOutput::~MidiOutput() void MidiOutput::sendMessageNow (const MidiMessage& message) { - internal->send (message); + internal->send (ump::BytestreamMidiView (&message)); } MidiDeviceListConnection MidiDeviceListConnection::make (std::function cb)