Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
7.5KB

  1. /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. * If you would like to incorporate Link into a proprietary software application,
  17. * please contact <link-devs@ableton.com>.
  18. */
  19. #pragma once
  20. #include <ableton/link/Phase.hpp>
  21. namespace ableton
  22. {
  23. namespace detail
  24. {
  25. inline Link::SessionState toSessionState(
  26. const link::ClientState& state, const bool isConnected)
  27. {
  28. const auto time = state.timeline.fromBeats(state.startStopState.beats);
  29. const auto startStopState =
  30. link::ApiStartStopState{state.startStopState.isPlaying, time};
  31. return {{state.timeline, startStopState}, isConnected};
  32. }
  33. inline link::IncomingClientState toIncomingClientState(const link::ApiState& state,
  34. const link::ApiState& originalState,
  35. const std::chrono::microseconds timestamp)
  36. {
  37. const auto timeline = originalState.timeline != state.timeline
  38. ? link::OptionalTimeline{state.timeline}
  39. : link::OptionalTimeline{};
  40. const auto startStopState =
  41. originalState.startStopState != state.startStopState
  42. ? link::OptionalStartStopState{{state.startStopState.isPlaying,
  43. state.timeline.toBeats(state.startStopState.time), timestamp}}
  44. : link::OptionalStartStopState{};
  45. return {timeline, startStopState, timestamp};
  46. }
  47. } // namespace detail
  48. inline Link::Link(const double bpm)
  49. : mPeerCountCallback([](std::size_t) {})
  50. , mTempoCallback([](link::Tempo) {})
  51. , mStartStopCallback([](bool) {})
  52. , mClock{}
  53. , mController(link::Tempo(bpm),
  54. [this](const std::size_t peers) {
  55. std::lock_guard<std::mutex> lock(mCallbackMutex);
  56. mPeerCountCallback(peers);
  57. },
  58. [this](const link::Tempo tempo) {
  59. std::lock_guard<std::mutex> lock(mCallbackMutex);
  60. mTempoCallback(tempo);
  61. },
  62. [this](const bool isPlaying) {
  63. std::lock_guard<std::mutex> lock(mCallbackMutex);
  64. mStartStopCallback(isPlaying);
  65. },
  66. mClock,
  67. util::injectVal(link::platform::IoContext{}))
  68. {
  69. }
  70. inline bool Link::isEnabled() const
  71. {
  72. return mController.isEnabled();
  73. }
  74. inline void Link::enable(const bool bEnable)
  75. {
  76. mController.enable(bEnable);
  77. }
  78. inline bool Link::isStartStopSyncEnabled() const
  79. {
  80. return mController.isStartStopSyncEnabled();
  81. }
  82. inline void Link::enableStartStopSync(bool bEnable)
  83. {
  84. mController.enableStartStopSync(bEnable);
  85. }
  86. inline std::size_t Link::numPeers() const
  87. {
  88. return mController.numPeers();
  89. }
  90. template <typename Callback>
  91. void Link::setNumPeersCallback(Callback callback)
  92. {
  93. std::lock_guard<std::mutex> lock(mCallbackMutex);
  94. mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); };
  95. }
  96. template <typename Callback>
  97. void Link::setTempoCallback(Callback callback)
  98. {
  99. std::lock_guard<std::mutex> lock(mCallbackMutex);
  100. mTempoCallback = [callback](const link::Tempo tempo) { callback(tempo.bpm()); };
  101. }
  102. template <typename Callback>
  103. void Link::setStartStopCallback(Callback callback)
  104. {
  105. std::lock_guard<std::mutex> lock(mCallbackMutex);
  106. mStartStopCallback = callback;
  107. }
  108. inline Link::Clock Link::clock() const
  109. {
  110. return mClock;
  111. }
  112. inline Link::SessionState Link::captureAudioSessionState() const
  113. {
  114. return detail::toSessionState(mController.clientStateRtSafe(), numPeers() > 0);
  115. }
  116. inline void Link::commitAudioSessionState(const Link::SessionState state)
  117. {
  118. mController.setClientStateRtSafe(
  119. detail::toIncomingClientState(state.mState, state.mOriginalState, mClock.micros()));
  120. }
  121. inline Link::SessionState Link::captureAppSessionState() const
  122. {
  123. return detail::toSessionState(mController.clientState(), numPeers() > 0);
  124. }
  125. inline void Link::commitAppSessionState(const Link::SessionState state)
  126. {
  127. mController.setClientState(
  128. detail::toIncomingClientState(state.mState, state.mOriginalState, mClock.micros()));
  129. }
  130. // Link::SessionState
  131. inline Link::SessionState::SessionState(
  132. const link::ApiState state, const bool bRespectQuantum)
  133. : mOriginalState(state)
  134. , mState(state)
  135. , mbRespectQuantum(bRespectQuantum)
  136. {
  137. }
  138. inline double Link::SessionState::tempo() const
  139. {
  140. return mState.timeline.tempo.bpm();
  141. }
  142. inline void Link::SessionState::setTempo(
  143. const double bpm, const std::chrono::microseconds atTime)
  144. {
  145. const auto desiredTl = link::clampTempo(
  146. link::Timeline{link::Tempo(bpm), mState.timeline.toBeats(atTime), atTime});
  147. mState.timeline.tempo = desiredTl.tempo;
  148. mState.timeline.timeOrigin = desiredTl.fromBeats(mState.timeline.beatOrigin);
  149. }
  150. inline double Link::SessionState::beatAtTime(
  151. const std::chrono::microseconds time, const double quantum) const
  152. {
  153. return link::toPhaseEncodedBeats(mState.timeline, time, link::Beats{quantum})
  154. .floating();
  155. }
  156. inline double Link::SessionState::phaseAtTime(
  157. const std::chrono::microseconds time, const double quantum) const
  158. {
  159. return link::phase(link::Beats{beatAtTime(time, quantum)}, link::Beats{quantum})
  160. .floating();
  161. }
  162. inline std::chrono::microseconds Link::SessionState::timeAtBeat(
  163. const double beat, const double quantum) const
  164. {
  165. return link::fromPhaseEncodedBeats(
  166. mState.timeline, link::Beats{beat}, link::Beats{quantum});
  167. }
  168. inline void Link::SessionState::requestBeatAtTime(
  169. const double beat, std::chrono::microseconds time, const double quantum)
  170. {
  171. if (mbRespectQuantum)
  172. {
  173. time = timeAtBeat(link::nextPhaseMatch(link::Beats{beatAtTime(time, quantum)},
  174. link::Beats{beat}, link::Beats{quantum})
  175. .floating(),
  176. quantum);
  177. }
  178. forceBeatAtTime(beat, time, quantum);
  179. }
  180. inline void Link::SessionState::forceBeatAtTime(
  181. const double beat, const std::chrono::microseconds time, const double quantum)
  182. {
  183. // There are two components to the beat adjustment: a phase shift
  184. // and a beat magnitude adjustment.
  185. const auto curBeatAtTime = link::Beats{beatAtTime(time, quantum)};
  186. const auto closestInPhase =
  187. link::closestPhaseMatch(curBeatAtTime, link::Beats{beat}, link::Beats{quantum});
  188. mState.timeline = shiftClientTimeline(mState.timeline, closestInPhase - curBeatAtTime);
  189. // Now adjust the magnitude
  190. mState.timeline.beatOrigin =
  191. mState.timeline.beatOrigin + (link::Beats{beat} - closestInPhase);
  192. }
  193. inline void Link::SessionState::setIsPlaying(
  194. const bool isPlaying, const std::chrono::microseconds time)
  195. {
  196. mState.startStopState = {isPlaying, time};
  197. }
  198. inline bool Link::SessionState::isPlaying() const
  199. {
  200. return mState.startStopState.isPlaying;
  201. }
  202. inline std::chrono::microseconds Link::SessionState::timeForIsPlaying() const
  203. {
  204. return mState.startStopState.time;
  205. }
  206. inline void Link::SessionState::requestBeatAtStartPlayingTime(
  207. const double beat, const double quantum)
  208. {
  209. if (isPlaying())
  210. {
  211. requestBeatAtTime(beat, mState.startStopState.time, quantum);
  212. }
  213. }
  214. inline void Link::SessionState::setIsPlayingAndRequestBeatAtTime(
  215. bool isPlaying, std::chrono::microseconds time, double beat, double quantum)
  216. {
  217. mState.startStopState = {isPlaying, time};
  218. requestBeatAtStartPlayingTime(beat, quantum);
  219. }
  220. } // namespace ableton