From 8e3bf786c0e761b418ce471da178d5c56f0dc108 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sat, 13 Nov 2021 00:13:50 +0100 Subject: [PATCH] Initial implementation of Metronome from Ableton LINK Added submodule for github ableton link, and compiled draft of Metronome class. --- .gitmodules | 3 + CMakeLists.txt | 10 ++++ Metronome.cpp | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ Metronome.h | 27 +++++++++ ext/link | 1 + main.cpp | 13 ++++- 6 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 Metronome.cpp create mode 100644 Metronome.h create mode 160000 ext/link diff --git a/.gitmodules b/.gitmodules index 9a5d305..fb51e52 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "ext/glm"] path = ext/glm url = https://github.com/g-truc/glm.git +[submodule "ext/link"] + path = ext/link + url = https://github.com/Ableton/link.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b85a53..9b85f73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,13 @@ set(BUILD_STATIC_LIBS ON) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/glm) message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net -- ${CMAKE_CURRENT_SOURCE_DIR}/ext/glm") +# +# Ableton LINK +# +#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/link) +include(${CMAKE_CURRENT_SOURCE_DIR}/ext/link/AbletonLinkConfig.cmake) +message(STATUS "Compiling Ableton 'Link' https://github.com/Ableton/link -- ${CMAKE_CURRENT_SOURCE_DIR}/ext/link") + # # GLAD # @@ -254,6 +261,7 @@ include_directories( ${STB_INCLUDE_DIR} ${DIRENT_INCLUDE_DIR} ${OSCPACK_INCLUDE_DIR} + ${link_HEADERS} ) link_directories( @@ -328,6 +336,7 @@ set(VMIX_SRCS Connection.cpp ActionManager.cpp Overlay.cpp + Metronome.cpp ) @@ -509,6 +518,7 @@ target_link_libraries(${VMIX_BINARY} LINK_PRIVATE ${GSTREAMER_GL_LIBRARY} Threads::Threads PNG::PNG + Ableton::Link ${PLATFORM_LIBS} ) diff --git a/Metronome.cpp b/Metronome.cpp new file mode 100644 index 0000000..e6decff --- /dev/null +++ b/Metronome.cpp @@ -0,0 +1,155 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#include "Metronome.h" + + +namespace ableton +{ + + class Engine + { + public: + Engine(Link& link) + : mLink(link) + , mQuantum(4.) + { + } + + void startPlaying() + { + auto sessionState = mLink.captureAppSessionState(); + sessionState.setIsPlayingAndRequestBeatAtTime(true, now(), 0., mQuantum); + mLink.commitAppSessionState(sessionState); + } + + void stopPlaying() + { + auto sessionState = mLink.captureAppSessionState(); + sessionState.setIsPlaying(false, now()); + mLink.commitAppSessionState(sessionState); + } + + bool isPlaying() const + { + return mLink.captureAppSessionState().isPlaying(); + } + + double beatTime() const + { + auto sessionState = mLink.captureAppSessionState(); + return sessionState.beatAtTime(now(), mQuantum); + } + + void setTempo(double tempo) + { + auto sessionState = mLink.captureAppSessionState(); + sessionState.setTempo(tempo, now()); + mLink.commitAppSessionState(sessionState); + } + + double quantum() const + { + return mQuantum; + } + + void setQuantum(double quantum) + { + mQuantum = quantum; + } + + bool isStartStopSyncEnabled() const + { + return mLink.isStartStopSyncEnabled(); + } + + void setStartStopSyncEnabled(bool enabled) + { + mLink.enableStartStopSync(enabled); + } + + private: + std::chrono::microseconds now() const + { + return mLink.clock().micros(); + } + + Link& mLink; + double mQuantum; + }; + + + struct State + { + std::atomic running; + Link link; + Engine engine; + double beats, phase, tempo; + State() : running(true), link(120.), engine(link), beats(0.), phase(4.), tempo(120.) + { + } + }; + + void update(State& state) + { + state.link.enable(true); + + while (state.running) + { + const auto time = state.link.clock().micros(); + auto sessionState = state.link.captureAppSessionState(); + + state.beats = sessionState.beatAtTime(time, state.engine.quantum()); + state.phase = sessionState.phaseAtTime(time, state.engine.quantum()); + state.tempo = sessionState.tempo(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + state.link.enable(false); + } + +} // namespace ableton + + +ableton::State link_state_; + +Metronome::Metronome() +{ + +} + +bool Metronome::init() +{ + std::thread(ableton::update, std::ref(link_state_)).detach(); + + return link_state_.running; +} + +void Metronome::terminate() +{ + link_state_.running = false; +} + + +double Metronome::beats() const +{ + return link_state_.beats; +} + +double Metronome::phase() const +{ + return link_state_.phase; +} + +double Metronome::tempo() const +{ + return link_state_.tempo; +} diff --git a/Metronome.h b/Metronome.h new file mode 100644 index 0000000..0b8b766 --- /dev/null +++ b/Metronome.h @@ -0,0 +1,27 @@ +#ifndef METRONOME_H +#define METRONOME_H + + +class Metronome +{ + // Private Constructor + Metronome(); + Metronome(Metronome const& copy) = delete; + Metronome& operator=(Metronome const& copy) = delete; + +public: + + static Metronome& manager () + { + // The only instance + static Metronome _instance; + return _instance; + } + + bool init (); + void terminate(); + + +}; + +#endif // METRONOME_H diff --git a/ext/link b/ext/link new file mode 160000 index 0000000..14f6cc9 --- /dev/null +++ b/ext/link @@ -0,0 +1 @@ +Subproject commit 14f6cc99ac41466d52ce780aa37e432fe92c289b diff --git a/main.cpp b/main.cpp index 5003340..ce5d835 100644 --- a/main.cpp +++ b/main.cpp @@ -28,7 +28,7 @@ #include "RenderingManager.h" #include "UserInterfaceManager.h" #include "Connection.h" - +#include "Metronome.h" #if defined(APPLE) extern "C"{ @@ -85,12 +85,18 @@ int main(int argc, char *argv[]) /// lock to inform an instance is running Settings::Lock(); + /// /// /// CONNECTION INIT /// if ( !Connection::manager().init() ) return 1; + /// METRONOME INIT + /// + if ( !Metronome::manager().init() ) + return 1; + /// /// RENDERING INIT /// @@ -140,6 +146,11 @@ int main(int argc, char *argv[]) /// Rendering::manager().terminate(); + /// + /// METRONOME TERMINATE + /// + Metronome::manager().terminate(); + /// /// CONNECTION TERMINATE ///