Skip to content

Commit

Permalink
Use ableton::platforms::stl::Clock (which is std::chrono::steady_cloc…
Browse files Browse the repository at this point in the history
…k) as clock reference
  • Loading branch information
JoergAtGithub committed Aug 18, 2024
1 parent 9300d07 commit 30b8374
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 54 deletions.
49 changes: 18 additions & 31 deletions src/engine/sync/abletonlink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <cmath>

#include "control/controlobject.h"
#include "control/controlpushbutton.h"
#include "engine/sync/enginesync.h"
#include "moc_abletonlink.cpp"
#include "preferences/usersettings.h"
Expand All @@ -21,11 +20,12 @@ AbletonLink::AbletonLink(const QString& group, EngineSync* pEngineSync)
m_oldTempo(kDefaultBpm),
m_audioBufferTimeMicros(0),
m_absTimeWhenPrevOutputBufferReachesDac(0),
m_sampleTimeAtStartCallback(0) {
m_pLink = std::make_unique<ableton::Link>(120.);
m_sampleTimeAtStartCallback(0),
m_pLink(std::make_unique<ableton::BasicLink<MixxxClockRef>>(120.)),
m_pLinkButton(std::make_unique<ControlPushButton>(ConfigKey(group, "sync_enabled"))),
m_pNumLinkPeers(std::make_unique<ControlObject>(ConfigKey(m_group, "num_peers"))) {
m_timeAtStartCallback = m_pLink->clock().micros();

m_pLinkButton = std::make_unique<ControlPushButton>(ConfigKey(group, "sync_enabled"));
m_pLinkButton->setButtonMode(ControlPushButton::TOGGLE);
m_pLinkButton->setStates(2);

Expand All @@ -35,7 +35,6 @@ AbletonLink::AbletonLink(const QString& group, EngineSync* pEngineSync)
&AbletonLink::slotControlSyncEnabled,
Qt::DirectConnection);

m_pNumLinkPeers = std::make_unique<ControlObject>(ConfigKey(m_group, "num_peers"));
m_pNumLinkPeers->setReadOnly();
m_pNumLinkPeers->forceSet(0);

Expand All @@ -50,14 +49,7 @@ AbletonLink::AbletonLink(const QString& group, EngineSync* pEngineSync)
// Start/Stop sync makes not much sense for a DJ set
m_pLink->enableStartStopSync(false);

nonAudioThreadDebugOutput();
audioThreadDebugOutput();

initTestTimer(1000, true);
}

AbletonLink::~AbletonLink() {
m_pLink.reset();
}

