Ardour  9.8-201-g363d29fd54
transport_master.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2018-2019 Paul Davis <paul@linuxaudiosystems.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #pragma once
20 
21 #include <vector>
22 
23 #include <optional>
24 
25 #include <glibmm/timer.h>
26 
27 #include <ltc.h>
28 
29 #include "pbd/properties.h"
30 #include "pbd/mutex.h"
31 #include "pbd/signals.h"
33 
34 #include "temporal/time.h"
35 
37 #include "ardour/region.h" /* for Properties::locked */
38 #include "ardour/types.h"
39 
40 #include "midi++/parser.h"
41 #include "midi++/types.h"
42 
43 namespace ARDOUR {
44 
45 class Session;
46 class AudioEngine;
47 class Location;
48 class MidiPort;
49 class AudioPort;
50 class Port;
51 
52 namespace Properties {
58 }
59 
61  /* This object uses memory fences to provide psuedo-atomic updating of
62  * non-atomic data. If after reading guard1 and guard2 with correct
63  * memory fencing they have the same value, then we know that the other
64  * members are all internally consistent.
65  *
66  * Traditionally, one might do this with a mutex, but this object
67  * provides lock-free write update. The reader might block while
68  * waiting for consistency, but this is extraordinarily unlikely. In
69  * this sense, the design is similar to a spinlock.
70  *
71  * any update starts by incrementing guard1, with a memory fence to
72  * ensure no reordering of this w.r.t later operations.
73  *
74  * then we update the "non-atomic" data members.
75  *
76  * then we update guard2, with another memory fence to prevent
77  * reordering.
78  *
79  * ergo, if guard1 == guard2, the update of the non-atomic members is
80  * complete and the values stored there are consistent.
81  */
82 
83  std::atomic<int> guard1;
86  double speed;
87  std::atomic<int> guard2;
88 
90  {
91  guard1.store (0);
92  position = 0;
93  timestamp = 0;
94  speed = 0;
95  guard2.store (0);
96  }
97 
98  void reset ()
99  {
100  guard1.store (0);
101  position = 0;
102  timestamp = 0;
103  speed = 0;
104  guard2.store (0);
105  }
106 
107  void update (samplepos_t p, samplepos_t t, double s)
108  {
109  guard1.fetch_add (1, std::memory_order_acquire);
110  position = p;
111  timestamp = t;
112  speed = s;
113  guard2.fetch_add (1, std::memory_order_acquire);
114  }
115 
116  void safe_read (SafeTime& dst) const
117  {
118  int tries = 0;
119 
120  do {
121  if (tries == 10) {
122  std::cerr << "SafeTime: atomic read of current time failed, sleeping!" << std::endl;
123  Glib::usleep (20);
124  tries = 0;
125  }
126  dst.guard1.store (guard1.load (std::memory_order_seq_cst), std::memory_order_seq_cst);
127  dst.position = position;
128  dst.timestamp = timestamp;
129  dst.speed = speed;
130  dst.guard2.store (guard2.load (std::memory_order_seq_cst), std::memory_order_seq_cst);
131  tries++;
132 
133  } while (dst.guard1.load (std::memory_order_seq_cst) != dst.guard2.load (std::memory_order_seq_cst));
134  }
135 };
136 
147 {
148 public:
149  TransportMaster (SyncSource t, std::string const& name);
150  virtual ~TransportMaster ();
151 
152  static std::shared_ptr<TransportMaster> factory (SyncSource, std::string const&, bool removeable);
153  static std::shared_ptr<TransportMaster> factory (XMLNode const&);
154 
155  virtual void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>) = 0;
156 
212  virtual bool speed_and_position (double& speed, samplepos_t& position, samplepos_t& lp, samplepos_t& when, samplepos_t now);
213 
214  virtual void reset (bool with_position) = 0;
215 
222  virtual bool locked () const = 0;
223 
230  virtual bool ok () const = 0;
231 
240  virtual bool usable () const
241  {
242  return true;
243  }
244 
251  virtual bool starting () const
252  {
253  return false;
254  }
255 
260  virtual samplecnt_t resolution () const = 0;
261 
268  virtual samplecnt_t update_interval () const = 0;
269 
274  virtual bool requires_seekahead () const = 0;
275 
282  {
283  return 0;
284  }
285 
290  virtual bool sample_clock_synced () const
291  {
292  return _sclock_synced;
293  }
294  virtual void set_sample_clock_synced (bool);
295 
299  virtual std::string delta_string () const
300  {
301  return "";
302  }
303 
305  {
306  return _current_delta;
307  }
308 
309  /* this is intended to be used by a UI and polled from a timeout. it should
310  return a string describing the current position of the TC source. it
311  should NOT do any computation, but should use a cached value
312  of the TC source position.
313  */
314  virtual std::string position_string () const = 0;
315 
316  /* This can be used as display value when transport is not locked,
317  * but a valid position has been received. e.g LTC standing TC.
318  */
319  virtual bool valid_position (std::string& tc, samplepos_t& pos, samplepos_t& when) const
320  {
321  return false;
322  }
323 
324  virtual bool can_loop () const
325  {
326  return false;
327  }
328 
329  virtual Location* loop_location () const
330  {
331  return 0;
332  }
333  bool has_loop () const
334  {
335  return loop_location () != 0;
336  }
337 
338  SyncSource type () const
339  {
340  return _type;
341  }
343  {
344  switch (_type) {
345  case Engine: /* also JACK */
346  return TRS_Engine;
347  case MTC:
348  return TRS_MTC;
349  case LTC:
350  return TRS_LTC;
351  case WallClock:
352  return TRS_WallClock;
353  case MIDIClock:
354  break;
355  }
356  return TRS_MIDIClock;
357  }
358 
359  std::string name () const
360  {
361  return _name;
362  }
363  void set_name (std::string const&);
364 
365  int set_state (XMLNode const&, int);
366  XMLNode& get_state () const;
367 
368  static const std::string state_node_name;
369  static void make_property_quarks ();
370 
371  virtual void set_session (Session*);
372 
373  std::shared_ptr<Port> port () const
374  {
375  return _port;
376  }
377 
378  bool check_collect ();
379  virtual void set_collect (bool);
380  bool collect () const
381  {
382  return _collect;
383  }
384 
385  /* called whenever the manager starts collecting (processing) this
386  transport master. Typically will re-initialize any state used to
387  deal with incoming data.
388  */
389  virtual void init () = 0;
390 
391  virtual void check_backend () {}
393  std::string allowed_request_string () const;
394 
396  {
397  return _request_mask;
398  }
400 
401  /* this is set at construction, and not changeable later, so it is not
402  * a property
403  */
404 
405  bool removeable () const
406  {
407  return _removeable;
408  }
409  void set_removeable (bool yn)
410  {
411  _removeable = yn;
412  }
413 
414  std::string display_name (bool sh /*ort*/ = true) const;
415 
416  virtual void unregister_port ();
418  virtual void create_port () = 0;
419 
420 protected:
427  PBD::Property<TransportRequestType> _request_mask; /* lists transport requests still accepted when we're in control */
431 
433 
434  /* DLL - chase incoming data */
435 
438 
439  double t0;
440  double t1;
441  double e2;
442  double b, c;
443 
444  std::shared_ptr<Port> _port;
445 
447 
448  virtual void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string name1, std::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
449 
452 
453  virtual void register_properties ();
454 
455  virtual std::string format_delta_time (sampleoffset_t) const;
456 };
457 
462 {
463 public:
465 
466  MIDI::Parser& transport_parser () { return parser; }
467  std::shared_ptr<MidiPort> midi_port () const
468  {
469  return _midi_port;
470  }
471  std::shared_ptr<Port> create_midi_port (std::string const& port_name);
472 
473  virtual void set_session (Session*);
474 
475 protected:
477 
478  void resync_latency (bool);
480  std::shared_ptr<MidiPort> _midi_port;
481 
482  virtual void parameter_changed (std::string const& p) {}
483 
485 
486 private:
488 };
489 
491 {
492 public:
493  TimecodeTransportMaster (std::string const& name, SyncSource type);
494 
496 
498  return timecode_format_valid;
499  }
500 
501  bool fr2997 () const
502  {
503  return _fr2997;
504  }
505  void set_fr2997 (bool);
506 
507 protected:
509 
513 
514 private:
516 };
517 
519 {
520 public:
521  MTC_TransportMaster (std::string const&);
523 
525 
526  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
527 
529 
530  void reset (bool with_pos);
531  bool locked () const;
532  bool ok () const;
533  void handle_locate (const MIDI::byte*);
534 
537  bool requires_seekahead () const
538  {
539  return false;
540  }
542  void init ();
543 
545  std::string position_string () const;
546  std::string delta_string () const;
547 
548  void create_port ();
549 
550 private:
553 
554  static const int sample_tolerance;
555 
556  samplepos_t mtc_frame; /* current time */
558  samplepos_t last_inbound_frame; /* when we got it; audio clocked */
563 
565  uint32_t reset_pending;
570 
574 
575  void queue_reset (bool with_pos);
576  void maybe_reset ();
577 
579  void update_mtc_time (const MIDI::byte*, bool, samplepos_t);
583  void init_mtc_dll (samplepos_t, double);
585  void parameter_changed (std::string const& p);
586 
587  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
588 };
589 
591 {
592 public:
593  LTC_TransportMaster (std::string const&);
595 
597 
598  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
599 
600  void reset (bool with_pos);
601  bool locked () const;
602  bool ok () const;
603 
606  bool requires_seekahead () const
607  {
608  return false;
609  }
611  {
612  return 0;
613  }
614  void init ();
616 
618  std::string position_string () const;
619  std::string delta_string () const;
620 
621  bool valid_position (std::string& tc, samplepos_t& pos, samplepos_t& when) const;
622 
623  void create_port ();
624 
625 private:
626  void parse_ltc (const pframes_t, Sample const*, samplecnt_t);
627  void process_ltc (samplepos_t const);
628  void init_dll (samplepos_t, int32_t);
629  bool detect_discontinuity (LTCFrameExt*, int, bool);
630  bool detect_ltc_fps (int, bool);
632  void resync_xrun ();
633  void resync_latency (bool);
635  void parameter_changed (std::string const& p);
636  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
637 
643 
647 
649 
654 
658 
659 
660  struct Biquad {
661  void reset () { z1 = z2 = 0;}
662  float z1, z2;
663  double a1, a2, b0, b1, b2;
664  };
665 
669 };
670 
672 {
673 public:
674  WallClock_TransportMaster (std::string const&);
676 
678 
679  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
680 
681  void reset (bool with_pos);
682  bool locked () const { return true; }
683  bool ok () const { return true; }
684 
687  bool requires_seekahead () const { return false; }
688  void init ();
689 
691  std::string position_string () const;
692 
693  void create_port () {}
694  void check_backend ();
695 
696 private:
698  bool _set;
699 };
700 
702 {
703 public:
704  MIDIClock_TransportMaster (std::string const& name, int ppqn = 24);
705 
708 
710 
712 
713  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
714 
715  void rebind (MidiPort&);
716 
717  void reset (bool with_pos);
718  bool locked () const;
719  bool ok () const;
720 
723  bool requires_seekahead () const
724  {
725  return false;
726  }
727  void init ();
728 
729  std::string position_string () const;
730  std::string delta_string () const;
731 
732  float bpm () const
733  {
734  return _bpm;
735  }
736 
737  void create_port ();
738 
739 protected:
741 
743  int ppqn;
744 
747 
750 
754 
758 
760 
761  double _speed;
762  bool _running;
763  double _bpm;
764 
765  void start (MIDI::Parser& parser, samplepos_t timestamp);
766  void contineu (MIDI::Parser& parser, samplepos_t timestamp); // we can't use continue because it is a C++ keyword
767  void stop (MIDI::Parser& parser, samplepos_t timestamp);
768  void position (MIDI::Parser& parser, MIDI::byte* message, size_t size, samplepos_t timestamp);
769 
771  samplepos_t calculate_song_position (uint16_t song_position_in_sixteenth_notes);
772  void calculate_filter_coefficients (double qpm);
773  void update_midi_clock (MIDI::Parser& parser, samplepos_t timestamp);
774 
775  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
776 };
777 
779 {
780 public:
783 
784  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
786 
787  bool starting () const
788  {
789  return _starting;
790  }
791  void reset (bool with_position);
792  bool locked () const;
793  bool ok () const;
794  bool usable () const;
797  {
798  return 1;
799  }
800  bool requires_seekahead () const
801  {
802  return false;
803  }
804  bool sample_clock_synced () const
805  {
806  return true;
807  }
808  void init ();
809  void check_backend ();
811 
812  std::string position_string () const;
813  std::string delta_string () const;
814 
815  void create_port () {}
816 
817 private:
819  bool _starting;
820 };
821 
822 }
823 
samplecnt_t update_interval() const
void reset(bool with_position)
std::string delta_string() const
void pre_process(pframes_t nframes, samplepos_t now, std::optional< samplepos_t >)
std::string position_string() const
Engine_TransportMaster(AudioEngine &)
bool speed_and_position(double &speed, samplepos_t &pos, samplepos_t &, samplepos_t &, samplepos_t)
bool allow_request(TransportRequestSource, TransportRequestType) const
void init_dll(samplepos_t, int32_t)
void connection_handler(std::weak_ptr< ARDOUR::Port >, std::string, std::weak_ptr< ARDOUR::Port >, std::string, bool)
std::string position_string() const
void set_session(Session *)
Timecode::TimecodeFormat apparent_timecode_format() const
PBD::ScopedConnectionList session_connections
void parse_ltc(const pframes_t, Sample const *, samplecnt_t)
void pre_process(pframes_t nframes, samplepos_t now, std::optional< samplepos_t >)
samplecnt_t seekahead_distance() const
void process_ltc(samplepos_t const)
bool detect_ltc_fps(int, bool)
PBD::ScopedConnection port_connection
LTC_TransportMaster(std::string const &)
samplecnt_t update_interval() const
samplecnt_t resolution() const
void reset(bool with_pos)
std::string delta_string() const
bool detect_discontinuity(LTCFrameExt *, int, bool)
bool valid_position(std::string &tc, samplepos_t &pos, samplepos_t &when) const
bool equal_ltc_sample_time(LTCFrame *a, LTCFrame *b)
void parameter_changed(std::string const &p)
PBD::ScopedConnectionList port_connections
~MIDIClock_TransportMaster()
Constructor for unit tests.
void calculate_filter_coefficients(double qpm)
samplecnt_t update_interval() const
samplepos_t last_timestamp
the time stamp and should-be transport position of the last inbound MIDI clock message
void position(MIDI::Parser &parser, MIDI::byte *message, size_t size, samplepos_t timestamp)
void calculate_one_ppqn_in_samples_at(samplepos_t time)
samplepos_t calculate_song_position(uint16_t song_position_in_sixteenth_notes)
void connection_handler(std::weak_ptr< ARDOUR::Port >, std::string, std::weak_ptr< ARDOUR::Port >, std::string, bool)
double one_ppqn_in_samples
the duration of one ppqn in sample time
std::string position_string() const
samplecnt_t resolution() const
samplepos_t first_timestamp
the timestamp of the first MIDI clock message
void start(MIDI::Parser &parser, samplepos_t timestamp)
double _speed
a DLL to track MIDI clock
void update_midi_clock(MIDI::Parser &parser, samplepos_t timestamp)
void contineu(MIDI::Parser &parser, samplepos_t timestamp)
void stop(MIDI::Parser &parser, samplepos_t timestamp)
std::string delta_string() const
int ppqn
pulses per quarter note for one MIDI clock sample (default 24)
MIDIClock_TransportMaster(std::string const &name, int ppqn=24)
void pre_process(pframes_t nframes, samplepos_t now, std::optional< samplepos_t >)
void reset_window(samplepos_t)
void update_mtc_time(const MIDI::byte *, bool, samplepos_t)
samplecnt_t resolution() const
void reset(bool with_pos)
void init_mtc_dll(samplepos_t, double)
void handle_locate(const MIDI::byte *)
bool outside_window(samplepos_t) const
void set_session(Session *)
PBD::ScopedConnectionList port_connections
std::string position_string() const
Timecode::TimecodeFormat apparent_timecode_format() const
void update_mtc_status(MIDI::MTC_Status)
void parameter_changed(std::string const &p)
void queue_reset(bool with_pos)
void connection_handler(std::weak_ptr< ARDOUR::Port >, std::string, std::weak_ptr< ARDOUR::Port >, std::string, bool)
MTC_TransportMaster(std::string const &)
samplecnt_t seekahead_distance() const
std::string delta_string() const
Timecode::TimecodeFormat mtc_timecode
void update_mtc_qtr(MIDI::Parser &, int, samplepos_t)
samplecnt_t update_interval() const
static const int sample_tolerance
void pre_process(pframes_t nframes, samplepos_t now, std::optional< samplepos_t >)
virtual Timecode::TimecodeFormat apparent_timecode_format() const =0
TimecodeTransportMaster(std::string const &name, SyncSource type)
std::shared_ptr< MidiPort > midi_port() const
std::shared_ptr< Port > create_midi_port(std::string const &port_name)
virtual void set_session(Session *)
PBD::ScopedConnectionList session_connections
std::shared_ptr< MidiPort > _midi_port
virtual void parameter_changed(std::string const &p)
The TransportMaster interface can be used to sync ARDOURs tempo to an external source like MTC,...
virtual std::string delta_string() const
virtual bool usable() const
std::shared_ptr< Port > port() const
PBD::Property< bool > _collect
static const std::string state_node_name
PBD::ScopedConnection backend_connection
sampleoffset_t _current_delta
XMLNode & get_state() const
virtual bool speed_and_position(double &speed, samplepos_t &position, samplepos_t &lp, samplepos_t &when, samplepos_t now)
virtual samplecnt_t seekahead_distance() const
PBD::Property< bool > _connected
PBD::Property< bool > _sclock_synced
sampleoffset_t current_delta() const
virtual bool starting() const
PBD::ScopedConnection port_connection
virtual std::string format_delta_time(sampleoffset_t) const
virtual void set_collect(bool)
std::string allowed_request_string() const
int set_state(XMLNode const &, int)
TransportRequestType request_mask() const
TransportMaster(SyncSource t, std::string const &name)
virtual bool requires_seekahead() const =0
TransportRequestSource request_type() const
void set_request_mask(TransportRequestType)
virtual void set_session(Session *)
virtual Location * loop_location() const
virtual bool allow_request(TransportRequestSource, TransportRequestType) const
virtual void init()=0
SyncSource type() const
virtual samplecnt_t update_interval() const =0
virtual bool locked() const =0
static void make_property_quarks()
static std::shared_ptr< TransportMaster > factory(XMLNode const &)
std::string name() const
static std::shared_ptr< TransportMaster > factory(SyncSource, std::string const &, bool removeable)
virtual void pre_process(pframes_t nframes, samplepos_t now, std::optional< samplepos_t >)=0
virtual bool can_loop() const
virtual void create_port()=0
virtual bool sample_clock_synced() const
void set_name(std::string const &)
virtual void unregister_port()
PBD::Property< std::string > _name
virtual void connection_handler(std::weak_ptr< ARDOUR::Port >, std::string name1, std::weak_ptr< ARDOUR::Port >, std::string name2, bool yn)
virtual void reset(bool with_position)=0
virtual void set_sample_clock_synced(bool)
virtual bool valid_position(std::string &tc, samplepos_t &pos, samplepos_t &when) const
std::shared_ptr< Port > _port
virtual std::string position_string() const =0
PBD::Property< TransportRequestType > _request_mask
virtual void register_properties()
virtual samplecnt_t resolution() const =0
virtual bool ok() const =0
virtual void check_backend()
std::string display_name(bool sh=true) const
WallClock_TransportMaster(std::string const &)
samplecnt_t resolution() const
Timecode::TimecodeFormat apparent_timecode_format() const
void pre_process(pframes_t nframes, samplepos_t now, std::optional< samplepos_t >)
samplecnt_t update_interval() const
std::string position_string() const
Definition: xml++.h:114
GtkImageIconNameData name
Definition: gtkimage.h:6
#define LIBARDOUR_API
libltc - en+decode linear timecode
PBD::PropertyDescriptor< bool > fr2997
PBD::PropertyDescriptor< bool > collect
PBD::PropertyDescriptor< bool > sclock_synced
PBD::PropertyDescriptor< bool > connected
PBD::PropertyDescriptor< ARDOUR::TransportRequestType > allowed_transport_requests
uint32_t pframes_t
Temporal::samplecnt_t samplecnt_t
Temporal::sampleoffset_t sampleoffset_t
Temporal::samplepos_t samplepos_t
DebugBits AudioEngine
Definition: session.h:1438
DebugBits Properties
TimecodeFormat
Definition: time.h:38
void safe_read(SafeTime &dst) const
void update(samplepos_t p, samplepos_t t, double s)
std::atomic< int > guard1
samplepos_t position
std::atomic< int > guard2
samplepos_t timestamp
Definition: ltc.h:218