From e0e1dc5363fd516d8aa5372a6880768e3ea85829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc=20Serf=C5=91z=C5=91?= Date: Thu, 13 Jun 2024 12:18:50 +0200 Subject: [PATCH] feat(midi): Recognize system messages in Log Node --- src/LogNode.cpp | 115 +++++++++--- src/LogNode.hpp | 24 ++- src/midi/MessageView.hpp | 391 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 503 insertions(+), 27 deletions(-) diff --git a/src/LogNode.cpp b/src/LogNode.cpp index 59f21f1..1e97bc6 100644 --- a/src/LogNode.cpp +++ b/src/LogNode.cpp @@ -46,6 +46,12 @@ void mc::LogNode::render_internal() ImGui::TextUnformatted("MIDI in"); ImNodes::EndInputAttribute(); + bool clear = false; + if (ImGui::Button("Clear")) + { + clear = true; + } + ImGui::SameLine(); int new_buffer_size = static_cast(m_max_buffer_size); ImGui::SetNextItemWidth(100.0F * m_scale_provider->get_scale_value()); if (ImGui::InputInt("Buffer Size", &new_buffer_size)) @@ -57,6 +63,10 @@ void mc::LogNode::render_internal() } std::unique_lock lock(m_buffer_mutex); + if (clear) + { + m_message_buffer.clear(); + } while (!m_message_buffer.empty() && m_message_buffer.size() > m_max_buffer_size) { m_message_buffer.pop_back(); @@ -130,6 +140,29 @@ void mc::LogNode::message_received(std::span message_bytes) m_input_indicator.trigger(); midi::MessageView message(message_bytes); + midi::tag_overloads message_visitor{ + [](midi::ChannelMessageViewTag, auto channel_message) -> BufferElement { + return parse_channel_message(channel_message); + }, + [](midi::SystemMessageViewTag, auto system_message) -> BufferElement { + return parse_system_message(system_message); + }, + []( + Tag, auto) -> std::enable_if_t && + !std::is_base_of_v, + BufferElement> { + return BufferElement{"Unknown"s}; + }}; + const BufferElement new_element = std::visit(message_visitor, message.parse()); + + std::lock_guard guard(m_buffer_mutex); + m_message_buffer.push_front(std::move(new_element)); +} + +mc::LogNode::BufferElement mc::LogNode::parse_channel_message( + midi::ChannelMessageView message_view) +{ + using namespace std::string_literals; midi::tag_overloads message_visitor{ [](midi::NoteOnMessageViewTag, auto note_on) -> BufferElement { return BufferElement{"Note On"s, @@ -150,35 +183,33 @@ void mc::LogNode::message_received(std::span message_bytes) fmt::format("Pressure: {}", poly_key_pressure.get_pressure())}; }, [](midi::AllSoundOffMessageViewTag, auto all_sound_off) -> BufferElement { - return BufferElement{"All Sound Off"s, all_sound_off.get_channel_human(), ""s, ""s}; + return BufferElement{"All Sound Off"s, all_sound_off.get_channel_human()}; }, [](midi::ResetAllControllersMessageViewTag, auto reset_all_controllers) -> BufferElement { - return BufferElement{ - "Reset All Controllers"s, reset_all_controllers.get_channel_human(), ""s, ""s}; + return BufferElement{"Reset All Controllers"s, + reset_all_controllers.get_channel_human()}; }, [](midi::LocalControlMessageViewTag, auto local_control) -> BufferElement { return BufferElement{"Local Control"s, local_control.get_channel_human(), - local_control.get_value() ? "On"s : "Off"s, - ""s}; + local_control.get_value() ? "On"s : "Off"s}; }, [](midi::AllNotesOffMessageViewTag, auto all_notes_off) -> BufferElement { - return BufferElement{"All Notes Off"s, all_notes_off.get_channel_human(), ""s, ""s}; + return BufferElement{"All Notes Off"s, all_notes_off.get_channel_human()}; }, [](midi::OmniModeOffMessageViewTag, auto omni_off) -> BufferElement { - return BufferElement{"Omni Mode Off"s, omni_off.get_channel_human(), ""s, ""s}; + return BufferElement{"Omni Mode Off"s, omni_off.get_channel_human()}; }, [](midi::OmniModeOnMessageViewTag, auto omni_on) -> BufferElement { - return BufferElement{"Omni Mode On"s, omni_on.get_channel_human(), ""s, ""s}; + return BufferElement{"Omni Mode On"s, omni_on.get_channel_human()}; }, [](midi::MonoModeOnMessageViewTag, auto mono_mode) -> BufferElement { return BufferElement{"Mono Mode On"s, mono_mode.get_channel_human(), - fmt::format("Channels: {}", mono_mode.get_num_channels()), - ""s}; + fmt::format("Channels: {}", mono_mode.get_num_channels())}; }, [](midi::PolyModeOnMessageViewTag, auto poly_mode) -> BufferElement { - return BufferElement{"Poly Mode On"s, poly_mode.get_channel_human(), ""s, ""s}; + return BufferElement{"Poly Mode On"s, poly_mode.get_channel_human()}; }, [](midi::ControlChangeMessageViewTag, auto control_change) -> BufferElement { return BufferElement{"Control Change"s, @@ -191,26 +222,68 @@ void mc::LogNode::message_received(std::span message_bytes) [](midi::ProgramChangeMessageViewTag, auto program_change) -> BufferElement { return BufferElement{"Program Change"s, program_change.get_channel_human(), - fmt::format("Program: {}", program_change.get_program_number()), - ""s}; + fmt::format("Program: {}", program_change.get_program_number())}; }, [](midi::ChannelPressureMessageViewTag, auto channel_pressure) -> BufferElement { return BufferElement{"Channel Aftertouch"s, channel_pressure.get_channel_human(), - fmt::format("Pressure: {}", channel_pressure.get_pressure()), - ""s}; + fmt::format("Pressure: {}", channel_pressure.get_pressure())}; }, [](midi::PitchBendMessageViewTag, auto pitch_bend) -> BufferElement { return BufferElement{"Pitch bend"s, pitch_bend.get_channel_human(), - fmt::format("Value: {}", pitch_bend.get_value_human()), - ""s}; + fmt::format("Value: {}", pitch_bend.get_value_human())}; }, [](auto, auto) -> BufferElement { - return BufferElement{"Unknown"s, std::nullopt, ""s, ""s}; + return BufferElement{"Unknown channel message"s}; }}; - const BufferElement new_element = std::visit(message_visitor, message.parse()); + return std::visit(message_visitor, message_view.parse()); +} - std::lock_guard guard(m_buffer_mutex); - m_message_buffer.push_front(std::move(new_element)); +mc::LogNode::BufferElement mc::LogNode::parse_system_message( + midi::SystemMessageView message_view) +{ + using namespace std::string_literals; + midi::tag_overloads message_visitor{ + [](midi::SystemExclusiveMessageViewTag, auto system_exclusive) -> BufferElement { + return BufferElement{"System Exclusive"s, + fmt::format("ID: {}", system_exclusive.get_manufacturer_id()), + fmt::format("{} bytes", system_exclusive.get_length())}; + }, + [](midi::TimeCodeQuarterFrameMessageViewTag, auto timecode_quarter) -> BufferElement { + return BufferElement{"Timecode Quarter Frame"s, + fmt::format("Type: {}", timecode_quarter.get_type()), + fmt::format("Values: {}", timecode_quarter.get_values())}; + }, + [](midi::SongPositionPointerMessageViewTag, auto song_position) -> BufferElement { + return BufferElement{"Song Position"s, std::to_string(song_position.get_position())}; + }, + [](midi::SongSelectMessageViewTag, auto song_select) -> BufferElement { + return BufferElement{"Song Select"s, std::to_string(song_select.get_song())}; + }, + [](midi::TuneRequestMessageViewTag, auto) -> BufferElement { + return BufferElement{"Tune Request"s}; + }, + [](midi::TimingClockMessageViewTag, auto) -> BufferElement { + return BufferElement{"Timing Clock"s}; + }, + [](midi::StartSequenceMessageViewTag, auto) -> BufferElement { + return BufferElement{"Start Sequence"s}; + }, + [](midi::ContinueSequenceMessageViewTag, auto) -> BufferElement { + return BufferElement{"Continue Sequence"s}; + }, + [](midi::StopSequenceMessageViewTag, auto) -> BufferElement { + return BufferElement{"Stop Sequence"s}; + }, + [](midi::ActiveSensingMessageViewTag, auto) -> BufferElement { + return BufferElement{"Active Sensing"s}; + }, + [](midi::ResetMessageViewTag, auto) -> BufferElement { + return BufferElement{"Reset everything"s}; + }, + [](auto, auto) -> BufferElement { + return BufferElement{"Unknown system message"s}; + }}; + return std::visit(message_visitor, message_view.parse()); } diff --git a/src/LogNode.hpp b/src/LogNode.hpp index 983fd94..afde69a 100644 --- a/src/LogNode.hpp +++ b/src/LogNode.hpp @@ -16,6 +16,13 @@ namespace mc { +namespace midi +{ +template +class ChannelMessageView; +template +class SystemMessageView; +} // namespace midi class LogNode final : public Node, private midi::GraphObserver { @@ -43,16 +50,22 @@ class LogNode final : public Node, private midi::GraphObserver struct BufferElement { - BufferElement(std::string&& name, - std::optional&& channel, - std::string&& data_0, - std::string&& data_1) + explicit BufferElement(std::string&& name, + std::optional&& channel = {}, + std::string&& data_0 = {}, + std::string&& data_1 = {}) : m_arrived(std::chrono::system_clock::now()), m_name(std::move(name)), m_channel(std::move(channel)), m_data_0(std::move(data_0)), m_data_1(std::move(data_1)) { } + BufferElement(std::string&& name, std::string&& data_0, std::string&& data_1 = {}) + : m_arrived(std::chrono::system_clock::now()), m_name(std::move(name)), + m_data_0(std::move(data_0)), m_data_1(std::move(data_1)) + { + } + std::chrono::time_point m_arrived; std::string m_name; std::optional m_channel; @@ -60,6 +73,9 @@ class LogNode final : public Node, private midi::GraphObserver std::string m_data_1; }; + static BufferElement parse_channel_message(midi::ChannelMessageView message_view); + static BufferElement parse_system_message(midi::SystemMessageView message_view); + std::mutex m_buffer_mutex; std::deque m_message_buffer; LogMidiNode m_log_midi_node; diff --git a/src/midi/MessageView.hpp b/src/midi/MessageView.hpp index 13e2cc7..e3f68b5 100644 --- a/src/midi/MessageView.hpp +++ b/src/midi/MessageView.hpp @@ -3,6 +3,7 @@ #include "../Utils.hpp" #include +#include #include #include #include @@ -63,6 +64,26 @@ class OmniModeOnMessageView; template class MonoModeOnMessageView; class PolyModeOnMessageView; +template +class SystemMessageView; +template +class SystemCommonMessageView; +template +class SystemExclusiveMessageView; +template +class TimeCodeQuarterFrameMessageView; +template +class SongPositionPointerMessageView; +template +class SongSelectMessageView; +class TuneRequestMessageView; +class SystemRealTimeMessageView; +class TimingClockMessageView; +class StartSequenceMessageView; +class ContinueSequenceMessageView; +class StopSequenceMessageView; +class ActiveSensingMessageView; +class ResetMessageView; template class MessageView @@ -104,6 +125,20 @@ class MessageView MonoModeOnMessageView, PolyModeOnMessageView, ChannelMessageView, + SystemMessageView, + SystemCommonMessageView, + SystemExclusiveMessageView, + TimeCodeQuarterFrameMessageView, + SongPositionPointerMessageView, + SongSelectMessageView, + TuneRequestMessageView, + SystemRealTimeMessageView, + TimingClockMessageView, + StartSequenceMessageView, + ContinueSequenceMessageView, + StopSequenceMessageView, + ActiveSensingMessageView, + ResetMessageView, MessageView> parse() const { @@ -111,6 +146,10 @@ class MessageView { return utils::variant_cast(ChannelMessageView(m_message_data).parse()); } + else if (is_system()) + { + return utils::variant_cast(SystemMessageView(m_message_data).parse()); + } return *this; } @@ -121,7 +160,7 @@ class MessageView MessageView(std::span) -> MessageView; MessageView(std::span) -> MessageView; -struct ChannelMessageViewTag +struct ChannelMessageViewTag : public MessageViewTag { }; @@ -206,7 +245,7 @@ class ChannelMessageView : public MessageView } }; -struct NoteMessageViewTag +struct NoteMessageViewTag : public ChannelMessageViewTag { }; @@ -745,4 +784,352 @@ class PitchBendMessageView : public ChannelMessageView } }; +struct SystemMessageViewTag : public MessageViewTag +{ +}; + +template +class SystemMessageView : public MessageView +{ +public: + using tag_t = SystemMessageViewTag; + + explicit SystemMessageView(typename MessageView::span_t message_data) + : MessageView(message_data) + { + } + + bool is_common() const { return this->m_message_data[0] < 0xf8; } + + bool is_realtime() const { return !is_common(); } + + std::variant, + SystemExclusiveMessageView, + TimeCodeQuarterFrameMessageView, + SongPositionPointerMessageView, + SongSelectMessageView, + TuneRequestMessageView, + SystemRealTimeMessageView, + TimingClockMessageView, + StartSequenceMessageView, + ContinueSequenceMessageView, + StopSequenceMessageView, + ActiveSensingMessageView, + ResetMessageView, + SystemMessageView> + parse() const; +}; + +struct SystemCommonMessageViewTag : public SystemMessageViewTag +{ +}; + +template +class SystemCommonMessageView : public SystemMessageView +{ +public: + using tag_t = SystemCommonMessageViewTag; + + explicit SystemCommonMessageView(typename MessageView::span_t message_data) + : SystemMessageView(message_data) + { + } + + std::variant, + TimeCodeQuarterFrameMessageView, + SongPositionPointerMessageView, + SongSelectMessageView, + TuneRequestMessageView, + SystemCommonMessageView> + parse() const + { + switch (this->m_message_data[0]) + { + case 0xf0: + return SystemExclusiveMessageView(this->m_message_data); + case 0xf1: + return TimeCodeQuarterFrameMessageView(this->m_message_data); + case 0xf2: + return SongPositionPointerMessageView(this->m_message_data); + case 0xf3: + return SongSelectMessageView(this->m_message_data); + case 0xf6: + return TuneRequestMessageView(this->m_message_data); + default: + return *this; + } + } +}; + +struct SystemExclusiveMessageViewTag : public SystemCommonMessageViewTag +{ +}; + +template +class SystemExclusiveMessageView : public SystemCommonMessageView +{ +public: + using tag_t = SystemExclusiveMessageViewTag; + constexpr inline static unsigned char end_of_exclusive = 0xf7; + + explicit SystemExclusiveMessageView(typename MessageView::span_t message_data) + : SystemCommonMessageView(message_data) + { + } + + unsigned char get_manufacturer_id() const { return this->m_message_data[1]; } + + std::size_t get_length() const + { + assert(this->m_message_data.size() > 1); + return this->m_message_data.size() - 1; + } +}; + +struct TimeCodeQuarterFrameMessageViewTag : public SystemCommonMessageViewTag +{ +}; + +template +class TimeCodeQuarterFrameMessageView : public SystemCommonMessageView +{ +public: + using tag_t = TimeCodeQuarterFrameMessageViewTag; + + explicit TimeCodeQuarterFrameMessageView(typename MessageView::span_t message_data) + : SystemCommonMessageView(message_data) + { + } + + unsigned char get_type() const { return (this->m_message_data[1] & 0x70) >> 4; } + + unsigned char get_values() const { return this->m_message_data[1] & 0x0F; } +}; + +struct SongPositionPointerMessageViewTag : public SystemCommonMessageViewTag +{ +}; + +template +class SongPositionPointerMessageView : public SystemCommonMessageView +{ +public: + using tag_t = SongPositionPointerMessageViewTag; + + explicit SongPositionPointerMessageView(typename MessageView::span_t message_data) + : SystemCommonMessageView(message_data) + { + } + + unsigned short get_position() const + { + const unsigned char lsb = this->m_message_data[1] & 0x7f; + const unsigned char msb = this->m_message_data[2] & 0x7f; + return static_cast(127) * msb + lsb; + } +}; + +struct SongSelectMessageViewTag : public SystemCommonMessageViewTag +{ +}; + +template +class SongSelectMessageView : public SystemCommonMessageView +{ +public: + using tag_t = SongSelectMessageViewTag; + + explicit SongSelectMessageView(typename MessageView::span_t message_data) + : SystemCommonMessageView(message_data) + { + } + + unsigned char get_song() const { return this->m_message_data[1] & 0x7f; } +}; + +struct TuneRequestMessageViewTag : public SystemCommonMessageViewTag +{ +}; + +class TuneRequestMessageView : public SystemCommonMessageView +{ +public: + using tag_t = TuneRequestMessageViewTag; + + explicit TuneRequestMessageView(typename MessageView::span_t message_data) + : SystemCommonMessageView(message_data) + { + } +}; + +struct SystemRealTimeMessageViewTag : public SystemMessageViewTag +{ +}; + +class SystemRealTimeMessageView : public SystemMessageView +{ +public: + using tag_t = SystemRealTimeMessageViewTag; + + explicit SystemRealTimeMessageView(typename MessageView::span_t message_data) + : SystemMessageView(message_data) + { + } + + std::variant + parse() const; +}; + +struct TimingClockMessageViewTag : public SystemRealTimeMessageViewTag +{ +}; + +class TimingClockMessageView : public SystemRealTimeMessageView +{ +public: + using tag_t = TimingClockMessageViewTag; + + explicit TimingClockMessageView(typename MessageView::span_t message_data) + : SystemRealTimeMessageView(message_data) + { + } +}; + +struct StartSequenceMessageViewTag : public SystemRealTimeMessageViewTag +{ +}; + +class StartSequenceMessageView : public SystemRealTimeMessageView +{ +public: + using tag_t = StartSequenceMessageViewTag; + + explicit StartSequenceMessageView(typename MessageView::span_t message_data) + : SystemRealTimeMessageView(message_data) + { + } +}; + +struct ContinueSequenceMessageViewTag : public SystemRealTimeMessageViewTag +{ +}; + +class ContinueSequenceMessageView : public SystemRealTimeMessageView +{ +public: + using tag_t = ContinueSequenceMessageViewTag; + + explicit ContinueSequenceMessageView(typename MessageView::span_t message_data) + : SystemRealTimeMessageView(message_data) + { + } +}; + +struct StopSequenceMessageViewTag : public SystemRealTimeMessageViewTag +{ +}; + +class StopSequenceMessageView : public SystemRealTimeMessageView +{ +public: + using tag_t = StopSequenceMessageViewTag; + + explicit StopSequenceMessageView(typename MessageView::span_t message_data) + : SystemRealTimeMessageView(message_data) + { + } +}; + +struct ActiveSensingMessageViewTag : public SystemRealTimeMessageViewTag +{ +}; + +class ActiveSensingMessageView : public SystemRealTimeMessageView +{ +public: + using tag_t = ActiveSensingMessageViewTag; + + explicit ActiveSensingMessageView(typename MessageView::span_t message_data) + : SystemRealTimeMessageView(message_data) + { + } +}; + +struct ResetMessageViewTag : public SystemRealTimeMessageViewTag +{ +}; + +class ResetMessageView : public SystemRealTimeMessageView +{ +public: + using tag_t = ResetMessageViewTag; + + explicit ResetMessageView(typename MessageView::span_t message_data) + : SystemRealTimeMessageView(message_data) + { + } +}; + +template +std::variant, + SystemExclusiveMessageView, + TimeCodeQuarterFrameMessageView, + SongPositionPointerMessageView, + SongSelectMessageView, + TuneRequestMessageView, + SystemRealTimeMessageView, + TimingClockMessageView, + StartSequenceMessageView, + ContinueSequenceMessageView, + StopSequenceMessageView, + ActiveSensingMessageView, + ResetMessageView, + SystemMessageView> +SystemMessageView::parse() const +{ + if (is_common()) + { + return utils::variant_cast(SystemCommonMessageView(this->m_message_data).parse()); + } + else if (is_realtime()) + { + return utils::variant_cast(SystemRealTimeMessageView(this->m_message_data).parse()); + } + return *this; +} + +inline std::variant +SystemRealTimeMessageView::parse() const +{ + switch (this->m_message_data[0]) + { + case 0xf8: + return TimingClockMessageView(this->m_message_data); + case 0xfa: + return StartSequenceMessageView(this->m_message_data); + case 0xfb: + return ContinueSequenceMessageView(this->m_message_data); + case 0xfc: + return StopSequenceMessageView(this->m_message_data); + case 0xfe: + return ActiveSensingMessageView(this->m_message_data); + case 0xff: + return ResetMessageView(this->m_message_data); + default: + return *this; + } +} + } // namespace mc::midi