/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to incorporate Link into a proprietary software application,
* please contact .
*/
#pragma once
#include
namespace ableton
{
template
inline BasicLink::BasicLink(const double bpm)
: mPeerCountCallback([](std::size_t) {})
, mTempoCallback([](link::Tempo) {})
, mController(link::Tempo(bpm),
[this](const std::size_t peers) {
std::lock_guard lock(mCallbackMutex);
mPeerCountCallback(peers);
},
[this](const link::Tempo tempo) {
std::lock_guard lock(mCallbackMutex);
mTempoCallback(tempo);
},
mClock,
util::injectVal(link::platform::IoContext{}))
{
}
template
inline bool BasicLink::isEnabled() const
{
return mController.isEnabled();
}
template
inline void BasicLink::enable(const bool bEnable)
{
mController.enable(bEnable);
}
template
inline std::size_t BasicLink::numPeers() const
{
return mController.numPeers();
}
template
template
void BasicLink::setNumPeersCallback(Callback callback)
{
std::lock_guard lock(mCallbackMutex);
mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); };
}
template
template
void BasicLink::setTempoCallback(Callback callback)
{
std::lock_guard lock(mCallbackMutex);
mTempoCallback = [callback](const link::Tempo tempo) { callback(tempo.bpm()); };
}
template
inline Clock BasicLink::clock() const
{
return mClock;
}
template
inline typename BasicLink::Timeline BasicLink::captureAudioTimeline() const
{
return BasicLink::Timeline{mController.timelineRtSafe(), numPeers() > 0};
}
template
inline void BasicLink::commitAudioTimeline(const Timeline timeline)
{
if (timeline.mOriginalTimeline != timeline.mTimeline)
{
mController.setTimelineRtSafe(timeline.mTimeline, mClock.micros());
}
}
template
inline typename BasicLink::Timeline BasicLink::captureAppTimeline() const
{
return Timeline{mController.timeline(), numPeers() > 0};
}
template
inline void BasicLink::commitAppTimeline(const Timeline timeline)
{
if (timeline.mOriginalTimeline != timeline.mTimeline)
{
mController.setTimeline(timeline.mTimeline, mClock.micros());
}
}
////////////////////
// Link::Timeline //
////////////////////
template
inline BasicLink::Timeline::Timeline(
const link::Timeline timeline, const bool bRespectQuantum)
: mOriginalTimeline(timeline)
, mbRespectQuantum(bRespectQuantum)
, mTimeline(timeline)
{
}
template
inline double BasicLink::Timeline::tempo() const
{
return mTimeline.tempo.bpm();
}
template
inline void BasicLink::Timeline::setTempo(
const double bpm, const std::chrono::microseconds atTime)
{
const auto desiredTl =
link::clampTempo(link::Timeline{link::Tempo(bpm), mTimeline.toBeats(atTime), atTime});
mTimeline.tempo = desiredTl.tempo;
mTimeline.timeOrigin = desiredTl.fromBeats(mTimeline.beatOrigin);
}
template
inline double BasicLink::Timeline::beatAtTime(
const std::chrono::microseconds time, const double quantum) const
{
return link::toPhaseEncodedBeats(mTimeline, time, link::Beats{quantum}).floating();
}
template
inline double BasicLink::Timeline::phaseAtTime(
const std::chrono::microseconds time, const double quantum) const
{
return link::phase(link::Beats{beatAtTime(time, quantum)}, link::Beats{quantum})
.floating();
}
template
inline std::chrono::microseconds BasicLink::Timeline::timeAtBeat(
const double beat, const double quantum) const
{
return link::fromPhaseEncodedBeats(mTimeline, link::Beats{beat}, link::Beats{quantum});
}
template
inline void BasicLink::Timeline::requestBeatAtTime(
const double beat, std::chrono::microseconds time, const double quantum)
{
if (mbRespectQuantum)
{
time = timeAtBeat(link::nextPhaseMatch(link::Beats{beatAtTime(time, quantum)},
link::Beats{beat}, link::Beats{quantum})
.floating(),
quantum);
}
forceBeatAtTime(beat, time, quantum);
}
template
inline void BasicLink::Timeline::forceBeatAtTime(
const double beat, const std::chrono::microseconds time, const double quantum)
{
// There are two components to the beat adjustment: a phase shift
// and a beat magnitude adjustment.
const auto curBeatAtTime = link::Beats{beatAtTime(time, quantum)};
const auto closestInPhase =
link::closestPhaseMatch(curBeatAtTime, link::Beats{beat}, link::Beats{quantum});
mTimeline = shiftClientTimeline(mTimeline, closestInPhase - curBeatAtTime);
// Now adjust the magnitude
mTimeline.beatOrigin = mTimeline.beatOrigin + (link::Beats{beat} - closestInPhase);
}
} // ableton