diff --git a/CMakeLists.txt b/CMakeLists.txt index f842662..a00b77c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,11 +28,13 @@ configure_file( set(BUILD_EXAMPLES OFF) set(SPNG_SHARED OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) 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 +68,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 +103,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 +112,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..3fdedcc --- /dev/null +++ b/src/midi/MidiGraph.cpp @@ -0,0 +1,88 @@ +#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) +{ + 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); + } +} + +} // namespace mc::midi diff --git a/src/midi/MidiGraph.hpp b/src/midi/MidiGraph.hpp new file mode 100644 index 0000000..e21260d --- /dev/null +++ b/src/midi/MidiGraph.hpp @@ -0,0 +1,61 @@ +#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: + Node* m_sender; + Node* m_receiver; +}; + +} // namespace mc::midi