sequencer
track.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <sequencer/assert.hpp>
10 #include <sequencer/vector.hpp>
11 
12 #include <algorithm>
13 #include <atomic>
14 #include <functional>
15 #include <memory>
16 #include <vector>
17 
18 namespace sequencer::midi
19 {
20  template < class Track >
21  void copy_track( const Track& from,
22  Track& to ) noexcept( std::is_nothrow_copy_assignable< Track >::value )
23  {
24  const auto size = std::min( from.size(), to.size() );
25  for ( auto step = std::size_t{0}; step < size; ++step )
26  {
27  to[ step ] = from[ step ];
28  }
29  }
30 
32  {
33  public:
34  static constexpr int do_not_send = -1;
35 
36  clock_to_step_t() = default;
37 
38  explicit clock_to_step_t( std::size_t steps ) noexcept : steps_( steps )
39  {
40  SEQUENCER_ASSERT( steps_ > 0 )
41  }
42 
43  constexpr void set_steps( std::size_t new_steps ) noexcept
44  {
45  SEQUENCER_ASSERT( new_steps > 0 )
46  steps_ = new_steps;
47  midi_beat_counter_ %= steps_ * pulses_per_step();
48  }
49 
50  constexpr void set_steps_per_beat( std::size_t steps ) noexcept
51  {
52  steps_per_beat_ = steps;
53  }
54 
55  constexpr std::size_t steps_per_beat() noexcept
56  {
57  return steps_per_beat_;
58  }
59 
60  constexpr void set_pulses_per_quarter_note( std::size_t pulses_per_quarter_note ) noexcept
61  {
62  pulses_per_quarter_note_ = pulses_per_quarter_note;
63  midi_beat_counter_ %= steps_ * pulses_per_step();
64  }
65 
66  void reset_beat_counter() noexcept
67  {
68  midi_beat_counter_ = 0;
69  }
70 
71  void set_midi_beat_counter( std::size_t counter ) noexcept
72  {
73  midi_beat_counter_ = counter;
74  }
75 
76  constexpr std::size_t midi_beat_counter() const noexcept
77  {
78  return midi_beat_counter_;
79  }
80 
81  constexpr std::size_t pulses_per_quarter_note() const noexcept
82  {
83  return std::size_t( pulses_per_quarter_note_ / speed_ + 0.1 );
84  }
85 
86  constexpr std::size_t pulses_per_step() const noexcept
87  {
88  return pulses_per_quarter_note() / steps_per_beat_;
89  }
90 
91  int process_message( message_t< 1 > message ) const
92  {
93  if ( process_control_message( message ) || !started_ )
94  {
95  return do_not_send;
96  }
97 
98  auto result = do_not_send;
99  if ( midi_beat_counter_ % pulses_per_step() == 0 )
100  {
101  result = int( midi_beat_counter_ / pulses_per_step() );
102  }
103  if ( ++midi_beat_counter_ == steps_ * pulses_per_step() )
104  {
105  midi_beat_counter_ = 0;
106  }
107  if ( ++loop_length_midi_beat_counter_ == loop_length_ * pulses_per_step() )
108  {
109  reset_counters();
110  }
111 
112  return result;
113  }
114 
115  constexpr bool started() const noexcept
116  {
117  return started_;
118  }
119 
120  constexpr void set_speed_multiplier( double speed ) noexcept
121  {
122  speed_ = speed;
123  }
124 
125  constexpr void set_loop_length( std::size_t length ) noexcept
126  {
127  loop_length_ = length;
128  }
129 
130  private:
131  bool process_control_message( message_t< 1 > message ) const
132  {
133  if ( message == realtime::realtime_start() )
134  {
135  started_ = true;
136  reset_counters();
137  return true;
138  }
139  if ( message == realtime::realtime_continue() )
140  {
141  started_ = true;
142  return true;
143  }
144  if ( message == realtime::realtime_stop() )
145  {
146  started_ = false;
147  return true;
148  }
149 
150  return false;
151  }
152 
153  void reset_counters() const
154  {
155  midi_beat_counter_ = 0;
156  loop_length_midi_beat_counter_ = 0;
157  }
158 
159  mutable std::size_t midi_beat_counter_{0};
160  mutable std::size_t loop_length_midi_beat_counter_{0};
161  std::size_t steps_per_beat_{4};
162  std::size_t pulses_per_quarter_note_{default_pulses_per_quarter_note};
163  double speed_{1};
164  std::size_t steps_{16};
165  std::size_t loop_length_{16};
166  mutable bool started_{false};
167  };
168 
169  template < class Step, class Parameter >
171  {
172  public:
176 
177  explicit track_impl_t( size_type size = 64, size_type initial_size = 16 )
178  : track_{size, std::min( size, initial_size )}
179  {
180  clear();
181  }
182 
183  // NOLINTNEXTLINE(bugprone-copy-constructor-init)
184  track_impl_t( const track_impl_t& other ) : track_{other.track_.size()}
185  {
186  copy( other, *this );
187  }
188 
190  {
191  if ( this != &other )
192  {
193  track_ = track_base_t{other.track_.size()};
194  copy( other, *this );
195  }
196  return *this;
197  }
198 
199  std::size_t steps() const noexcept
200  {
201  return track_.size();
202  }
203 
204  void set_steps( std::size_t new_steps, std::size_t copy_offset = 0 )
205  {
206  auto new_track = track_base_t( new_steps );
207  copy_track( track_, new_track );
208 
209  if ( new_steps > steps() )
210  {
211  for ( size_type new_step = track_.size(); new_step < new_steps; ++new_step )
212  {
213  new_track[ new_step ] = ( copy_offset > 0 && new_step >= copy_offset )
214  ? new_track[ new_step - copy_offset ]
215  : Step{};
216  }
217  }
218 
219  track_ = std::move( new_track );
220  }
221 
223  {
224  return track_[ i ];
225  }
226 
227  const value_type& operator[]( size_type i ) const noexcept
228  {
229  return track_[ i ];
230  }
231 
232  void clear() noexcept
233  {
234  for ( auto& step : track_ )
235  {
236  step = Step{};
237  }
238  }
239 
240  constexpr void set_channel( std::uint8_t channel ) noexcept
241  {
243  channel_ = channel;
244  }
245 
246  constexpr std::uint8_t channel() const noexcept
247  {
248  return channel_;
249  }
250 
251  template < class Sender >
252  void send_note_off_messages( const Sender& sender ) const
253  {
254  for ( auto& entry : current_notes_ )
255  {
256  if ( --entry.second == 0 )
257  {
258  sender( channel::voice::note_off( channel(), to_uint8_t( entry.first ), 0 ) );
259  }
260  }
261 
262  current_notes_.erase(
263  std::remove_if( begin( current_notes_ ), end( current_notes_ ),
264  []( const auto& entry ) { return entry.second == 0; } ),
265  end( current_notes_ ) );
266  }
267 
268  template < class Sender >
269  bool send_note_on_messages( int idx, const Sender& sender ) const
270  {
271  const auto& step = track_[ idx ];
272  const auto is_active = step.is_active();
273  const auto trig_active = step.evaluate_trig_condition();
274  if ( is_active && trig_active )
275  {
276  const auto note = get_note( step );
277  sender( channel::voice::note_on( channel(), to_uint8_t( note ),
278  get_velocity( step ) ) );
279  current_notes_.emplace_back( note, get_length( step ) );
280  return true;
281  }
282  return false;
283  }
284 
285  template < class Sender >
286  void send_all_notes_off_message( const Sender& sender ) const
287  {
288  sender( channel::voice::all_notes_off( channel() ) );
289  current_notes_.clear();
290  }
291 
292  constexpr note_t base_note() const noexcept
293  {
294  return base_note_;
295  }
296 
297  Parameter& parameter() noexcept
298  {
299  return parameter_;
300  }
301 
302  const Parameter& parameter() const noexcept
303  {
304  return parameter_;
305  }
306 
307  template < class Sender >
308  bool process_control_message( message_t< 1 > message, const Sender& sender ) const
309  {
310  if ( message == realtime::realtime_start() )
311  {
312  return true;
313  }
314  if ( message == realtime::realtime_continue() )
315  {
316  return true;
317  }
318  if ( message == realtime::realtime_stop() )
319  {
320  send_all_notes_off_message( sender );
321  return true;
322  }
323 
324  return false;
325  }
326 
327  constexpr void set_pulses_per_quarter_note( std::size_t pulses_per_quarter_note ) noexcept
328  {
329  pulses_per_quarter_note_ = pulses_per_quarter_note;
330  }
331 
332  private:
333  note_t get_note( const Step& step ) const noexcept
334  {
335  return step.note() ? step.note()->load() : base_note() + parameter().note_offset();
336  }
337 
338  std::uint8_t get_velocity( const Step& step ) const noexcept
339  {
340  return step.velocity() ? step.velocity()->load() : parameter().velocity();
341  }
342 
343  std::size_t get_length( const Step& step ) const noexcept
344  {
345  return ( step.length() ? step.length()->load() : parameter().note_length() )
346  .to_double() *
347  pulses_per_quarter_note_;
348  }
349 
350  void copy( const track_impl_t& from, track_impl_t& to )
351  {
352  to.parameter_ = from.parameter_;
353  to.channel_ = from.channel();
354  to.base_note_ = from.base_note();
355  to.pulses_per_quarter_note_ = from.pulses_per_quarter_note_;
356  copy_track( from.track_, to.track_ );
357  }
358 
359  track_base_t track_;
360  Parameter parameter_{};
361  std::uint8_t channel_{0};
362  note_t base_note_{36};
363  std::size_t pulses_per_quarter_note_{24};
364  mutable std::vector< std::pair< note_t, std::size_t /*remaining_pulses*/ > > current_notes_;
365  };
366 
367  template < class Step, class Parameter >
368  class track_t
369  {
370  public:
373 
374  explicit track_t( size_type size = 64, size_type initial_size = 16 )
375  : impl_{size, std::min( size, initial_size )}, clock_to_step_{size}
376  {
377  impl_.set_pulses_per_quarter_note( clock_to_step_.pulses_per_quarter_note() );
378  }
379 
380  template < class Sender >
381  bool send_messages( std::size_t idx, const Sender& sender ) const
382  {
383  if ( is_muted() )
384  {
385  return false;
386  }
387 
388  return impl_.send_note_on_messages( idx, sender );
389  }
390 
391  template < class Sender >
392  void send_messages( message_t< 1 > message, const Sender& sender ) const
393  {
394  const auto step = clock_to_step_.process_message( message );
395  auto note_on_send = false;
396  if ( !impl_.process_control_message( message, sender ) && clock_to_step_.started() )
397  {
398  impl_.send_note_off_messages( sender );
399  if ( step != clock_to_step_t::do_not_send )
400  {
401  note_on_send = send_messages( step, sender );
402  }
403  }
404 
405  if ( parameter().lfo_enabled() )
406  {
407  const auto msg = lfo_( clock_to_step_.pulses_per_quarter_note(),
408  message == realtime::realtime_start(), note_on_send );
409  if ( msg )
410  {
411  sender( *msg );
412  }
413  }
414  }
415 
416  template < class Sender >
417  void send_all_notes_off_message( const Sender& sender ) const
418  {
419  impl_.send_all_notes_off_message( sender );
420  }
421 
422  std::size_t steps() const noexcept
423  {
424  return impl_.steps();
425  }
426 
427  void set_steps( std::size_t new_steps, std::size_t copy_offset = 0 )
428  {
429  clock_to_step_.set_steps( new_steps );
430  impl_.set_steps( new_steps, copy_offset );
431  }
432 
434  {
435  return impl_[ i ];
436  }
437 
438  const value_type& operator[]( size_type i ) const noexcept
439  {
440  return impl_[ i ];
441  }
442 
443  constexpr void set_steps_per_beat( std::size_t steps ) noexcept
444  {
445  clock_to_step_.set_steps_per_beat( steps );
446  }
447 
448  constexpr void set_pulses_per_quarter_note( std::size_t pulses_per_quarter_note ) noexcept
449  {
450  clock_to_step_.set_pulses_per_quarter_note( pulses_per_quarter_note );
451  impl_.set_pulses_per_quarter_note( pulses_per_quarter_note );
452  }
453 
454  constexpr void reset_beat_counter() noexcept
455  {
456  clock_to_step_.reset_beat_counter();
457  }
458 
459  auto midi_beat_counter() const noexcept
460  {
461  return clock_to_step_.midi_beat_counter();
462  }
463 
464  void clear() noexcept
465  {
466  impl_.clear();
467  }
468 
469  template < class F >
470  void set_lfo( F f )
471  {
472  lfo_ = f;
473  }
474 
475  template < class F >
476  void set_lfo( std::uint8_t min, std::uint8_t max, F f )
477  {
478  set_lfo( parameter().lfo_func( min, max, f ) );
479  }
480 
481  Parameter& parameter() noexcept
482  {
483  return impl_.parameter();
484  }
485 
486  const Parameter& parameter() const noexcept
487  {
488  return impl_.parameter();
489  }
490 
491  constexpr void set_channel( std::uint8_t channel ) noexcept
492  {
493  impl_.set_channel( channel );
494  }
495 
496  constexpr std::uint8_t channel() const noexcept
497  {
498  return impl_.channel();
499  }
500 
501  void mute( bool do_mute = true ) noexcept
502  {
503  is_muted_ = do_mute;
504  }
505 
506  bool is_muted() const noexcept
507  {
508  return is_muted_;
509  }
510 
511  constexpr note_t base_note() const noexcept
512  {
513  return impl_.base_note();
514  }
515 
516  constexpr void set_speed_multiplier( double speed ) noexcept
517  {
518  clock_to_step_.set_speed_multiplier( speed );
519  }
520 
521  constexpr void set_loop_length( std::size_t loop_length ) noexcept
522  {
523  clock_to_step_.set_loop_length( loop_length );
524  }
525 
526  private:
528  copyable_atomic< bool > is_muted_{false};
529  std::function< std::optional< message_t< 3 > >( std::size_t, bool, bool ) > lfo_;
530  mutable clock_to_step_t clock_to_step_;
531  };
532 
533  template < class Tracks, class Sender >
534  void send_all_notes_off_message( Tracks& tracks, const Sender& sender )
535  {
536  for ( auto& track : tracks )
537  {
538  track.send_all_notes_off_message( sender );
539  }
540  }
541 } // namespace sequencer::midi
void set_lfo(std::uint8_t min, std::uint8_t max, F f)
Definition: track.hpp:476
void send_all_notes_off_message(const Sender &sender) const
Definition: track.hpp:286
constexpr message_t< 1 > realtime_start() noexcept
Definition: realtime.hpp:13
constexpr void set_speed_multiplier(double speed) noexcept
Definition: track.hpp:516
constexpr void set_steps_per_beat(std::size_t steps) noexcept
Definition: track.hpp:50
bool is_muted() const noexcept
Definition: track.hpp:506
note_t
Definition: note.hpp:11
constexpr bool started() const noexcept
Definition: track.hpp:115
void set_steps(std::size_t new_steps, std::size_t copy_offset=0)
Definition: track.hpp:204
Definition: track.hpp:170
constexpr std::size_t steps_per_beat() noexcept
Definition: track.hpp:55
size_type size() const noexcept
Definition: vector.hpp:35
value_type & operator[](size_type i) noexcept
Definition: track.hpp:222
void send_messages(message_t< 1 > message, const Sender &sender) const
Definition: track.hpp:392
typename track_impl_t< Step, Parameter >::value_type value_type
Definition: track.hpp:372
const auto F
Definition: note.hpp:40
track_impl_t & operator=(const track_impl_t &other)
Definition: track.hpp:189
constexpr note_t base_note() const noexcept
Definition: track.hpp:511
Parameter & parameter() noexcept
Definition: track.hpp:481
value_type & operator[](size_type i) noexcept
Definition: track.hpp:433
const value_type & operator[](size_type i) const noexcept
Definition: track.hpp:227
static constexpr int do_not_send
Definition: track.hpp:34
int process_message(message_t< 1 > message) const
Definition: track.hpp:91
void clear() noexcept
Definition: track.hpp:232
void send_note_off_messages(const Sender &sender) const
Definition: track.hpp:252
std::conditional_t< greater_than< number_of_bytes, 0 >::value, std::array< std::byte, number_of_bytes >, std::vector< std::byte > > message_t
Definition: message_type.hpp:18
#define SEQUENCER_ASSERT(cond)
Definition: assert.hpp:8
constexpr void set_channel(std::uint8_t channel) noexcept
Definition: track.hpp:491
constexpr note_t base_note() const noexcept
Definition: track.hpp:292
constexpr message_t< 3 > note_on(std::uint8_t channel, std::uint8_t note, std::uint8_t velocity) noexcept
Definition: channel_voice.hpp:13
constexpr void set_pulses_per_quarter_note(std::size_t pulses_per_quarter_note) noexcept
Definition: track.hpp:60
constexpr void set_pulses_per_quarter_note(std::size_t pulses_per_quarter_note) noexcept
Definition: track.hpp:327
constexpr message_t< 3 > all_notes_off(std::uint8_t channel) noexcept
Definition: channel_voice.hpp:267
constexpr std::uint8_t to_uint8_t(note_t note) noexcept
Definition: note.hpp:21
constexpr void set_loop_length(std::size_t length) noexcept
Definition: track.hpp:125
constexpr message_t< 3 > note_off(std::uint8_t channel, std::uint8_t note, std::uint8_t velocity) noexcept
Definition: channel_voice.hpp:23
track_t(size_type size=64, size_type initial_size=16)
Definition: track.hpp:374
constexpr std::uint8_t channel() const noexcept
Definition: track.hpp:246
constexpr void reset_beat_counter() noexcept
Definition: track.hpp:454
constexpr void set_pulses_per_quarter_note(std::size_t pulses_per_quarter_note) noexcept
Definition: track.hpp:448
const value_type & operator[](size_type i) const noexcept
Definition: track.hpp:438
clock_to_step_t(std::size_t steps) noexcept
Definition: track.hpp:38
typename track_base_t::size_type size_type
Definition: track.hpp:175
void mute(bool do_mute=true) noexcept
Definition: track.hpp:501
constexpr message_t< 1 > realtime_continue() noexcept
Definition: realtime.hpp:18
constexpr void set_channel(std::uint8_t channel) noexcept
Definition: track.hpp:240
typename vec_t::value_type value_type
Definition: vector.hpp:13
constexpr message_t< 1 > realtime_stop() noexcept
Definition: realtime.hpp:23
constexpr std::size_t pulses_per_step() const noexcept
Definition: track.hpp:86
void clear() noexcept
Definition: track.hpp:464
Parameter & parameter() noexcept
Definition: track.hpp:297
const Parameter & parameter() const noexcept
Definition: track.hpp:486
constexpr void set_loop_length(std::size_t loop_length) noexcept
Definition: track.hpp:521
void copy_track(const Track &from, Track &to) noexcept(std::is_nothrow_copy_assignable< Track >::value)
Definition: track.hpp:21
std::size_t steps() const noexcept
Definition: track.hpp:422
track_impl_t(size_type size=64, size_type initial_size=16)
Definition: track.hpp:177
void send_all_notes_off_message(Tracks &tracks, const Sender &sender)
Definition: track.hpp:534
typename vec_t::size_type size_type
Definition: vector.hpp:12
void set_midi_beat_counter(std::size_t counter) noexcept
Definition: track.hpp:71
void reset_beat_counter() noexcept
Definition: track.hpp:66
Definition: clock.hpp:13
constexpr std::uint8_t channel() const noexcept
Definition: track.hpp:496
std::size_t steps() const noexcept
Definition: track.hpp:199
constexpr void set_steps_per_beat(std::size_t steps) noexcept
Definition: track.hpp:443
typename track_base_t::value_type value_type
Definition: track.hpp:174
constexpr std::size_t midi_beat_counter() const noexcept
Definition: track.hpp:76
const Parameter & parameter() const noexcept
Definition: track.hpp:302
track_impl_t(const track_impl_t &other)
Definition: track.hpp:184
bool send_note_on_messages(int idx, const Sender &sender) const
Definition: track.hpp:269
Definition: track.hpp:31
auto midi_beat_counter() const noexcept
Definition: track.hpp:459
void send_all_notes_off_message(const Sender &sender) const
Definition: track.hpp:417
void set_lfo(F f)
Definition: track.hpp:470
Definition: track.hpp:368
bool send_messages(std::size_t idx, const Sender &sender) const
Definition: track.hpp:381
constexpr void set_steps(std::size_t new_steps) noexcept
Definition: track.hpp:43
constexpr auto max_number_of_midi_channels
Definition: constants.hpp:9
typename track_impl_t< Step, Parameter >::size_type size_type
Definition: track.hpp:371
constexpr auto default_pulses_per_quarter_note
Definition: constants.hpp:10
void set_steps(std::size_t new_steps, std::size_t copy_offset=0)
Definition: track.hpp:427
bool process_control_message(message_t< 1 > message, const Sender &sender) const
Definition: track.hpp:308
constexpr void set_speed_multiplier(double speed) noexcept
Definition: track.hpp:120
constexpr std::size_t pulses_per_quarter_note() const noexcept
Definition: track.hpp:81