summaryrefslogtreecommitdiff
path: root/nutemp/t.h
diff options
context:
space:
mode:
Diffstat (limited to 'nutemp/t.h')
-rw-r--r--nutemp/t.h402
1 files changed, 402 insertions, 0 deletions
diff --git a/nutemp/t.h b/nutemp/t.h
new file mode 100644
index 0000000000..645893f51c
--- /dev/null
+++ b/nutemp/t.h
@@ -0,0 +1,402 @@
+#ifndef __ardour_tempo_h__
+#define __ardour_tempo_h__
+
+#include <list>
+#include <string>
+#include <vector>
+#include <cmath>
+#include <exception>
+
+#include <glibmm/threads.h>
+
+#include "evoral/Beats.hpp"
+
+#include "ardour/ardour.h"
+#include "ardour/superclock.h"
+
+#include "timecode/bbt_time.h"
+
+namespace ARDOUR {
+
+class Meter;
+class TempoMap;
+
+/** Tempo, the speed at which musical time progresses (BPM).
+ */
+
+class LIBARDOUR_API Tempo {
+ public:
+ /**
+ * @param npm Note Types per minute
+ * @param type Note Type (default `4': quarter note)
+ */
+ Tempo (double npm, int type = 4) : _superclocks_per_note_type (double_npm_to_sc (npm)), _note_type (type) {}
+
+ /* these two methods should only be used to show and collect information to the user (for whom
+ * bpm as a floating point number is the obvious representation)
+ */
+ double note_types_per_minute () const { return (superclock_ticks_per_second * 60.0) / _superclocks_per_note_type; }
+ void set_note_types_per_minute (double npm) { _superclocks_per_note_type = double_npm_to_sc (npm); }
+
+ int note_type () const { return _note_type; }
+
+ superclock_t superclocks_per_note_type () const {
+ return _superclocks_per_note_type;
+ }
+ superclock_t superclocks_per_note_type (int note_type) const {
+ return (_superclocks_per_note_type * _note_type) / note_type;
+ }
+ superclock_t superclocks_per_quarter_note () const {
+ return superclocks_per_note_type (4);
+ }
+
+ Tempo& operator=(Tempo const& other) {
+ if (&other != this) {
+ _superclocks_per_note_type = other._superclocks_per_note_type;
+ _note_type = other._note_type;
+ }
+ return *this;
+ }
+
+ protected:
+ superclock_t _superclocks_per_note_type;
+ int8_t _note_type;
+
+ static inline double sc_to_double_npm (superclock_t sc) { return (superclock_ticks_per_second * 60.0) / sc; }
+ static inline superclock_t double_npm_to_sc (double npm) { return llrint ((superclock_ticks_per_second / npm) * 60.0); }
+};
+
+/** Meter, or time signature (subdivisions per bar, and which note type is a single subdivision). */
+class LIBARDOUR_API Meter {
+ public:
+ Meter (int8_t dpb, int8_t nv) : _note_value (nv), _divisions_per_bar (dpb) {}
+
+ int divisions_per_bar () const { return _divisions_per_bar; }
+ int note_value() const { return _note_value; }
+
+ inline bool operator==(const Meter& other) { return _divisions_per_bar == other.divisions_per_bar() && _note_value == other.note_value(); }
+ inline bool operator!=(const Meter& other) { return _divisions_per_bar != other.divisions_per_bar() || _note_value != other.note_value(); }
+
+ Meter& operator=(Meter const & other) {
+ if (&other != this) {
+ _divisions_per_bar = other._divisions_per_bar;
+ _note_value = other._note_value;
+ }
+ return *this;
+ }
+
+ Timecode::BBT_Time bbt_add (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & add) const;
+ Timecode::BBT_Time bbt_subtract (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & sub) const;
+ Timecode::BBT_Offset bbt_delta (Timecode::BBT_Time const & bbt, Timecode::BBT_Time const & sub) const;
+
+ Timecode::BBT_Time round_up_to_bar (Timecode::BBT_Time const &) const;
+ Timecode::BBT_Time round_down_to_bar (Timecode::BBT_Time const &) const;
+ Timecode::BBT_Time round_to_bar (Timecode::BBT_Time const &) const;
+
+ Evoral::Beats to_quarters (Timecode::BBT_Offset const &) const;
+
+ protected:
+ /** The type of "note" that a division represents. For example, 4 is
+ a quarter (crotchet) note, 8 is an eighth (quaver) note, etc.
+ */
+ int8_t _note_value;
+ /* how many of '_note_value' make up a bar or measure */
+ int8_t _divisions_per_bar;
+};
+
+/** Helper class to keep track of the Meter *AND* Tempo in effect
+ at a given point in time.
+*/
+class LIBARDOUR_API TempoMetric : public Tempo, public Meter {
+ public:
+ TempoMetric (Tempo const & t, Meter const & m, bool ramp) : Tempo (t), Meter (m), _c_per_quarter (0.0), _c_per_superclock (0.0), _ramped (ramp) {}
+ ~TempoMetric () {}
+
+ double c_per_superclock () const { return _c_per_superclock; }
+ double c_per_quarter () const { return _c_per_quarter; }
+
+ void compute_c_superclock (framecnt_t sr, superclock_t end_superclocks_per_note_type, superclock_t duration);
+ void compute_c_quarters (framecnt_t sr, superclock_t end_superclocks_per_note_type, Evoral::Beats const & duration);
+
+ superclock_t superclocks_per_bar (framecnt_t sr) const;
+ superclock_t superclocks_per_grid (framecnt_t sr) const;
+
+ superclock_t superclock_at_qn (Evoral::Beats const & qn) const;
+ superclock_t superclock_per_note_type_at_superclock (superclock_t) const;
+
+ bool ramped () const { return _ramped; }
+ void set_ramped (bool yn) { _ramped = yn; } /* caller must mark something dirty to force recompute */
+
+ private:
+ double _c_per_quarter;
+ double _c_per_superclock;
+ bool _ramped;
+};
+
+/** Tempo Map - mapping of timecode to musical time.
+ * convert audio-samples, sample-rate to Bar/Beat/Tick, Meter/Tempo
+ */
+
+/* TempoMap concepts
+
+ we have several different ways of talking about time:
+
+ * PULSE : whole notes, just because. These are linearly related to any other
+ note type, so if you know a number of pulses (whole notes), you
+ know the corresponding number of any other note type (e.g. quarter
+ notes).
+
+ * QUARTER NOTES : just what the name says. A lot of MIDI software and
+ concepts assume that a "beat" is a quarter-note.
+
+ * BEAT : a fraction of a PULSE. Defined by the meter in effect, so requires
+ meter (time signature) information to convert to/from PULSE or QUARTER NOTES.
+ In a 5/8 time, a BEAT is 1/8th note. In a 4/4 time, a beat is quarter note.
+ This means that measuring time in BEATS is potentially non-linear (if
+ the time signature changes, there will be a different number of BEATS
+ corresponding to a given time in any other unit).
+
+ * SUPERCLOCK : a very high resolution clock whose frequency
+ has as factors all common sample rates and all common note
+ type divisors. Related to MINUTES or SAMPLES only when a
+ sample rate is known. Related to PULSE or QUARTER NOTES only
+ when a tempo is known.
+
+ * MINUTES : wallclock time measurement. related to SAMPLES or SUPERCLOCK
+ only when a sample rate is known.
+
+
+ * SAMPLES : audio time measurement. Related to MINUTES or SUPERCLOCK only
+ when a sample rate is known
+
+ * BBT : bars|beats|ticks ... linearly related to BEATS but with the added
+ semantics of bars ("measures") added, in which beats are broken up
+ into groups of bars ("measures"). Requires meter (time signature)
+ information to compute to/from a given BEATS value. Contains no
+ additional time information compared to BEATS, but does have
+ additional semantic information.
+
+ Nick sez: not every note onset is on a tick
+ Paul wonders: if it's 8 samples off, does it matter?
+ Nick sez: it should not phase with existing audio
+
+ */
+
+class LIBARDOUR_API TempoMapPoint
+{
+ public:
+ enum Flag {
+ ExplicitTempo = 0x1,
+ ExplicitMeter = 0x2,
+ };
+
+ TempoMapPoint (Flag f, Tempo const& t, Meter const& m, superclock_t sc, Evoral::Beats const & q, Timecode::BBT_Time const & bbt, PositionLockStyle psl, bool ramp = false)
+ : _flags (f), _explicit (t, m, psl, ramp), _sclock (sc), _quarters (q), _bbt (bbt), _dirty (true) {}
+ TempoMapPoint (TempoMapPoint const & tmp, superclock_t sc, Evoral::Beats const & q, Timecode::BBT_Time const & bbt)
+ : _flags (Flag (0)), _reference (&tmp), _sclock (sc), _quarters (q), _bbt (bbt), _dirty (true) {}
+ ~TempoMapPoint () {}
+
+ bool is_explicit() const { return _flags != Flag (0); }
+ bool is_implicit() const { return _flags == Flag (0); }
+
+ superclock_t superclocks_per_note_type (int8_t note_type) const {
+ if (is_explicit()) {
+ return _explicit.metric.superclocks_per_note_type (note_type);
+ }
+ return _reference->superclocks_per_note_type (note_type);
+ }
+
+ struct BadTempoMetricLookup : public std::exception {
+ virtual const char* what() const throw() { return "cannot obtain non-const Metric from implicit map point"; }
+ };
+
+ bool dirty() const { return _dirty; }
+
+ superclock_t sclock() const { return _sclock; }
+ Evoral::Beats const & quarters() const { return _quarters; }
+ Timecode::BBT_Time const & bbt() const { return _bbt; }
+ bool ramped() const { return metric().ramped(); }
+ TempoMetric const & metric() const { return is_explicit() ? _explicit.metric : _reference->metric(); }
+ /* Implicit points are not allowed to return non-const references to their reference metric */
+ TempoMetric & metric() { if (is_explicit()) { return _explicit.metric; } throw BadTempoMetricLookup(); }
+ PositionLockStyle lock_style() const { return is_explicit() ? _explicit.lock_style : _reference->lock_style(); }
+
+ /* None of these properties can be set for an Implicit point, because
+ * they are determined by the TempoMapPoint pointed to by _reference.
+ */
+
+ void set_sclock (superclock_t sc) { if (is_explicit()) { _sclock = sc; _dirty = true; } }
+ void set_quarters (Evoral::Beats const & q) { if (is_explicit()) { _quarters = q; _dirty = true; } }
+ void set_bbt (Timecode::BBT_Time const & bbt) { if (is_explicit()) { _bbt = bbt; _dirty = true; } }
+ void set_dirty (bool yn) { if (is_explicit()) { _dirty = yn; } }
+ void set_lock_style (PositionLockStyle psl) { if (is_explicit()) { _explicit.lock_style = psl; _dirty = true; } }
+
+ void make_explicit (Flag f) {
+ _flags = Flag (_flags|f);
+ /* since _metric and _reference are part of an anonymous union,
+ avoid possible compiler glitches by copying to a stack
+ variable first, then assign.
+ */
+ TempoMetric tm (_explicit.metric);
+ _explicit.metric = tm;
+ _dirty = true;
+ }
+
+ void make_implicit (TempoMapPoint & tmp) { _flags = Flag (0); _reference = &tmp; }
+
+ Evoral::Beats quarters_at (superclock_t sc) const;
+ Evoral::Beats quarters_at (Timecode::BBT_Time const &) const;
+
+ Timecode::BBT_Time bbt_at (Evoral::Beats const &) const;
+
+#if 0
+ XMLNode& get_state() const;
+ int set_state (XMLNode const&, int version);
+#endif
+
+ struct SuperClockComparator {
+ bool operator() (TempoMapPoint const & a, TempoMapPoint const & b) const { return a.sclock() < b.sclock(); }
+ };
+
+ struct QuarterComparator {
+ bool operator() (TempoMapPoint const & a, TempoMapPoint const & b) const { return a.quarters() < b.quarters(); }
+ };
+
+ struct BBTComparator {
+ bool operator() (TempoMapPoint const & a, TempoMapPoint const & b) const { return a.bbt() < b.bbt(); }
+ };
+
+ protected:
+ friend class TempoMap;
+ void map_reset_set_sclock_for_sr_change (superclock_t sc) { _sclock = sc; }
+
+ private:
+ struct ExplicitInfo {
+ ExplicitInfo (Tempo const & t, Meter const & m, PositionLockStyle psl, bool ramp) : metric (t, m, ramp), lock_style (psl) {}
+
+ TempoMetric metric;
+ PositionLockStyle lock_style;
+ };
+
+ Flag _flags;
+ union {
+ TempoMapPoint const * _reference;
+ ExplicitInfo _explicit;
+ };
+ superclock_t _sclock;
+ Evoral::Beats _quarters;
+ Timecode::BBT_Time _bbt;
+ bool _dirty;
+};
+
+typedef std::list<TempoMapPoint> TempoMapPoints;
+
+class LIBARDOUR_API TempoMap
+{
+ public:
+ TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, framecnt_t sr);
+
+ void set_sample_rate (framecnt_t sr);
+ framecnt_t sample_rate() const { return _sample_rate; }
+
+ void remove_explicit_point (superclock_t);
+
+ void move_implicit (superclock_t current, superclock_t destination);
+ void move_explicit (superclock_t current, superclock_t destination);
+
+ //bool set_tempo_at (Tempo const &, Evoral::Beats const &, PositionLockStyle psl, bool ramp = false);
+ bool set_tempo (Tempo const &, Timecode::BBT_Time const &, bool ramp = false);
+ bool set_tempo (Tempo const &, superclock_t, bool ramp = false);
+
+ //bool set_meter_at (Meter const &, Evoral::Beats const &);
+
+ bool set_meter (Meter const &, Timecode::BBT_Time const &);
+ bool set_meter (Meter const &, superclock_t);
+
+ Meter const & meter_at (superclock_t sc) const;
+ Meter const & meter_at (Evoral::Beats const & b) const;
+ Meter const & meter_at (Timecode::BBT_Time const & bbt) const;
+ Tempo const & tempo_at (superclock_t sc) const;
+ Tempo const & tempo_at (Evoral::Beats const &b) const;
+ Tempo const & tempo_at (Timecode::BBT_Time const & bbt) const;
+
+ Timecode::BBT_Time bbt_at (superclock_t sc) const;
+ Timecode::BBT_Time bbt_at (Evoral::Beats const &) const;
+ Evoral::Beats quarter_note_at (superclock_t sc) const;
+ Evoral::Beats quarter_note_at (Timecode::BBT_Time const &) const;
+ superclock_t superclock_at (Evoral::Beats const &) const;
+ superclock_t superclock_at (Timecode::BBT_Time const &) const;
+
+ struct EmptyTempoMapException : public std::exception {
+ virtual const char* what() const throw() { return "TempoMap is empty"; }
+ };
+
+ void dump (std::ostream&);
+ void rebuild (superclock_t limit);
+
+ private:
+ TempoMapPoints _points;
+ framecnt_t _sample_rate;
+ mutable Glib::Threads::RWLock _lock;
+
+ /* these return an iterator that refers to the TempoMapPoint at or most immediately preceding the given position.
+ *
+ * Conceptually, these could be const methods, but C++ prevents them returning a non-const iterator in that case.
+ *
+ * Note that they cannot return an invalid iterator (e.g. _points.end()) because:
+ *
+ * - if the map is empty, an exception is thrown
+ * - if the given time is before the first map entry, _points.begin() is returned
+ * - if the given time is after the last map entry, the equivalent of _points.rbegin() is returned
+ * - if the given time is within the map entries, a valid iterator will be returned
+ */
+
+ TempoMapPoints::iterator iterator_at (superclock_t sc);
+ TempoMapPoints::iterator iterator_at (Evoral::Beats const &);
+ TempoMapPoints::iterator iterator_at (Timecode::BBT_Time const &);
+
+ TempoMapPoints::const_iterator const_iterator_at (superclock_t sc) const { return const_cast<TempoMap*>(this)->iterator_at (sc); }
+ TempoMapPoints::const_iterator const_iterator_at (Evoral::Beats const & b) const { return const_cast<TempoMap*>(this)->iterator_at (b); }
+ TempoMapPoints::const_iterator const_iterator_at (Timecode::BBT_Time const & bbt) const { return const_cast<TempoMap*>(this)->iterator_at (bbt); }
+
+ /* Returns the TempoMapPoint at or most immediately preceding the given time. If the given time is
+ * before the first map entry, then the first map entry will be returned, which underlies the semantics
+ * that the first map entry's values propagate backwards in time if not at absolute zero.
+ *
+ * As for iterator_at(), define both const+const and non-const variants, because C++ won't let us return a non-const iterator
+ from a const method (which is a bit silly, but presumably aids compiler reasoning).
+ */
+
+ TempoMapPoint & point_at (superclock_t sc) { return *iterator_at (sc); }
+ TempoMapPoint & point_at (Evoral::Beats const & b) { return *iterator_at (b); }
+ TempoMapPoint & point_at (Timecode::BBT_Time const & bbt) { return *iterator_at (bbt); }
+
+ TempoMapPoint const & const_point_at (superclock_t sc) const { return *const_iterator_at (sc); }
+ TempoMapPoint const & const_point_at (Evoral::Beats const & b) const { return *const_iterator_at (b); }
+ TempoMapPoint const & const_point_at (Timecode::BBT_Time const & bbt) const { return *const_iterator_at (bbt); }
+
+ Meter const & meter_at_locked (superclock_t sc) const { return const_point_at (sc).metric(); }
+ Meter const & meter_at_locked (Evoral::Beats const & b) const { return const_point_at (b).metric(); }
+ Meter const & meter_at_locked (Timecode::BBT_Time const & bbt) const { return const_point_at (bbt).metric(); }
+ Tempo const & tempo_at_locked (superclock_t sc) const { return const_point_at (sc).metric(); }
+ Tempo const & tempo_at_locked (Evoral::Beats const &b) const { return const_point_at (b).metric(); }
+ Tempo const & tempo_at_locked (Timecode::BBT_Time const & bbt) const { return const_point_at (bbt).metric(); }
+ Timecode::BBT_Time bbt_at_locked (superclock_t sc) const;
+ Timecode::BBT_Time bbt_at_locked (Evoral::Beats const &) const;
+ Evoral::Beats quarter_note_at_locked (superclock_t sc) const;
+ Evoral::Beats quarter_note_at_locked (Timecode::BBT_Time const &) const;
+ superclock_t superclock_at_locked (Evoral::Beats const &) const;
+ superclock_t superclock_at_locked (Timecode::BBT_Time const &) const;
+
+ void move_explicit_to (TempoMapPoints::iterator, superclock_t destination);
+
+ void rebuild_locked (superclock_t limit);
+};
+
+}
+
+std::ostream& operator<<(std::ostream&, ARDOUR::TempoMapPoint const &);
+std::ostream& operator<<(std::ostream&, ARDOUR::Tempo const &);
+std::ostream& operator<<(std::ostream&, ARDOUR::Meter const &);
+
+#endif /* __ardour_tempo_h__ */