diff --git a/libs/temporal/enums.cc b/libs/temporal/enums.cc new file mode 100644 index 0000000000..9a56a5db13 --- /dev/null +++ b/libs/temporal/enums.cc @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 Paul Davis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "pbd/enumwriter.h" + +#include "temporal/types.h" + +using namespace PBD; +using namespace Temporal; +using namespace std; + +void +setup_libtemporal_enums () +{ + EnumWriter& enum_writer (EnumWriter::instance()); + vector i; + vector s; + + Temporal::TimeDomain td; + Temporal::OverlapType _OverlapType; + +#define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() +#define REGISTER_BITS(e) enum_writer.register_bits (typeid(e).name(), i, s); i.clear(); s.clear() +#define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e) +#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e) + + REGISTER_ENUM (AudioTime); + REGISTER_ENUM (BeatTime); + REGISTER (td); + + REGISTER_ENUM (Temporal::OverlapNone); + REGISTER_ENUM (Temporal::OverlapInternal); + REGISTER_ENUM (Temporal::OverlapStart); + REGISTER_ENUM (Temporal::OverlapEnd); + REGISTER_ENUM (Temporal::OverlapExternal); + REGISTER(_OverlapType); + + +} + +void Temporal::init () +{ + setup_libtemporal_enums (); +} + diff --git a/libs/temporal/range.cc b/libs/temporal/range.cc index 13d0956b6c..c6721b5439 100644 --- a/libs/temporal/range.cc +++ b/libs/temporal/range.cc @@ -95,3 +95,9 @@ Range::subtract (RangeList & sub) const return result; } + +template<> OverlapType coverage_exclusive_ends (int64_t sa, int64_t eaE, int64_t sb, int64_t ebE) +{ + /* convert end positions to inclusive */ + return coverage_inclusive_ends (sa, eaE-1, sb, ebE-1); +} diff --git a/libs/temporal/tempo.cc b/libs/temporal/tempo.cc index 87ad7fc915..6141a4580a 100644 --- a/libs/temporal/tempo.cc +++ b/libs/temporal/tempo.cc @@ -87,7 +87,7 @@ Point::time() const /*NOTREACHED*/ abort(); /*NOTREACHED*/ - return timepos_t(); + return timepos_t (AudioTime); } Tempo::Tempo (XMLNode const & node) @@ -2473,7 +2473,7 @@ TempoMap::bbt_duration_at (superclock_t pos, const BBT_Time& bbt, int /* dir_ign timecnt_t TempoMap::full_duration_at (timepos_t const & pos, timecnt_t const & duration, TimeDomain return_domain) const { - timepos_t p; + timepos_t p (return_domain); Beats b; superclock_t s; diff --git a/libs/temporal/temporal/beats.h b/libs/temporal/temporal/beats.h index 9e40010784..8098816b24 100644 --- a/libs/temporal/temporal/beats.h +++ b/libs/temporal/temporal/beats.h @@ -30,6 +30,7 @@ #include "pbd/compose.h" #include "pbd/failed_constructor.h" +#include "pbd/integer_division.h" #include "pbd/string_convert.h" @@ -395,19 +396,12 @@ public: return b; } - template - Beats operator*(Number factor) const { - return ticks ((_beats * PPQN + _ticks) * factor); - } + Beats operator*(int32_t factor) const {return ticks (to_ticks() * factor); } + Beats operator/(int32_t factor) const { return ticks (to_ticks() / factor);} + Beats operator*(ratio_t const & factor) const {return ticks (int_div_round (to_ticks() * factor.numerator(), factor.denominator())); } + Beats operator/(ratio_t const & factor) const {return ticks (int_div_round (to_ticks() * factor.denominator(), factor.numerator())); } - template - Beats operator/(Number factor) const { - return ticks ((_beats * PPQN + _ticks) / factor); - } - - Beats operator% (Beats const & b) { - return Beats::ticks (to_ticks() % b.to_ticks()); - } + Beats operator% (Beats const & b) { return Beats::ticks (to_ticks() % b.to_ticks());} Beats operator%= (Beats const & b) { const Beats B (Beats::ticks (to_ticks() % b.to_ticks())); diff --git a/libs/temporal/temporal/range.h b/libs/temporal/temporal/range.h index 054e2200ca..4d7d04525b 100644 --- a/libs/temporal/temporal/range.h +++ b/libs/temporal/temporal/range.h @@ -25,17 +25,10 @@ #include "temporal/visibility.h" #include "temporal/timeline.h" +#include "temporal/types.h" namespace Temporal { -enum /*LIBTEMPORAL_API*/ OverlapType { - OverlapNone, // no overlap - OverlapInternal, // the overlap is 100% within the object - OverlapStart, // overlap covers start, but ends within - OverlapEnd, // overlap begins within and covers end - OverlapExternal // overlap extends to (at least) begin+end -}; - /** end position arguments are inclusive */ template /*LIBTEMPORAL_API*/ OverlapType coverage_inclusive_ends (T sa, T ea, T sb, T eb) { @@ -137,6 +130,8 @@ template return coverage_inclusive_ends (sa, eaE.decrement(), sb, ebE.decrement()); } +template<> /*LIBTEMPORAL_API*/ OverlapType coverage_exclusive_ends (int64_t sa, int64_t eaE, int64_t sb, int64_t ebE); + class RangeList; @@ -174,9 +169,9 @@ class LIBTEMPORAL_API Range { * looping). If the argument is earlier than or equal to the end of * this range, do nothing. */ - timepos_t squish (timepos_t t) const { + timepos_t squish (timepos_t const & t) const { if (t >= _end) { - t = _start + (_start.distance (t) % length()); + return _start + (_start.distance (t) % length()); } return t; } diff --git a/libs/temporal/temporal/timeline.h b/libs/temporal/temporal/timeline.h index 7840bd0c97..6686c4e527 100644 --- a/libs/temporal/temporal/timeline.h +++ b/libs/temporal/temporal/timeline.h @@ -46,21 +46,35 @@ class timecnt_t; class LIBTEMPORAL_API timepos_t : public int62_t { public: timepos_t () : int62_t (false, 0) {} + timepos_t (TimeDomain d) : int62_t (d != AudioTime, 0) {} + + /* for now (Sept2020) do not allow implicit type conversions */ + + explicit timepos_t (samplepos_t s) : int62_t (false, samples_to_superclock (s, _thread_sample_rate)) {} explicit timepos_t (timecnt_t const &); /* will throw() if val is negative */ explicit timepos_t (Temporal::Beats const & b) : int62_t (false, b.to_ticks()) {} /* superclock_t and samplepos_t are the same underlying primitive type, - * which means we cannot use polymorphism to differentiate them. + * which means we cannot use polymorphism to differentiate them. But it + * turns out that we more or less never construct timepos_t from an + * integer representing superclocks. So, there's a normal constructor + * for the samples case above, and ::from_superclock() here. */ static timepos_t from_superclock (superclock_t s) { return timepos_t (false, s); } - static timepos_t from_samples (samplepos_t s) { return timepos_t (false, samples_to_superclock (s, _thread_sample_rate)); } + static timepos_t from_ticks (int64_t t) { return timepos_t (true, t); } + + static timepos_t zero (bool is_beats) { return timepos_t (is_beats, 0); } bool is_beats() const { return flagged(); } bool is_superclock() const { return !flagged(); } + bool positive () const { return val() > 0; } + bool negative () const { return val() < 0; } + bool zero () const { return val() == 0; } + Temporal::TimeDomain time_domain () const { if (flagged()) return Temporal::BeatTime; return Temporal::AudioTime; } - superclock_t superclocks() const { if (is_superclock()) return v; return _superclocks (); } + superclock_t superclocks() const { if (is_superclock()) return val(); return _superclocks (); } int64_t samples() const { return superclock_to_samples (superclocks(), _thread_sample_rate); } int64_t ticks() const { if (is_beats()) return val(); return _ticks (); } Beats beats() const { if (is_beats()) return Beats::ticks (val()); return _beats (); } @@ -69,8 +83,12 @@ class LIBTEMPORAL_API timepos_t : public int62_t { timepos_t & operator= (superclock_t s) { v = s; 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; } + timepos_t operator-() const { return timepos_t (int62_t::operator-()); } + + /* if both values are zero, the time domain doesn't matter */ + bool operator== (timepos_t const & other) const { return (val() == 0 && other.val() == 0) || (v == other.v); } + bool operator!= (timepos_t const & other) const { return (val() != 0 || other.val() != 0) && (v != other.v); } + bool operator< (timecnt_t const & other) const; bool operator> (timecnt_t const & other) const; @@ -89,8 +107,7 @@ class LIBTEMPORAL_API timepos_t : public int62_t { /* donn't provide operator+(samplepos_t) or operator+(superclock_t) * because the compiler can't disambiguate them and neither can we. - * to add such types, use ::from_samples() or ::from_superclock() to - * create a timepo_t and then add that. + * to add such types, create a timepo_t and then add that. */ /* operator-() poses severe and thorny problems for a class that represents position on a timeline. @@ -168,6 +185,16 @@ class LIBTEMPORAL_API timepos_t : public int62_t { timepos_t operator% (timecnt_t const &) const; timepos_t & operator%=(timecnt_t const &); + /* Although multiplication and division of positions seems unusual, + * these are used in Evoral::Curve when scaling a list of timed events + * along the x (time) axis. + */ + + timepos_t operator/(ratio_t const & n) const; + timepos_t operator*(ratio_t const & n) const; + timepos_t & operator/=(ratio_t const & n); + timepos_t & operator*=(ratio_t const & n); + bool operator< (superclock_t s) { return v < s; } bool operator< (Temporal::Beats const & b) { return beats() < b; } bool operator<= (superclock_t s) { return v <= s; } @@ -188,16 +215,14 @@ class LIBTEMPORAL_API timepos_t : public int62_t { bool string_to (std::string const & str); std::string to_string () const; - static timepos_t const & max() { return _max_timepos; } + static timepos_t max (TimeDomain td) { return timepos_t (td != AudioTime, int62_t::max); } private: - int64_t v; /* special private constructor for use when constructing timepos_t as a return value using arithmetic ops */ explicit timepos_t (bool b, int64_t v) : int62_t (b, v) {} - - static timepos_t _max_timepos; + explicit timepos_t (int62_t const & v) : int62_t (v) {} /* these can only be called after verifying that the time domain does * not match the relevant one i.e. call _beats() to get a Beats value @@ -226,8 +251,10 @@ class LIBTEMPORAL_API timepos_t : public int62_t { timepos_t expensive_add (Temporal::Beats const &) const; timepos_t expensive_add (timepos_t const & s) const; + int62_t operator- (int62_t) const { assert (0); } + int62_t operator- (int64_t) const { assert (0); } + using int62_t::operator int64_t; - using int62_t::operator-; using int62_t::operator-=; }; @@ -257,10 +284,16 @@ class LIBTEMPORAL_API timepos_t : public int62_t { class LIBTEMPORAL_API timecnt_t { public: /* default to zero superclocks @ zero */ - timecnt_t () : _distance (int62_t (false, 0)), _position (timepos_t::from_superclock (0)) {} + timecnt_t () : _distance (false, 0), _position (AudioTime) {} + timecnt_t (TimeDomain td) : _distance (td != AudioTime, 0), _position (td) {} timecnt_t (timecnt_t const &other) : _distance (other.distance()), _position (other.position()) {} + /* construct from sample count (position doesn't matter due to linear nature * of audio time */ + explicit timecnt_t (samplepos_t s, timepos_t const & pos) : _distance (int62_t (false, samples_to_superclock (s, _thread_sample_rate))), _position (pos) {} + explicit timecnt_t (samplepos_t s) : _distance (int62_t (false, samples_to_superclock (s, _thread_sample_rate))), _position (AudioTime) {} + /* construct from timeline types */ + explicit timecnt_t (timepos_t const & d) : _distance (d), _position (timepos_t::zero (d.flagged())) {} explicit timecnt_t (timepos_t const & d, timepos_t const & p) : _distance (d), _position (p) { assert (p.is_beats() == d.is_beats()); } explicit timecnt_t (timecnt_t const &, timepos_t const & pos); @@ -270,19 +303,24 @@ class LIBTEMPORAL_API timecnt_t { /* construct from 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()); } + static timecnt_t zero_at (TimeDomain td, timepos_t const & pos) { return timecnt_t (timepos_t (td), pos); } + /* superclock_t and samplepos_t are the same underlying primitive type, - * which means we cannot use polymorphism to differentiate them. + * See comments in timepos_t above. */ static timecnt_t from_superclock (superclock_t s, timepos_t const & pos) { return timecnt_t (int62_t (false, s), pos); } - static timecnt_t from_samples (samplepos_t s, timepos_t const & pos) { return timecnt_t (int62_t (false, samples_to_superclock (s, _thread_sample_rate)), pos); } + static timecnt_t from_ticks (int64_t ticks, timepos_t const & pos) { return timecnt_t (int62_t (true, ticks), pos); } /* Construct from just a distance value - position is assumed to be zero */ explicit timecnt_t (Temporal::Beats const & b) : _distance (true, b.to_ticks()), _position (Beats()) {} static timecnt_t from_superclock (superclock_t s) { return timecnt_t (int62_t (false, s), timepos_t::from_superclock (0)); } static timecnt_t from_samples (samplepos_t s) { return timecnt_t (int62_t (false, samples_to_superclock (s, _thread_sample_rate)), timepos_t::from_superclock (0)); } + static timecnt_t from_ticks (int64_t ticks) { return timecnt_t (int62_t (true, ticks), timepos_t::from_ticks (0)); } + int64_t magnitude() const { return _distance.val(); } int62_t const & distance() const { return _distance; } timepos_t const & position() const { return _position; } + timepos_t const & origin() const { return _position; } /* alias */ void set_position (timepos_t const &pos); bool positive() const { return _distance.val() > 0; } @@ -316,9 +354,14 @@ class LIBTEMPORAL_API timecnt_t { timecnt_t operator- (timecnt_t const & t) const; timecnt_t operator+ (timecnt_t const & t) const; + timecnt_t operator- (timepos_t const & t) const; + timecnt_t operator+ (timepos_t const & t) const; + timecnt_t & operator-= (timecnt_t const & t); timecnt_t & operator+= (timecnt_t const & t); + timecnt_t decrement () const { return timecnt_t (_distance - 1, _position); } + //timecnt_t operator- (timepos_t const & t) const; //timecnt_t operator+ (timepos_t const & t) const; //timecnt_t & operator-= (timepos_t); @@ -417,7 +460,9 @@ struct numeric_limits { namespace std { std::ostream& operator<< (std::ostream & o, Temporal::timecnt_t const & tc); +std::ostream& operator>> (std::istream & o, Temporal::timecnt_t const & tc); std::ostream& operator<< (std::ostream & o, Temporal::timepos_t const & tp); +std::ostream& operator>> (std::istream & o, Temporal::timepos_t const & tp); } #if 0 diff --git a/libs/temporal/temporal/types.h b/libs/temporal/temporal/types.h index 68251bce4d..56eed44f56 100644 --- a/libs/temporal/temporal/types.h +++ b/libs/temporal/temporal/types.h @@ -19,11 +19,17 @@ #ifndef __libpbd_position_types_h__ #define __libpbd_position_types_h__ +#include #include + #include +#include "pbd/integer_division.h" + namespace Temporal { +extern void init (); + /* Any position measured in audio samples. Assumed to be non-negative but not enforced. */ @@ -52,7 +58,45 @@ static const samplecnt_t max_samplecnt = INT64_MAX; static const int32_t ticks_per_beat = 1920; -typedef boost::rational ratio_t; +template +class _ratio_t { + public: + /* do not allow negative values, this is just a ratio */ + + _ratio_t (T n, T d) : _numerator (abs (n)), _denominator (abs(d)) { assert (_denominator != 0); } + _ratio_t (T n) : _numerator (abs (n)), _denominator (1) {} + + T numerator() const { return _numerator; } + T denominator() const { return _denominator; } + + bool is_unity() const { return _numerator == _denominator; } + bool is_zero() const { return _numerator == 0; } + + /* provide an easy way to multiply double by ratio_t. Note that this + must be written as ratio_t * double, not the other way around. We + are not trying to duplicate boost::rational here (which also doesn't + allow this without a lot of syntactic fluff. + */ + double operator* (double v) const { return (v * (double) _numerator) / (double) _denominator; } + + /* ditto for int64_t */ + + int64_t operator* (int64_t v) const { return int_div_round (v * _numerator, _denominator); } + + private: + T _numerator; + T _denominator; +}; + +typedef _ratio_t ratio_t; + +enum OverlapType { + OverlapNone, // no overlap + OverlapInternal, // the overlap is 100% within the object + OverlapStart, // overlap covers start, but ends within + OverlapEnd, // overlap begins within and covers end + OverlapExternal // overlap extends to (at least) begin+end +}; enum TimeDomain { /* simple ordinals, since these are mutually exclusive */ diff --git a/libs/temporal/temporal/visibility.h b/libs/temporal/temporal/visibility.h index e189469aab..b9ba81a074 100644 --- a/libs/temporal/temporal/visibility.h +++ b/libs/temporal/temporal/visibility.h @@ -23,16 +23,22 @@ #define LIBTEMPORAL_DLL_IMPORT __declspec(dllimport) #define LIBTEMPORAL_DLL_EXPORT __declspec(dllexport) #define LIBTEMPORAL_DLL_LOCAL + #define LIBTEMPORAL_TEMPLATE_DLL_IMPORT __declspec(dllimport) + #define LIBTEMPORAL_TEMPLATE_DLL_EXPORT __declspec(dllexport) #else #define LIBTEMPORAL_DLL_IMPORT __attribute__ ((visibility ("default"))) #define LIBTEMPORAL_DLL_EXPORT __attribute__ ((visibility ("default"))) #define LIBTEMPORAL_DLL_LOCAL __attribute__ ((visibility ("hidden"))) + #define LIBTEMPORAL_TEMPLATE_DLL_IMPORT __attribute__ ((visibility ("default"))) + #define LIBTEMPORAL_TEMPLATE_DLL_EXPORT __attribute__ ((visibility ("default"))) #endif #ifdef LIBTEMPORAL_DLL_EXPORTS // defined if we are building the libtimecode DLL (instead of using it) #define LIBTEMPORAL_API LIBTEMPORAL_DLL_EXPORT + #define LIBTEMPORAL_TEMPLATE_API LIBTEMPORAL_TEMPLATE_DLL_EXPORT #else #define LIBTEMPORAL_API LIBTEMPORAL_DLL_IMPORT + #define LIBTEMPORAL_TEMPLATE_API LIBTEMPORAL_TEMPLATE_DLL_IMPORT #endif #define LIBTEMPORAL_LOCAL LIBTEMPORAL_DLL_LOCAL diff --git a/libs/temporal/timeline.cc b/libs/temporal/timeline.cc index be4746bff6..8cba5a6d2c 100644 --- a/libs/temporal/timeline.cc +++ b/libs/temporal/timeline.cc @@ -228,7 +228,7 @@ timepos_t::operator= (timecnt_t const & t) void timepos_t::set_superclock (superclock_t s) { - v = s; + v = build (false, s); } void @@ -261,6 +261,38 @@ timepos_t::_ticks () const return _beats().to_ticks(); } +timepos_t +timepos_t::operator/(ratio_t const & n) const +{ + /* this cannot make the value negative, since ratio_t is always positive */ + /* note: v / (N/D) = (v * D) / N */ + + return timepos_t (is_beats(), int_div_round (val() * n.denominator(), n.numerator())); +} + +timepos_t +timepos_t::operator*(ratio_t const & n) const +{ + /* this cannot make the value negative, since ratio_t is always positive */ + return timepos_t (is_beats(), int_div_round (val() * n.numerator(), n.denominator())); +} + +timepos_t & +timepos_t::operator/=(ratio_t const & n) +{ + /* this cannot make the value negative, since ratio_t is always positive */ + v = build (flagged(), int_div_round (val() * n.numerator(), n.denominator())); + return *this; +} + +timepos_t & +timepos_t::operator*=(ratio_t const & n) +{ + /* this cannot make the value negative, since ratio_t is always positive */ + v = build (flagged(), int_div_round (val() * n.denominator(), n.numerator())); + return *this; +} + timepos_t timepos_t::expensive_add (Beats const & b) const { diff --git a/libs/temporal/wscript b/libs/temporal/wscript index 573afe1588..576eb62417 100644 --- a/libs/temporal/wscript +++ b/libs/temporal/wscript @@ -27,6 +27,7 @@ I18N_PACKAGE = 'libtemporal' temporal_sources = [ 'debug.cc', 'bbt_time.cc', + 'enums.cc', 'tempo.cc', 'time.cc', 'timeline.cc',