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.

Link.hpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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 beat timeline that
  32. * starts running from beat 0 at the initial tempo when
  33. * constructed. A Link instance is initially disabled after
  34. * construction, which means that it will not communicate on the
  35. * network. Once enabled, a Link instance initiates network
  36. * communication in an effort to discover other peers. When peers are
  37. * discovered, they immediately become part of a shared Link session.
  38. *
  39. * Each method of the Link type documents its thread-safety and
  40. * realtime-safety properties. When a method is marked thread-safe,
  41. * it means it is safe to call from multiple threads
  42. * concurrently. When a method is marked realtime-safe, it means that
  43. * it does not block and is appropriate for use in the thread that
  44. * performs audio IO.
  45. *
  46. * Link provides one Timeline capture/commit method pair for use in the
  47. * audio thread and one for all other application contexts. In
  48. * general, modifying the Link timeline should be done in the audio
  49. * thread for the most accurate timing results. The ability to modify
  50. * the Link timeline from application threads should only be used in
  51. * cases where an application's audio thread is not actively running
  52. * or if it doesn't generate audio at all. Modifying the Link
  53. * timeline from both the audio thread and an application thread
  54. * concurrently is not advised and will potentially lead to
  55. * unexpected behavior.
  56. */
  57. template <typename Clock>
  58. class BasicLink
  59. {
  60. public:
  61. class Timeline;
  62. /*! @brief Construct with an initial tempo. */
  63. BasicLink(double bpm);
  64. /*! @brief Link instances cannot be copied or moved */
  65. BasicLink(const BasicLink<Clock>&) = delete;
  66. BasicLink& operator=(const BasicLink<Clock>&) = delete;
  67. BasicLink(BasicLink<Clock>&&) = delete;
  68. BasicLink& operator=(BasicLink<Clock>&&) = delete;
  69. /*! @brief Is Link currently enabled?
  70. * Thread-safe: yes
  71. * Realtime-safe: yes
  72. */
  73. bool isEnabled() const;
  74. /*! @brief Enable/disable Link.
  75. * Thread-safe: yes
  76. * Realtime-safe: no
  77. */
  78. void enable(bool bEnable);
  79. /*! @brief How many peers are currently connected in a Link session?
  80. * Thread-safe: yes
  81. * Realtime-safe: yes
  82. */
  83. std::size_t numPeers() const;
  84. /*! @brief Register a callback to be notified when the number of
  85. * peers in the Link session changes.
  86. * Thread-safe: yes
  87. * Realtime-safe: no
  88. *
  89. * @discussion The callback is invoked on a Link-managed thread.
  90. *
  91. * @param callback The callback signature is:
  92. * void (std::size_t numPeers)
  93. */
  94. template <typename Callback>
  95. void setNumPeersCallback(Callback callback);
  96. /*! @brief Register a callback to be notified when the session
  97. * tempo changes.
  98. * Thread-safe: yes
  99. * Realtime-safe: no
  100. *
  101. * @discussion The callback is invoked on a Link-managed thread.
  102. *
  103. * @param callback The callback signature is: void (double bpm)
  104. */
  105. template <typename Callback>
  106. void setTempoCallback(Callback callback);
  107. /*! @brief The clock used by Link.
  108. * Thread-safe: yes
  109. * Realtime-safe: yes
  110. *
  111. * @discussion The Clock type is a platform-dependent
  112. * representation of the system clock. It exposes a ticks() method
  113. * that returns the current ticks of the system clock as well as
  114. * micros(), which is a normalized representation of the current system
  115. * time in std::chrono::microseconds. It also provides conversion
  116. * functions ticksToMicros() and microsToTicks() to faciliate
  117. * converting between these units.
  118. */
  119. Clock clock() const;
  120. /*! @brief Capture the current Link timeline from the audio thread.
  121. * Thread-safe: no
  122. * Realtime-safe: yes
  123. *
  124. * @discussion This method should ONLY be called in the audio thread
  125. * and must not be accessed from any other threads. The returned
  126. * Timeline stores a snapshot of the current Link state, so it
  127. * should be captured and used in a local scope. Storing the
  128. * Timeline for later use in a different context is not advised
  129. * because it will provide an outdated view on the Link state.
  130. */
  131. Timeline captureAudioTimeline() const;
  132. /*! @brief Commit the given timeline to the Link session from the
  133. * audio thread.
  134. * Thread-safe: no
  135. * Realtime-safe: yes
  136. *
  137. * @discussion This method should ONLY be called in the audio
  138. * thread. The given timeline will replace the current Link
  139. * timeline. Modifications to the session based on the new timeline
  140. * will be communicated to other peers in the session.
  141. */
  142. void commitAudioTimeline(Timeline timeline);
  143. /*! @brief Capture the current Link timeline from an application
  144. * thread.
  145. * Thread-safe: yes
  146. * Realtime-safe: no
  147. *
  148. * @discussion Provides a mechanism for capturing the Link timeline
  149. * from an application thread (other than the audio thread). The
  150. * returned Timeline stores a snapshot of the current Link state,
  151. * so it should be captured and used in a local scope. Storing the
  152. * Timeline for later use in a different context is not advised
  153. * because it will provide an outdated view on the Link state.
  154. */
  155. Timeline captureAppTimeline() const;
  156. /*! @brief Commit the given timeline to the Link session from an
  157. * application thread.
  158. * Thread-safe: yes
  159. * Realtime-safe: no
  160. *
  161. * @discussion The given timeline will replace the current Link
  162. * timeline. Modifications to the session based on the new timeline
  163. * will be communicated to other peers in the session.
  164. */
  165. void commitAppTimeline(Timeline timeline);
  166. /*! @class Timeline
  167. * @brief Representation of a mapping between time and beats for
  168. * varying quanta.
  169. *
  170. * @discussion A Timeline object is intended for use in a local
  171. * scope within a single thread - none of its methods are
  172. * thread-safe. All of its methods are non-blocking, so it is safe
  173. * to use from a realtime thread.
  174. */
  175. class Timeline
  176. {
  177. public:
  178. Timeline(const link::Timeline timeline, const bool bRespectQuantum);
  179. /*! @brief: The tempo of the timeline, in bpm */
  180. double tempo() const;
  181. /*! @brief: Set the timeline tempo to the given bpm value, taking
  182. * effect at the given time.
  183. */
  184. void setTempo(double bpm, std::chrono::microseconds atTime);
  185. /*! @brief: Get the beat value corresponding to the given time
  186. * for the given quantum.
  187. *
  188. * @discussion: The magnitude of the resulting beat value is
  189. * unique to this Link instance, but its phase with respect to
  190. * the provided quantum is shared among all session
  191. * peers. For non-negative beat values, the following
  192. * property holds: fmod(beatAtTime(t, q), q) == phaseAtTime(t, q)
  193. */
  194. double beatAtTime(std::chrono::microseconds time, double quantum) const;
  195. /*! @brief: Get the session phase at the given time for the given
  196. * quantum.
  197. *
  198. * @discussion: The result is in the interval [0, quantum). The
  199. * result is equivalent to fmod(beatAtTime(t, q), q) for
  200. * non-negative beat values. This method is convenient if the
  201. * client is only interested in the phase and not the beat
  202. * magnitude. Also, unlike fmod, it handles negative beat values
  203. * correctly.
  204. */
  205. double phaseAtTime(std::chrono::microseconds time, double quantum) const;
  206. /*! @brief: Get the time at which the given beat occurs for the
  207. * given quantum.
  208. *
  209. * @discussion: The inverse of beatAtTime, assuming a constant
  210. * tempo. beatAtTime(timeAtBeat(b, q), q) === b.
  211. */
  212. std::chrono::microseconds timeAtBeat(double beat, double quantum) const;
  213. /*! @brief: Attempt to map the given beat to the given time in the
  214. * context of the given quantum.
  215. *
  216. * @discussion: This method behaves differently depending on the
  217. * state of the session. If no other peers are connected,
  218. * then this instance is in a session by itself and is free to
  219. * re-map the beat/time relationship whenever it pleases. In this
  220. * case, beatAtTime(time, quantum) == beat after this method has
  221. * been called.
  222. *
  223. * If there are other peers in the session, this instance
  224. * should not abruptly re-map the beat/time relationship in the
  225. * session because that would lead to beat discontinuities among
  226. * the other peers. In this case, the given beat will be mapped
  227. * to the next time value greater than the given time with the
  228. * same phase as the given beat.
  229. *
  230. * This method is specifically designed to enable the concept of
  231. * "quantized launch" in client applications. If there are no other
  232. * peers in the session, then an event (such as starting
  233. * transport) happens immediately when it is requested. If there
  234. * are other peers, however, we wait until the next time at which
  235. * the session phase matches the phase of the event, thereby
  236. * executing the event in-phase with the other peers in the
  237. * session. The client only needs to invoke this method to
  238. * achieve this behavior and should not need to explicitly check
  239. * the number of peers.
  240. */
  241. void requestBeatAtTime(double beat, std::chrono::microseconds time, double quantum);
  242. /*! @brief: Rudely re-map the beat/time relationship for all peers
  243. * in a session.
  244. *
  245. * @discussion: DANGER: This method should only be needed in
  246. * certain special circumstances. Most applications should not
  247. * use it. It is very similar to requestBeatAtTime except that it
  248. * does not fall back to the quantizing behavior when it is in a
  249. * session with other peers. Calling this method will
  250. * unconditionally map the given beat to the given time and
  251. * broadcast the result to the session. This is very anti-social
  252. * behavior and should be avoided.
  253. *
  254. * One of the few legitimate uses of this method is to
  255. * synchronize a Link session with an external clock source. By
  256. * periodically forcing the beat/time mapping according to an
  257. * external clock source, a peer can effectively bridge that
  258. * clock into a Link session. Much care must be taken at the
  259. * application layer when implementing such a feature so that
  260. * users do not accidentally disrupt Link sessions that they may
  261. * join.
  262. */
  263. void forceBeatAtTime(double beat, std::chrono::microseconds time, double quantum);
  264. private:
  265. friend BasicLink<Clock>;
  266. link::Timeline mOriginalTimeline;
  267. bool mbRespectQuantum;
  268. link::Timeline mTimeline;
  269. };
  270. private:
  271. using Controller = ableton::link::Controller<link::PeerCountCallback,
  272. link::TempoCallback,
  273. Clock,
  274. link::platform::IoContext>;
  275. std::mutex mCallbackMutex;
  276. link::PeerCountCallback mPeerCountCallback;
  277. link::TempoCallback mTempoCallback;
  278. Clock mClock;
  279. Controller mController;
  280. };
  281. using Link = BasicLink<link::platform::Clock>;
  282. } // ableton
  283. #include <ableton/Link.ipp>