Paul Davis
0acc1a3fec
If time domains differ, it is necessary to first convert the argument duration into a duration at the position of "this", in the correct time domain. Then we recursively call the operator again, but this time we will use the fast path that just adds two timepos_t values.
909 lines
20 KiB
C++
909 lines
20 KiB
C++
/*
|
|
Copyright (C) 2017 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <exception>
|
|
#include <sstream>
|
|
|
|
#include "pbd/enumwriter.h"
|
|
#include "pbd/error.h"
|
|
#include "pbd/compose.h"
|
|
|
|
#include "temporal/debug.h"
|
|
#include "temporal/timeline.h"
|
|
#include "temporal/tempo.h"
|
|
|
|
#include "pbd/i18n.h"
|
|
|
|
using namespace PBD;
|
|
using namespace Temporal;
|
|
|
|
struct TemporalStatistics
|
|
{
|
|
int64_t audio_to_beats;
|
|
int64_t audio_to_bars;
|
|
int64_t beats_to_audio;
|
|
int64_t beats_to_bars;
|
|
int64_t bars_to_audio;
|
|
int64_t bars_to_beats;
|
|
|
|
TemporalStatistics ()
|
|
: audio_to_beats (0)
|
|
, audio_to_bars (0)
|
|
, beats_to_audio (0)
|
|
, beats_to_bars (0)
|
|
, bars_to_audio (0)
|
|
, bars_to_beats (0)
|
|
{}
|
|
|
|
void dump (std::ostream & str) {
|
|
str << "TemporalStatistics\n"
|
|
<< "Audio => Beats " << audio_to_beats << ' '
|
|
<< "Audio => Bars " << audio_to_bars << ' '
|
|
<< "Beats => Audio " << beats_to_audio << ' '
|
|
<< "Beats => Bars " << beats_to_bars << ' '
|
|
<< "Bars => Audio " << bars_to_audio << ' '
|
|
<< "Bars => Beats " << bars_to_beats
|
|
<< std::endl;
|
|
}
|
|
};
|
|
|
|
static TemporalStatistics stats;
|
|
|
|
void Temporal::dump_stats (std::ostream& o) { stats.dump (o); }
|
|
|
|
/* timecnt */
|
|
|
|
timecnt_t timecnt_t::_max_timecnt (timecnt_t::from_superclock (int62_t::max - 1));
|
|
|
|
timecnt_t::timecnt_t (timecnt_t const & tc, timepos_t const & pos)
|
|
: _position (pos)
|
|
{
|
|
if (tc.distance() < 0) {
|
|
throw std::domain_error (X_("negative distance in timecnt constructor"));
|
|
}
|
|
|
|
_distance = tc.distance();
|
|
}
|
|
|
|
|
|
timepos_t
|
|
timecnt_t::end (TimeDomain return_domain) const
|
|
{
|
|
if (_distance.flagged() && _position.time_domain() == BeatTime && return_domain == BeatTime) {
|
|
/* everything in BeatTime, so just add */
|
|
return timepos_t (_position.beats() + Beats::ticks (magnitude()));
|
|
}
|
|
|
|
if (!_distance.flagged() && _position.time_domain() == AudioTime && return_domain == AudioTime) {
|
|
/* everything in AudioTime, so just add */
|
|
return timepos_t::from_superclock (_position.superclocks() + magnitude());
|
|
}
|
|
|
|
if (_distance.flagged()) { /* _distance in beats */
|
|
|
|
if (_position.time_domain() == BeatTime) {
|
|
|
|
/* distance & position in beats, so return must be audio (all 3 as beats is handled above) */
|
|
return timepos_t::from_superclock (TempoMap::use()->superclock_at ( _position.beats() + Beats::ticks (magnitude())));
|
|
|
|
} else if (_position.time_domain() == AudioTime) {
|
|
|
|
const Beats b = TempoMap::use()->quarters_at_superclock (_position.superclocks() + magnitude());
|
|
|
|
if (return_domain == BeatTime) {
|
|
return timepos_t (b);
|
|
} else {
|
|
return timepos_t::from_superclock (TempoMap::use()->superclock_at (b));
|
|
}
|
|
}
|
|
|
|
} /* else _distance in audio time */
|
|
|
|
if (_position.time_domain() == AudioTime) {
|
|
/* distance & position in audio, so return must be beats (all 3 as audio is handled above) */
|
|
return timepos_t (TempoMap::use()->quarters_at_superclock (_position.superclocks() + magnitude()));
|
|
|
|
} /* else if (_position.time_domain() == BeatTime) { */
|
|
|
|
const superclock_t sc = TempoMap::use()->superclock_at (_position.beats()) + magnitude();
|
|
|
|
if (return_domain == AudioTime) {
|
|
return timepos_t::from_superclock (sc);
|
|
}
|
|
|
|
return timepos_t (TempoMap::use()->quarters_at_superclock (sc));
|
|
}
|
|
|
|
void
|
|
timecnt_t::set_time_domain (TimeDomain td)
|
|
{
|
|
if (time_domain() == td) {
|
|
return;
|
|
}
|
|
|
|
_position.set_time_domain (td);
|
|
|
|
if (_distance.flagged()) {
|
|
/* beats -> superclock */
|
|
_distance = int62_t (false, TempoMap::use()->superclock_at (Beats::ticks (magnitude())));
|
|
} else {
|
|
/* superclock -> beats */
|
|
_distance = int62_t (true, TempoMap::use()->quarters_at_superclock (magnitude()).to_ticks());
|
|
}
|
|
}
|
|
|
|
void
|
|
timecnt_t::set_position (timepos_t const & pos)
|
|
{
|
|
_position = pos;
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::abs () const
|
|
{
|
|
return timecnt_t (_distance.abs(), _position);
|
|
}
|
|
|
|
superclock_t
|
|
timecnt_t::compute_superclocks() const
|
|
{
|
|
assert (_distance.flagged());
|
|
TempoMap::SharedPtr tm (TempoMap::use());
|
|
return tm->convert_duration (*this, _position, AudioTime).superclocks();
|
|
}
|
|
|
|
Beats
|
|
timecnt_t::compute_beats() const
|
|
{
|
|
assert (!_distance.flagged());
|
|
return TempoMap::use()->convert_duration (*this, _position, BeatTime).beats();
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator*(ratio_t const & r) const
|
|
{
|
|
const int62_t v (_distance.flagged(), int_div_round (_distance.val() * r.numerator(), r.denominator()));
|
|
return timecnt_t (v, _position);
|
|
}
|
|
|
|
ratio_t
|
|
timecnt_t::operator/ (timecnt_t const & other) const
|
|
{
|
|
if (time_domain() == other.time_domain()) {
|
|
return ratio_t (distance().val(), other.distance().val());
|
|
}
|
|
|
|
if (time_domain() == AudioTime) {
|
|
return ratio_t (distance().val(), other.samples());
|
|
}
|
|
|
|
return ratio_t (beats().to_ticks(), other.beats().to_ticks());
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator/(ratio_t const & r) const
|
|
{
|
|
/* note: x / (N/D) => x * (D/N) => (x * D) / N */
|
|
|
|
const int62_t v (_distance.flagged(), int_div_round (_distance.val() * r.denominator(), r.numerator()));
|
|
return timecnt_t (v, _position);
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator% (timecnt_t const & d) const
|
|
{
|
|
return timecnt_t (_distance % d.distance(), _position);
|
|
}
|
|
|
|
timecnt_t &
|
|
timecnt_t::operator%= (timecnt_t const & d)
|
|
{
|
|
_distance %= d.distance();
|
|
return *this;
|
|
}
|
|
|
|
bool
|
|
timecnt_t::string_to (std::string const & str)
|
|
{
|
|
superclock_t s;
|
|
samplecnt_t sm;
|
|
int64_t ticks;
|
|
Beats beats;
|
|
char sep;
|
|
|
|
if (isdigit (str[0])) {
|
|
/* old school position format: we assume samples */
|
|
std::stringstream ss (str);
|
|
ss >> sm;
|
|
_distance = int62_t (false, samples_to_superclock (sm, TEMPORAL_SAMPLE_RATE));
|
|
_position = timepos_t (AudioTime);
|
|
// std::cerr << "deserialized timecnt from older " << str << " as " << *this << std::endl;
|
|
return true;
|
|
}
|
|
|
|
std::stringstream ss (str.substr (1));
|
|
|
|
switch (str[0]) {
|
|
case 'a':
|
|
ss >> s;
|
|
_distance = int62_t (false, s);
|
|
break;
|
|
case 'b':
|
|
ss >> ticks;
|
|
_distance = int62_t (true, ticks);
|
|
break;
|
|
}
|
|
|
|
/* eat separator character */
|
|
|
|
ss >> sep;
|
|
|
|
/* grab what's left, generate a new string and parse _position with it */
|
|
|
|
std::string remaining;
|
|
ss >> remaining;
|
|
|
|
_position.string_to (remaining);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
timecnt_t::to_string () const
|
|
{
|
|
std::stringstream ss;
|
|
|
|
if (_distance.flagged()) {
|
|
ss << 'b';
|
|
} else {
|
|
ss << 'a';
|
|
}
|
|
|
|
ss << _distance.val();
|
|
|
|
/* add a separator. character doesn't matter as long as it will never be
|
|
parsed as part of a numerical value. Using '@' makes it "read
|
|
nicely" e.g. "3 beats at superclock 28229992292"
|
|
*/
|
|
|
|
ss << '@';
|
|
ss << _position.to_string();
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator+ (timecnt_t const & other) const
|
|
{
|
|
if (time_domain() == other.time_domain()) {
|
|
int62_t v (_distance.flagged(), _distance.val() + other.distance().val());
|
|
return timecnt_t (v, _position);
|
|
}
|
|
|
|
/* mismatched time domains */
|
|
|
|
if (time_domain() == AudioTime) {
|
|
/* other must be beats */
|
|
return timecnt_t (_distance + other.superclocks(), _position);
|
|
}
|
|
|
|
return timecnt_t (beats() + other.beats(), _position);
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator- (timecnt_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
if (other.time_domain() == AudioTime) {
|
|
return timecnt_t (_distance - other.distance(), _position);
|
|
} else {
|
|
return timecnt_t (_distance - other.samples(), _position);
|
|
}
|
|
}
|
|
|
|
return timecnt_t (beats() - other.beats(), _position);
|
|
}
|
|
|
|
timecnt_t &
|
|
timecnt_t::operator+= (timecnt_t const & other)
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
if (other.time_domain() == AudioTime) {
|
|
_distance += other.distance();
|
|
} else {
|
|
_distance += other.samples();
|
|
}
|
|
} else {
|
|
_distance += other.ticks ();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator+ (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
if (other.time_domain() == AudioTime) {
|
|
/* both audio, just add and use an arbitrary position */
|
|
return timecnt_t (_distance + other.val(), _position);
|
|
} else {
|
|
return timecnt_t (_distance + other.samples(), _position);
|
|
}
|
|
}
|
|
|
|
return timecnt_t (beats() + other.beats(), _position);
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator- (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
if (other.time_domain() == AudioTime) {
|
|
return timecnt_t (_distance - other.val(), _position);
|
|
} else {
|
|
return timecnt_t (_distance - other.samples(), _position);
|
|
}
|
|
}
|
|
|
|
return timecnt_t (beats() - other.beats(), _position);
|
|
}
|
|
|
|
timecnt_t &
|
|
timecnt_t::operator-= (timecnt_t const & other)
|
|
{
|
|
if (time_domain() == other.time_domain()) {
|
|
_distance -= other.distance();
|
|
} else if (time_domain() == AudioTime) {
|
|
_distance -= other.samples();
|
|
} else {
|
|
_distance -= other.ticks ();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
timecnt_t
|
|
timecnt_t::operator- () const
|
|
{
|
|
return timecnt_t (-_distance, _position);
|
|
}
|
|
|
|
bool
|
|
timecnt_t::expensive_lt (timecnt_t const & other) const
|
|
{
|
|
if (!_distance.flagged()) { /* Audio */
|
|
return _distance.val() < other.superclocks();
|
|
}
|
|
|
|
return Beats::ticks (_distance.val()) < other.beats ();
|
|
}
|
|
|
|
bool
|
|
timecnt_t::expensive_gt (timecnt_t const & other) const
|
|
{
|
|
if (!_distance.flagged()) { /* Audio */
|
|
return _distance.val() > other.superclocks();
|
|
}
|
|
|
|
return Beats::ticks (_distance.val()) > other.beats ();
|
|
}
|
|
|
|
bool
|
|
timecnt_t::expensive_lte (timecnt_t const & other) const
|
|
{
|
|
if (!_distance.flagged()) { /* Audio */
|
|
return _distance.val() <= other.superclocks();
|
|
}
|
|
|
|
return Beats::ticks (_distance.val()) <= other.beats ();
|
|
}
|
|
|
|
bool
|
|
timecnt_t::expensive_gte (timecnt_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return _distance.val() >= other.superclocks();
|
|
}
|
|
|
|
return Beats::ticks (_distance.val()) >= other.beats ();
|
|
}
|
|
|
|
std::ostream&
|
|
std::operator<< (std::ostream & o, timecnt_t const & tc)
|
|
{
|
|
return o << tc.to_string();
|
|
}
|
|
|
|
std::istream&
|
|
std::operator>> (std::istream & o, timecnt_t & tc)
|
|
{
|
|
std::string str;
|
|
o >> str; /* will break at whitespace */
|
|
tc.string_to (str);
|
|
return o;
|
|
}
|
|
|
|
|
|
/* timepos */
|
|
|
|
timepos_t::timepos_t (timecnt_t const & t)
|
|
{
|
|
if (t.distance() < 0) {
|
|
std::cerr << "timecnt_t has negative distance distance " << " val " << t.distance().val() << " flagged " << t.distance().flagged() << std::endl;
|
|
throw std::domain_error("negative value for timepos_t constructor");
|
|
}
|
|
|
|
v = build (t.distance().flagged(), t.distance ().val());
|
|
}
|
|
|
|
void
|
|
timepos_t::set_time_domain (TimeDomain td)
|
|
{
|
|
if (td == time_domain()) {
|
|
return;
|
|
}
|
|
|
|
if (td == AudioTime) {
|
|
v = build (false, _superclocks());
|
|
} else {
|
|
v = build (true, _beats().to_ticks());
|
|
}
|
|
}
|
|
|
|
// timepos_t timepos_t::_max_timepos (Temporal::AudioTime);
|
|
|
|
timepos_t &
|
|
timepos_t::operator= (timecnt_t const & t)
|
|
{
|
|
v = build (t.distance().flagged(), t.distance().val());
|
|
return *this;
|
|
}
|
|
|
|
bool
|
|
timepos_t::operator< (timecnt_t const & t) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() < t.superclocks();
|
|
}
|
|
|
|
return beats() < t.beats ();
|
|
}
|
|
|
|
bool
|
|
timepos_t::operator> (timecnt_t const & t) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() > t.superclocks();
|
|
}
|
|
|
|
return beats() > t.beats ();
|
|
}
|
|
|
|
bool
|
|
timepos_t::operator<= (timecnt_t const & t) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() <= t.superclocks();
|
|
}
|
|
|
|
return beats() <= t.beats ();
|
|
}
|
|
|
|
bool
|
|
timepos_t::operator>= (timecnt_t const & t) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() >= t.superclocks();
|
|
}
|
|
|
|
return beats() >= t.beats ();
|
|
}
|
|
|
|
superclock_t
|
|
timepos_t::_superclocks () const
|
|
{
|
|
assert (time_domain() == BeatTime);
|
|
stats.beats_to_audio++;
|
|
|
|
return TempoMap::use()->superclock_at (beats ());
|
|
}
|
|
|
|
Temporal::Beats
|
|
timepos_t::_beats () const
|
|
{
|
|
assert (time_domain() == AudioTime);
|
|
stats.audio_to_beats++;
|
|
|
|
/* see notes in Temporal::TempoPoint::quarters_at_superclock() for
|
|
* more. Basically, specially case "max-superclocks" and return
|
|
* "max-beats"
|
|
*/
|
|
|
|
if (val() == int62_t::max) {
|
|
return std::numeric_limits<Beats>::max ();
|
|
}
|
|
|
|
return TempoMap::use()->quarters_at_superclock (v);
|
|
}
|
|
|
|
int64_t
|
|
timepos_t::_ticks () const
|
|
{
|
|
assert (time_domain() == AudioTime);
|
|
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 (timepos_t const & other) const
|
|
{
|
|
/* Called when other's time domain does not match our own, requiring us
|
|
to call either ::beats() or ::superclocks() on other to convert it to
|
|
our time domain.
|
|
*/
|
|
|
|
assert (is_beats() != other.is_beats ());
|
|
|
|
if (is_beats()) {
|
|
/* we are known to use music time, so val() is in ticks */
|
|
return timepos_t::from_ticks (val() + other.ticks());
|
|
}
|
|
|
|
/* we are known to use audio time, so val() is in superclocks */
|
|
return timepos_t::from_superclock (val() + other.superclocks());
|
|
}
|
|
|
|
/* */
|
|
|
|
/* ::distance() assumes that @param other is later on the timeline than this, and
|
|
* thus returns a positive value if this condition is satisfied.
|
|
*/
|
|
|
|
timecnt_t
|
|
timepos_t::distance (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == other.time_domain()) {
|
|
// std::cerr << "\ncomputing distance in " << enum_2_string (time_domain()) << std::endl;
|
|
return timecnt_t (int62_t (is_beats(), other.val() - val()), *this);
|
|
}
|
|
|
|
// std::cerr << "\ncomputing distance on " << enum_2_string (time_domain()) << " w/other = " << enum_2_string (other.time_domain()) << std::endl;
|
|
|
|
return expensive_distance (other);
|
|
}
|
|
|
|
timecnt_t
|
|
timepos_t::expensive_distance (timepos_t const & other) const
|
|
{
|
|
/* Called when other's time domain does not match our own, requiring us
|
|
to call either ::beats() or ::superclocks() on other to convert it to
|
|
our time domain.
|
|
*/
|
|
|
|
assert (is_beats() != other.is_beats ());
|
|
|
|
if (is_beats()) {
|
|
/* we are known to use beat time: val() is ticks */
|
|
return timecnt_t::from_ticks (other.ticks() - val(), *this);
|
|
}
|
|
/* we known to be audio: val() is superclocks */
|
|
|
|
// std::cerr << "other " << other << " SC = " << other.superclocks() << " vs. us @ " << val() << std::endl;
|
|
return timecnt_t::from_superclock (other.superclocks() - val(), *this);
|
|
}
|
|
|
|
/* */
|
|
|
|
timepos_t
|
|
timepos_t::earlier (Temporal::BBT_Offset const & offset) const
|
|
{
|
|
TempoMap::SharedPtr tm (TempoMap::use());
|
|
|
|
if (is_superclock()) {
|
|
return timepos_t (tm->superclock_at (tm->bbt_walk (tm->bbt_at (*this), -offset)));
|
|
}
|
|
|
|
return timepos_t (tm->bbtwalk_to_quarters (beats(), -offset));
|
|
}
|
|
|
|
|
|
timepos_t
|
|
timepos_t::earlier (timepos_t const & other) const
|
|
{
|
|
if (is_superclock()) {
|
|
return timepos_t::from_superclock (val() - other.superclocks());
|
|
}
|
|
|
|
return timepos_t::from_ticks (val() - other.ticks());
|
|
}
|
|
|
|
timepos_t
|
|
timepos_t::earlier (timecnt_t const & distance) const
|
|
{
|
|
if (is_superclock()) {
|
|
return timepos_t::from_superclock (val() - distance.superclocks());
|
|
}
|
|
|
|
return timepos_t::from_ticks (val() - distance.ticks());
|
|
}
|
|
|
|
bool
|
|
timepos_t::expensive_lt (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return val() < other.superclocks();
|
|
}
|
|
|
|
return ticks() < other.ticks ();
|
|
}
|
|
|
|
bool
|
|
timepos_t::expensive_gt (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() > other.superclocks();
|
|
}
|
|
|
|
return beats() > other.beats ();
|
|
}
|
|
|
|
bool
|
|
timepos_t::expensive_lte (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() <= other.superclocks();
|
|
}
|
|
|
|
return beats() <= other.beats ();
|
|
}
|
|
|
|
bool
|
|
timepos_t::expensive_gte (timepos_t const & other) const
|
|
{
|
|
if (time_domain() == AudioTime) {
|
|
return superclocks() >= other.superclocks();
|
|
}
|
|
|
|
return beats() >= other.beats ();
|
|
}
|
|
|
|
/* */
|
|
|
|
timepos_t &
|
|
timepos_t::shift_earlier (timepos_t const & d)
|
|
{
|
|
if (is_superclock()) {
|
|
v = build (false, val() - d.superclocks());
|
|
} else {
|
|
v = build (true, val() - d.ticks());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
timepos_t &
|
|
timepos_t::shift_earlier (timecnt_t const & d)
|
|
{
|
|
if (is_superclock()) {
|
|
v = build (false, val() - d.superclocks());
|
|
} else {
|
|
v = build (true, val() - d.ticks());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
timepos_t &
|
|
timepos_t::shift_earlier (Temporal::BBT_Offset const & offset)
|
|
{
|
|
TempoMap::SharedPtr tm (TempoMap::use());
|
|
|
|
if (is_superclock()) {
|
|
v = build (false, (tm->superclock_at (tm->bbt_walk (tm->bbt_at (*this), -offset))));
|
|
} else {
|
|
v = build (true, tm->bbtwalk_to_quarters (beats(), -offset).to_ticks());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/* */
|
|
|
|
timepos_t &
|
|
timepos_t::operator+= (Temporal::BBT_Offset const & offset)
|
|
{
|
|
TempoMap::SharedPtr tm (TempoMap::use());
|
|
if (is_beats()) {
|
|
v = build (true, tm->bbtwalk_to_quarters (beats(), offset).to_ticks());
|
|
} else {
|
|
v = build (false, tm->superclock_at (tm->bbt_walk (tm->bbt_at (*this), offset)));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/* */
|
|
|
|
timepos_t
|
|
timepos_t::operator+(timecnt_t const & d) const
|
|
{
|
|
if (d.time_domain() == time_domain()) {
|
|
if (time_domain() == AudioTime) {
|
|
return operator+ (timepos_t::from_superclock (d.superclocks()));
|
|
} else {
|
|
return operator+ (timepos_t::from_ticks (d.ticks()));
|
|
}
|
|
}
|
|
|
|
TempoMap::SharedPtr tm (TempoMap::use());
|
|
|
|
timecnt_t dur_at_this = tm->convert_duration (d, *this, time_domain());
|
|
|
|
assert (dur_at_this.time_domain() == time_domain());
|
|
|
|
return operator+ (dur_at_this);
|
|
}
|
|
|
|
timepos_t &
|
|
timepos_t::operator+=(timecnt_t const & d)
|
|
{
|
|
if (d.time_domain() == time_domain()) {
|
|
if (time_domain() == AudioTime) {
|
|
return operator+= (timepos_t::from_superclock (d.superclocks()));
|
|
} else {
|
|
return operator+= (timepos_t::from_ticks (d.ticks()));
|
|
}
|
|
}
|
|
|
|
TempoMap::SharedPtr tm (TempoMap::use());
|
|
|
|
timecnt_t dur_at_this = tm->convert_duration (d, *this, time_domain());
|
|
|
|
assert (dur_at_this.time_domain() == time_domain());
|
|
|
|
return operator+= (dur_at_this);
|
|
}
|
|
|
|
/* */
|
|
|
|
timepos_t &
|
|
timepos_t::operator+=(timepos_t const & d)
|
|
{
|
|
if (d.is_beats() == is_beats()) {
|
|
|
|
/* same time domain, keep flag bit, add values */
|
|
|
|
v = build (flagged(), val() + d.val());
|
|
|
|
} else {
|
|
|
|
/* different time domain, return a value in the same domain as
|
|
* this one
|
|
*/
|
|
|
|
if (is_beats()) {
|
|
v = build (true, val() + d.ticks());
|
|
} else {
|
|
v = build (false, val() + d.superclocks());
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
std::ostream&
|
|
std::operator<< (std::ostream & o, timepos_t const & tp)
|
|
{
|
|
return o << tp.to_string();
|
|
}
|
|
|
|
std::istream&
|
|
std::operator>> (std::istream & o, timepos_t & tp)
|
|
{
|
|
std::string str;
|
|
o >> str; /* should break on whitespace */
|
|
tp.string_to (str);
|
|
return o;
|
|
}
|
|
|
|
std::string
|
|
timepos_t::to_string () const
|
|
{
|
|
if (is_beats()) {
|
|
return string_compose ("b%1", val());
|
|
}
|
|
|
|
return string_compose ("a%1", val());
|
|
}
|
|
|
|
bool
|
|
timepos_t::string_to (std::string const & str)
|
|
{
|
|
using std::string;
|
|
using std::cerr;
|
|
using std::endl;
|
|
|
|
superclock_t s;
|
|
samplepos_t sm;
|
|
int64_t ticks;
|
|
Beats beats;
|
|
|
|
if (isdigit (str[0])) {
|
|
/* old school position format: we assume samples */
|
|
std::stringstream ss (str);
|
|
ss >> sm;
|
|
v = build (false, samples_to_superclock (sm, TEMPORAL_SAMPLE_RATE));
|
|
// cerr << "deserialized timepos from older " << str << " as " << *this << " with sm = " << sm << " and sr = " << TEMPORAL_SAMPLE_RATE << " s2sc " << endl;
|
|
return true;
|
|
}
|
|
|
|
std::stringstream ss (str.substr (1));
|
|
|
|
switch (str[0]) {
|
|
case 'a':
|
|
ss >> s;
|
|
v = build (false, s);
|
|
// cerr << "deserialized timepos from " << str << " as " << *this << endl;
|
|
return true;
|
|
case 'b':
|
|
ss >> ticks;
|
|
v = build (true, ticks);
|
|
// cerr << "deserialized timepos from " << str << " as " << *this << endl;
|
|
return true;
|
|
}
|
|
|
|
std::cerr << "Unknown timepos string representation \"" << str << "\"" << std::endl;
|
|
|
|
return false;
|
|
}
|