sequencer
clock.hpp
Go to the documentation of this file.
1 #pragma once
2 
8 
9 #include <future>
10 #include <mutex>
11 #include <thread>
12 
13 namespace sequencer::midi
14 {
15  template < class SequencerClock >
16  class clock
17  {
18  public:
19  explicit clock( const SequencerClock& sequencer_clock )
20  : sequencer_clock_( sequencer_clock )
21  {
22  }
23 
24  explicit clock( SequencerClock&& sequencer_clock )
25  : sequencer_clock_( std::move( sequencer_clock ) )
26  {
27  }
28 
29  void start()
30  {
31  std::lock_guard lock{clock_mutex_};
32  clock_base_.start();
33  sequencer_clock_.start();
34  }
35 
36  void stop()
37  {
38  std::lock_guard lock{clock_mutex_};
39  clock_base_.stop();
40  sequencer_clock_.stop();
41  }
42 
43  void reset()
44  {
45  std::lock_guard lock{clock_mutex_};
46  clock_base_.reset();
47  sequencer_clock_.reset();
48  }
49 
50  void shut_down()
51  {
52  std::lock_guard lock{clock_mutex_};
53  shut_down_ = true;
54  clock_base_.stop();
55  sequencer_clock_.stop();
56  }
57 
58  constexpr int pulses_per_quarter_note() const noexcept
59  {
60  return clock_base_.pulses_per_quarter_note();
61  }
62 
64  {
65  std::lock_guard lock( clock_mutex_ );
66  offset_ = now_as_beat_duration();
67  sequencer_clock_.reset();
68  sequencer_clock_.start();
69  tempo_ = tempo;
70  }
71 
72  bool is_running() const noexcept
73  {
74  return sequencer_clock_.is_running();
75  }
76 
77  template < class Sender >
78  void run( const Sender& sender )
79  {
80  update_clock_base( 0.0_beats, sender );
81  while ( true )
82  {
83  std::lock_guard lock{clock_mutex_};
84  update_clock_base( now_as_beat_duration(), sender );
85  if ( shut_down_ )
86  {
87  return;
88  }
89  }
90  }
91 
92  private:
93  beat_duration now_as_beat_duration() const noexcept
94  {
95  return offset_ +
96  ( sequencer_clock_.now() - typename SequencerClock::time_point{} ) * tempo_;
97  }
98 
99  template < class Sender >
100  void update_clock_base( beat_duration dt, const Sender& sender )
101  {
102  const auto t = beat_time_point{dt};
103  clock_base_.update( t, sender );
104  }
105 
106  std::mutex clock_mutex_;
107  SequencerClock sequencer_clock_;
108  clock_base clock_base_{beat_time_point{-clock_base{}.tick()}};
109  beat_duration offset_ = 0.0_beats;
110  beats_per_minute tempo_{120.0_bpm};
111  bool shut_down_{false};
112  };
113 
114  inline auto make_clock()
115  {
116  using underlying_clock_type = chrono::clock_object_adapter< std::chrono::steady_clock >;
117  using sequencer_clock_type = chrono::sequencer_clock< underlying_clock_type >;
118 
119  return clock{sequencer_clock_type{underlying_clock_type{}}};
120  }
121 
122  template < class MidiClock, class Sender >
123  auto start_clock_in_thread( MidiClock& midi_clock, const Sender& sender )
124  {
125  std::promise< void > controller_ready_promise;
126  const auto controller_ready = controller_ready_promise.get_future();
127  auto clock_done =
128  std::async( std::launch::async, [sender, &midi_clock, &controller_ready_promise] {
129  controller_ready_promise.set_value();
130  midi_clock.run( sender );
131  // wait a bit to make sure that the last messages where sent
132  // TODO solve without sleep if possible
133  std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
134  } );
135  controller_ready.wait();
136  return clock_done;
137  }
138 } // namespace sequencer::midi
Definition: clock_base.hpp:8
void stop()
Definition: clock.hpp:36
constexpr void start() noexcept
Definition: clock_base.hpp:63
Definition: beat_duration.hpp:106
Definition: clock.hpp:16
constexpr beat_duration tick() const noexcept
Definition: clock_base.hpp:78
Definition: beat_duration.hpp:13
constexpr int pulses_per_quarter_note() const noexcept
Definition: clock.hpp:58
std::chrono::duration< double, std::milli > milliseconds
Definition: units.hpp:7
Definition: beats_per_minute.hpp:8
Definition: sequencer_clock.hpp:12
Definition: chrono_adapter.hpp:9
bool is_running() const noexcept
Definition: clock.hpp:72
constexpr int pulses_per_quarter_note() const noexcept
Definition: clock_base.hpp:73
void reset() noexcept
Definition: clock_base.hpp:56
void start()
Definition: clock.hpp:29
void update(beat_time_point t, Sender sender)
Definition: clock_base.hpp:24
void reset()
Definition: clock.hpp:43
constexpr void stop() noexcept
Definition: clock_base.hpp:68
Definition: clock.hpp:13
Definition: beat_time_point.hpp:8
void shut_down()
Definition: clock.hpp:50
void run(const Sender &sender)
Definition: clock.hpp:78
clock(SequencerClock &&sequencer_clock)
Definition: clock.hpp:24
auto start_clock_in_thread(MidiClock &midi_clock, const Sender &sender)
Definition: clock.hpp:123
void set_tempo(beats_per_minute tempo)
Definition: clock.hpp:63
auto make_clock()
Definition: clock.hpp:114
clock(const SequencerClock &sequencer_clock)
Definition: clock.hpp:19