Ardour  9.0-pre0-582-g084a23a80d
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/threads.h>
26 #include <glibmm/timer.h>
27 
28 #include <ltc.h>
29 
30 #include "pbd/properties.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  virtual bool can_loop () const
317  {
318  return false;
319  }
320 
321  virtual Location* loop_location () const
322  {
323  return 0;
324  }
325  bool has_loop () const
326  {
327  return loop_location () != 0;
328  }
329 
330  SyncSource type () const
331  {
332  return _type;
333  }
335  {
336  switch (_type) {
337  case Engine: /* also JACK */
338  return TRS_Engine;
339  case MTC:
340  return TRS_MTC;
341  case LTC:
342  return TRS_LTC;
343  case MIDIClock:
344  break;
345  }
346  return TRS_MIDIClock;
347  }
348 
349  std::string name () const
350  {
351  return _name;
352  }
353  void set_name (std::string const&);
354 
355  int set_state (XMLNode const&, int);
356  XMLNode& get_state () const;
357 
358  static const std::string state_node_name;
359  static void make_property_quarks ();
360 
361  virtual void set_session (Session*);
362 
363  std::shared_ptr<Port> port () const
364  {
365  return _port;
366  }
367 
368  bool check_collect ();
369  virtual void set_collect (bool);
370  bool collect () const
371  {
372  return _collect;
373  }
374 
375  /* called whenever the manager starts collecting (processing) this
376  transport master. Typically will re-initialize any state used to
377  deal with incoming data.
378  */
379  virtual void init () = 0;
380 
381  virtual void check_backend () {}
383  std::string allowed_request_string () const;
384 
386  {
387  return _request_mask;
388  }
390 
391  /* this is set at construction, and not changeable later, so it is not
392  * a property
393  */
394 
395  bool removeable () const
396  {
397  return _removeable;
398  }
399  void set_removeable (bool yn)
400  {
401  _removeable = yn;
402  }
403 
404  std::string display_name (bool sh /*ort*/ = true) const;
405 
406  virtual void unregister_port ();
408  virtual void create_port () = 0;
409 
410 protected:
417  PBD::Property<TransportRequestType> _request_mask; /* lists transport requests still accepted when we're in control */
421 
423 
424  /* DLL - chase incoming data */
425 
428 
429  double t0;
430  double t1;
431  double e2;
432  double b, c;
433 
434  std::shared_ptr<Port> _port;
435 
437 
438  virtual void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string name1, std::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
439 
442 
443  virtual void register_properties ();
444 
445  virtual std::string format_delta_time (sampleoffset_t) const;
446 };
447 
452 {
453 public:
455 
456  MIDI::Parser& transport_parser () { return parser; }
457  std::shared_ptr<MidiPort> midi_port () const
458  {
459  return _midi_port;
460  }
461  std::shared_ptr<Port> create_midi_port (std::string const& port_name);
462 
463  virtual void set_session (Session*);
464 
465 protected:
467 
468  void resync_latency (bool);
470  std::shared_ptr<MidiPort> _midi_port;
471 
472  virtual void parameter_changed (std::string const& p) {}
473 
475 
476 private:
478 };
479 
481 {
482 public:
483  TimecodeTransportMaster (std::string const& name, SyncSource type);
484 
486 
488  return timecode_format_valid;
489  }
490 
491  bool fr2997 () const
492  {
493  return _fr2997;
494  }
495  void set_fr2997 (bool);
496 
497 protected:
499 
503 
504 private:
506 };
507 
509 {
510 public:
511  MTC_TransportMaster (std::string const&);
513 
515 
516  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
517 
519 
520  void reset (bool with_pos);
521  bool locked () const;
522  bool ok () const;
523  void handle_locate (const MIDI::byte*);
524 
527  bool requires_seekahead () const
528  {
529  return false;
530  }
532  void init ();
533 
535  std::string position_string () const;
536  std::string delta_string () const;
537 
538  void create_port ();
539 
540 private:
543 
544  static const int sample_tolerance;
545 
546  samplepos_t mtc_frame; /* current time */
548  samplepos_t last_inbound_frame; /* when we got it; audio clocked */
553 
554  Glib::Threads::Mutex reset_lock;
555  uint32_t reset_pending;
560 
564 
565  void queue_reset (bool with_pos);
566  void maybe_reset ();
567 
569  void update_mtc_time (const MIDI::byte*, bool, samplepos_t);
573  void init_mtc_dll (samplepos_t, double);
575  void parameter_changed (std::string const& p);
576 
577  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
578 };
579 
581 {
582 public:
583  LTC_TransportMaster (std::string const&);
585 
587 
588  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
589 
590  void reset (bool with_pos);
591  bool locked () const;
592  bool ok () const;
593 
596  bool requires_seekahead () const
597  {
598  return false;
599  }
601  {
602  return 0;
603  }
604  void init ();
606 
608  std::string position_string () const;
609  std::string delta_string () const;
610 
611  void create_port ();
612 
613 private:
614  void parse_ltc (const pframes_t, Sample const*, samplecnt_t);
615  void process_ltc (samplepos_t const);
616  void init_dll (samplepos_t, int32_t);
617  bool detect_discontinuity (LTCFrameExt*, int, bool);
618  bool detect_ltc_fps (int, bool);
620  void resync_xrun ();
621  void resync_latency (bool);
623  void parameter_changed (std::string const& p);
624  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
625 
631 
635 
640 
644 
645 
646  struct Biquad {
647  void reset () { z1 = z2 = 0;}
648  float z1, z2;
649  double a1, a2, b0, b1, b2;
650  };
651 
655 };
656 
658 {
659 public:
660  MIDIClock_TransportMaster (std::string const& name, int ppqn = 24);
661 
664 
666 
668 
669  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
670 
671  void rebind (MidiPort&);
672 
673  void reset (bool with_pos);
674  bool locked () const;
675  bool ok () const;
676 
679  bool requires_seekahead () const
680  {
681  return false;
682  }
683  void init ();
684 
685  std::string position_string () const;
686  std::string delta_string () const;
687 
688  float bpm () const
689  {
690  return _bpm;
691  }
692 
693  void create_port ();
694 
695 protected:
697 
699  int ppqn;
700 
703 
706 
710 
714 
716 
717  double _speed;
718  bool _running;
719  double _bpm;
720 
721  void start (MIDI::Parser& parser, samplepos_t timestamp);
722  void contineu (MIDI::Parser& parser, samplepos_t timestamp); // we can't use continue because it is a C++ keyword
723  void stop (MIDI::Parser& parser, samplepos_t timestamp);
724  void position (MIDI::Parser& parser, MIDI::byte* message, size_t size, samplepos_t timestamp);
725 
727  samplepos_t calculate_song_position (uint16_t song_position_in_sixteenth_notes);
728  void calculate_filter_coefficients (double qpm);
729  void update_midi_clock (MIDI::Parser& parser, samplepos_t timestamp);
730 
731  void connection_handler (std::weak_ptr<ARDOUR::Port>, std::string, std::weak_ptr<ARDOUR::Port>, std::string, bool);
732 };
733 
735 {
736 public:
739 
740  void pre_process (pframes_t nframes, samplepos_t now, std::optional<samplepos_t>);
742 
743  bool starting () const
744  {
745  return _starting;
746  }
747  void reset (bool with_position);
748  bool locked () const;
749  bool ok () const;
750  bool usable () const;
753  {
754  return 1;
755  }
756  bool requires_seekahead () const
757  {
758  return false;
759  }
760  bool sample_clock_synced () const
761  {
762  return true;
763  }
764  void init ();
765  void check_backend ();
767 
768  std::string position_string () const;
769  std::string delta_string () const;
770 
771  void create_port () {}
772 
773 private:
775  bool _starting;
776 };
777 
778 }
779 
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 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
Glib::Threads::Mutex reset_lock
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)
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:1349
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