13
0

Temporal: fundamental changes to get timeline.cc to compile

This commit is contained in:
Paul Davis 2020-08-03 14:03:05 -06:00
parent 35d0519109
commit 2c69a1425f
3 changed files with 113 additions and 73 deletions

View File

@ -30,6 +30,8 @@
#include <glibmm/threads.h>
#include "pbd/enum_convert.h"
#include "pbd/integer_division.h"
#include "pbd/rcu.h"
#include "pbd/signals.h"
#include "pbd/statefuldestructible.h"
@ -224,7 +226,7 @@ class LIBTEMPORAL_API Tempo : public Rampable {
static void superbeats_to_beats_ticks (int64_t sb, int32_t& b, int32_t& t) {
b = sb / big_numerator;
uint64_t remain = sb - (b * big_numerator);
int64_t remain = sb - (b * big_numerator);
t = int_div_round ((Temporal::ticks_per_beat * remain), big_numerator);
}
@ -566,6 +568,54 @@ typedef std::list<TempoMapPoint> TempoMapPoints;
class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
{
/* Any given thread must be able to carry out tempo-related arithmetic
* and time domain conversions using a consistent version of a
* TempoMap. The map could be updated at any time, and for any reason
* (typically from a GUI thread), but some other thread could be
* using the map to convert from audio to music time (for example).
*
* We do not want to use locks for this - this math may happen in a
* realtime thread, and even worse, the lock may need to be held for
* long periods of time in order to have the desired effect: a thread
* may be performing some tempo-based arithmetic as part of a complex
* operation that requires multiple steps. The tempo map it uses must
* remain consistent across all steps, and so we would have to hold the
* lock across them all. That would create awkward and difficult
* semantics for map users - somewhat arbitrary rules about how long
* one could hold the map for, etc.
*
* Elsewhere in the codebase, we use RCU to solve this sort of
* issue. For example, if we need to operate an the current list of
* Routes, we get read-only copy of the list, and iterate over it,
* knowing that even if the canonical version is being changed, the
* copy we are using will not.
*
* However, the tempo map's use is often implicit rather than
* explicit. The callstack to convert between an audio domain time and
* a music domain time should not require passing a tempo map into
* every call.
*
* The approach taken here is to use a combination of RCU and
* thread-local variables. Any given thread is by definition ... single
* threaded. If the thread has a thread-local copy of a tempo map, it
* will not change except at explicit calls to change it. The tempo map
* can be accessed from any method executed by the thread. But the
* relationship between the thread-local copy and "actual" tempo map(s)
* is managed via RCU, meaning that read-only access is cheap (no
* actual copy required).
*
*/
public:
typedef boost::shared_ptr<TempoMap> SharedPtr;
private:
static Glib::Threads::Private<SharedPtr> _tempo_map_p;
static SerializedRCUManager<TempoMap> _map_mgr;
public:
static void update_thread_tempo_map() { SharedPtr p = _map_mgr.reader(); _tempo_map_p.set (&p); }
static SharedPtr fetch() { update_thread_tempo_map(); SharedPtr p = *_tempo_map_p.get(); return p; }
/* and now on with the rest of the show ... */
public:
TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, samplecnt_t sr);
~TempoMap();

View File

@ -25,11 +25,15 @@
#include <cassert>
#include <limits>
#include <boost/rational.hpp>
#include "pbd/enumwriter.h"
#include "pbd/int62.h"
#include "temporal/types.h"
#include "temporal/beats.h"
#include "temporal/bbt_time.h"
#include "temporal/superclock.h"
#include "temporal/visibility.h"
namespace Temporal {
@ -41,36 +45,28 @@ class timecnt_t;
* 64th bit: sign bit
*/
class timepos_t {
private:
int64_t mark_beats (int64_t n) { return n|(1LL<62); }
class timepos_t : public int62_t {
public:
timepos_t ();
timepos_t (superclock_t s) {
#ifdef SUPERSAFE_TIMEPOS
v = s&~(1LL<<62);
#else
v = s;
#endif
}
timepos_t (superclock_t s) : int62_t (false, s) {}
explicit timepos_t (timecnt_t const &); /* will throw() if val is negative */
explicit timepos_t (Temporal::Beats const & b) { v = mark_beats (b.to_ticks()); }
explicit timepos_t (Temporal::Beats const & b) : int62_t (false, b.to_ticks()) {}
bool is_beats() const { return v & (1LL<<62); }
bool is_superclock() const { return !is_beats (); }
bool is_beats() const { return flagged(); }
bool is_superclock() const { return !flagged(); }
Temporal::TimeDomain time_domain () const { if (is_beats()) return Temporal::BeatTime; return Temporal::AudioTime; }
Temporal::TimeDomain time_domain () const { if (flagged()) return Temporal::BeatTime; return Temporal::AudioTime; }
superclock_t superclocks() const { assert (is_superclock()); return v; }
int64_t ticks() const { assert (is_beats()); return v; }
Beats beats() const { assert (is_beats()); int64_t b = v/ticks_per_beat; return Beats (b, v - (b * ticks_per_beat)); }
superclock_t superclocks() const { if (is_superclock()) return v; return _superclocks (); }
int64_t ticks() const { if (is_beats()) return val(); return _ticks (); }
Beats beats() const { if (is_beats()) return Beats::ticks (val()); return _beats (); }
/* return a timepos_t that is the next (later) possible position given
* this one
*/
timepos_t increment () const {
return timepos_t (v + 1);
return timepos_t (val() + 1);
}
/* return a timepos_t that is the previous (earlier) possible position given
@ -78,32 +74,32 @@ class timepos_t {
*/
timepos_t decrement () const {
if (is_beats()) {
return timepos_t (v - 1); /* beats can go negative */
return timepos_t (val() - 1); /* beats can go negative */
}
return timepos_t (v > 0 ? v - 1 : v); /* samples cannot go negative */
return timepos_t (val() > 0 ? val() - 1 : val()); /* samples cannot go negative */
}
timepos_t & operator= (timecnt_t const & t); /* will throw() if val is negative */
timepos_t & operator= (superclock_t s) { v = s; return *this; }
timepos_t & operator= (Temporal::Beats const & b) { v = mark_beats (b.to_ticks()); return *this; }
timepos_t & operator= (Temporal::Beats const & b) { operator= (build (true, b.to_ticks())); return *this; }
bool operator== (timepos_t const & other) const { return v == other.v; }
bool operator!= (timepos_t const & other) const { return v != other.v; }
#if 0
bool operator< (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return v < other.v; return expensive_lt (other); }
bool operator> (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return v > other.v; return expensive_gt (other); }
bool operator<= (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return v <= other.v; return expensive_lte (other); }
bool operator>= (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return v >= other.v; return expensive_gte (other); }
bool operator< (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return val() < other.val(); return expensive_lt (other); }
bool operator> (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return val() > other.val(); return expensive_gt (other); }
bool operator<= (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return val() <= other.val(); return expensive_lte (other); }
bool operator>= (timecnt_t const & other) const { if (is_beats() == other.is_beats()) return val() >= other.val(); return expensive_gte (other); }
#endif
bool operator< (timepos_t const & other) const { if (is_beats() == other.is_beats()) return v < other.v; return expensive_lt (other); }
bool operator> (timepos_t const & other) const { if (is_beats() == other.is_beats()) return v > other.v; return expensive_gt (other); }
bool operator<= (timepos_t const & other) const { if (is_beats() == other.is_beats()) return v <= other.v; return expensive_lte (other); }
bool operator>= (timepos_t const & other) const { if (is_beats() == other.is_beats()) return v >= other.v; return expensive_gte (other); }
bool operator< (timepos_t const & other) const { if (is_beats() == other.is_beats()) return val() < other.val(); return expensive_lt (other); }
bool operator> (timepos_t const & other) const { if (is_beats() == other.is_beats()) return val() > other.val(); return expensive_gt (other); }
bool operator<= (timepos_t const & other) const { if (is_beats() == other.is_beats()) return val() <= other.val(); return expensive_lte (other); }
bool operator>= (timepos_t const & other) const { if (is_beats() == other.is_beats()) return val() >= other.val(); return expensive_gte (other); }
timepos_t operator+(timecnt_t const & d) const;
timepos_t operator+(timepos_t const & d) const { if (is_beats() == d.is_beats()) return timepos_t (v + d.ticks()); return expensive_add (d.superclocks()); }
timepos_t operator+(superclock_t s) const { if (is_superclock()) return timepos_t (v + s); return expensive_add (s); }
timepos_t operator+(Temporal::Beats const &b ) const { if (is_beats()) { return timepos_t (ticks() + b.to_ticks()); return expensive_add (b); } }
timepos_t operator+(Temporal::Beats const &b ) const { if (is_beats()) return timepos_t (ticks() + b.to_ticks()); return expensive_add (b); }
/* operator-() poses severe and thorny problems for a class that represents position on a timeline.
@ -195,7 +191,7 @@ class timepos_t {
bool operator!= (superclock_t s) { return v != s; }
bool operator!= (Temporal::Beats const & b) { return beats() != b; }
void set_sample (samplepos_t s);
void set_superclock (superclock_t s);
void set_beat (Temporal::Beats const &);
void set_bbt (Temporal::BBT_Time const &);
@ -205,20 +201,16 @@ class timepos_t {
static timepos_t const & max() { return _max_timepos; }
private:
/* these are mutable because we may need to update them at arbitrary
times, even within contexts that are otherwise const. For example, an
audio-locked position whose _beats value is out of date. The audio time
is canonical and will not change, but beats() needs to be callable, and
we'd prefer to claim const-ness for it than not.
*/
mutable superclock_t v;
int64_t v;
/* special constructor for max_timepos */
//timepos_t (TimeDomainStatus);
static timepos_t _max_timepos;
superclock_t _superclocks() const;
int64_t _ticks() const;
Beats _beats() const;
bool expensive_lt (timepos_t const &) const;
bool expensive_lte (timepos_t const &) const;
bool expensive_gt (timepos_t const &) const;
@ -257,27 +249,20 @@ class timepos_t {
*/
class LIBTEMPORAL_API timecnt_t {
public:
timecnt_t () : _distance (0), _position (0) {}
timecnt_t () : _distance (false, 0), _position (0) {}
timecnt_t (timepos_t const & d, timepos_t const & p) : _distance (d), _position (p) { assert (p.is_beats() == d.is_beats()); }
timecnt_t (timecnt_t const &, timepos_t const & pos);
timecnt_t (superclock_t s, timepos_t const & pos) : _distance (s), _position (pos) { assert (_distance.is_beats() == _position.is_beats()); }
explicit timecnt_t (Temporal::Beats const & b, timepos_t const & pos) : _distance (b), _position (pos) { assert ( _distance.is_beats() == _position.is_beats()); }
timecnt_t (superclock_t s, timepos_t const & pos) : _distance (false, s), _position (pos) { assert (_distance.flagged() == _position.is_beats()); }
explicit timecnt_t (Temporal::Beats const & b, timepos_t const & pos) : _distance (true, b.to_ticks()), _position (pos) { assert ( _distance.flagged() == _position.is_beats()); }
timepos_t const & distance() const { return _distance; }
int62_t const & distance() const { return _distance; }
timepos_t const & position() const { return _position; }
void set_position (timepos_t const &pos);
/* provides a more compact form and faster execution for "timecnt_t > 0 */
bool positive() const { return _distance > 0; }
/* provides a more compact form and faster execution for "timecnt_t < 0 */
bool negative() const {return _distance < 0; }
/* provides a more compact form and faster execution for "timecnt_t == 0 */
bool zero() const { return _distance == 0; }
bool zero() const { return _distance.val() == 0; }
static timecnt_t const & max() { return _max_timecnt; }
@ -285,29 +270,28 @@ class LIBTEMPORAL_API timecnt_t {
Temporal::TimeDomain time_domain () const { return _position.time_domain (); }
superclock_t superclocks() const { if (_position.is_superclock()) return _distance.superclocks(); return compute_superclocks(); }
Temporal::Beats beats () const { if (_position.is_beats()) return _distance.beats(); return compute_beats(); }
int64_t ticks () const { if (_position.is_beats()) return _distance.ticks(); return compute_ticks(); }
superclock_t superclocks() const { if (_position.is_superclock()) return _distance.val(); return compute_superclocks(); }
Temporal::Beats beats () const { if (_position.is_beats()) return Beats::ticks (_distance.val()); return compute_beats(); }
int64_t ticks () const { if (_position.is_beats()) return _distance.val(); return compute_ticks(); }
timecnt_t & operator= (superclock_t s) { _distance = s; return *this; }
timecnt_t & operator= (Temporal::Beats const & b) { _distance = b; return *this; }
timecnt_t & operator= (timepos_t const & s) { _distance = s; return *this; }
timecnt_t & operator= (superclock_t s) { _distance = int62_t (false, s); return *this; }
timecnt_t & operator= (Temporal::Beats const & b) { _distance = int62_t (true, b.to_ticks()); return *this; }
/* return a timecnt_t that is the next/previous (earlier/later) possible position given
* this one
*/
timecnt_t operator++ () { _distance = _distance + 1; return *this; }
timecnt_t operator-- () { _distance = _distance.earlier (1); return *this; }
timecnt_t operator++ () { _distance += 1; return *this; }
timecnt_t operator-- () { _distance -= 1; return *this; }
timecnt_t operator*(double) const;
timecnt_t operator/(double n) const;
timecnt_t operator*(boost::rational<int64_t> const &) const;
timecnt_t operator/(boost::rational<int64_t> const &) const;
timecnt_t operator-() const { return timecnt_t (_distance.is_beats() ? -_distance.ticks() : -_distance.superclocks(), _position); }
timecnt_t operator- (timecnt_t const & t) const { return timecnt_t (_distance.earlier (t.distance()), _position); }
timecnt_t operator-() const { return timecnt_t (-_distance.val(), _position); }
timecnt_t operator- (timecnt_t const & t) const { return timecnt_t (_distance - t.distance(), _position); }
timecnt_t operator+ (timecnt_t const & t) const { return timecnt_t (_distance + t.distance(), _position); }
timecnt_t & operator-= (timecnt_t const & t) { _distance.earlier (t.distance()); return *this; }
timecnt_t & operator+= (timecnt_t const & t) { _distance = _distance + t.distance(); return *this; }
timecnt_t & operator-= (timecnt_t const & t) { _distance -= t.distance(); return *this; }
timecnt_t & operator+= (timecnt_t const & t) { _distance += t.distance(); return *this; }
//timecnt_t operator- (timepos_t const & t) const;
//timecnt_t operator+ (timepos_t const & t) const;
@ -320,7 +304,7 @@ class LIBTEMPORAL_API timecnt_t {
bool operator<= (timecnt_t const & other) const { return _distance <= other.distance(); }
timecnt_t & operator=(timecnt_t const & other) {
if (this != &other) {
if (_distance.is_beats() == other.distance().is_beats()) {
if (_distance.flagged() == other.distance().flagged()) {
_distance = other.distance();
} else {
/* unclear what to do here but we cannot allow
@ -339,6 +323,7 @@ class LIBTEMPORAL_API timecnt_t {
*/
bool operator== (timepos_t const & other) const { return _distance == other; }
#if 0
bool operator< (superclock_t s) { if (_distance.is_superclock()) return _distance < s; return false; }
bool operator< (Temporal::Beats const & b) { if (_distance.is_beats()) return _distance < b; return false; }
bool operator<= (superclock_t s) { if (_distance.is_superclock()) return _distance <= s; return false; }
@ -351,7 +336,7 @@ class LIBTEMPORAL_API timecnt_t {
bool operator== (Temporal::Beats const & b) { if (_distance.is_beats()) return _distance == b; return false; }
bool operator!= (superclock_t s) { if (_distance.is_superclock()) return _distance != s; return false; }
bool operator!= (Temporal::Beats const & b) { if (_distance.is_beats()) return _distance != b; return false; }
#endif
timecnt_t operator% (timecnt_t const &) const;
timecnt_t & operator%=(timecnt_t const &);
@ -359,7 +344,7 @@ class LIBTEMPORAL_API timecnt_t {
std::string to_string () const;
private:
timepos_t _distance; /* aka "duration" */
int62_t _distance; /* aka "duration" */
timepos_t _position; /* aka "origin */
static timecnt_t _max_timecnt;
@ -389,5 +374,10 @@ namespace std {
}
namespace std {
std::ostream& operator<< (std::ostream & o, Temporal::timecnt_t const & tc);
std::ostream& operator<< (std::ostream & o, Temporal::timepos_t const & tp);
}
#endif /* __libtemporal_timeline_h__ */

View File

@ -24,7 +24,7 @@ APPNAME = 'temporal'
VERSION = TEMPORAL_VERSION
I18N_PACKAGE = 'libtemporal'
temporal_sources = [ 'time.cc', 'bbt_time.cc', 'tempo.cc' ]
temporal_sources = [ 'time.cc', 'bbt_time.cc', 'tempo.cc', 'timeline.cc' ]
def options(opt):
autowaf.set_options(opt)