void AbletonLink::slotControlSyncEnabled(double value) {
Expand Down Expand Up @@ -108,19 +100,22 @@ mixxx::Bpm AbletonLink::getBpm() const {
}

double AbletonLink::getBeatDistance() const {
ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState sessionState =
m_pLink->captureAudioSessionState();
auto beats = sessionState.beatAtTime(m_absTimeWhenPrevOutputBufferReachesDac, getQuantum());
return std::fmod(beats, 1.);
}

mixxx::Bpm AbletonLink::getBaseBpm() const {
ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState sessionState =
m_pLink->captureAudioSessionState();
mixxx::Bpm tempo(sessionState.tempo());
return tempo;
}

void AbletonLink::updateLeaderBeatDistance(double beatDistance) {
ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState sessionState =
m_pLink->captureAudioSessionState();
auto currentBeat = sessionState.beatAtTime(
m_absTimeWhenPrevOutputBufferReachesDac, getQuantum());
auto newBeat = currentBeat - std::fmod(currentBeat, 1.0) + beatDistance;
Expand All @@ -130,7 +125,8 @@ void AbletonLink::updateLeaderBeatDistance(double beatDistance) {
}

void AbletonLink::forceUpdateLeaderBeatDistance(double beatDistance) {
ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState sessionState =
m_pLink->captureAudioSessionState();
auto currentBeat = sessionState.beatAtTime(
m_absTimeWhenPrevOutputBufferReachesDac, getQuantum());
auto newBeat = currentBeat - std::fmod(currentBeat, 1.0) + beatDistance;
Expand All @@ -141,7 +137,8 @@ void AbletonLink::forceUpdateLeaderBeatDistance(double beatDistance) {
}

void AbletonLink::updateLeaderBpm(mixxx::Bpm bpm) {
ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState sessionState =
m_pLink->captureAudioSessionState();
sessionState.setTempo(bpm.value(), m_absTimeWhenPrevOutputBufferReachesDac);
m_pLink->commitAudioSessionState(sessionState);
}
Expand Down Expand Up @@ -177,7 +174,8 @@ void AbletonLink::onCallbackStart(int sampleRate,
m_absTimeWhenPrevOutputBufferReachesDac = absTimeWhenPrevOutputBufferReachesDac;

if (m_pLink->isEnabled()) {
ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState
sessionState = m_pLink->captureAudioSessionState();
const mixxx::Bpm tempo(sessionState.tempo());
if (m_oldTempo != tempo) {
m_oldTempo = tempo;
Expand All @@ -200,7 +198,8 @@ void AbletonLink::audioThreadDebugOutput() {
qDebug() << "isEnabled()" << m_pLink->isEnabled();
qDebug() << "numPeers()" << m_pLink->numPeers();

ableton::Link::SessionState sessionState = m_pLink->captureAudioSessionState();
ableton::BasicLink<MixxxClockRef>::SessionState sessionState =
m_pLink->captureAudioSessionState();

qDebug() << "sessionState.tempo()" << sessionState.tempo();
qDebug() << "sessionState.beatAtTime()"
Expand All @@ -218,15 +217,3 @@ void AbletonLink::audioThreadDebugOutput() {
m_pLink->clock().micros())
.count();
}

// Debug output function, which must not be called in audio thread
void AbletonLink::nonAudioThreadDebugOutput() {
qDebug() << "isStartStopSyncEnabled()" << m_pLink->isStartStopSyncEnabled();
}

void AbletonLink::initTestTimer(int ms, bool isRepeating) {
m_pTestTimer = new QTimer(this);
connect(m_pTestTimer, &QTimer::timeout, this, QOverload<>::of(&AbletonLink::testPrint));
m_pTestTimer->setSingleShot(!isRepeating);
m_pTestTimer->start(ms);
}
31 changes: 12 additions & 19 deletions src/engine/sync/abletonlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

#include <ableton/Link.hpp>
#include <ableton/link/HostTimeFilter.hpp>
#include <ableton/platforms/stl/Clock.hpp>

#include "control/controlpushbutton.h"
#include "engine/channels/enginechannel.h"
#include "engine/enginebuffer.h"
#include "engine/sync/syncable.h"
Expand All @@ -21,11 +23,16 @@
/// for maximum timing accuracy. Call the appropriate, realtime-safe functions
/// from the audio callback to do this.

// Note that the resolution of std::chrono::steady_clock is not guaranteed
// to be high resolution, but it is guaranteed to be monotonic.
// However, on all major platforms, it is high resolution enough.
using MixxxClockRef = ableton::platforms::stl::Clock;

class AbletonLink : public QObject, public Syncable {
Q_OBJECT
public:
AbletonLink(const QString& group, EngineSync* pEngineSync);
~AbletonLink() override;
~AbletonLink() override = default;

const QString& getGroup() const override {
return m_group;
Expand Down Expand Up @@ -93,17 +100,10 @@ class AbletonLink : public QObject, public Syncable {
std::chrono::microseconds absTimeWhenPrevOutputBufferReachesDac);
void onCallbackEnd(int sampleRate, int bufferSize);

private slots:
void testPrint() {
nonAudioThreadDebugOutput();
audioThreadDebugOutput();
}

private:
std::unique_ptr<ableton::Link> m_pLink;
ableton::link::HostTimeFilter<ableton::link::platform::Clock> m_hostTimeFilter;
ableton::link::HostTimeFilter<MixxxClockRef> m_hostTimeFilter;
const QString m_group;
EngineSync* m_pEngineSync;
EngineSync* m_pEngineSync; // unowned, must outlive this.
SyncMode m_mode;

mixxx::Bpm m_oldTempo;
Expand All @@ -113,6 +113,7 @@ class AbletonLink : public QObject, public Syncable {
std::chrono::microseconds m_sampleTimeAtStartCallback;
std::chrono::microseconds m_timeAtStartCallback;

std::unique_ptr<ableton::BasicLink<MixxxClockRef>> m_pLink;
std::unique_ptr<ControlPushButton> m_pLinkButton;
std::unique_ptr<ControlObject> m_pNumLinkPeers;

Expand All @@ -123,20 +124,12 @@ class AbletonLink : public QObject, public Syncable {

double getQuantum() const {
// Mixxx doesn't know about bars/time-signatures yet - phase
// syncronisation can't be implemented therefore yet
// synchronisation can't be implemented therefore yet
return 1.0;
}

// Test/Debug code

QTimer* m_pTestTimer;
const double beat = 0.0;

/// Link getters to call from audio thread.
void audioThreadDebugOutput();

/// Link getters to call from non-audio thread.
void nonAudioThreadDebugOutput();

void initTestTimer(int ms, bool isRepeating);
};
9 changes: 7 additions & 2 deletions src/soundio/sounddevicenetwork.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include <QSharedPointer>
#include <QString>
#include <QThread>
#include <ableton/Link.hpp>
#include <ableton/link/HostTimeFilter.hpp>
#include <ableton/platforms/stl/Clock.hpp>

#ifdef __LINUX__
#include <pthread.h>
Expand All @@ -25,6 +25,11 @@ class SoundManager;
class EngineNetworkStream;
class SoundDeviceNetworkThread;

// Note that the resolution of std::chrono::steady_clock is not guaranteed
// to be high resolution, but it is guaranteed to be monotonic.
// However, on all major platforms, it is high resolution enough.
using MixxxClockRef = ableton::platforms::stl::Clock;

class SoundDeviceNetwork : public SoundDevice {
public:
SoundDeviceNetwork(UserSettingsPointer config,
Expand Down Expand Up @@ -71,7 +76,7 @@ class SoundDeviceNetwork : public SoundDevice {
qint64 m_targetTime;
PerformanceTimer m_clkRefTimer;

ableton::link::HostTimeFilter<ableton::link::platform::Clock> m_hostTimeFilter;
ableton::link::HostTimeFilter<MixxxClockRef> m_hostTimeFilter;
};

class SoundDeviceNetworkThread : public QThread {
Expand Down
9 changes: 7 additions & 2 deletions src/soundio/sounddeviceportaudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include <portaudio.h>

#include <QString>
#include <ableton/Link.hpp>
#include <ableton/link/HostTimeFilter.hpp>
#include <ableton/platforms/stl/Clock.hpp>
#include <memory>

#include "control/pollingcontrolproxy.h"
Expand All @@ -17,6 +17,11 @@

class SoundManager;

// Note that the resolution of std::chrono::steady_clock is not guaranteed
// to be high resolution, but it is guaranteed to be monotonic.
// However, on all major platforms, it is high resolution enough.
using MixxxClockRef = ableton::platforms::stl::Clock;

class SoundDevicePortAudio : public SoundDevice {
public:
SoundDevicePortAudio(UserSettingsPointer config,
Expand Down Expand Up @@ -88,7 +93,7 @@ class SoundDevicePortAudio : public SoundDevice {
PerformanceTimer m_clkRefTimer;
PaTime m_lastCallbackEntrytoDacSecs;

ableton::link::HostTimeFilter<ableton::link::platform::Clock> m_hostTimeFilter;
ableton::link::HostTimeFilter<MixxxClockRef> m_hostTimeFilter;
double m_cummulatedBufferTime;
MovingInterquartileMean m_meanOutputLatency;
};

0 comments on commit 30b8374

Please sign in to comment.