From 19a7f1c1ccbbc83fb84996ac655454c1e7c779c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc=20Serf=C5=91z=C5=91?= Date: Thu, 28 Mar 2024 21:23:31 +0100 Subject: [PATCH] Implemented MidiGraph --- CMakeLists.txt | 9 ++-- src/midi/CMakeLists.txt | 10 ++++ src/midi/MidiGraph.cpp | 101 ++++++++++++++++++++++++++++++++++++++++ src/midi/MidiGraph.hpp | 60 ++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 src/midi/CMakeLists.txt create mode 100644 src/midi/MidiGraph.cpp create mode 100644 src/midi/MidiGraph.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f842662..e70e335 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory(external/libspng EXCLUDE_FROM_ALL) set(RTMIDI_API_JACK OFF) set(RTMIDI_BUILD_STATIC_LIBS ON) add_subdirectory(external/rtmidi EXCLUDE_FROM_ALL) +add_subdirectory(src/midi) set(sources ${g_font_cpp_path} @@ -66,10 +67,6 @@ set(sources src/ResourceLoader.cpp src/Theme.cpp src/Version.cpp - src/midi/InputObserver.cpp - src/midi/MidiEngine.cpp - src/midi/MidiProbe.cpp - src/midi/OutputObserver.cpp ) if(WIN32) @@ -105,7 +102,8 @@ target_link_libraries(midiconn PRIVATE SDL2::SDL2 fmt::fmt freetype - resources) + resources + midi) if(NOT WIN32 AND MC_CHECK_FOR_UPDATES) target_link_libraries(midiconn PRIVATE curl) endif() @@ -113,7 +111,6 @@ target_compile_definitions(midiconn PRIVATE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG IMGUI_ENABLE_FREETYPE ) -target_compile_features(midiconn PRIVATE cxx_std_20) set(CPACK_PACKAGE_NAME "midiconn") set(CPACK_PACKAGE_VENDOR "safeworlds") diff --git a/src/midi/CMakeLists.txt b/src/midi/CMakeLists.txt new file mode 100644 index 0000000..8d79a26 --- /dev/null +++ b/src/midi/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(midi + InputObserver.cpp + MidiEngine.cpp + MidiGraph.cpp + MidiProbe.cpp + OutputObserver.cpp +) + +target_compile_features(midi PUBLIC cxx_std_20) +target_link_libraries(midi PRIVATE fmt::fmt rtmidi spdlog::spdlog) diff --git a/src/midi/MidiGraph.cpp b/src/midi/MidiGraph.cpp new file mode 100644 index 0000000..d8a46c1 --- /dev/null +++ b/src/midi/MidiGraph.cpp @@ -0,0 +1,101 @@ +#include "MidiGraph.hpp" + +namespace mc::midi +{ + +Node::~Node() +{ + for (auto& conn : m_sender_connections) + { + Connection::destroy(conn); + } + for (auto& conn : m_receiver_connections) + { + Connection::destroy(conn); + } +} + +void Node::process(data_span data) +{ +} + +void Node::broadcast_data(data_span data) +{ + size_t idx = 0; + for (auto it = m_sender_connections.begin(), end = m_sender_connections.end(); it != end; + ++it, ++idx) + { + if (idx == 0) + { + (*it)->receiver()->receive_data(data); + } + else + { + data_vec data_copy(data.size()); + std::copy(data.begin(), data.end(), data_copy.begin()); + (*it)->receiver()->receive_data(data_copy); + } + } +} + +void Node::connect_as_sender(connection_ptr connection) +{ + m_sender_connections.emplace(connection); +} + +void Node::connect_as_receiver(connection_ptr connection) +{ + m_receiver_connections.emplace(connection); +} + +void Node::remove_connection(connection_ptr connection) +{ + remove_connection_impl(m_sender_connections, connection); + remove_connection_impl(m_receiver_connections, connection); +} + +void Node::remove_connection_impl(std::set& set, connection_ptr connection) +{ + set.erase(connection); +} + +void Node::receive_data(data_span data) +{ + process(data); + broadcast_data(data); +} + +weak_connection_ptr Connection::create(Node& sender, Node& receiver) +{ + if (is_loop(sender, receiver)) + { + return weak_connection_ptr(); + } + const auto conn = std::shared_ptr(new Connection()); + conn->m_sender = &sender; + conn->m_receiver = &receiver; + sender.connect_as_sender(conn); + receiver.connect_as_receiver(conn); + return conn; +} + +void Connection::destroy(weak_connection_ptr connection) +{ + auto connection_shared = connection.lock(); + if (connection_shared) + { + connection_shared->m_sender->remove_connection(connection_shared); + connection_shared->m_receiver->remove_connection(connection_shared); + } +} + +bool Connection::is_loop(Node& sender, Node& receiver) +{ + return &sender == &receiver || std::any_of(receiver.m_sender_connections.begin(), + receiver.m_sender_connections.end(), + [&sender](const connection_ptr& connection) { + return is_loop(sender, *connection->m_receiver); + }); +} + +} // namespace mc::midi diff --git a/src/midi/MidiGraph.hpp b/src/midi/MidiGraph.hpp new file mode 100644 index 0000000..eb300c5 --- /dev/null +++ b/src/midi/MidiGraph.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mc::midi +{ + +class Connection; + +using connection_ptr = std::shared_ptr; +using weak_connection_ptr = std::weak_ptr; +using data_vec = std::vector; +using data_span = std::span; + +class Node +{ +public: + virtual ~Node(); + +protected: + virtual void process(data_span data); + void broadcast_data(data_span data); + +private: + friend class Connection; + + void connect_as_sender(connection_ptr connection); + void connect_as_receiver(connection_ptr connection); + void remove_connection(connection_ptr connection); + void remove_connection_impl(std::set& set, connection_ptr connection); + + void receive_data(data_span data); + + std::set m_sender_connections; + std::set m_receiver_connections; +}; + +class Connection final +{ +private: + Connection() = default; + +public: + static weak_connection_ptr create(Node& sender, Node& receiver); + static void destroy(weak_connection_ptr connection); + + Node* receiver() const { return m_receiver; } + +private: + static bool is_loop(Node& sender, Node& receiver); + + Node* m_sender; + Node* m_receiver; +}; + +} // namespace mc::midi