sequencer
digitakt.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <sequencer/assert.hpp>
12 
13 #include <cassert>
14 #include <iostream>
15 #include <string>
16 #include <vector>
17 
19 {
20  enum class mode_t
21  {
22  play,
23  mute,
28  };
29 
30  enum class control_mode_t
31  {
32  trig,
33  quantize,
34  source,
35  assign,
36  filter,
37  delay,
38  amp,
39  reverb,
40  lfo,
41  master
42  };
43 
44  inline device_spec_cc_t get_spec( const std::string& spec_filename )
45  {
46  auto spec_file = std::ifstream( spec_filename );
47  if ( !spec_file.is_open() )
48  {
49  std::cerr << "Error: Could not open device specification file.\n";
50  return {};
51  }
52  return read_device_spec_cc( spec_file );
53  }
54 
55  template < class Sender, class Receiver >
56  struct clock_comm_t
57  {
58  Sender clock_sender;
59  Receiver clock_receiver;
60  };
61 
62  template < class Clock, class Backend, class ClockSender, class ClockReceiver, class Sender >
63  class backend_t : public clock_comm_t< ClockSender, ClockReceiver >,
64  public Sender,
65  public Backend,
66  public Clock
67  {
68  public:
69  template < class Callback >
70  explicit backend_t( Callback callback )
71  : clock_comm_t< ClockSender, ClockReceiver >{ClockSender{}, ClockReceiver{callback}},
72  Sender{}, Backend{get_spec( "device_spec/elektron/digitakt.txt" )},
73  Clock( [this]( midi::message_t< 1 > message ) {
75  } )
76  {
77  }
78 
79  void set_control( int id, int value ) noexcept
80  {
81  Backend::set_control( id, value, Sender::sender() );
82  }
83  };
84 
85 #define ADD_LFO_DESTINATION( section_name ) \
86  { \
87  auto& entry = device_spec_.control_parameter[ track_parameter_t::idx::section_name ]; \
88  for ( decltype( entry.size() ) i = 0; i < entry.size(); ++i ) \
89  { \
90  device_spec_ \
91  .control_parameter[ track_parameter_t::idx::lfo ] \
92  [ track_parameter_t::lfo_idx::destination ] \
93  .str_map.push_back( #section_name + std::string( ":" ) + entry[ i ].name ); \
94  lfo_map_.emplace_back( control_mode_t::section_name, i ); \
95  } \
96  }
97 
99  {
100  if ( value == 0 )
101  {
102  return {};
103  }
104  switch ( value - 1 )
105  {
106  // fill
107  case 0:
108  // not fill
109  case 1:
110  // pre
111  case 2:
112  // not pre
113  case 3:
114  // neighbor
115  case 4:
116  // not neighbor
117  case 5:
118  // first
119  case 6:
120  // not first
121  case 7:
122  return {};
123  // percentage
124  case 8:
126 
127  case 9:
129 
130  case 10:
132 
133  case 11:
135 
136  case 12:
138 
139  case 13:
140  return midi::trig_condition::random{13};
141 
142  case 14:
143  return midi::trig_condition::random{19};
144 
145  case 15:
146  return midi::trig_condition::random{25};
147 
148  case 16:
149  return midi::trig_condition::random{33};
150 
151  case 17:
152  return midi::trig_condition::random{41};
153 
154  case 18:
155  return midi::trig_condition::random{50};
156 
157  case 19:
158  return midi::trig_condition::random{59};
159 
160  case 20:
161  return midi::trig_condition::random{67};
162 
163  case 21:
164  return midi::trig_condition::random{75};
165 
166  case 22:
167  return midi::trig_condition::random{81};
168 
169  case 23:
170  return midi::trig_condition::random{87};
171 
172  case 24:
173  return midi::trig_condition::random{91};
174 
175  case 25:
176  return midi::trig_condition::random{94};
177 
178  case 26:
179  return midi::trig_condition::random{96};
180 
181  case 27:
182  return midi::trig_condition::random{98};
183 
184  case 28:
185  return midi::trig_condition::random{99};
186 
187  case 29:
189 
190  case 30:
192 
193  case 31:
195 
196  case 32:
198 
199  case 33:
201 
202  case 34:
204 
205  case 35:
207 
208  case 36:
210 
211  case 37:
213 
214  case 38:
216 
217  case 39:
219 
220  case 40:
222 
223  case 41:
225 
226  case 42:
228 
229  case 43:
231 
232  case 44:
234 
235  case 45:
237 
238  case 46:
240 
241  case 47:
243 
244  case 48:
246 
247  case 49:
249 
250  case 50:
252 
253  case 51:
255 
256  case 52:
258 
259  case 53:
261 
262  case 54:
264 
265  case 55:
267 
268  case 56:
270 
271  case 57:
273 
274  case 58:
276 
277  case 59:
279 
280  case 60:
282 
283  case 61:
285 
286  case 62:
288 
289  case 63:
291  }
292 
293  return {};
294  }
295 
297  {
298  public:
301  using patterns_t = std::vector< pattern_t >;
302  using banks_t = std::vector< patterns_t >;
303 
304  explicit backend_impl( const device_spec_cc_t& device_spec = device_spec_cc_t{} )
305  : banks_( 16, patterns_t( 16, midi::make_pattern< track_t >( 16, 16 ) ) ),
306  device_spec_{device_spec}
307  {
308  for ( auto& patterns : banks_ )
309  {
310  for ( auto& pattern : patterns )
311  {
312  for ( auto& track : pattern )
313  {
314  track.parameter().resize( device_spec.control_parameter.size() );
315  for ( decltype( track.parameter().size() ) i = 0;
316  i < track.parameter().size(); ++i )
317  {
318  track.parameter()[ i ].resize(
319  device_spec.control_parameter[ i ].size() );
320  for ( decltype( track.parameter()[ i ].size() ) j = 0;
321  j < track.parameter()[ i ].size(); ++j )
322  {
323  const auto value = device_spec.control_parameter[ i ][ j ].value;
324  track.parameter()[ i ][ j ] =
325  device_spec.control_parameter[ i ][ j ].map.empty()
326  ? value
327  : device_spec.control_parameter[ i ][ j ].map[ value ];
328  }
329  }
330  }
331  }
332  }
333 
334  device_spec_
335  .control_parameter[ track_parameter_t::idx::lfo ]
336  [ track_parameter_t::lfo_idx::destination ]
337  .str_map.emplace_back( "none" );
344  device_spec_
345  .control_parameter[ track_parameter_t::idx::lfo ]
346  [ track_parameter_t::lfo_idx::destination ]
347  .max =
348  std::int16_t( device_spec_
349  .control_parameter[ track_parameter_t::idx::lfo ]
350  [ track_parameter_t::lfo_idx::destination ]
351  .str_map.size() -
352  1 );
353  }
354 #undef ADD_LFO_DESTINATION
355 
356  void set_step( int idx, bool value = true ) noexcept
357  {
358  switch ( mode_ )
359  {
360  case mode_t::mute:
361  current_pattern()[ idx ].mute( !current_pattern()[ idx ].is_muted() );
362  return;
364  current_track_idx_ = decltype( current_track_idx_ )( idx );
365  set_mode( mode_t::play );
366  return;
368  current_pattern_idx_ = decltype( current_pattern_idx_ )( idx );
369  set_mode( mode_t::play );
370  return;
371  case mode_t::bank_select:
372  current_bank_idx_ = decltype( current_bank_idx_ )( idx );
373  set_mode( mode_t::pattern_select );
374  return;
375  case mode_t::play:
376  {
377  const auto step = value ? midi::step_t{value} : midi::step_t{};
378  current_track()[ track_t::size_type( idx ) ] = step;
379  return;
380  }
381  case mode_t::step_select:
382  if ( current_step_ == idx ||
383  !current_track()[ track_t::size_type( idx ) ].is_active() )
384  {
385  set_mode( mode_t::step_select );
386  current_step_ = -1;
387  return;
388  }
389  current_step_ = idx;
390  return;
391  }
392  }
393 
394  template < class Sender >
395  void set_control( int id, int value, Sender sender ) noexcept
396  {
397  namespace trig_condition = sequencer::midi::trig_condition;
398 
399  const auto adjust_value_for_midi = [id, &value, this] {
400  value -= spec()[ std::size_t( id ) ].min;
401  };
402 
403  switch ( control_mode() )
404  {
406  {
407  if ( mode() == mode_t::step_select )
408  {
409  switch ( id )
410  {
411  case 0:
412  SEQUENCER_ASSERT( current_step_ != -1 )
413  current_track()[ current_step_ ].set_note( sequencer::midi::note_t(
414  to_uint8_t( current_track().base_note() ) + std::uint8_t( value ) ) );
415  return;
416  case 1:
417  SEQUENCER_ASSERT( current_step_ != -1 )
418  current_track()[ current_step_ ].set_velocity( std::uint8_t( value ) );
419  return;
420  case 2:
421  SEQUENCER_ASSERT( current_step_ != -1 )
422  current_track()[ current_step_ ].set_length(
423  beat_duration{length_map()[ std::size_t( value ) ]} );
424  return;
425  case 3:
426  SEQUENCER_ASSERT( current_step_ != -1 )
427  current_track()[ current_step() ].set_trig_condition(
428  get_trig_condition( value ) );
429  return;
430  }
431  return;
432  }
433 
434  current_track().parameter()[ track_parameter_t::idx::trig ][ std::size_t( id ) ] =
435  value;
436  adjust_value_for_midi();
438  current_track().channel(),
439  device_spec_
440  .control_parameter[ track_parameter_t::idx::trig ][ std::size_t( id ) ]
441  .cc_msb,
442  value ) );
443  }
444  return;
445 
447  current_track().parameter()[ track_parameter_t::idx::source ][ std::size_t( id ) ] =
448  value;
449  adjust_value_for_midi();
451  current_track().channel(),
452  device_spec_
453  .control_parameter[ track_parameter_t::idx::source ][ std::size_t( id ) ]
454  .cc_msb,
455  value ) );
456  return;
457 
459  current_track().parameter()[ track_parameter_t::idx::filter ][ std::size_t( id ) ] =
460  value;
461  adjust_value_for_midi();
463  current_track().channel(),
464  device_spec_
465  .control_parameter[ track_parameter_t::idx::filter ][ std::size_t( id ) ]
466  .cc_msb,
467  value ) );
468  return;
469 
470  case control_mode_t::amp:
471  current_track().parameter()[ track_parameter_t::idx::amp ][ std::size_t( id ) ] =
472  value;
473  adjust_value_for_midi();
475  current_track().channel(),
476  device_spec_
477  .control_parameter[ track_parameter_t::idx::amp ][ std::size_t( id ) ]
478  .cc_msb,
479  value ) );
480  return;
481 
482  case control_mode_t::lfo:
483  {
484  if ( spec()[ std::size_t( id ) ].map.empty() )
485  {
486  current_track()
487  .parameter()[ track_parameter_t::idx::lfo ][ std::size_t( id ) ] = value;
488  }
489  else
490  {
491  current_track()
492  .parameter()[ track_parameter_t::idx::lfo ][ std::size_t( id ) ] =
493  spec()[ std::size_t( id ) ].map[ value ];
494  }
495  const auto dest = current_track()
496  .parameter()[ track_parameter_t::idx::lfo ]
497  [ track_parameter_t::lfo_idx::destination ]
498  .load();
499  if ( dest > 0 )
500  {
501  const auto target = lfo_map_[ std::size_t( dest - 1 ) ];
502  const auto& entry = spec( target.first )[ target.second ];
503  current_track().set_lfo(
504  entry.min, entry.max,
505  [cc_msb = entry.cc_msb,
506  channel = current_track().channel()]( std::uint8_t value ) {
507  return midi::channel::voice::control_change( channel, cc_msb, value );
508  } );
509  }
510  adjust_value_for_midi();
511  if ( device_spec_
512  .control_parameter[ track_parameter_t::idx::lfo ][ std::size_t( id ) ]
513  .cc_lsb > 0 )
514  {
515  const auto [ msb, lsb ] = to_msb_lsb( value, spec()[ std::size_t( id ) ].min,
516  spec()[ std::size_t( id ) ].max );
518  current_track().channel(),
519  device_spec_
520  .control_parameter[ track_parameter_t::idx::lfo ][ std::size_t( id ) ]
521  .cc_msb,
522  msb ) );
524  current_track().channel(),
525  device_spec_
526  .control_parameter[ track_parameter_t::idx::lfo ][ std::size_t( id ) ]
527  .cc_lsb,
528  lsb ) );
529  return;
530  }
532  current_track().channel(),
533  device_spec_
534  .control_parameter[ track_parameter_t::idx::lfo ][ std::size_t( id ) ]
535  .cc_msb,
536  value ) );
537  return;
538  }
539 
541  current_track().parameter()[ track_parameter_t::idx::delay ][ std::size_t( id ) ] =
542  value;
543  adjust_value_for_midi();
545  current_track().channel(),
546  device_spec_
547  .control_parameter[ track_parameter_t::idx::delay ][ std::size_t( id ) ]
548  .cc_msb,
549  value ) );
550  return;
551 
553  current_track().parameter()[ track_parameter_t::idx::reverb ][ std::size_t( id ) ] =
554  value;
555  adjust_value_for_midi();
557  current_track().channel(),
558  device_spec_
559  .control_parameter[ track_parameter_t::idx::reverb ][ std::size_t( id ) ]
560  .cc_msb,
561  value ) );
562  return;
563  }
564  }
565 
566  int get_control_value( int id ) const noexcept
567  {
568  switch ( control_mode() )
569  {
571  return current_track()
572  .parameter()[ track_parameter_t::idx::trig ][ std::size_t( id ) ]
573  .load();
575  return current_track()
576  .parameter()[ track_parameter_t::idx::source ][ std::size_t( id ) ]
577  .load();
579  return current_track()
580  .parameter()[ track_parameter_t::idx::filter ][ std::size_t( id ) ]
581  .load();
582  case control_mode_t::amp:
583  return current_track()
584  .parameter()[ track_parameter_t::idx::amp ][ std::size_t( id ) ]
585  .load();
586  case control_mode_t::lfo:
587 
588  if ( spec()[ std::size_t( id ) ].map.empty() )
589  {
590  return current_track()
591  .parameter()[ track_parameter_t::idx::lfo ][ std::size_t( id ) ]
592  .load();
593  }
594  return int( std::distance(
595  begin( spec()[ std::size_t( id ) ].map ),
596  std::find( begin( spec()[ std::size_t( id ) ].map ),
597  end( spec()[ std::size_t( id ) ].map ),
598  current_track()
599  .parameter()[ track_parameter_t::idx::lfo ][ std::size_t( id ) ]
600  .load() ) ) );
602  return current_track()
603  .parameter()[ track_parameter_t::idx::delay ][ std::size_t( id ) ]
604  .load();
606  return current_track()
607  .parameter()[ track_parameter_t::idx::reverb ][ std::size_t( id ) ]
608  .load();
609  default:
610  return 0;
611  }
612  }
613 
614  bool is_step_set( int step ) const noexcept
615  {
616  if ( mode_ == mode_t::mute )
617  {
618  return current_pattern()[ step ].is_muted();
619  }
620 
621  return current_track()[ track_t::size_type( step ) ].is_active();
622  }
623 
624  constexpr void set_mode( mode_t mode ) noexcept
625  {
626  if ( mode_ == mode && mode_ == mode_t::mute )
627  {
628  mode_ = mode_t::play;
629  return;
630  }
631  if ( mode_ == mode && mode_ == mode_t::step_select )
632  {
633  mode_ = mode_t::play;
634  return;
635  }
636  mode_ = mode;
637  if ( mode_ != mode_t::step_select )
638  {
639  current_step_ = -1;
640  }
641  }
642 
643  constexpr mode_t mode() const noexcept
644  {
645  return mode_;
646  }
647 
648  constexpr void set_control_mode( control_mode_t mode ) noexcept
649  {
650  control_mode_ = mode;
651  }
652 
653  constexpr control_mode_t control_mode() const noexcept
654  {
655  return control_mode_;
656  }
657 
658  template < class Sender >
660  {
661  current_pattern().send_messages( message, sender );
662  }
663 
664  track_t& current_track() noexcept
665  {
666  return current_pattern()[ current_track_idx_ ];
667  }
668 
669  const track_t& current_track() const noexcept
670  {
671  return current_pattern()[ current_track_idx_ ];
672  }
673 
675  {
676  return banks_[ current_bank_idx_ ][ current_pattern_idx_ ];
677  }
678 
679  const pattern_t& current_pattern() const noexcept
680  {
681  return banks_[ current_bank_idx_ ][ current_pattern_idx_ ];
682  }
683 
684  constexpr int current_step() const noexcept
685  {
686  return current_step_;
687  }
688 
689  const std::vector< midi::device_entry_t >& spec( control_mode_t mode ) const
690  {
691  return mode == control_mode_t::trig
692  ? device_spec_.control_parameter[ track_parameter_t::idx::trig ]
693  : mode == control_mode_t::source
694  ? device_spec_.control_parameter[ track_parameter_t::idx::source ]
695  : mode == control_mode_t::filter
696  ? device_spec_
697  .control_parameter[ track_parameter_t::idx::filter ]
698  : mode == control_mode_t::amp
699  ? device_spec_
700  .control_parameter[ track_parameter_t::idx::amp ]
701  : mode == control_mode_t::lfo
702  ? device_spec_.control_parameter
704  : mode == control_mode_t::delay
705  ? device_spec_.control_parameter
706  [ track_parameter_t::idx::delay ]
707  : device_spec_.control_parameter
708  [ track_parameter_t::idx::reverb ];
709  }
710 
711  const std::vector< midi::device_entry_t >& spec() const
712  {
713  return spec( control_mode() );
714  }
715 
716  private:
717  constexpr std::pair< std::uint8_t, std::uint8_t > to_msb_lsb( int value, int min,
718  int max ) const noexcept
719  {
720  const auto spread = max - min;
721  const auto a = ( spread % midi::number_of_values_per_byte > 0 )
722  ? 1 + spread / midi::number_of_values_per_byte
724  const auto msb = value / a;
725  const auto lsb =
726  int( ( ( value % a ) / double( a ) ) * midi::number_of_values_per_byte );
727  return {msb, lsb};
728  }
729 
730  banks_t banks_;
731  banks_t::size_type current_bank_idx_ = 0;
732  patterns_t::size_type current_pattern_idx_ = 0;
733  pattern_t::size_type current_track_idx_ = 0;
734  mode_t mode_ = mode_t::play;
735  control_mode_t control_mode_ = control_mode_t::trig;
736  int current_step_{-1};
737  device_spec_cc_t device_spec_;
738  std::vector< std::pair< control_mode_t, std::size_t > > lfo_map_;
739  };
740 } // namespace sequencer::backend::digitakt
typename std::vector< value_type >::size_type size_type
Definition: pattern.hpp:14
mode_t
Definition: digitakt.hpp:20
Definition: digitakt.hpp:63
void set_step(int idx, bool value=true) noexcept
Definition: digitakt.hpp:356
note_t
Definition: note.hpp:11
track_t & current_track() noexcept
Definition: digitakt.hpp:664
Definition: trig_condition.hpp:16
pattern_t & current_pattern() noexcept
Definition: digitakt.hpp:674
const std::vector< midi::device_entry_t > & spec() const
Definition: digitakt.hpp:711
Definition: trig_condition.hpp:36
Definition: callable.hpp:9
const std::array< double, 128 > & length_map()
Definition: digitakt_parameter.hpp:47
void filter(Container &c, TransferFunction f, double base_frequency)
Definition: transfer_function.hpp:42
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
std::vector< patterns_t > banks_t
Definition: digitakt.hpp:302
#define SEQUENCER_ASSERT(cond)
Definition: assert.hpp:8
const std::vector< midi::device_entry_t > & spec(control_mode_t mode) const
Definition: digitakt.hpp:689
constexpr auto number_of_values_per_byte
Definition: constants.hpp:7
constexpr void set_control_mode(control_mode_t mode) noexcept
Definition: digitakt.hpp:648
void set_control(int id, int value) noexcept
Definition: digitakt.hpp:79
const track_t & current_track() const noexcept
Definition: digitakt.hpp:669
constexpr auto lsb(std::byte msb)
Definition: byte.hpp:37
void receive_clock_message(sequencer::midi::message_t< 1 > message, Sender sender)
Definition: digitakt.hpp:659
Definition: beat_duration.hpp:13
constexpr message_t< 3 > control_change(std::uint8_t channel, std::byte controller, std::byte value) noexcept
Definition: channel_voice.hpp:44
Clock([this](midi::message_t< 1 > message) { clock_comm_t< ClockSender, ClockReceiver >::clock_sender.sender()(message);})
Definition: digitakt.hpp:73
constexpr std::uint8_t to_uint8_t(note_t note) noexcept
Definition: note.hpp:21
backend_t(Callback callback)
Definition: digitakt.hpp:70
const pattern_t & current_pattern() const noexcept
Definition: digitakt.hpp:679
device_spec_cc_t read_device_spec_cc(std::istream &stream)
Definition: digitakt_parameter.hpp:251
T lfo(std::size_t pulse_count, std::size_t pulses_per_quarter_note, int speed, int phase, T min, T max, lfo_mode mode)
Definition: lfo.hpp:62
Sender clock_sender
Definition: digitakt.hpp:58
constexpr mode_t mode() const noexcept
Definition: digitakt.hpp:643
backend_impl(const device_spec_cc_t &device_spec=device_spec_cc_t{})
Definition: digitakt.hpp:304
midi::trig_condition_t get_trig_condition(int value)
Definition: digitakt.hpp:98
bool is_step_set(int step) const noexcept
Definition: digitakt.hpp:614
constexpr int current_step() const noexcept
Definition: digitakt.hpp:684
Definition: step.hpp:15
Receiver clock_receiver
Definition: digitakt.hpp:59
Definition: trig_condition.hpp:19
constexpr control_mode_t control_mode() const noexcept
Definition: digitakt.hpp:653
std::vector< pattern_t > patterns_t
Definition: digitakt.hpp:301
constexpr void set_mode(mode_t mode) noexcept
Definition: digitakt.hpp:624
Definition: pattern.hpp:10
void set_control(int id, int value, Sender sender) noexcept
Definition: digitakt.hpp:395
device_spec_cc_t get_spec(const std::string &spec_filename)
Definition: digitakt.hpp:44
control_mode_t
Definition: digitakt.hpp:30
Definition: track.hpp:368
Definition: digitakt.hpp:18
int get_control_value(int id) const noexcept
Definition: digitakt.hpp:566
typename track_impl_t< Step, Parameter >::size_type size_type
Definition: track.hpp:371
Definition: digitakt.hpp:296
Definition: digitakt_parameter.hpp:53
#define ADD_LFO_DESTINATION(section_name)
Definition: digitakt.hpp:85