From 16e9b5aaca06351f5db5fe3374f36c0923ecf3ba Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 7 Sep 2022 18:11:07 -0600 Subject: [PATCH] remove useless directory --- nutemp/makefile | 19 - nutemp/t | 0 nutemp/t.cc | 1406 ----------------------------------------------- nutemp/t.h | 429 --------------- 4 files changed, 1854 deletions(-) delete mode 100644 nutemp/makefile delete mode 100644 nutemp/t delete mode 100644 nutemp/t.cc delete mode 100644 nutemp/t.h diff --git a/nutemp/makefile b/nutemp/makefile deleted file mode 100644 index 82d1a9884c..0000000000 --- a/nutemp/makefile +++ /dev/null @@ -1,19 +0,0 @@ - -AD = .. -CXXFLAGS = -g -CPPFLAGS = -I $(AD)/libs/ardour -I $(AD)/libs/evoral -I $(AD)/libs/pbd -I $(AD)/build/libs/pbd -I $(AD)/libs/temporal -CPPFLAGS += `pkg-config --cflags glibmm-2.4` `pkg-config --cflags libxml-2.0` -LDFLAGS = -L $(AD)/build/libs/evoral -levoral -Xlinker -rpath=$(AD)/build/libs/evoral -LDFLAGS += -L $(AD)/build/libs/pbd -lpbd -Xlinker -rpath=$(AD)/build/libs/pbd -LDFLAGS += -L $(AD)/build/libs/temporal -ltemporal -Xlinker -rpath=$(AD)/build/libs/temporal -LDFLAGS += `pkg-config --cflags --libs glibmm-2.4` `pkg-config --cflags --libs libxml-2.0` - -all: t - -t.o: t.cc t.h - -t: t.o - $(CXX) $(LDFLAGS) -o $@ $? - -clean: - rm -f t t.o diff --git a/nutemp/t b/nutemp/t deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/nutemp/t.cc b/nutemp/t.cc deleted file mode 100644 index b7bdab8f88..0000000000 --- a/nutemp/t.cc +++ /dev/null @@ -1,1406 +0,0 @@ -#include "t.h" - -using namespace ARDOUR; -using std::cerr; -using std::cout; -using std::endl; - -/* overloaded operator* that avoids floating point math when multiplying a superclock position by a number of quarter notes */ -superclock_t operator*(superclock_t sc, Temporal::Beats const & b) { return (sc * ((b.get_beats() * Temporal::Beats::PPQN) + b.get_ticks())) / Temporal::Beats::PPQN; } - -Timecode::BBT_Time -Meter::bbt_add (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & add) const -{ - int32_t bars = bbt.bars; - int32_t beats = bbt.beats; - int32_t ticks = bbt.ticks; - - if ((bars ^ add.bars) < 0) { - /* signed-ness varies */ - if (abs(add.bars) >= abs(bars)) { - /* addition will change which side of "zero" the answer is on; - adjust bbt.bars towards zero to deal with "unusual" BBT math - */ - if (bars < 0) { - bars++; - } else { - bars--; - } - } - } - - if ((beats ^ add.beats) < 0) { - /* signed-ness varies */ - if (abs (add.beats) >= abs (beats)) { - /* adjust bbt.beats towards zero to deal with "unusual" BBT math */ - if (beats < 0) { - beats++; - } else { - beats--; - } - } - } - - Timecode::BBT_Offset r (bars + add.bars, beats + add.beats, ticks + add.ticks); - - if (r.ticks >= Temporal::Beats::PPQN) { - r.beats += r.ticks / Temporal::Beats::PPQN; - r.ticks %= Temporal::Beats::PPQN; - } - - if (r.beats > _divisions_per_bar) { - r.bars += r.beats / _divisions_per_bar; - r.beats %= _divisions_per_bar; - } - - if (r.beats == 0) { - r.beats = 1; - } - - if (r.bars == 0) { - r.bars = 1; - } - - return Timecode::BBT_Time (r.bars, r.beats, r.ticks); -} - -Timecode::BBT_Time -Meter::bbt_subtract (Timecode::BBT_Time const & bbt, Timecode::BBT_Offset const & sub) const -{ - int32_t bars = bbt.bars; - int32_t beats = bbt.beats; - int32_t ticks = bbt.ticks; - - if ((bars ^ sub.bars) < 0) { - /* signed-ness varies */ - if (abs (sub.bars) >= abs (bars)) { - /* adjust bbt.bars towards zero to deal with "unusual" BBT math */ - if (bars < 0) { - bars++; - } else { - bars--; - } - } - } - - if ((beats ^ sub.beats) < 0) { - /* signed-ness varies */ - if (abs (sub.beats) >= abs (beats)) { - /* adjust bbt.beats towards zero to deal with "unusual" BBT math */ - if (beats < 0) { - beats++; - } else { - beats--; - } - } - } - - Timecode::BBT_Offset r (bars - sub.bars, beats - sub.beats, ticks - sub.ticks); - - if (r.ticks < 0) { - r.beats -= 1 - (r.ticks / Temporal::Beats::PPQN); - r.ticks = Temporal::Beats::PPQN + (r.ticks % Temporal::Beats::PPQN); - } - - if (r.beats <= 0) { - r.bars -= 1 - (r.beats / _divisions_per_bar); - r.beats = _divisions_per_bar + (r.beats % _divisions_per_bar); - } - - if (r.beats == 0) { - r.beats = 1; - } - - if (r.bars <= 0) { - r.bars -= 1; - } - - return Timecode::BBT_Time (r.bars, r.beats, r.ticks); -} - -Timecode::BBT_Offset -Meter::bbt_delta (Timecode::BBT_Time const & a, Timecode::BBT_Time const & b) const -{ - return Timecode::BBT_Offset (a.bars - b.bars, a.beats - b.beats, a.ticks - b.ticks); -} - -Timecode::BBT_Time -Meter::round_up_to_bar (Timecode::BBT_Time const & bbt) const -{ - Timecode::BBT_Time b = bbt.round_up_to_beat (); - if (b.beats > 1) { - b.bars++; - b.beats = 1; - } - return b; -} - -Temporal::Beats -Meter::to_quarters (Timecode::BBT_Offset const & offset) const -{ - Temporal::Beats b; - - b += (offset.bars * _divisions_per_bar * 4) / _note_value; - b += (offset.beats * 4) / _note_value; - b += Temporal::Beats::ticks (offset.ticks); - - return b; -} - -superclock_t -TempoMetric::superclock_per_note_type_at_superclock (superclock_t sc) const -{ - return superclocks_per_note_type () * expm1 (_c_per_superclock * sc); -} - -superclock_t -TempoMetric::superclocks_per_grid (samplecnt_t sr) const -{ - return (superclock_ticks_per_second * Meter::note_value()) / (note_types_per_minute() / Tempo::note_type()); -} - -superclock_t -TempoMetric::superclocks_per_bar (samplecnt_t sr) const -{ - return superclocks_per_grid (sr) * _divisions_per_bar; -} - -/* -Ramp Overview - - | * -Tempo | * -Tt----|-----------------*| -Ta----|--------------|* | - | * | | - | * | | - | * | | -T0----|* | | - * | | | - _______________|___|____ - time a t (next tempo) - [ c ] defines c - -Duration in beats at time a is the integral of some Tempo function. -In our case, the Tempo function (Tempo at time t) is -T(t) = T0(e^(ct)) - ->>1/S(t) = (1/S0)(e^ct) => (1/S)(t) = (e^(ct))/S0 => S(t) = S0/(e^(ct)) - -with function constant -c = log(Ta/T0)/a - ->>c = log ((1/Sa)/(1/S0)) / a => c = log (S0/Sa) / a - -so -a = log(Ta/T0)/c - ->>a = log ((1/Ta)/(1/S0) / c => a = log (S0/Sa) / c - -The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is: -b(t) = T0(e^(ct) - 1) / c - ->>b(t) = 1/S0(e^(ct) - 1) / c => b(t) = (e^(ct) - 1) / (c * S0) - -To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be: -t(b) = log((c.b / T0) + 1) / c - ->>t(b) = log((c*b / (1/S0)) + 1) / c => t(b) = log ((c*b * S0) + 1) / c - -The time t at which Tempo T occurs is a as above: -t(T) = log(T / T0) / c - ->> t(1/S) = log ((1/S) / (1/S0) /c => t(1/S) = log (S0/S) / c - -The beat at which a Tempo T occurs is: -b(T) = (T - T0) / c - ->> b(1/S) = (1/S - 1/S0) / c - -The Tempo at which beat b occurs is: -T(b) = b.c + T0 - ->> T(b) = b.c + (1/S0) - -We define c for this tempo ramp by placing a new tempo section at some time t after this one. -Our problem is that we usually don't know t. -We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function. -Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals: -t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1)) - -By substituting our expanded t as a in the c function above, our problem is reduced to: -c = T0 (e^(log (Ta / T0)) - 1) / b - ->> c = (1/S0) (e^(log ((1/Sa) / (1/S0))) - 1) / b => c = (1/S0) (e^(log (S0/Sa)) - 1) / b => c (e^(log (S0/Sa)) - 1) / (b * S0) - -Of course the word 'beat' has been left loosely defined above. -In music, a beat is defined by the musical pulse (which comes from the tempo) -and the meter in use at a particular time (how many pulse divisions there are in one bar). -It would be more accurate to substitute the work 'pulse' for 'beat' above. - - */ - -/* equation to compute c is: - * - * c = log (Ta / T0) / a - * - * where - * - * a : time into section (from section start - * T0 : tempo at start of section - * Ta : tempo at time a into section - * - * THE UNITS QUESTION - * - * log (Ta / T0) / (time-units) => C is in per-time-units (1/time-units) - * - * so the question is what are the units of a, and thus c? - * - * we could use ANY time units (because we can measure a in any time units) - * but whichever one we pick dictates how we can use c in the future since - * all subsequent computations will need to use the same time units. - * - * options: - * - * pulses ... whole notes, possibly useful, since we can use it for any other note_type - * quarter notes ... linearly related to pulses - * beats ... not a fixed unit of time - * minutes ... linearly related to superclocks - * samples ... needs sample rate - * superclocks ... frequently correct - * - * so one answer might be to compute c in two different units so that we have both available. - * - * hence, compute_c_superclocks() and compute_c_pulses() - */ - -void -TempoMetric::compute_c_superclock (samplecnt_t sr, superclock_t end_scpqn, superclock_t superclock_duration) -{ - if ((superclocks_per_quarter_note() == end_scpqn) || !ramped()) { - _c_per_superclock = 0.0; - return; - } - - _c_per_superclock = log ((double) superclocks_per_quarter_note () / end_scpqn) / superclock_duration; -} -void -TempoMetric::compute_c_quarters (samplecnt_t sr, superclock_t end_scpqn, Temporal::Beats const & quarter_duration) -{ - if ((superclocks_per_quarter_note () == end_scpqn) || !ramped()) { - _c_per_quarter = 0.0; - return; - } - - _c_per_quarter = log (superclocks_per_quarter_note () / (double) end_scpqn) / quarter_duration.to_double(); -} - -superclock_t -TempoMetric::superclock_at_qn (Temporal::Beats const & qn) const -{ - if (_c_per_quarter == 0.0) { - /* not ramped, use linear */ - return llrint (superclocks_per_quarter_note () * qn.to_double()); - } - - return llrint (superclocks_per_quarter_note() * (log1p (_c_per_quarter * qn.to_double()) / _c_per_quarter)); -} - -void -TempoMapPoint::set_map (TempoMap* m) -{ - _map = m; -} - -void -TempoMapPoint::set_dirty (bool yn) -{ - if (yn != _dirty) { - _dirty = yn; - if (yn && _map) { - _map->set_dirty (true); - } - } -} - -Temporal::Beats -TempoMapPoint::quarters_at (superclock_t sc) const -{ - /* This TempoMapPoint must already have a fully computed metric and position */ - - if (!ramped()) { - return _quarters + Temporal::Beats ((sc - _sclock) / (double) (metric().superclocks_per_quarter_note ())); - } - - return _quarters + Temporal::Beats (expm1 (metric().c_per_superclock() * (sc - _sclock)) / (metric().c_per_superclock() * metric().superclocks_per_quarter_note ())); -} - -Temporal::Beats -TempoMapPoint::quarters_at (Timecode::BBT_Time const & bbt) const -{ - /* This TempoMapPoint must already have a fully computed metric and position */ - - Timecode::BBT_Offset offset = metric().bbt_delta (bbt, _bbt); - return _quarters + metric().to_quarters (offset); -} - -Timecode::BBT_Time -TempoMapPoint::bbt_at (Temporal::Beats const & qn) const -{ - /* This TempoMapPoint must already have a fully computed metric and position */ - - Temporal::Beats quarters_delta = qn - _quarters; - int32_t ticks_delta = quarters_delta.to_ticks (Temporal::Beats::PPQN); - return metric().bbt_add (_bbt, Timecode::BBT_Offset (0, 0, ticks_delta)); -} - -TempoMap::TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, samplecnt_t sr) - : _sample_rate (sr) - , _dirty (false) -{ - TempoMapPoint tmp (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), initial_tempo, initial_meter, 0, Temporal::Beats(), Timecode::BBT_Time(), AudioTime); - _points.push_back (tmp); -} - -void -TempoMap::set_dirty (bool yn) -{ - _dirty = yn; -} - -Meter const & -TempoMap::meter_at (superclock_t sc) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return meter_at_locked (sc); -} - -Meter const & -TempoMap::meter_at (Temporal::Beats const & b) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return meter_at_locked (b); -} - -Meter const & -TempoMap::meter_at (Timecode::BBT_Time const & bbt) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return meter_at_locked (bbt); -} - -Tempo const & -TempoMap::tempo_at (superclock_t sc) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return tempo_at_locked (sc); -} - -Tempo const & -TempoMap::tempo_at (Temporal::Beats const &b) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return tempo_at_locked (b); -} - -Tempo const & -TempoMap::tempo_at (Timecode::BBT_Time const & bbt) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return tempo_at_locked (bbt); -} - -void -TempoMap::rebuild (superclock_t limit) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - - /* step one: remove all implicit points after a dirty explicit point */ - - restart: - TempoMapPoints::iterator tmp = _points.begin(); - TempoMapPoints::iterator first_explicit_dirty = _points.end(); - - while ((tmp != _points.end()) && (tmp->is_implicit() || (tmp->is_explicit() && !tmp->dirty ()))) { - ++tmp; - } - - first_explicit_dirty = tmp; - - if (first_explicit_dirty == _points.end()) { - /* nothing is dirty */ - return; - } - - /* remove all implicit points, because we're going to recalculate them all */ - - while (tmp != _points.end()) { - TempoMapPoints::iterator next = tmp; - ++next; - - if (tmp->is_implicit()) { - _points.erase (tmp); - } - - tmp = next; - } - - /* compute C-by-quarters for all ramped sections, because we need it shortly */ - - for (tmp = first_explicit_dirty; tmp != _points.end(); ) { - TempoMapPoints::iterator nxt = tmp; - ++nxt; - - if (tmp->ramped() && (nxt != _points.end())) { - tmp->compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters()); - } - - tmp = nxt; - } - - TempoMapPoints::iterator prev = _points.end(); - - /* Compute correct quarter-note and superclock times for all music-time locked explicit points */ - - for (tmp = first_explicit_dirty; tmp != _points.end(); ) { - - TempoMapPoints::iterator next = tmp; - ++next; - - if (prev != _points.end()) { - if ((tmp->lock_style() == MusicTime)) { - /* determine superclock and quarter note time for this (music-time) locked point */ - - Temporal::Beats qn = prev->quarters_at (tmp->bbt()); - superclock_t sc = prev->sclock() + prev->metric().superclock_at_qn (qn - prev->quarters()); - - if (qn != tmp->quarters() || tmp->sclock() != sc) { - tmp->set_quarters (qn); - tmp->set_sclock (sc); - _points.sort (TempoMapPoint::SuperClockComparator()); - goto restart; - } - } - } - - prev = tmp; - tmp = next; - } - - /* _points is guaranteed sorted in superclock and quarter note order. It may not be sorted BBT order because of re-ordering - * of music-time locked points. - */ - - prev = _points.end(); - - /* step two: add new implicit points between each pair of explicit - * points, after the dirty explicit point - */ - - bool hit_dirty = false; - superclock_t first_dirty = 0; - - for (tmp = _points.begin(); tmp != _points.end(); ) { - - if (!hit_dirty) { - if (!tmp->dirty()) { - ++tmp; - continue; - } - hit_dirty = true; - first_dirty = tmp->sclock(); - } - - TempoMapPoints::iterator next = tmp; - ++next; - - - if (prev != _points.end()) { - if ((tmp->lock_style() == AudioTime)) { - /* audio-locked explicit point: recompute it's BBT and quarter-note position since this may have changed */ - tmp->set_quarters (prev->quarters_at (tmp->sclock())); - if (static_cast(tmp->metric()) != static_cast(prev->metric())) { - /* new meter, must be on bar/measure start */ - tmp->set_bbt (prev->metric().round_up_to_bar (prev->bbt_at (tmp->quarters()))); - } else { - /* no meter change, tempo change required to be on beat */ - tmp->set_bbt (prev->bbt_at (tmp->quarters()).round_up_to_beat()); - } - } - } - - superclock_t sc = tmp->sclock(); - Temporal::Beats qn (tmp->quarters ()); - Timecode::BBT_Time bbt (tmp->bbt()); - const bool ramped = tmp->ramped () && next != _points.end(); - - /* Temporal::Beats are really quarter notes. This counts how many quarter notes - there are between grid points in this section of the tempo map. - */ - const Temporal::Beats qn_step = (Temporal::Beats (1) * 4) / tmp->metric().note_value(); - - /* compute implicit points as far as the next explicit point, or limit, - whichever comes first. - */ - - const superclock_t sc_limit = (next == _points.end() ? limit : (*next).sclock()); - - while (1) { - - /* define next beat in superclocks, beats and bbt */ - - qn += qn_step; - bbt = tmp->metric().bbt_add (bbt, Timecode::BBT_Offset (0, 1, 0)); - - if (!ramped) { - sc += tmp->metric().superclocks_per_note_type(); - } else { - sc = tmp->sclock() + tmp->metric().superclock_at_qn (qn - tmp->quarters()); - } - - if (sc >= sc_limit) { - break; - } - - _points.insert (next, TempoMapPoint (*tmp, sc, qn, bbt)); - } - - tmp->set_dirty (false); - prev = tmp; - tmp = next; - } - - Changed (first_dirty, _points.back().sclock()); /* EMIT SIGNAL */ -} - -bool -TempoMap::set_tempo_and_meter (Tempo const & tempo, Meter const & meter, superclock_t sc, bool ramp, bool flexible) -{ - /* CALLER MUST HOLD LOCK */ - - assert (!_points.empty()); - - /* special case: first map entry is later than the new point */ - - if (_points.front().sclock() > sc) { - /* first point is later than sc. There's no iterator to reference a point at or before sc */ - - /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, - even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not - fractional. - */ - - Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat(); - Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); - - _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, b, bbt, AudioTime, ramp)); - return true; - } - - /* special case #3: only one map entry, at the same time as the new point. - This is the common case when editing tempo/meter in a session with a single tempo/meter - */ - - if (_points.size() == 1 && _points.front().sclock() == sc) { - /* change metrics */ - *((Tempo*) &_points.front().metric()) = tempo; - *((Meter*) &_points.front().metric()) = meter; - _points.front().make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter)); - return true; - } - - /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */ - - TempoMapPoints::iterator i = iterator_at (sc); - TempoMapPoints::iterator nxt = i; - ++nxt; - - if (i->sclock() == sc) { - /* change metrics */ - *((Tempo*) &i->metric()) = tempo; - *((Meter*) &i->metric()) = meter; - i->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter)); - /* done */ - return true; - } - - if (!flexible && (sc - i->sclock() < i->metric().superclocks_per_note_type())) { - cerr << "new tempo too close to previous ...\n"; - return false; - } - - TempoMapPoints::iterator e (i); - while (!e->is_explicit() && e != _points.begin()) { - --e; - } - - if (e->metric().ramped()) { - /* need to adjust ramp constants for preceding explicit point, since the new point will be positioned right after it - and thus defines the new ramp distance. - */ - e->compute_c_superclock (_sample_rate, tempo.superclocks_per_quarter_note (), sc); - } - - /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, - even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not - fractional. - */ - - Temporal::Beats qn = i->quarters_at (sc).round_to_beat(); - - /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc" - */ - - Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat (); - - /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before" - */ - - if (i != _points.end()) { - ++i; - } - - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, qn, bbt, AudioTime, ramp)); - return true; -} - -bool -TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - - assert (!_points.empty()); - - /* special case: first map entry is later than the new point */ - - if (_points.front().sclock() > sc) { - /* first point is later than sc. There's no iterator to reference a point at or before sc */ - - /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, - even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not - fractional. - */ - - Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat(); - Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); - - _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, t, _points.front().metric(), sc, b, bbt, AudioTime, ramp)); - return true; - } - - /* special case #3: only one map entry, at the same time as the new point. - This is the common case when editing tempo/meter in a session with a single tempo/meter - */ - - if (_points.size() == 1 && _points.front().sclock() == sc) { - /* change tempo */ - *((Tempo*) &_points.front().metric()) = t; - _points.front().make_explicit (TempoMapPoint::ExplicitTempo); - return true; - } - - /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */ - - TempoMapPoints::iterator i = iterator_at (sc); - TempoMapPoints::iterator nxt = i; - ++nxt; - - if (i->sclock() == sc) { - /* change tempo */ - *((Tempo*) &i->metric()) = t; - i->make_explicit (TempoMapPoint::ExplicitTempo); - /* done */ - return true; - } - - if (sc - i->sclock() < i->metric().superclocks_per_note_type()) { - cerr << "new tempo too close to previous ...\n"; - return false; - } - - Meter const & meter (i->metric()); - - TempoMapPoints::iterator e (i); - while (!e->is_explicit() && e != _points.begin()) { - --e; - } - - if (e->metric().ramped()) { - /* need to adjust ramp constants for preceding explicit point, since the new point will be positioned right after it - and thus defines the new ramp distance. - */ - e->compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc); - } - - /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, - even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not - fractional. - */ - - Temporal::Beats qn = i->quarters_at (sc).round_to_beat(); - - /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc" - */ - - Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat (); - - /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before" - */ - - if (i != _points.end()) { - ++i; - } - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, sc, qn, bbt, AudioTime, ramp)); - return true; -} - -bool -TempoMap::set_tempo (Tempo const & t, Timecode::BBT_Time const & bbt, bool ramp) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - - /* tempo changes are required to be on-beat */ - - Timecode::BBT_Time on_beat = bbt.round_up_to_beat(); - - assert (!_points.empty()); - - if (_points.front().bbt() > on_beat) { - cerr << "Cannot insert tempo at " << bbt << " before first point at " << _points.front().bbt() << endl; - return false; - } - - if (_points.size() == 1 && _points.front().bbt() == on_beat) { - /* change Meter */ - *((Tempo*) &_points.front().metric()) = t; - _points.front().make_explicit (TempoMapPoint::ExplicitTempo); - return true; - } - - TempoMapPoints::iterator i = iterator_at (on_beat); - - if (i->bbt() == on_beat) { - *((Tempo*) &i->metric()) = t; - i->make_explicit (TempoMapPoint::ExplicitTempo); - return true; - } - - Meter const & meter (i->metric()); - ++i; - - /* stick a prototype music-locked point up front and let ::rebuild figure out the superclock and quarter time */ - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, t, meter, 0, Temporal::Beats(), on_beat, MusicTime, ramp)); - return true; -} - -bool -TempoMap::set_meter (Meter const & m, Timecode::BBT_Time const & bbt) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - Timecode::BBT_Time measure_start (m.round_up_to_bar (bbt)); - - assert (!_points.empty()); - - if (_points.front().bbt() > measure_start) { - cerr << "Cannot insert meter at " << bbt << " before first point at " << _points.front().bbt() << endl; - return false; - } - - if (_points.size() == 1 && _points.front().bbt() == measure_start) { - /* change Meter */ - *((Meter*) &_points.front().metric()) = m; - _points.front().make_explicit (TempoMapPoint::ExplicitMeter); - return true; - } - - TempoMapPoints::iterator i = iterator_at (measure_start); - - if (i->bbt() == measure_start) { - *((Meter*) &i->metric()) = m; - i->make_explicit (TempoMapPoint::ExplicitMeter); - return true; - } - - Temporal::Beats qn = i->quarters_at (measure_start); - superclock_t sc = i->sclock() + i->metric().superclock_at_qn (qn); - - Tempo const & tempo (i->metric()); - ++i; - - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, qn, measure_start, MusicTime)); - return true; -} - -bool -TempoMap::set_meter (Meter const & m, superclock_t sc) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - assert (!_points.empty()); - - /* special case #2: first map entry is later than the new point */ - - if (_points.front().sclock() > sc) { - /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, - even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not - fractional. - */ - - Temporal::Beats b = _points.front().quarters_at (sc).round_to_beat(); - Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); - - _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitMeter, _points.front().metric(), m, sc, b, bbt, AudioTime)); - return true; - } - - /* special case #3: only one map entry, at the same time as the new point. - - This is the common case when editing tempo/meter in a session with a single tempo/meter - */ - - if (_points.size() == 1 && _points.front().sclock() == sc) { - /* change meter */ - *((Meter*) &_points.front().metric()) = m; - _points.front().make_explicit (TempoMapPoint::ExplicitMeter); - return true; - } - - TempoMapPoints::iterator i = iterator_at (sc); - - if (i->sclock() == sc) { - /* change meter */ - *((Meter*) &i->metric()) = m; - - /* enforce rule described below regarding meter change positions */ - - if (i->bbt().beats != 1) { - i->set_bbt (Timecode::BBT_Time (i->bbt().bars + 1, 1, 0)); - } - - i->make_explicit (TempoMapPoint::ExplicitMeter); - - return true; - } - - if (sc - i->sclock() < i->metric().superclocks_per_note_type()) { - cerr << "new tempo too close to previous ...\n"; - return false; - } - - /* determine quarters and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, - even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not - fractional. - */ - - Temporal::Beats b = i->quarters_at (sc).round_to_beat(); - - /* rule: all Meter changes must start a new measure. So determine the nearest, lower beat to "sc". If this is not - the first division of the measure, move to the next measure. - */ - - Timecode::BBT_Time bbt = i->bbt_at (b).round_down_to_beat (); - - if (bbt.beats != 1) { - bbt.bars += 1; - bbt.beats = 1; - bbt.ticks = 0; - } - - Tempo const & tempo (i->metric()); - ++i; - - _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitMeter, tempo, m, sc, b, bbt, AudioTime)); - return true; -} - -TempoMapPoints::iterator -TempoMap::iterator_at (superclock_t sc) -{ - /* CALLER MUST HOLD LOCK */ - - if (_points.empty()) { - throw EmptyTempoMapException(); - } - - if (_points.size() == 1) { - return _points.begin(); - } - - /* Construct an arbitrary TempoMapPoint. The only property we care about is it's superclock time, - so other values used in the constructor are arbitrary and irrelevant. - */ - - TempoMetric const & metric (_points.front().metric()); - const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, sc, Temporal::Beats(), Timecode::BBT_Time(), AudioTime); - TempoMapPoint::SuperClockComparator scmp; - - TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, scmp); - - if (tmp != _points.begin()) { - return --tmp; - } - - return tmp; -} - -TempoMapPoints::iterator -TempoMap::iterator_at (Temporal::Beats const & qn) -{ - /* CALLER MUST HOLD LOCK */ - - if (_points.empty()) { - throw EmptyTempoMapException(); - } - - if (_points.size() == 1) { - return _points.begin(); - } - - /* Construct an arbitrary TempoMapPoint. The only property we care about is its quarters time, - so other values used in the constructor are arbitrary and irrelevant. - */ - - TempoMetric const & metric (_points.front().metric()); - const TempoMapPoint tp (TempoMapPoint::Flag (0), metric, metric, 0, qn, Timecode::BBT_Time(), AudioTime); - TempoMapPoint::QuarterComparator bcmp; - - TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp); - - if (tmp != _points.begin()) { - return --tmp; - } - - return tmp; -} - -TempoMapPoints::iterator -TempoMap::iterator_at (Timecode::BBT_Time const & bbt) -{ - /* CALLER MUST HOLD LOCK */ - - if (_points.empty()) { - throw EmptyTempoMapException(); - } - - if (_points.size() == 1) { - return _points.begin(); - } - - /* Construct an arbitrary TempoMapPoint. The only property we care about is its bbt time, - so other values used in the constructor are arbitrary and irrelevant. - */ - - TempoMetric const & metric (_points.front().metric()); - const TempoMapPoint tp (TempoMapPoint::Flag(0), metric, metric, 0, Temporal::Beats(), bbt, MusicTime); - TempoMapPoint::BBTComparator bcmp; - - TempoMapPoints::iterator tmp = upper_bound (_points.begin(), _points.end(), tp, bcmp); - - if (tmp != _points.begin()) { - return --tmp; - } - - return tmp; -} - -Timecode::BBT_Time -TempoMap::bbt_at (superclock_t sc) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return bbt_at_locked (sc); -} - -Timecode::BBT_Time -TempoMap::bbt_at_locked (superclock_t sc) const -{ - TempoMapPoint point (const_point_at (sc)); - Temporal::Beats b ((sc - point.sclock()) / (double) point.metric().superclocks_per_quarter_note()); - return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, b.get_beats(), b.get_ticks())); -} - -Timecode::BBT_Time -TempoMap::bbt_at (Temporal::Beats const & qn) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return bbt_at_locked (qn); -} - -Timecode::BBT_Time -TempoMap::bbt_at_locked (Temporal::Beats const & qn) const -{ - //Glib::Threads::RWLock::ReaderLock lm (_lock); - TempoMapPoint const & point (const_point_at (qn)); - Temporal::Beats delta (qn - point.quarters()); - return point.metric().bbt_add (point.bbt(), Timecode::BBT_Offset (0, delta.get_beats(), delta.get_ticks())); -} - -superclock_t -TempoMap::superclock_at (Temporal::Beats const & qn) const -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - return superclock_at_locked (qn); -} - -superclock_t -TempoMap::superclock_at_locked (Temporal::Beats const & qn) const -{ - assert (!_points.empty()); - - /* only the quarters property matters, since we're just using it to compare */ - const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, qn, Timecode::BBT_Time(), MusicTime); - TempoMapPoint::QuarterComparator bcmp; - - TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp); - - if (i == _points.end()) { - --i; - } - - /* compute distance from reference point to b. Remember that Temporal::Beats is always interpreted as quarter notes */ - - const Temporal::Beats q_delta = qn - i->quarters(); - superclock_t sclock_delta = i->metric().superclock_at_qn (q_delta); - return i->sclock() + sclock_delta; -} - -superclock_t -TempoMap::superclock_at (Timecode::BBT_Time const & bbt) const -{ - //Glib::Threads::RWLock::ReaderLock lm (_lock); - return superclock_at_locked (bbt); -} - -superclock_t -TempoMap::superclock_at_locked (Timecode::BBT_Time const & bbt) const -{ - //Glib::Threads::RWLock::ReaderLock lm (_lock); - assert (!_points.empty()); - - /* only the bbt property matters, since we're just using it to compare */ - const TempoMapPoint tmp (TempoMapPoint::Flag (0), Tempo (120), Meter (4, 4), 0, Temporal::Beats(), bbt, MusicTime); - TempoMapPoint::BBTComparator bcmp; - - TempoMapPoints::const_iterator i = upper_bound (_points.begin(), _points.end(), tmp, bcmp); - - if (i == _points.end()) { - --i; - } - - /* this computes the distance from the point, in beats whose size is - determined by the meter. - */ - - const Timecode::BBT_Offset delta = i->metric().bbt_delta (bbt, i->bbt()); - - /* convert to quarter notes */ - const int32_t ticks = delta.ticks + (Temporal::Beats::PPQN * delta.beats * 4) / i->metric().note_value(); - - /* get distance in superclocks */ - return i->sclock() + i->metric().superclock_at_qn (Temporal::Beats::ticks (ticks)); -} - -void -TempoMap::set_sample_rate (samplecnt_t new_sr) -{ - //Glib::Threads::RWLock::ReaderLock lm (_lock); - double ratio = new_sr / (double) _sample_rate; - - for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) { - i->map_reset_set_sclock_for_sr_change (llrint (ratio * i->sclock())); - } -} - -void -TempoMap::dump (std::ostream& ostr) -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - dump_locked (ostr); -} - -void -TempoMap::dump_locked (std::ostream& ostr) -{ - ostr << "\n\n------------\n"; - for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) { - ostr << *i << std::endl; - } -} - -void -TempoMap::remove_explicit_point (superclock_t sc) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - TempoMapPoints::iterator p = iterator_at (sc); - - if (p->sclock() == sc) { - _points.erase (p); - } -} - -bool -TempoMap::move_to (superclock_t current, superclock_t destination, bool push) -{ - Glib::Threads::RWLock::WriterLock lm (_lock); - TempoMapPoints::iterator p = iterator_at (current); - - if (p->sclock() != current) { - cerr << "No point @ " << current << endl; - return false; - } - - /* put a "dirty" flag in at the nearest explicit point to the removal point - */ - - if (p != _points.begin()) { - TempoMapPoints::iterator prev (p); - --prev; - while (prev != _points.begin() && !prev->is_explicit()) { - --prev; - } - prev->set_dirty (true); - } else { - TempoMapPoints::iterator nxt (p); - ++nxt; - while (nxt != _points.end() && !nxt->is_explicit()) { - ++nxt; - } - if (nxt != _points.end()) { - nxt->set_dirty (true); - } - } - - if (!set_tempo_and_meter (p->metric(), p->metric(), destination, p->ramped(), true)) { - return false; - } - - if (push) { - - p = iterator_at (destination); - ++p; - - const superclock_t delta = destination - current; - - while (p != _points.end()) { - p->set_sclock (p->sclock() + delta); - ++p; - } - } - - _points.erase (p); - - return true; -} - -void -TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, Temporal::Beats const & resolution) -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - TempoMapPoints::iterator p = iterator_at (start); - - while (p != _points.end() && p->sclock() < start) { - ++p; - } - - if (resolution == Temporal::Beats()) { - /* just hand over the points as-is */ - while (p != _points.end() && p->sclock() < end) { - ret.push_back (*p); - ++p; - } - return; - } - - superclock_t pos = p->sclock(); - Temporal::Beats qpos; - TempoMapPoints::iterator nxt = p; - ++nxt; - - while ((p != _points.end()) && (pos < end)) { - /* recompute grid down to @param resolution level - */ - - superclock_t sclock_delta = p->metric().superclock_at_qn (qpos); - - ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), - p->metric(), p->metric(), - p->sclock() + sclock_delta, - p->quarters() + qpos, - p->metric().bbt_add (p->bbt(), Timecode::BBT_Offset (0, qpos.get_beats(), qpos.get_ticks())), - AudioTime, - p->ramped())); - - qpos += resolution; - pos += sclock_delta; - - if (pos >= nxt->sclock()) { - p = nxt; - ++nxt; - } - } -} - - -void -TempoMap::get_bar_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, int32_t bar_gap) -{ - Glib::Threads::RWLock::ReaderLock lm (_lock); - - for (TempoMapPoints::iterator p = iterator_at (start); (p != _points.end()) && (p->sclock() < end); ++p) { - - if ((p->sclock() >= start) && (p->bbt().beats == 1) && ((p->bbt().bars == 1) || (p->bbt().bars % bar_gap == 0))) { - ret.push_back (TempoMapPoint (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo), - p->metric(), p->metric(), - p->sclock(), - p->quarters(), - p->bbt(), - AudioTime, - p->ramped())); - } - } -} - -std::ostream& -operator<<(std::ostream& str, Meter const & m) -{ - return str << m.divisions_per_bar() << '/' << m.note_value(); -} - -std::ostream& -operator<<(std::ostream& str, Tempo const & t) -{ - return str << t.note_types_per_minute() << " 1/" << t.note_type() << " notes per minute (" << t.superclocks_per_note_type() << " sc-per-1/" << t.note_type() << ')'; -} - -std::ostream& -operator<<(std::ostream& str, TempoMapPoint const & tmp) -{ - str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second - << (tmp.is_explicit() ? " EXP" : " imp") - << " qn " << tmp.quarters () - << " bbt " << tmp.bbt() - << " lock to " << tmp.lock_style() - ; - - if (tmp.is_explicit()) { - str << " tempo " << *((Tempo*) &tmp.metric()) - << " meter " << *((Meter*) &tmp.metric()) - ; - } - - if (tmp.is_explicit() && tmp.ramped()) { - str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter(); - } - return str; -} - -/*******/ - -#define SAMPLERATE 48000 -#define SECONDS_TO_SUPERCLOCK(s) (superclock_ticks_per_second * s) - -using std::cerr; -using std::cout; -using std::endl; - -void -test_bbt_math () -{ - using namespace Timecode; - - BBT_Time a; - BBT_Time b1 (1,1,1919); - BBT_Time n1 (-1,1,1919); - std::vector meters; - - meters.push_back (Meter (4, 4)); - meters.push_back (Meter (5, 8)); - meters.push_back (Meter (11, 7)); - meters.push_back (Meter (3, 4)); - -#define PRINT_RESULT(m,str,op,op1,Bars,Beats,Ticks) cout << m << ' ' << (op1) << ' ' << str << ' ' << BBT_Offset ((Bars),(Beats),(Ticks)) << " = " << m.op ((op1), BBT_Offset ((Bars), (Beats), (Ticks))) << endl; - - for (std::vector::iterator m = meters.begin(); m != meters.end(); ++m) { - for (int B = 1; B < 4; ++B) { - for (int b = 1; b < 13; ++b) { - PRINT_RESULT((*m), "+", bbt_add, a, B, b, 0); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, 1); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN/2); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN - 1); - PRINT_RESULT((*m), "+", bbt_add, a, B, b, Temporal::Beats::PPQN - 2); - - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 0); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, 1); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN/2); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN - 1); - PRINT_RESULT((*m), "+", bbt_add, b1, B, b, Temporal::Beats::PPQN - 2); - - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 0); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, 1); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN/2); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN - 1); - PRINT_RESULT((*m), "+", bbt_add, n1, B, b, Temporal::Beats::PPQN - 2); - } - } - - for (int B = 1; B < 4; ++B) { - for (int b = 1; b < 13; ++b) { - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 0); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, 1); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN/2); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN - 1); - PRINT_RESULT ((*m), "-", bbt_subtract, a, B, b, Temporal::Beats::PPQN - 2); - } - } - } -} - -int -main () -{ - TempoMap tmap (Tempo (140), Meter (4,4), SAMPLERATE); - - //test_bbt_math (); - //return 0; - - tmap.set_tempo (Tempo (7), SECONDS_TO_SUPERCLOCK(7)); - tmap.set_tempo (Tempo (23), SECONDS_TO_SUPERCLOCK(23)); - tmap.set_tempo (Tempo (24), SECONDS_TO_SUPERCLOCK(24), true); - tmap.set_tempo (Tempo (40), SECONDS_TO_SUPERCLOCK(28), true); - tmap.set_tempo (Tempo (100), SECONDS_TO_SUPERCLOCK(100)); - tmap.set_tempo (Tempo (123), SECONDS_TO_SUPERCLOCK(23)); - - tmap.set_meter (Meter (3, 4), SECONDS_TO_SUPERCLOCK(23)); - tmap.set_meter (Meter (5, 8), SECONDS_TO_SUPERCLOCK(100)); - tmap.set_meter (Meter (5, 7), SECONDS_TO_SUPERCLOCK(7)); - tmap.set_meter (Meter (4, 4), SECONDS_TO_SUPERCLOCK(24)); - tmap.set_meter (Meter (11, 7), SECONDS_TO_SUPERCLOCK(23)); - - tmap.set_meter (Meter (3, 8), Timecode::BBT_Time (17, 1, 0)); - - tmap.rebuild (SECONDS_TO_SUPERCLOCK (120)); - tmap.dump (std::cout); - - if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) { - tmap.rebuild (SECONDS_TO_SUPERCLOCK (120)); - tmap.dump (std::cout); - } - - TempoMapPoints grid; - tmap.get_grid (grid, SECONDS_TO_SUPERCLOCK (12.3), SECONDS_TO_SUPERCLOCK (44), Temporal::Beats::ticks ((Temporal::Beats::PPQN * 4) / 12)); - cout << "grid contains " << grid.size() << endl; - for (TempoMapPoints::iterator p = grid.begin(); p != grid.end(); ++p) { - cout << *p << endl; - } - - TempoMapPoints bbt_grid; - tmap.get_bar_grid (bbt_grid, SECONDS_TO_SUPERCLOCK (0), SECONDS_TO_SUPERCLOCK (100), 4); - cout << "bbt_grid contains " << bbt_grid.size() << endl; - for (TempoMapPoints::iterator p = bbt_grid.begin(); p != bbt_grid.end(); ++p) { - cout << *p << endl; - } - - return 0; -} diff --git a/nutemp/t.h b/nutemp/t.h deleted file mode 100644 index d18775fccb..0000000000 --- a/nutemp/t.h +++ /dev/null @@ -1,429 +0,0 @@ -#ifndef __ardour_tempo_h__ -#define __ardour_tempo_h__ - -#include -#include -#include -#include -#include - -#include - -#include "pbd/signals.h" - -#include "temporal/beats.h" - -#include "ardour/ardour.h" -#include "ardour/superclock.h" - -#include "temporal/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; - - Temporal::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 (samplecnt_t sr, superclock_t end_superclocks_per_note_type, superclock_t duration); - void compute_c_quarters (samplecnt_t sr, superclock_t end_superclocks_per_note_type, Temporal::Beats const & duration); - - superclock_t superclocks_per_bar (samplecnt_t sr) const; - superclock_t superclocks_per_grid (samplecnt_t sr) const; - - superclock_t superclock_at_qn (Temporal::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, Temporal::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), _map (0) {} - TempoMapPoint (TempoMapPoint const & tmp, superclock_t sc, Temporal::Beats const & q, Timecode::BBT_Time const & bbt) - : _flags (Flag (0)), _reference (&tmp), _sclock (sc), _quarters (q), _bbt (bbt), _dirty (true), _map (0) {} - ~TempoMapPoint () {} - - void set_map (TempoMap* m); - - Flag flags() const { return _flags; } - 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; } - Temporal::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(); } - PositionLockStyle lock_style() const { return is_explicit() ? _explicit.lock_style : _reference->lock_style(); } - - void compute_c_superclock (samplecnt_t sr, superclock_t end_superclocks_per_note_type, superclock_t duration) { if (is_explicit()) { _explicit.metric.compute_c_superclock (sr, end_superclocks_per_note_type, duration); } } - void compute_c_quarters (samplecnt_t sr, superclock_t end_superclocks_per_note_type, Temporal::Beats const & duration) { if (is_explicit()) { _explicit.metric.compute_c_quarters (sr, end_superclocks_per_note_type, duration); } } - - /* 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 (Temporal::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); - 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; } - - Temporal::Beats quarters_at (superclock_t sc) const; - Temporal::Beats quarters_at (Timecode::BBT_Time const &) const; - - Timecode::BBT_Time bbt_at (Temporal::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; - Temporal::Beats _quarters; - Timecode::BBT_Time _bbt; - bool _dirty; - TempoMap* _map; -}; - -typedef std::list TempoMapPoints; - -class LIBARDOUR_API TempoMap -{ - public: - TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, samplecnt_t sr); - - void set_dirty (bool yn); - - void set_sample_rate (samplecnt_t sr); - samplecnt_t sample_rate() const { return _sample_rate; } - - void remove_explicit_point (superclock_t); - - bool move_to (superclock_t current, superclock_t destination, bool push = false); - - bool set_tempo_and_meter (Tempo const &, Meter const &, superclock_t, bool ramp, bool flexible); - - 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 (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 (Temporal::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 (Temporal::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 (Temporal::Beats const &) const; - Temporal::Beats quarter_note_at (superclock_t sc) const; - Temporal::Beats quarter_note_at (Timecode::BBT_Time const &) const; - superclock_t superclock_at (Temporal::Beats const &) const; - superclock_t superclock_at (Timecode::BBT_Time const &) const; - - TempoMapPoint const & const_point_at (superclock_t sc) const { return *const_iterator_at (sc); } - TempoMapPoint const & const_point_at (Temporal::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); } - - TempoMapPoint const & const_point_after (superclock_t sc) const; - TempoMapPoint const & const_point_after (Temporal::Beats const & b) const; - TempoMapPoint const & const_point_after (Timecode::BBT_Time const & bbt) const; - - /* If resolution == Temporal::Beats() (i.e. zero), then the grid that is - returned will contain a mixture of implicit and explicit points, - and will only be valid as long as this map remains unchanged - (because the implicit points may reference explicit points in the - map. - - If resolution != Temporal::Beats() (i.e. non-zero), then the in-out @param - grid will contain only explicit points that do not reference this - map in anyway. - */ - void get_grid (TempoMapPoints& points, superclock_t start, superclock_t end, Temporal::Beats const & resolution); - - void get_bar_grid (TempoMapPoints& points, superclock_t start, superclock_t end, int32_t bar_gap); - - struct EmptyTempoMapException : public std::exception { - virtual const char* what() const throw() { return "TempoMap is empty"; } - }; - - void dump (std::ostream&); - void rebuild (superclock_t limit); - - PBD::Signal2 Changed; - - private: - TempoMapPoints _points; - samplecnt_t _sample_rate; - mutable Glib::Threads::RWLock _lock; - bool _dirty; - - /* 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 (Temporal::Beats const &); - TempoMapPoints::iterator iterator_at (Timecode::BBT_Time const &); - - TempoMapPoints::const_iterator const_iterator_at (superclock_t sc) const { return const_cast(this)->iterator_at (sc); } - TempoMapPoints::const_iterator const_iterator_at (Temporal::Beats const & b) const { return const_cast(this)->iterator_at (b); } - TempoMapPoints::const_iterator const_iterator_at (Timecode::BBT_Time const & bbt) const { return const_cast(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 (Temporal::Beats const & b) { return *iterator_at (b); } - TempoMapPoint & point_at (Timecode::BBT_Time const & bbt) { return *iterator_at (bbt); } - - Meter const & meter_at_locked (superclock_t sc) const { return const_point_at (sc).metric(); } - Meter const & meter_at_locked (Temporal::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 (Temporal::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 (Temporal::Beats const &) const; - Temporal::Beats quarter_note_at_locked (superclock_t sc) const; - Temporal::Beats quarter_note_at_locked (Timecode::BBT_Time const &) const; - superclock_t superclock_at_locked (Temporal::Beats const &) const; - superclock_t superclock_at_locked (Timecode::BBT_Time const &) const; - - void rebuild_locked (superclock_t limit); - void dump_locked (std::ostream&); -}; - -} - -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__ */