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 
20 #pragma once
21 
22 #include <ableton/discovery/NetworkByteStreamSerializable.hpp>
23 #include <ableton/link/Beats.hpp>
24 #include <ableton/link/Tempo.hpp>
25 #include <cmath>
26 #include <cstdint>
27 #include <tuple>
28 
29 namespace ableton
30 {
31 namespace link
32 {
33 
34 // A tuple of (tempo, beats, time), with integral units
35 // based on microseconds. This type establishes a bijection between
36 // beats and wall time, given a valid tempo. It also serves as a
37 // payload entry.
38 
39 struct Timeline
40 {
41   static const std::int32_t key = 'tmln';
42   static_assert(key == 0x746d6c6e, "Unexpected byte order");
43 
toBeatsableton::link::Timeline44   Beats toBeats(const std::chrono::microseconds time) const
45   {
46     return beatOrigin + tempo.microsToBeats(time - timeOrigin);
47   }
48 
fromBeatsableton::link::Timeline49   std::chrono::microseconds fromBeats(const Beats beats) const
50   {
51     return timeOrigin + tempo.beatsToMicros(beats - beatOrigin);
52   }
53 
operator ==(const Timeline & lhs,const Timeline & rhs)54   friend bool operator==(const Timeline& lhs, const Timeline& rhs)
55   {
56     return std::tie(lhs.tempo, lhs.beatOrigin, lhs.timeOrigin)
57            == std::tie(rhs.tempo, rhs.beatOrigin, rhs.timeOrigin);
58   }
59 
operator !=(const Timeline & lhs,const Timeline & rhs)60   friend bool operator!=(const Timeline& lhs, const Timeline& rhs)
61   {
62     return !(lhs == rhs);
63   }
64 
65   // Model the NetworkByteStreamSerializable concept
sizeInByteStream(const Timeline & tl)66   friend std::uint32_t sizeInByteStream(const Timeline& tl)
67   {
68     return discovery::sizeInByteStream(std::tie(tl.tempo, tl.beatOrigin, tl.timeOrigin));
69   }
70 
71   template <typename It>
toNetworkByteStream(const Timeline & tl,It out)72   friend It toNetworkByteStream(const Timeline& tl, It out)
73   {
74     return discovery::toNetworkByteStream(
75       std::tie(tl.tempo, tl.beatOrigin, tl.timeOrigin), std::move(out));
76   }
77 
78   template <typename It>
fromNetworkByteStreamableton::link::Timeline79   static std::pair<Timeline, It> fromNetworkByteStream(It begin, It end)
80   {
81     using namespace std;
82     using namespace discovery;
83     Timeline timeline;
84     auto result =
85       Deserialize<tuple<Tempo, Beats, chrono::microseconds>>::fromNetworkByteStream(
86         move(begin), move(end));
87     tie(timeline.tempo, timeline.beatOrigin, timeline.timeOrigin) = move(result.first);
88     return make_pair(move(timeline), move(result.second));
89   }
90 
91   Tempo tempo;
92   Beats beatOrigin;
93   std::chrono::microseconds timeOrigin;
94 };
95 
96 } // namespace link
97 } // namespace ableton
98