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.

375 lines
15KB

  1. /*! @file Link.hpp
  2. * @copyright 2016, Ableton AG, Berlin. All rights reserved.
  3. * @brief Library for cross-device shared tempo and quantized beat grid
  4. *
  5. * @license:
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. * If you would like to incorporate Link into a proprietary software application,
  20. * please contact <link-devs@ableton.com>.
  21. */
  22. #pragma once
  23. #include <ableton/platforms/Config.hpp>
  24. #include <chrono>
  25. #include <mutex>
  26. namespace ableton
  27. {
  28. /*! @class Link
  29. * @brief Class that represents a participant in a Link session.
  30. *
  31. * @discussion Each Link instance has its own session state which
  32. * represents a beat timeline and a transport start/stop state. The
  33. * timeline starts running from beat 0 at the initial tempo when
  34. * constructed. The timeline always advances at a speed defined by
  35. * its current tempo, even if transport is stopped. Synchronizing to the
  36. * transport start/stop state of Link is optional for every peer.
  37. * The transport start/stop state is only shared with other peers when
  38. * start/stop synchronization is enabled.
  39. *
  40. * A Link instance is initially disabled after construction, which
  41. * means that it will not communicate on the network. Once enabled,
  42. * a Link instance initiates network communication in an effort to
  43. * discover other peers. When peers are discovered, they immediately
  44. * become part of a shared Link session.
  45. *
  46. * Each method of the Link type documents its thread-safety and
  47. * realtime-safety properties. When a method is marked thread-safe,
  48. * it means it is safe to call from multiple threads
  49. * concurrently. When a method is marked realtime-safe, it means that
  50. * it does not block and is appropriate for use in the thread that
  51. * performs audio IO.
  52. *
  53. * Link provides one session state capture/commit method pair for use
  54. * in the audio thread and one for all other application contexts. In
  55. * general, modifying the session state should be done in the audio
  56. * thread for the most accurate timing results. The ability to modify
  57. * the session state from application threads should only be used in
  58. * cases where an application's audio thread is not actively running
  59. * or if it doesn't generate audio at all. Modifying the Link session
  60. * state from both the audio thread and an application thread
  61. * concurrently is not advised and will potentially lead to unexpected
  62. * behavior.
  63. */
  64. class Link
  65. {
  66. public:
  67. using Clock = link::platform::Clock;
  68. class SessionState;
  69. /*! @brief Construct with an initial tempo. */
  70. Link(double bpm);
  71. /*! @brief Link instances cannot be copied or moved */
  72. Link(const Link&) = delete;
  73. Link& operator=(const Link&) = delete;
  74. Link(Link&&) = delete;
  75. Link& operator=(Link&&) = delete;
  76. /*! @brief Is Link currently enabled?
  77. * Thread-safe: yes
  78. * Realtime-safe: yes
  79. */
  80. bool isEnabled() const;
  81. /*! @brief Enable/disable Link.
  82. * Thread-safe: yes
  83. * Realtime-safe: no
  84. */
  85. void enable(bool bEnable);
  86. /*! @brief: Is start/stop synchronization enabled?
  87. * Thread-safe: yes
  88. * Realtime-safe: no
  89. */
  90. bool isStartStopSyncEnabled() const;
  91. /*! @brief: Enable start/stop synchronization.
  92. * Thread-safe: yes
  93. * Realtime-safe: no
  94. */
  95. void enableStartStopSync(bool bEnable);
  96. /*! @brief How many peers are currently connected in a Link session?
  97. * Thread-safe: yes
  98. * Realtime-safe: yes
  99. */
  100. std::size_t numPeers() const;
  101. /*! @brief Register a callback to be notified when the number of
  102. * peers in the Link session changes.
  103. * Thread-safe: yes
  104. * Realtime-safe: no
  105. *
  106. * @discussion The callback is invoked on a Link-managed thread.
  107. *
  108. * @param callback The callback signature is:
  109. * void (std::size_t numPeers)
  110. */
  111. template <typename Callback>
  112. void setNumPeersCallback(Callback callback);
  113. /*! @brief Register a callback to be notified when the session
  114. * tempo changes.
  115. * Thread-safe: yes
  116. * Realtime-safe: no
  117. *
  118. * @discussion The callback is invoked on a Link-managed thread.
  119. *
  120. * @param callback The callback signature is: void (double bpm)
  121. */
  122. template <typename Callback>
  123. void setTempoCallback(Callback callback);
  124. /*! brief: Register a callback to be notified when the state of
  125. * start/stop isPlaying changes.
  126. * Thread-safe: yes
  127. * Realtime-safe: no
  128. *
  129. * @discussion The callback is invoked on a Link-managed thread.
  130. *
  131. * @param callback The callback signature is:
  132. * void (bool isPlaying)
  133. */
  134. template <typename Callback>
  135. void setStartStopCallback(Callback callback);
  136. /*! @brief The clock used by Link.
  137. * Thread-safe: yes
  138. * Realtime-safe: yes
  139. *
  140. * @discussion The Clock type is a platform-dependent
  141. * representation of the system clock. It exposes a ticks() method
  142. * that returns the current ticks of the system clock as well as
  143. * micros(), which is a normalized representation of the current system
  144. * time in std::chrono::microseconds. It also provides conversion
  145. * functions ticksToMicros() and microsToTicks() to faciliate
  146. * converting between these units.
  147. */
  148. Clock clock() const;
  149. /*! @brief Capture the current Link Session State from the audio thread.
  150. * Thread-safe: no
  151. * Realtime-safe: yes
  152. *
  153. * @discussion This method should ONLY be called in the audio thread
  154. * and must not be accessed from any other threads. The returned
  155. * object stores a snapshot of the current Link Session State, so it
  156. * should be captured and used in a local scope. Storing the
  157. * Session State for later use in a different context is not advised
  158. * because it will provide an outdated view.
  159. */
  160. SessionState captureAudioSessionState() const;
  161. /*! @brief Commit the given Session State to the Link session from the
  162. * audio thread.
  163. * Thread-safe: no
  164. * Realtime-safe: yes
  165. *
  166. * @discussion This method should ONLY be called in the audio
  167. * thread. The given Session State will replace the current Link
  168. * state. Modifications will be communicated to other peers in the
  169. * session.
  170. */
  171. void commitAudioSessionState(SessionState state);
  172. /*! @brief Capture the current Link Session State from an application
  173. * thread.
  174. * Thread-safe: yes
  175. * Realtime-safe: no
  176. *
  177. * @discussion Provides a mechanism for capturing the Link Session
  178. * State from an application thread (other than the audio thread).
  179. * The returned Session State stores a snapshot of the current Link
  180. * state, so it should be captured and used in a local scope.
  181. * Storing the it for later use in a different context is not
  182. * advised because it will provide an outdated view.
  183. */
  184. SessionState captureAppSessionState() const;
  185. /*! @brief Commit the given Session State to the Link session from an
  186. * application thread.
  187. * Thread-safe: yes
  188. * Realtime-safe: no
  189. *
  190. * @discussion The given Session State will replace the current Link
  191. * Session State. Modifications of the Session State will be
  192. * communicated to other peers in the session.
  193. */
  194. void commitAppSessionState(SessionState state);
  195. /*! @class SessionState
  196. * @brief Representation of a timeline and the start/stop state
  197. *
  198. * @discussion A SessionState object is intended for use in a local scope within
  199. * a single thread - none of its methods are thread-safe. All of its methods are
  200. * non-blocking, so it is safe to use from a realtime thread.
  201. * It provides functions to observe and manipulate the timeline and start/stop
  202. * state.
  203. *
  204. * The timeline is a representation of a mapping between time and beats for varying
  205. * quanta.
  206. * The start/stop state represents the user intention to start or stop transport at
  207. * a specific time. Start stop synchronization is an optional feature that allows to
  208. * share the user request to start or stop transport between a subgroup of peers in
  209. * a Link session. When observing a change of start/stop state, audio playback of a
  210. * peer should be started or stopped the same way it would have happened if the user
  211. * had requested that change at the according time locally. The start/stop state can
  212. * only be changed by the user. This means that the current local start/stop state
  213. * persists when joining or leaving a Link session. After joining a Link session
  214. * start/stop change requests will be communicated to all connected peers.
  215. */
  216. class SessionState
  217. {
  218. public:
  219. SessionState(const link::ApiState state, const bool bRespectQuantum);
  220. /*! @brief: The tempo of the timeline, in bpm */
  221. double tempo() const;
  222. /*! @brief: Set the timeline tempo to the given bpm value, taking
  223. * effect at the given time.
  224. */
  225. void setTempo(double bpm, std::chrono::microseconds atTime);
  226. /*! @brief: Get the beat value corresponding to the given time
  227. * for the given quantum.
  228. *
  229. * @discussion: The magnitude of the resulting beat value is
  230. * unique to this Link instance, but its phase with respect to
  231. * the provided quantum is shared among all session
  232. * peers. For non-negative beat values, the following
  233. * property holds: fmod(beatAtTime(t, q), q) == phaseAtTime(t, q)
  234. */
  235. double beatAtTime(std::chrono::microseconds time, double quantum) const;
  236. /*! @brief: Get the session phase at the given time for the given
  237. * quantum.
  238. *
  239. * @discussion: The result is in the interval [0, quantum). The
  240. * result is equivalent to fmod(beatAtTime(t, q), q) for
  241. * non-negative beat values. This method is convenient if the
  242. * client is only interested in the phase and not the beat
  243. * magnitude. Also, unlike fmod, it handles negative beat values
  244. * correctly.
  245. */
  246. double phaseAtTime(std::chrono::microseconds time, double quantum) const;
  247. /*! @brief: Get the time at which the given beat occurs for the
  248. * given quantum.
  249. *
  250. * @discussion: The inverse of beatAtTime, assuming a constant
  251. * tempo. beatAtTime(timeAtBeat(b, q), q) === b.
  252. */
  253. std::chrono::microseconds timeAtBeat(double beat, double quantum) const;
  254. /*! @brief: Attempt to map the given beat to the given time in the
  255. * context of the given quantum.
  256. *
  257. * @discussion: This method behaves differently depending on the
  258. * state of the session. If no other peers are connected,
  259. * then this instance is in a session by itself and is free to
  260. * re-map the beat/time relationship whenever it pleases. In this
  261. * case, beatAtTime(time, quantum) == beat after this method has
  262. * been called.
  263. *
  264. * If there are other peers in the session, this instance
  265. * should not abruptly re-map the beat/time relationship in the
  266. * session because that would lead to beat discontinuities among
  267. * the other peers. In this case, the given beat will be mapped
  268. * to the next time value greater than the given time with the
  269. * same phase as the given beat.
  270. *
  271. * This method is specifically designed to enable the concept of
  272. * "quantized launch" in client applications. If there are no other
  273. * peers in the session, then an event (such as starting
  274. * transport) happens immediately when it is requested. If there
  275. * are other peers, however, we wait until the next time at which
  276. * the session phase matches the phase of the event, thereby
  277. * executing the event in-phase with the other peers in the
  278. * session. The client only needs to invoke this method to
  279. * achieve this behavior and should not need to explicitly check
  280. * the number of peers.
  281. */
  282. void requestBeatAtTime(double beat, std::chrono::microseconds time, double quantum);
  283. /*! @brief: Rudely re-map the beat/time relationship for all peers
  284. * in a session.
  285. *
  286. * @discussion: DANGER: This method should only be needed in
  287. * certain special circumstances. Most applications should not
  288. * use it. It is very similar to requestBeatAtTime except that it
  289. * does not fall back to the quantizing behavior when it is in a
  290. * session with other peers. Calling this method will
  291. * unconditionally map the given beat to the given time and
  292. * broadcast the result to the session. This is very anti-social
  293. * behavior and should be avoided.
  294. *
  295. * One of the few legitimate uses of this method is to
  296. * synchronize a Link session with an external clock source. By
  297. * periodically forcing the beat/time mapping according to an
  298. * external clock source, a peer can effectively bridge that
  299. * clock into a Link session. Much care must be taken at the
  300. * application layer when implementing such a feature so that
  301. * users do not accidentally disrupt Link sessions that they may
  302. * join.
  303. */
  304. void forceBeatAtTime(double beat, std::chrono::microseconds time, double quantum);
  305. /*! @brief: Set if transport should be playing or stopped, taking effect
  306. * at the given time.
  307. */
  308. void setIsPlaying(bool isPlaying, std::chrono::microseconds time);
  309. /*! @brief: Is transport playing? */
  310. bool isPlaying() const;
  311. /*! @brief: Get the time at which a transport start/stop occurs */
  312. std::chrono::microseconds timeForIsPlaying() const;
  313. /*! @brief: Convenience function to attempt to map the given beat to the time
  314. * when transport is starting to play in context of the given quantum.
  315. * This function evaluates to a no-op if isPlaying() equals false.
  316. */
  317. void requestBeatAtStartPlayingTime(double beat, double quantum);
  318. /*! @brief: Convenience function to start or stop transport at a given time and
  319. * attempt to map the given beat to this time in context of the given quantum.
  320. */
  321. void setIsPlayingAndRequestBeatAtTime(
  322. bool isPlaying, std::chrono::microseconds time, double beat, double quantum);
  323. private:
  324. friend Link;
  325. link::ApiState mOriginalState;
  326. link::ApiState mState;
  327. bool mbRespectQuantum;
  328. };
  329. private:
  330. std::mutex mCallbackMutex;
  331. link::PeerCountCallback mPeerCountCallback;
  332. link::TempoCallback mTempoCallback;
  333. link::StartStopStateCallback mStartStopCallback;
  334. Clock mClock;
  335. link::platform::Controller mController;
  336. };
  337. } // namespace ableton
  338. #include <ableton/Link.ipp>