Ardour  8.12
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 #ifndef __ardour_transport_master_h__
20 #define __ardour_transport_master_h__
21 
22 #include <vector>
23 
24 #include <boost/atomic.hpp>
25 #include <boost/optional.hpp>
26 
27 #include <glibmm/threads.h>
28 #include <glibmm/timer.h>
29 
30 #include <ltc.h>
31 
32 #include "pbd/properties.h"
33 #include "pbd/signals.h"
35 
36 #include "temporal/time.h"
37 
39 #include "ardour/region.h" /* for Properties::locked */
40 #include "ardour/types.h"
41 
42 #include "midi++/parser.h"
43 #include "midi++/types.h"
44 
45 namespace ARDOUR {
46 
47 class Session;
48 class AudioEngine;
49 class Location;
50 class MidiPort;
51 class AudioPort;
52 class Port;
53 
54 namespace Properties {
60 }
61 
63  /* This object uses memory fences to provide psuedo-atomic updating of
64  * non-atomic data. If after reading guard1 and guard2 with correct
65  * memory fencing they have the same value, then we know that the other
66  * members are all internally consistent.
67  *
68  * Traditionally, one might do this with a mutex, but this object
69  * provides lock-free write update. The reader might block while
70  * waiting for consistency, but this is extraordinarily unlikely. In
71  * this sense, the design is similar to a spinlock.
72  *
73  * any update starts by incrementing guard1, with a memory fence to
74  * ensure no reordering of this w.r.t later operations.
75  *
76  * then we update the "non-atomic" data members.
77  *
78  * then we update guard2, with another memory fence to prevent
79  * reordering.
80  *
81  * ergo, if guard1 == guard2, the update of the non-atomic members is
82  * complete and the values stored there are consistent.
83  */
84 
85  boost::atomic<int> guard1;
88  double speed;
89  boost::atomic<int> guard2;
90 
92  {
93  guard1.store (0);
94  position = 0;
95  timestamp = 0;
96  speed = 0;
97  guard2.store (0);
98  }
99 
100  void reset ()
101  {
102  guard1.store (0);
103  position = 0;
104  timestamp = 0;
105  speed = 0;
106  guard2.store (0);
107  }
108 
109  void update (samplepos_t p, samplepos_t t, double s)
110  {
111  guard1.fetch_add (1, boost::memory_order_acquire);
112  position = p;
113  timestamp = t;
114  speed = s;
115  guard2.fetch_add (1, boost::memory_order_acquire);
116  }
117 
118  void safe_read (SafeTime& dst) const
119  {
120  int tries = 0;
121 
122  do {
123  if (tries == 10) {
124  std::cerr << "SafeTime: atomic read of current time failed, sleeping!" << std::endl;
125  Glib::usleep (20);
126  tries = 0;
127  }
128  dst.guard1.store (guard1.load (boost::memory_order_seq_cst), boost::memory_order_seq_cst);
129  dst.position = position;
130  dst.timestamp = timestamp;
131  dst.speed = speed;
132  dst.guard2.store (guard2.load (boost::memory_order_seq_cst), boost::memory_order_seq_cst);
133  tries++;
134 
135  } while (dst.guard1.load (boost::memory_order_seq_cst) != dst.guard2.load (boost::memory_order_seq_cst));
136  }
137 };
138 
149 {
150 public:
151  TransportMaster (SyncSource t, std::string const& name);
152  virtual ~TransportMaster ();
153 
154  static std::shared_ptr<TransportMaster> factory (SyncSource, std::string const&, bool removeable);
155  static std::shared_ptr<TransportMaster> factory (XMLNode const&);
156 
157  virtual void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>) = 0;
158 
214  virtual bool speed_and_position (double& speed, samplepos_t& position, samplepos_t& lp, samplepos_t& when, samplepos_t now);
215 
216  virtual void reset (bool with_position) = 0;
217 
224  virtual bool locked () const = 0;
225 
232  virtual bool ok () const = 0;
233 
242  virtual bool usable () const
243  {
244  return true;
245  }
246 
253  virtual bool starting () const
254  {
255  return false;
256  }
257 
262  virtual samplecnt_t resolution () const = 0;
263 
270  virtual samplecnt_t update_interval () const = 0;
271 
276  virtual bool requires_seekahead () const = 0;
277 
284  {
285  return 0;
286  }
287 
292  virtual bool sample_clock_synced () const
293  {
294  return _sclock_synced;
295  }
296  virtual void set_sample_clock_synced (bool);
297 
301  virtual std::string delta_string () const
302  {
303  return "";
304  }
305 
307  {
308  return _current_delta;
309  }
310 
311  /* this is intended to be used by a UI and polled from a timeout. it should
312  return a string describing the current position of the TC source. it
313  should NOT do any computation, but should use a cached value
314  of the TC source position.
315  */
316  virtual std::string position_string () const = 0;
317 
318  virtual bool can_loop () const
319  {
320  return false;
321  }
322 
323  virtual Location* loop_location () const
324  {
325  return 0;
326  }
327  bool has_loop () const
328  {
329  return loop_location () != 0;
330  }
331 
332  SyncSource type () const
333  {
334  return _type;
335  }
337  {
338  switch (_type) {
339  case Engine: /* also JACK */
340  return TRS_Engine;
341  case MTC:
342  return TRS_MTC;
343  case LTC:
344  return TRS_LTC;
345  case MIDIClock:
346  break;
347  }
348  return TRS_MIDIClock;
349  }
350 
351  std::string name () const
352  {
353  return _name;
354  }
355  void set_name (std::string const&);
356 
357  int set_state (XMLNode const&, int);
358  XMLNode& get_state () const;
359 
360  static const std::string state_node_name;
361  static void make_property_quarks ();
362 
363  virtual void set_session (Session*);
364 
365  std::shared_ptr<Port> port () const
366  {
367  return _port;
368  }
369 
370  bool check_collect ();
371  virtual void set_collect (bool);
372  bool collect () const
373  {
374  return _collect;
375  }
376 
377  /* called whenever the manager starts collecting (processing) this
378  transport master. Typically will re-initialize any state used to
379  deal with incoming data.
380  */
381  virtual void init () = 0;
382 
383  virtual void check_backend () {}
385  std::string allowed_request_string () const;
386 
388  {
389  return _request_mask;
390  }
392 
393  /* this is set at construction, and not changeable later, so it is not
394  * a property
395  */
396 
397  bool removeable () const
398  {
399  return _removeable;
400  }
401  void set_removeable (bool yn)
402  {
403  _removeable = yn;
404  }
405 
406  std::string display_name (bool sh /*ort*/ = true) const;
407 
408  virtual void unregister_port ();
410  virtual void create_port () = 0;
411 
412 protected:
419  PBD::Property<TransportRequestType> _request_mask; /* lists transport requests still accepted when we're in control */
423 
425 
426  /* DLL - chase incoming data */
427 
430 
431  double t0;
432  double t1;
433  double e2;
434  double b, c;
435 
436  std::shared_ptr<Port> _port;
437 
439 
440  virtual void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string name1, std::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
441 
444 
445  virtual void register_properties ();
446 
447  virtual std::string format_delta_time (sampleoffset_t) const;
448 };
449 
454 {
455 public:
457 
458  MIDI::Parser& transport_parser () { return parser; }
459  std::shared_ptr<MidiPort> midi_port () const
460  {
461  return _midi_port;
462  }
463  std::shared_ptr<Port> create_midi_port (std::string const& port_name);
464 
465  virtual void set_session (Session*);
466 
467 protected:
469 
470  void resync_latency (bool);
472  std::shared_ptr<MidiPort> _midi_port;
473 
474  virtual void parameter_changed (std::string const& p) {}
475 
477 
478 private:
480 };
481 
483 {
484 public:
485  TimecodeTransportMaster (std::string const& name, SyncSource type);
486 
488 
490  return timecode_format_valid;
491  }
492 
493  bool fr2997 () const
494  {
495  return _fr2997;
496  }
497  void set_fr2997 (bool);
498 
499 protected:
501 
505 
506 private:
508 };
509 
511 {
512 public:
513  MTC_TransportMaster (std::string const&);
515 
517 
518  void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
519 
521 
522  void reset (bool with_pos);
523  bool locked () const;
524  bool ok () const;
525  void handle_locate (const MIDI::byte*);
526 
529  bool requires_seekahead () const
530  {
531  return false;
532  }
534  void init ();
535 
537  std::string position_string () const;
538  std::string delta_string () const;
539 
540  void create_port ();
541 
542 private:
545 
546  static const int sample_tolerance;
547 
548  samplepos_t mtc_frame; /* current time */
550  samplepos_t last_inbound_frame; /* when we got it; audio clocked */
555 
556  Glib::Threads::Mutex reset_lock;
557  uint32_t reset_pending;
562 
566 
567  void queue_reset (bool with_pos);
568  void maybe_reset ();
569 
571  void update_mtc_time (const MIDI::byte*, bool, samplepos_t);
575  void init_mtc_dll (samplepos_t, double);
577  void parameter_changed (std::string const& p);
578 
579  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
580 };
581 
583 {
584 public:
585  LTC_TransportMaster (std::string const&);
587 
589 
590  void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
591 
592  void reset (bool with_pos);
593  bool locked () const;
594  bool ok () const;
595 
598  bool requires_seekahead () const
599  {
600  return false;
601  }
603  {
604  return 0;
605  }
606  void init ();
608 
610  std::string position_string () const;
611  std::string delta_string () const;
612 
613  void create_port ();
614 
615 private:
616  void parse_ltc (const pframes_t, Sample const*, samplecnt_t);
617  void process_ltc (samplepos_t const);
618  void init_dll (samplepos_t, int32_t);
619  bool detect_discontinuity (LTCFrameExt*, int, bool);
620  bool detect_ltc_fps (int, bool);
622  void resync_xrun ();
623  void resync_latency (bool);
625  void parameter_changed (std::string const& p);
626  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
627 
633 
637 
642 
646 
647 
648  struct Biquad {
649  void reset () { z1 = z2 = 0;}
650  float z1, z2;
651  double a1, a2, b0, b1, b2;
652  };
653 
657 };
658 
660 {
661 public:
662  MIDIClock_TransportMaster (std::string const& name, int ppqn = 24);
663 
666 
668 
670 
671  void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
672 
673  void rebind (MidiPort&);
674 
675  void reset (bool with_pos);
676  bool locked () const;
677  bool ok () const;
678 
681  bool requires_seekahead () const
682  {
683  return false;
684  }
685  void init ();
686 
687  std::string position_string () const;
688  std::string delta_string () const;
689 
690  float bpm () const
691  {
692  return _bpm;
693  }
694 
695  void create_port ();
696 
697 protected:
699 
701  int ppqn;
702 
705 
708 
712 
716 
718 
719  double _speed;
720  bool _running;
721  double _bpm;
722 
723  void start (MIDI::Parser& parser, samplepos_t timestamp);
724  void contineu (MIDI::Parser& parser, samplepos_t timestamp); // we can't use continue because it is a C++ keyword
725  void stop (MIDI::Parser& parser, samplepos_t timestamp);
726  void position (MIDI::Parser& parser, MIDI::byte* message, size_t size, samplepos_t timestamp);
727 
729  samplepos_t calculate_song_position (uint16_t song_position_in_sixteenth_notes);
730  void calculate_filter_coefficients (double qpm);
731  void update_midi_clock (MIDI::Parser& parser, samplepos_t timestamp);
732 
733  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
734 };
735 
737 {
738 public:
741 
742  void pre_process (pframes_t nframes, samplepos_t now, boost::optional<samplepos_t>);
744 
745  bool starting () const
746  {
747  return _starting;
748  }
749  void reset (bool with_position);
750  bool locked () const;
751  bool ok () const;
752  bool usable () const;
755  {
756  return 1;
757  }
758  bool requires_seekahead () const
759  {
760  return false;
761  }
762  bool sample_clock_synced () const
763  {
764  return true;
765  }
766  void init ();
767  void check_backend ();
769 
770  std::string position_string () const;
771  std::string delta_string () const;
772 
773  void create_port () {}
774 
775 private:
777  bool _starting;
778 };
779 
780 }
781 
782 #endif /* __ardour_transport_master_h__ */
samplecnt_t update_interval() const
void reset(bool with_position)
std::string delta_string() const
std::string position_string() const
Engine_TransportMaster(AudioEngine &)
void pre_process(pframes_t nframes, samplepos_t now, boost::optional< samplepos_t >)
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 pre_process(pframes_t nframes, samplepos_t now, boost::optional< samplepos_t >)
void set_session(Session *)
Timecode::TimecodeFormat apparent_timecode_format() const
PBD::ScopedConnectionList session_connections
void parse_ltc(const pframes_t, Sample const *, samplecnt_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 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 pre_process(pframes_t nframes, samplepos_t now, boost::optional< samplepos_t >)
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, boost::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
Glib::Threads::Mutex reset_lock
void update_mtc_qtr(MIDI::Parser &, int, samplepos_t)
samplecnt_t update_interval() const
static const int sample_tolerance
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 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 pre_process(pframes_t nframes, samplepos_t now, boost::optional< samplepos_t >)=0
virtual void set_sample_clock_synced(bool)
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
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:1413
DebugBits Properties
TimecodeFormat
Definition: time.h:39
void safe_read(SafeTime &dst) const
void update(samplepos_t p, samplepos_t t, double s)
boost::atomic< int > guard1
samplepos_t position
boost::atomic< int > guard2
samplepos_t timestamp
Definition: ltc.h:218