first pass at integrating nutempo v1 with new 64 bit superclock/ticks representation

This compiles libtemporal, but nothing more
This commit is contained in:
Paul Davis 2020-07-23 11:21:53 -06:00
parent e0b5b12129
commit a80960468d
7 changed files with 1227 additions and 155 deletions

View File

@ -1,39 +1,27 @@
/*
* Copyright (C) 2017 Paul Davis <paul@linuxaudiosystems.com>
*
* 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.
*/
Copyright (C) 2002-2010 Paul Davis
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cmath>
#include <cassert>
#include "temporal/bbt_time.h"
using namespace Timecode;
/* This defines the smallest division of a "beat".
The number is intended to have as many integer factors as possible so that
1/Nth divisions are integer numbers of ticks.
1920 has many factors, though going up to 3840 gets a couple more.
This needs to match Temporal::Beats::PPQN
*/
const double BBT_Time::ticks_per_beat = 1920.0;
using namespace Temporal;
BBT_Offset::BBT_Offset (double dbeats)
{
@ -46,5 +34,53 @@ BBT_Offset::BBT_Offset (double dbeats)
bars = 0;
beats = lrint (floor (dbeats));
ticks = lrint (floor (BBT_Time::ticks_per_beat * fmod (dbeats, 1.0)));
ticks = lrint (floor (Temporal::ticks_per_beat * fmod (dbeats, 1.0)));
}
std::ostream&
std::operator<< (std::ostream& o, Temporal::BBT_Time const & bbt)
{
o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
return o;
}
std::ostream&
std::operator<< (std::ostream& o, const Temporal::BBT_Offset& bbt)
{
o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
return o;
}
std::istream&
std::operator>>(std::istream& i, Temporal::BBT_Offset& bbt)
{
int32_t B, b, t;
char skip_pipe_char;
i >> B;
i >> skip_pipe_char;
i >> b;
i >> skip_pipe_char;
i >> t;
bbt = Temporal::BBT_Time (B, b, t);
return i;
}
std::istream&
std::operator>>(std::istream& i, Temporal::BBT_Time& bbt)
{
int32_t B, b, t;
char skip_pipe_char;
i >> B;
i >> skip_pipe_char;
i >> b;
i >> skip_pipe_char;
i >> t;
bbt = Temporal::BBT_Time (B, b, t);
return i;
}

View File

@ -1,38 +1,51 @@
/*
* Copyright (C) 2017 Paul Davis <paul@linuxaudiosystems.com>
*
* 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.
*/
Copyright (C) 2002-2010 Paul Davis
#ifndef __timecode_bbt_time_h__
#define __timecode_bbt_time_h__
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __libtemporal_bbt_time_h__
#define __libtemporal_bbt_time_h__
#include <ostream>
#include <istream>
#include <sstream>
#include <stdint.h>
#include <iomanip>
#include <exception>
#include <cmath>
#include <limits>
#include <cstdio>
#include "pbd/failed_constructor.h"
#include "pbd/string_convert.h"
#include "temporal/visibility.h"
#include "temporal/types.h"
namespace Timecode {
namespace Temporal {
struct IllegalBBTTimeException : public std::exception {
virtual const char* what() const throw() { return "illegal BBT time (bars or beats were zero, or ticks was too large)"; }
};
struct BBT_Offset;
/** Bar, Beat, Tick Time (i.e. Tempo-Based Time) */
struct LIBTEMPORAL_API BBT_Time
{
static const double ticks_per_beat;
/* note that it is illegal for BBT_Time to have bars==0 or
* beats==0. The "neutral" or "default" value is 1|1|0
*/
@ -41,12 +54,15 @@ struct LIBTEMPORAL_API BBT_Time
int32_t beats;
int32_t ticks;
struct IllegalBBTTimeException : public std::exception {
virtual const char* what() const throw() { return "illegal BBT time (bars or beats were zero)"; }
};
bool is_bar() const { return beats == 1 && ticks == 0; }
bool is_beat() const { return ticks == 0; }
BBT_Time () : bars (1), beats (1), ticks (0) {}
BBT_Time (int32_t ba, uint32_t be, uint32_t t) : bars (ba), beats (be), ticks (t) { if (!bars || !beats) { throw IllegalBBTTimeException(); } }
BBT_Time (int32_t ba, uint32_t be, uint32_t t) : bars (ba), beats (be), ticks (t) {
if (!bars || !beats) {
throw IllegalBBTTimeException();
}
}
bool operator< (const BBT_Time& other) const {
return bars < other.bars ||
@ -80,10 +96,17 @@ struct LIBTEMPORAL_API BBT_Time
return bars != other.bars || beats != other.beats || ticks != other.ticks;
}
/* it would be nice to provide operator+(BBT_Time const&) and
* operator-(BBT_Time const&) but this math requires knowledge of the
* meter (time signature) used to define 1 bar, and so cannot be
* carried out with only two BBT_Time values.
bool operator< (const BBT_Offset& other) const;
bool operator<= (const BBT_Offset& other) const;
bool operator> (const BBT_Offset& other) const;
bool operator>= (const BBT_Offset& other) const;
bool operator== (const BBT_Offset& other) const;
bool operator!= (const BBT_Offset& other) const;
/* it would be nice to provide +,-,* and / operators for BBT_Time but
* this math requires knowledge of the meter (time signature) used to
* define 1 bar, and so cannot be carried out with only two BBT_Time
* values.
*/
BBT_Time round_to_beat () const { return ticks >= (ticks_per_beat/2) ? BBT_Time (bars, beats+1, 0) : BBT_Time (bars, beats, 0); }
@ -91,8 +114,22 @@ struct LIBTEMPORAL_API BBT_Time
BBT_Time round_up_to_beat () const { return ticks ? BBT_Time (bars, beats+1, 0) : *this; }
/* cannot implement round_to_bar() without knowing meter (time
* signature) information.
* signature) information, since it requires knowing how many beats
* are in a bar, in order to decide if we are closer to the previous or
* next bar time.
*/
BBT_Time round_up_to_bar () const { return beats > 1 ? BBT_Time (bars+1, 1, 0) : BBT_Time (bars, 1, 0); }
BBT_Time round_down_to_bar () const { return BBT_Time (bars, 1, 0); }
BBT_Time next_bar () const { return (bars == -1) ? BBT_Time (1, 1, 0) : BBT_Time (bars+1, 1, 0); }
BBT_Time prev_bar () const { return (bars == 1) ? BBT_Time (-1, 1, 0) : BBT_Time (bars-1, 1, 0); }
void print_padded (std::ostream& o) {
o << std::setfill ('0') << std::right
<< std::setw (3) << bars << "|"
<< std::setw (2) << beats << "|"
<< std::setw (4) << ticks;
}
};
struct LIBTEMPORAL_API BBT_Offset
@ -110,33 +147,217 @@ struct LIBTEMPORAL_API BBT_Offset
BBT_Offset (int32_t ba, uint32_t be, uint32_t t) : bars (ba), beats (be), ticks (t) {}
BBT_Offset (BBT_Time const & bbt) : bars (bbt.bars), beats (bbt.beats), ticks (bbt.ticks) {}
BBT_Offset (double beats);
/* unlike BBT_Time, we can define +,-,* and / operators for BBT_Offset
* because there is no requirement that the result is "well-formed" or
* reflect the structure of a tempo map. It is just as valid to talk
* about an offset of 18 beats as an offset of 4 bars and 2 beats.
*/
BBT_Offset operator+(const BBT_Offset& other) const {
return BBT_Offset (bars+other.bars, beats+other.beats, ticks+other.ticks);
}
BBT_Offset operator-() const {
return BBT_Offset (-bars, -beats, -ticks);
}
BBT_Offset operator-(const BBT_Offset& other) const {
return BBT_Offset (bars-other.bars, beats-other.beats, ticks-other.ticks);
}
BBT_Offset & operator+=(const BBT_Offset& other) {
bars += other.bars;
beats += other.beats;
ticks += other.ticks;
return *this;
}
BBT_Offset & operator-=(const BBT_Offset& other) {
bars -= other.bars;
beats -= other.beats;
ticks -= other.ticks;
return *this;
}
BBT_Offset & operator*=(int factor) {
bars *= factor;
beats *= factor;
ticks *= factor;
return *this;
}
BBT_Offset & operator*=(double factor) {
bars = lrint (bars * factor);
beats = lrint (beats * factor);
ticks = lrint (ticks * factor);
return *this;
}
BBT_Offset & operator/=(int factor) {
bars /= factor;
beats /= factor;
ticks /= factor;
return *this;
}
BBT_Offset & operator/=(double factor) {
bars = lrint (bars / factor);
beats = lrint (beats / factor);
ticks = lrint (ticks / factor);
return *this;
}
bool operator< (const BBT_Offset& other) const {
return bars < other.bars ||
(bars == other.bars && beats < other.beats) ||
(bars == other.bars && beats == other.beats && ticks < other.ticks);
}
bool operator<= (const BBT_Offset& other) const {
return bars < other.bars ||
(bars <= other.bars && beats <= other.beats) ||
(bars <= other.bars && beats <= other.beats && ticks <= other.ticks);
}
bool operator> (const BBT_Offset& other) const {
return bars > other.bars ||
(bars == other.bars && beats > other.beats) ||
(bars == other.bars && beats == other.beats && ticks > other.ticks);
}
bool operator>= (const BBT_Offset& other) const {
return bars > other.bars ||
(bars >= other.bars && beats >= other.beats) ||
(bars >= other.bars && beats >= other.beats && ticks >= other.ticks);
}
bool operator== (const BBT_Offset& other) const {
return bars == other.bars && beats == other.beats && ticks == other.ticks;
}
bool operator!= (const BBT_Offset& other) const {
return bars != other.bars || beats != other.beats || ticks != other.ticks;
}
};
inline BBT_Offset LIBTEMPORAL_API bbt_delta (Temporal::BBT_Time const & a, Temporal::BBT_Time const & b) {
return Temporal::BBT_Offset (a.bars - b.bars, a.beats - b.beats, a.ticks - b.ticks);
}
inline std::ostream&
operator<< (std::ostream& o, const Timecode::BBT_Time& bbt)
inline bool
BBT_Time::operator< (const BBT_Offset& other) const
{
o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
return o;
return bars < other.bars ||
(bars == other.bars && beats < other.beats) ||
(bars == other.bars && beats == other.beats && ticks < other.ticks);
}
inline std::ostream&
operator<< (std::ostream& o, const Timecode::BBT_Offset& bbt)
inline bool
BBT_Time::operator<= (const BBT_Offset& other) const
{
o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
return o;
return bars < other.bars ||
(bars <= other.bars && beats <= other.beats) ||
(bars <= other.bars && beats <= other.beats && ticks <= other.ticks);
}
inline std::ostream&
print_padded (std::ostream& o, const Timecode::BBT_Time& bbt)
inline bool
BBT_Time::operator> (const BBT_Offset& other) const
{
o << std::setfill ('0') << std::right
<< std::setw (3) << bbt.bars << "|"
<< std::setw (2) << bbt.beats << "|"
<< std::setw (4) << bbt.ticks;
return o;
return bars > other.bars ||
(bars == other.bars && beats > other.beats) ||
(bars == other.bars && beats == other.beats && ticks > other.ticks);
}
#endif /* __timecode_bbt_time_h__ */
inline bool
BBT_Time::operator>= (const BBT_Offset& other) const
{
return bars > other.bars ||
(bars >= other.bars && beats >= other.beats) ||
(bars >= other.bars && beats >= other.beats && ticks >= other.ticks);
}
inline bool
BBT_Time::operator== (const BBT_Offset& other) const
{
return bars == other.bars && beats == other.beats && ticks == other.ticks;
}
inline bool
BBT_Time::operator!= (const BBT_Offset& other) const
{
return bars != other.bars || beats != other.beats || ticks != other.ticks;
}
} /* end of namespace Temporal */
/* Putting these into std:: seems wrong, but g++ is unable to find them
* otherwise
*/
namespace std {
std::ostream& operator<< (std::ostream& o, Temporal::BBT_Time const & bbt);
std::ostream& operator<< (std::ostream& o, Temporal::BBT_Offset const & bbt);
std::istream& operator>> (std::istream& i, Temporal::BBT_Time& bbt);
std::istream& operator>> (std::istream& i, Temporal::BBT_Offset& bbt);
template<>
struct numeric_limits<Temporal::BBT_Time> {
static Temporal::BBT_Time lowest() {
return Temporal::BBT_Time(1, 1, 0);
}
static Temporal::BBT_Time min() {
return Temporal::BBT_Time(1, 1, 0);
}
static Temporal::BBT_Time max() {
return Temporal::BBT_Time(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
}
};
template<>
struct numeric_limits<Temporal::BBT_Offset> {
static Temporal::BBT_Offset lowest() {
return Temporal::BBT_Time(0, 0, 0);
}
static Temporal::BBT_Offset min() {
return Temporal::BBT_Time(0, 0, 0);
}
static Temporal::BBT_Time max() {
return Temporal::BBT_Time(std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max());
}
};
} /* end of namespace std */
namespace PBD {
template<>
inline bool to_string (Temporal::BBT_Time val, std::string & str)
{
std::ostringstream ostr;
ostr << val;
str = ostr.str();
return true;
}
template<>
inline bool string_to (std::string const & str, Temporal::BBT_Time & val)
{
std::istringstream istr (str);
istr >> val;
return (bool) istr;
}
} /* end namespace PBD */
#endif /* __libtemporal_bbt_time_h__ */

View File

@ -135,6 +135,8 @@ public:
return Beats(ticks / ppqn, (ticks % ppqn) * PPQN / ppqn);
}
static int64_t make_ticks (Beats const & b) { return b.get_beats() * ticks_per_beat + b.get_ticks(); }
int64_t to_ticks() const { return (int64_t)_beats * PPQN + _ticks; }
int64_t to_ticks(uint32_t ppqn) const { return (int64_t)_beats * ppqn + (_ticks * ppqn / PPQN); }

View File

@ -0,0 +1,34 @@
/*
Copyright (C) 2018 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.
*/
#ifndef __temporal_debug_h__
#define __temporal_debug_h__
#include "temporal/visibility.h"
#include "pbd/debug.h"
namespace PBD {
namespace DEBUG {
LIBTEMPORAL_API extern DebugBits TemporalDomainConvert;
LIBTEMPORAL_API extern DebugBits TemporalMap;
}
}
#endif /* __ardour_debug_h__ */

View File

@ -0,0 +1,137 @@
/*
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __libtemporal_tempo_h__
#define __libtemporal_tempo_h__
#include <cassert>
#include <cmath>
#include <exception>
#include <limits>
#include <ostream>
#include <string>
#include "pbd/enumwriter.h"
#include "pbd/integer_division.h"
#include "temporal/beats.h"
#include "temporal/types.h"
#include "temporal/superclock.h"
#include "temporal/visibility.h"
/************************* !!!! ATTENTION !!!! *************************
NO FLOATING POINT ARITHMETIC IS ALLOWED IN THIS HEADER OR
ANY OF THE OBJECTS DEFINED HERE.
Exceptions:
1) constructors/methods that accept double from the user
2) clearly labelled methods that warn a developer to avoid
their use or use them only for display purposes.
************************************************************************/
namespace Temporal {
class TempoValue {
private:
/* beats per minute * big_numerator => rational number expressing (possibly fractional) bpm as superbeats-per-minute
*
* It is not required that big_numerator equal superclock_ticks_per_second but since the values in both cases have similar
* desired properties (many, many factors), it doesn't hurt to use the same number.
*/
static const superclock_t big_numerator = superclock_ticks_per_second;
public:
TempoValue (double bpm) : val (bpm) {
/* since we allow the user to provide bpm as a floating point value,
we allow use of floating point math to determine the two critical
integer values (superbeats-per-second and superclocks-per-beat
*/
_sbps = (superclock_t) llround (bpm * (big_numerator / 60));
_scpb = (superclock_t) llround ((60./ bpm) * superclock_ticks_per_second);
}
double given_bpm_for_display_only () const { return val; }
double actual_bpm_for_display_only () const { return (_sbps * 60) / (double) big_numerator; }
uint64_t ticks_per_second() const { return int_div_round ((_sbps * Temporal::ticks_per_beat), big_numerator); }
superclock_t superclocks_per_beat() const { return _scpb; }
Temporal::Beats superclocks_as_beats (superclock_t sc) const {
/* convert sc into superbeats, given that sc represents some number of seconds */
const superclock_t whole_seconds = sc / superclock_ticks_per_second;
const superclock_t remainder = sc - (whole_seconds * superclock_ticks_per_second);
const superclock_t superbeats = (_sbps * whole_seconds) + int_div_round ((_sbps * remainder), superclock_ticks_per_second);
/* convert superbeats to beats:ticks */
uint32_t b = superbeats / big_numerator;
const uint64_t remain = superbeats - (b * big_numerator);
uint32_t t = int_div_round ((Temporal::ticks_per_beat * remain), big_numerator);
return Beats (b, t);
}
superclock_t beats_as_superclocks (Temporal::Beats const & b) const {
/* no symmetrical with superclocks_as_beats() because Beats already breaks apart the beats:ticks,
with the ticks value denominated by Temporal::ticks_per_beat
*/
return (_scpb * b.get_beats()) + int_div_round ((_scpb * b.get_ticks()), superclock_t (Temporal::ticks_per_beat));
}
Temporal::Beats seconds_as_beats (uint64_t num, uint64_t denom) const {
return superclocks_as_beats ((num * superclock_ticks_per_second) / denom);
}
double beats_as_float_seconds_avoid_me (Temporal::Beats const & b) const {
return beats_as_superclocks (b) / (double) superclock_ticks_per_second;
}
private:
double val; /* as given to constructor */
uint64_t _sbps; /* superbeats-per-second */
superclock_t _scpb; /* superclocks-per-beat */
};
inline std::ostream&
operator<<(std::ostream& os, const TempoValue& v)
{
os << v.actual_bpm_for_display_only ();
return os;
}
inline std::istream&
operator>>(std::istream& istr, TempoValue& v)
{
#if 0
uint16_t w;
uint64_t f;
char d; /* delimiter, whatever it is */
istr >> w >> d >> f;
v = TempoValue (w, f);
#endif
return istr;
}
} /* namespace Temporal */
#endif /* __libtemporal_tempo_h__ */

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 2020 Paul Davis
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
@ -16,41 +16,130 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __libtemporal_tempo_h__
#define __libtemporal_tempo_h__
#ifndef __temporal_tempo_h__
#define __temporal_tempo_h__
#include <cassert>
#include <list>
#include <string>
#include <vector>
#include <cmath>
#include <exception>
#include <limits>
#include <ostream>
#include <string>
#include "pbd/enumwriter.h"
#include "pbd/integer_division.h"
#include <boost/intrusive/list.hpp>
#include <glibmm/threads.h>
#include "pbd/enum_convert.h"
#include "pbd/signals.h"
#include "pbd/statefuldestructible.h"
#include "temporal/beats.h"
#include "temporal/types.h"
#include "temporal/superclock.h"
#include "temporal/visibility.h"
#include "temporal/beats.h"
#include "temporal/bbt_time.h"
#include "temporal/superclock.h"
#include "temporal/timeline.h"
#include "temporal/types.h"
/************************* !!!! ATTENTION !!!! *************************
/* A tempo map is built from 3 types of entities
NO FLOATING POINT ARITHMETIC IS ALLOWED IN THIS HEADER OR
ANY OF THE OBJECTS DEFINED HERE.
1) tempo markers
2) meter (time signature) markers
3) position markers
Exceptions:
1) constructors/methods that accept double from the user
2) clearly labelled methods that warn a developer to avoid
their use or use them only for display purposes.
************************************************************************/
Beats increase monotonically throughout the tempo map (BBT may not).
The map has a single time domain at any time, and can only be using either
AudioTime or BeatTime. BarTime is not legal as a map time domain.
*/
namespace Temporal {
class TempoValue {
class Meter;
class TempoMap;
/* Conceptually, Point is similar to timepos_t. However, whereas timepos_t can
* use the TempoMap to translate between time domains, Point cannot. Why not?
* Because Point is foundational in building the tempo map, and we cannot
* create a circular functional dependency between them. So a Point always has
* its superclock and beat time defined and no translation between them is possible.
*/
class LIBTEMPORAL_API Point {
public:
Point (TempoMap const & map, superclock_t sc, Beats const & b, BBT_Time const & bbt) : _sclock (sc), _quarters (b), _bbt (bbt), _map (&map) {}
Point (TempoMap const & map, XMLNode const &);
virtual ~Point() {}
virtual void set (superclock_t sc, Beats const & b, BBT_Time const & bbt) {
_sclock = sc;
_quarters = b;
_bbt = bbt;
}
superclock_t sclock() const { return _sclock; }
Beats const & beats() const { return _quarters; }
BBT_Time const & bbt() const { return _bbt; }
#if 0
samplepos_t sample() const;
#endif
timepos_t time() const;
struct sclock_comparator {
bool operator() (Point const & a, Point const & b) const {
return a.sclock() < b.sclock();
}
};
struct beat_comparator {
bool operator() (Point const & a, Point const & b) const {
return a.beats() < b.beats();
}
};
struct bbt_comparator {
bool operator() (Point const & a, Point const & b) const {
return a.bbt() < b.bbt();
}
};
/* all time members are supposed to be synced at all times, so we need
test only one.
*/
inline bool operator== (Point const & other) const { return _sclock == other._sclock; }
inline bool operator!= (Point const & other) const { return _sclock != other._sclock; }
TempoMap const & map() const { return *_map; }
boost::intrusive::list_member_hook<> _point_hook;
protected:
superclock_t _sclock;
Beats _quarters;
BBT_Time _bbt;
TempoMap const * _map;
void add_state (XMLNode &) const;
protected:
friend class TempoMap;
void map_reset_set_sclock_for_sr_change (superclock_t sc) { _sclock = sc; }
};
class LIBTEMPORAL_API Rampable {
protected:
virtual ~Rampable() {}
private:
friend class TempoMap;
virtual bool set_ramped (bool yn) = 0;
};
/** Tempo, the speed at which musical time progresses (BPM).
*/
class LIBTEMPORAL_API Tempo : public Rampable {
private:
/* beats per minute * big_numerator => rational number expressing (possibly fractional) bpm as superbeats-per-minute
*
@ -60,78 +149,629 @@ class TempoValue {
static const superclock_t big_numerator = superclock_ticks_per_second;
public:
enum Type {
Ramped,
Constant
};
TempoValue (double bpm) : val (bpm) {
static std::string xml_node_name;
/* since we allow the user to provide bpm as a floating point value,
we allow use of floating point math to determine the two critical
integer values (superbeats-per-second and superclocks-per-beat
*/
_sbps = (superclock_t) round (bpm * (big_numerator / 60));
_scpb = (superclock_t) round ((60./ bpm) * superclock_ticks_per_second);
Tempo (XMLNode const &);
/**
* @param npm Note Types per minute
* @param note_type Note Type (default `4': quarter note)
*/
Tempo (double npm, int note_type = 4)
: _npm (npm)
, _enpm (npm)
, _superclocks_per_note_type (double_npm_to_scpn (npm))
, _end_superclocks_per_note_type (double_npm_to_scpn (npm))
, _super_note_type_per_second (double_npm_to_snps (npm))
, _end_super_note_type_per_second (double_npm_to_snps (npm))
, _note_type (note_type)
, _active (true)
, _locked_to_meter (false)
, _clamped (false)
, _type (Tempo::Constant) {}
Tempo (double npm, double enpm, int note_type = 4)
: _npm (npm)
, _enpm (npm)
, _superclocks_per_note_type (double_npm_to_scpn (npm))
, _end_superclocks_per_note_type (double_npm_to_scpn (enpm))
, _super_note_type_per_second (double_npm_to_snps (npm))
, _end_super_note_type_per_second (double_npm_to_snps (enpm))
, _note_type (note_type)
, _active (true)
, _locked_to_meter (false)
, _clamped (false)
, _type (npm != enpm ? Tempo::Ramped : Tempo::Constant) {}
/* these five 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; }
double end_note_types_per_minute () const { return (superclock_ticks_per_second * 60.0) / _end_superclocks_per_note_type; }
double quarter_notes_per_minute() const { return (superclock_ticks_per_second * 60.0 * 4.0) / (_note_type * _superclocks_per_note_type); }
double samples_per_note_type(samplecnt_t sr) const { return superclock_to_samples (superclocks_per_note_type (), sr); }
double samples_per_quarter_note(samplecnt_t sr) const { return superclock_to_samples (superclocks_per_quarter_note(), sr); }
void set_note_types_per_minute (double npm) { _superclocks_per_note_type = double_npm_to_scpn (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);
}
superclock_t end_superclocks_per_note_type () const {
return _end_superclocks_per_note_type;
}
superclock_t end_superclocks_per_note_type (int note_type) const {
return (_end_superclocks_per_note_type * _note_type) / note_type;
}
superclock_t end_superclocks_per_quarter_note () const {
return end_superclocks_per_note_type (4);
}
superclock_t superclocks_per_ppqn () const {
return superclocks_per_quarter_note() / ticks_per_beat;
}
double given_bpm_for_display_only () const { return val; }
double actual_bpm_for_display_only () const { return (_sbps * 60) / (double) big_numerator; }
uint64_t ticks_per_second() const { return int_div_round ((_sbps * Temporal::ticks_per_beat), big_numerator); }
superclock_t superclocks_per_beat() const { return _scpb; }
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);
t = int_div_round ((Temporal::ticks_per_beat * remain), big_numerator);
}
bool active () const { return _active; }
void set_active (bool yn) { _active = yn; }
Temporal::Beats superclocks_as_beats (superclock_t sc) const {
/* convert sc into superbeats, given that sc represents some number of seconds */
const superclock_t whole_seconds = sc / superclock_ticks_per_second;
const superclock_t remainder = sc - (whole_seconds * superclock_ticks_per_second);
const superclock_t superbeats = (_sbps * whole_seconds) + int_div_round ((_sbps * remainder), superclock_ticks_per_second);
bool locked_to_meter () const { return _locked_to_meter; }
void set_locked_to_meter (bool yn) { _locked_to_meter = yn; }
/* convert superbeats to beats:ticks */
bool clamped() const { return _clamped; }
bool set_clamped (bool yn);
uint32_t b = superbeats / big_numerator;
const uint64_t remain = superbeats - (b * big_numerator);
uint32_t t = int_div_round ((Temporal::ticks_per_beat * remain), big_numerator);
Type type() const { return _type; }
return Beats (b, t);
bool ramped () const { return _type != Constant; }
XMLNode& get_state () const;
int set_state (XMLNode const&, int version);
bool operator== (Tempo const & other) const {
return _superclocks_per_note_type == other._superclocks_per_note_type &&
_end_superclocks_per_note_type == other._end_superclocks_per_note_type &&
_note_type == other._note_type &&
_active == other._active &&
_locked_to_meter == other._locked_to_meter &&
_clamped == other._clamped &&
_type == other._type;
}
superclock_t beats_as_superclocks (Temporal::Beats const & b) const {
/* no symmetrical with superclocks_as_beats() because Beats already breaks apart the beats:ticks,
with the ticks value denominated by Temporal::ticks_per_beat
*/
return (_scpb * b.get_beats()) + int_div_round ((_scpb * b.get_ticks()), superclock_t (Temporal::ticks_per_beat));
bool operator!= (Tempo const & other) const {
return _superclocks_per_note_type != other._superclocks_per_note_type ||
_end_superclocks_per_note_type != other._end_superclocks_per_note_type ||
_note_type != other._note_type ||
_active != other._active ||
_locked_to_meter != other._locked_to_meter ||
_clamped != other._clamped ||
_type != other._type;
}
Temporal::Beats seconds_as_beats (uint64_t num, uint64_t denom) const {
return superclocks_as_beats ((num * superclock_ticks_per_second) / denom);
}
protected:
double _npm;
double _enpm;
superclock_t _superclocks_per_note_type;
superclock_t _end_superclocks_per_note_type;
uint64_t _super_note_type_per_second;
uint64_t _end_super_note_type_per_second;
int8_t _note_type;
bool _active;
bool _locked_to_meter; /* XXX name has unclear meaning with nutempo */
bool _clamped;
Type _type;
double beats_as_float_seconds_avoid_me (Temporal::Beats const & b) const {
return beats_as_superclocks (b) / (double) superclock_ticks_per_second;
}
static inline uint64_t double_npm_to_snps (double npm) { return (uint64_t) llround (npm * big_numerator / 60); }
static inline superclock_t double_npm_to_scpn (double npm) { return (superclock_t) llround ((60./npm) * superclock_ticks_per_second); }
private:
double val; /* as given to constructor */
uint64_t _sbps; /* superbeats-per-second */
superclock_t _scpb; /* superclocks-per-beat */
bool set_ramped (bool yn);
};
inline std::ostream&
operator<<(std::ostream& os, const TempoValue& v)
{
os << v.actual_bpm_for_display_only ();
return os;
}
/** Meter, or time signature (subdivisions per bar, and which note type is a single subdivision). */
class LIBTEMPORAL_API Meter {
public:
inline std::istream&
operator>>(std::istream& istr, TempoValue& v)
static std::string xml_node_name;
Meter (XMLNode const &);
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; }
int32_t ticks_per_grid () const { return (4 * Beats::PPQN) / _note_value; }
inline bool operator==(const Meter& other) const { return _divisions_per_bar == other.divisions_per_bar() && _note_value == other.note_value(); }
inline bool operator!=(const Meter& other) const { 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;
}
BBT_Time bbt_add (BBT_Time const & bbt, BBT_Offset const & add) const;
BBT_Time bbt_subtract (BBT_Time const & bbt, BBT_Offset const & sub) const;
BBT_Time round_to_bar (BBT_Time const &) const;
BBT_Time round_down_to_bar (BBT_Time const &) const;
BBT_Time round_up_to_bar (BBT_Time const &) const;
BBT_Time round_up_to_beat (BBT_Time const &) const;
BBT_Time round_to_beat (BBT_Time const &) const;
Beats to_quarters (BBT_Offset const &) const;
XMLNode& get_state () const;
int set_state (XMLNode const&, int version);
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;
};
/* A MeterPoint is literally just the combination of a Meter with a Point
*/
class LIBTEMPORAL_API MeterPoint : public Meter, public Point
{
#if 0
uint16_t w;
uint64_t f;
char d; /* delimiter, whatever it is */
istr >> w >> d >> f;
v = TempoValue (w, f);
public:
MeterPoint (TempoMap const & map, Meter const & m, superclock_t sc, Beats const & b, BBT_Time const & bbt) : Meter (m), Point (map, sc, b, bbt) {}
MeterPoint (TempoMap const & map, XMLNode const &);
Beats quarters_at (BBT_Time const & bbt) const;
BBT_Time bbt_at (Beats const & beats) const;
bool operator== (MeterPoint const & other) const {
return Meter::operator== (other) && Point::operator== (other);
}
bool operator!= (MeterPoint const & other) const {
return Meter::operator!= (other) || Point::operator!= (other);
}
boost::intrusive::list_member_hook<> _meter_hook;
XMLNode& get_state () const;
};
/* A TempoPoint is a combination of a Tempo with a Point. However, if the temp
* is ramped, then at some point we will need to compute the ramp coefficients
* (c-per-quarter and c-per-superclock) and store them so that we can compute
* time-at-quarter-note on demand.
*/
class LIBTEMPORAL_API TempoPoint : public Tempo, public Point
{
public:
TempoPoint (TempoMap const & map, Tempo const & t, superclock_t sc, Beats const & b, BBT_Time const & bbt) : Tempo (t), Point (map, sc, b, bbt), _omega (0.0) {}
TempoPoint (Tempo const & t, Point const & p) : Tempo (t), Point (p), _omega (0) {}
TempoPoint (TempoMap const & map, XMLNode const &);
/* just change the tempo component, without moving */
TempoPoint& operator=(Tempo const & t) {
*((Tempo*)this) = t;
return *this;
}
superclock_t superclock_at (Beats const & qn) const;
void compute_omega (samplecnt_t sr, superclock_t end_superclocks_per_note_type, Beats const & duration);
bool actually_ramped () const { return Tempo::ramped() && (_omega != 0); }
Beats quarters_at (superclock_t sc) const;
superclock_t superclocks_per_note_type_at (timepos_t const &) const;
#ifdef ALLOW_DOUBLE_TEMPO_MATH
double note_types_per_minute_at (timepos_t const & pos) const {
return Tempo::sc_to_double_npm (superclocks_per_note_type_at (pos));
}
#endif
return istr;
XMLNode& get_state () const;
int set_state (XMLNode const&, int version);
bool operator== (TempoPoint const & other) const {
return Tempo::operator== (other) && Point::operator== (other);
}
bool operator!= (TempoPoint const & other) const {
return Tempo::operator!= (other) || Point::operator!= (other);
}
double omega() const { return _omega; }
boost::intrusive::list_member_hook<> _tempo_hook;
private:
double _omega;
};
/** Helper class to perform computations that require both Tempo and Meter
at a given point in time.
It may seem nicer to make this IS-A TempoPoint and IS-A MeterPoint. Doing
so runs into multiple inheritance of Point, plus the major semantic issue
that pairing a tempo and a meter does in fact allow for two positions, not
one. That means we have to provide accessors to the TempoPoint and
MeterPoint and thus it may as well be HAS-A rather than IS-A.
This object should always be short lived. It holds references to a
TempoPoint and a MeterPoint that are not lifetime-managed. It's just a
convenience object, in essence, to avoid having to replicate the
computation code that requires both tempo and meter information every place
it is used.
*/
class LIBTEMPORAL_API TempoMetric {
public:
TempoMetric (TempoPoint & t, MeterPoint & m) : _tempo (&t), _meter (&m) {}
~TempoMetric () {}
TempoPoint & tempo() const { return *_tempo; }
MeterPoint & meter() const { return *_meter; }
/* even more convenient wrappers for individual aspects of a
* TempoMetric (i.e. just tempo or just meter information required
*/
superclock_t superclock_at (Beats const & qn) const { return _tempo->superclock_at (qn); }
Beats quarters_at (superclock_t sc) const { return _tempo->quarters_at (sc); }
Beats quarters_at (BBT_Time const & bbt) const { return _meter->quarters_at (bbt); }
BBT_Time bbt_at (Beats const & beats) const { return _meter->bbt_at (beats); }
superclock_t superclocks_per_note_type () const { return _tempo->superclocks_per_note_type (); }
superclock_t end_superclocks_per_note_type () const {return _tempo->end_superclocks_per_note_type (); }
superclock_t superclocks_per_note_type (int note_type) const {return _tempo->superclocks_per_note_type (note_type); }
superclock_t superclocks_per_quarter_note () const {return _tempo->superclocks_per_quarter_note (); }
superclock_t superclocks_per_ppqn () const {return _tempo->superclocks_per_ppqn (); }
int note_type () const { return _tempo->note_type(); }
int divisions_per_bar () const { return _meter->divisions_per_bar(); }
int note_value() const { return _meter->note_value(); }
BBT_Time bbt_add (BBT_Time const & bbt, BBT_Offset const & add) const { return _meter->bbt_add (bbt, add); }
BBT_Time bbt_subtract (BBT_Time const & bbt, BBT_Offset const & sub) const { return _meter->bbt_subtract (bbt, sub); }
BBT_Time round_to_bar (BBT_Time const & bbt) const { return _meter->round_to_bar (bbt); }
Beats to_quarters (BBT_Offset const & bbo) const { return _meter->to_quarters (bbo); }
/* combination methods that require both tempo and meter information */
superclock_t superclocks_per_bar (samplecnt_t sr) const {
return superclocks_per_grid (sr) * _meter->divisions_per_bar();
}
superclock_t superclocks_per_grid (samplecnt_t sr) const {
return llrint (_tempo->superclocks_per_note_type() * ((double) _tempo->note_type() / _meter->note_value()));
}
superclock_t superclocks_per_note_type_at_superclock (superclock_t sc) const {
if (!_tempo->actually_ramped()) {
return _tempo->superclocks_per_note_type ();
}
return _tempo->superclocks_per_note_type() * exp (-_tempo->omega() * (sc - _tempo->sclock()));
}
BBT_Time bbt_at (superclock_t sc) const;
superclock_t superclock_at (BBT_Time const &) const;
protected:
TempoPoint* _tempo;
MeterPoint* _meter;
};
/* A music time point is a place where BBT time (BarTime) is reset from
* whatever it would be when just inferring from the usual counting. Its
* position is given by a Point that might use superclock or Beats, and the
* Point's BBT time member is overwritten.
*/
class LIBTEMPORAL_API MusicTimePoint : public Point
{
public:
MusicTimePoint (TempoMap const & map) : Point (map, 0, Beats(), BBT_Time()) {}
MusicTimePoint (BBT_Time const & bbt_time, Point const & p) : Point (p) { _bbt = bbt_time; }
MusicTimePoint (TempoMap const & map, XMLNode const &);
boost::intrusive::list_member_hook<> _bartime_hook;
XMLNode & get_state () const;
};
/** 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 LIBTEMPORAL_API TempoMapPoint : public Point, public TempoMetric
{
public:
TempoMapPoint (TempoMap & map, TempoMetric const & tm, superclock_t sc, Beats const & q, BBT_Time const & bbt)
: Point (map, sc, q, bbt), TempoMetric (tm), _floating (false) {}
~TempoMapPoint () {}
/* called by a GUI that is manipulating the position of this point */
void start_float ();
void end_float ();
bool floating() const { return _floating; }
bool is_explicit_meter() const { return _meter->sclock() == sclock(); }
bool is_explicit_tempo() const { return _tempo->sclock() == sclock(); }
bool is_explicit_position() const { return false; }
bool is_explicit () const { return is_explicit_meter() || is_explicit_tempo() || is_explicit_position(); }
private:
bool _floating;
};
typedef std::list<TempoMapPoint> TempoMapPoints;
class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
{
public:
TempoMap (Tempo const & initial_tempo, Meter const & initial_meter, samplecnt_t sr);
~TempoMap();
void set_dirty (bool yn);
bool set_ramped (TempoPoint&, bool);
void set_sample_rate (samplecnt_t sr);
samplecnt_t sample_rate() const { return _sample_rate; }
void insert_time (timepos_t const & pos, timecnt_t const & duration);
bool remove_time (timepos_t const & pos, timecnt_t const & duration);
void change_tempo (TempoPoint&, Tempo const &);
MusicTimePoint & set_bartime (BBT_Time const &, timepos_t const &);
void remove_bartime (MusicTimePoint const & tp);
TempoPoint & set_tempo (Tempo const &, BBT_Time const &);
TempoPoint & set_tempo (Tempo const &, Beats const &);
TempoPoint & set_tempo (Tempo const &, timepos_t const &);
void remove_tempo (TempoPoint const &);
MeterPoint & set_meter (Meter const &, BBT_Time const &);
MeterPoint & set_meter (Meter const &, Beats const &);
MeterPoint & set_meter (Meter const &, timepos_t const &);
void remove_meter (MeterPoint const &);
/* these are a convenience method that just wrap some odd semantics */
bool move_tempo (TempoPoint const & point, timepos_t const & destination, bool push = false);
bool move_meter (MeterPoint const & point, timepos_t const & destination, bool push = false);
bool can_remove (TempoPoint const &) const;
bool can_remove (MeterPoint const &) const;
bool is_initial (TempoPoint const &) const;
bool is_initial (MeterPoint const &) const;
uint32_t n_meters() const;
uint32_t n_tempos() const;
Tempo const * next_tempo (Tempo const &) const;
Meter const * next_meter (Meter const &) const;
TempoMetric metric_at (timepos_t const &) const;
TempoMetric metric_at (superclock_t sc) const;
TempoMetric metric_at (Beats const &b) const;
TempoMetric metric_at (BBT_Time const & bbt) const;
TempoPoint const * previous_tempo (TempoPoint const &) const;
/* convenience function */
BBT_Time round_to_bar (BBT_Time const & bbt) const {
return metric_at (bbt).meter().round_to_bar (bbt);
}
BBT_Time bbt_at (superclock_t sc) const;
BBT_Time bbt_at (Beats const &) const;
BBT_Time bbt_at (timepos_t const &) const;
Beats quarter_note_at (superclock_t sc) const;
Beats quarter_note_at (BBT_Time const &) const;
Beats quarter_note_at (timepos_t const &) const;
#if 0
samplepos_t sample_at (Beats const &) const;
samplepos_t sample_at (BBT_Time const &) const;
samplepos_t sample_at (timepos_t const &) const;
#endif
superclock_t superclock_at (Beats const &) const;
superclock_t superclock_at (BBT_Time const &) const;
superclock_t superclock_at (timepos_t const &) const;
#if 0
int update_music_times (int gen, samplepos_t, Beats & b, BBT_Time & bbt, bool force);
int update_samples_and_beat_times (int gen, BBT_Time const & bbt, samplepos_t & pos, Beats & b, bool force);
int update_samples_and_bbt_times (int gen, Beats const & b, samplepos_t & pos, BBT_Time & bbt, bool force);
void update_one_domain_from_another (timepos_t const & src, void* dst, TimeDomain) const;
#endif
/* ways to walk along the tempo map, measure distance between points,
* etc.
*/
Beats superclock_delta_as_quarters (superclock_t start, superclock_t distance) const;
Beats scwalk_to_quarters (superclock_t pos, superclock_t distance) const;
Beats scwalk_to_quarters (Beats const & pos, superclock_t distance) const;
superclock_t superclock_plus_quarters_as_superclock (superclock_t start, Beats const & distance) const;
superclock_t superclock_quarters_delta_as_superclock (superclock_t start, Beats const & distance) const;
superclock_t superclock_plus_bbt (superclock_t pos, BBT_Time op) const;
superclock_t bbt_duration_at (superclock_t pos, const BBT_Time& bbt, int dir) const;
Beats bbtwalk_to_quarters (Beats const & start, BBT_Offset const & distance) const;
superclock_t superclock_per_quarter_note_at (superclock_t) const;
Temporal::timecnt_t full_duration_at (Temporal::timepos_t const &, Temporal::timecnt_t const & duration, Temporal::TimeDomain domain) const;
BBT_Time bbt_walk (BBT_Time const &, BBT_Offset const &) const;
TimeDomain time_domain() const { return _time_domain; }
void set_time_domain (TimeDomain td);
void get_grid (TempoMapPoints& points, superclock_t start, superclock_t end, uint32_t bar_mod);
typedef std::list<Point*> Metrics;
template<class T> void apply_with_metrics (T& obj, void (T::*method)(Metrics &)) {
Glib::Threads::RWLock::ReaderLock lm (_lock);
Metrics metrics;
for (Tempos::iterator t = _tempos.begin(); t != _tempos.end(); ++t) {
metrics.push_back (&*t);
}
for (Meters::iterator m = _meters.begin(); m != _meters.end(); ++m) {
metrics.push_back (&*m);
}
(obj.*method)(metrics);
}
struct EmptyTempoMapException : public std::exception {
virtual const char* what() const throw() { return "TempoMap is empty"; }
};
void dump (std::ostream&) const;
PBD::Signal0<void> Changed;
XMLNode& get_state();
int set_state (XMLNode const&, int version);
typedef boost::intrusive::member_hook<TempoPoint,boost::intrusive::list_member_hook<>, &TempoPoint::_tempo_hook> TempoHookOption;
typedef boost::intrusive::member_hook<MeterPoint,boost::intrusive::list_member_hook<>, &MeterPoint::_meter_hook> MeterHookOption;
typedef boost::intrusive::member_hook<MusicTimePoint,boost::intrusive::list_member_hook<>, &MusicTimePoint::_bartime_hook> BarTimeHookOption;
typedef boost::intrusive::member_hook<Point,boost::intrusive::list_member_hook<>, &Point::_point_hook> PointHookOption;
typedef boost::intrusive::list<TempoPoint,TempoHookOption> Tempos;
typedef boost::intrusive::list<MeterPoint,MeterHookOption> Meters;
typedef boost::intrusive::list<MusicTimePoint,BarTimeHookOption> MusicTimes;
typedef boost::intrusive::list<Point,PointHookOption> Points;
private:
Tempos _tempos;
Meters _meters;
MusicTimes _bartimes;
Points _points;
samplecnt_t _sample_rate;
mutable Glib::Threads::RWLock _lock;
bool _dirty;
int _generation;
TimeDomain _time_domain;
TempoPoint _initial_tempo;
MeterPoint _initial_meter;
MusicTimePoint _initial_music_time;
/* These return the TempoMetric in effect at the given time. If
can_match is true, then the TempoMetric may refer to a Tempo or
Meter at the given time. If can_match is false, the TempoMetric will
only refer to the Tempo or Metric preceding the given time.
*/
TempoMetric metric_at_locked (superclock_t, bool can_match = true) const;
TempoMetric metric_at_locked (Beats const &, bool can_match = true) const;
TempoMetric metric_at_locked (BBT_Time const &, bool can_match = true) const;
int set_tempos_from_state (XMLNode const &);
int set_meters_from_state (XMLNode const &);
int set_music_times_from_state (XMLNode const &);
TempoPoint & set_tempo (Tempo const &, superclock_t);
MeterPoint & set_meter (Meter const &, superclock_t);
void dump_locked (std::ostream&) const;
TempoPoint* add_tempo (TempoPoint &);
MeterPoint* add_meter (MeterPoint &);
MusicTimePoint* add_or_replace_bartime (MusicTimePoint &);
void add_point (Point &);
void reset_starting_at (superclock_t);
void reset_starting_at (Beats const &);
};
} /* end of namespace Temporal */
#ifdef COMPILER_MSVC
#pragma warning(disable:4101)
#endif
namespace PBD {
DEFINE_ENUM_CONVERT(Temporal::Tempo::Type);
DEFINE_ENUM_CONVERT(Temporal::TimeDomain);
} /* namespace PBD */
namespace std {
std::ostream& operator<<(std::ostream& str, Temporal::TempoMapPoint const &);
std::ostream& operator<<(std::ostream& str, Temporal::Tempo const &);
std::ostream& operator<<(std::ostream& str, Temporal::Meter const &);
std::ostream& operator<<(std::ostream& str, Temporal::Point const &);
std::ostream& operator<<(std::ostream& str, Temporal::TempoPoint const &);
std::ostream& operator<<(std::ostream& str, Temporal::MeterPoint const &);
std::ostream& operator<<(std::ostream& str, Temporal::TempoMetric const &);
}
} /* namespace Temporal */
#endif /* __libtemporal_tempo_h__ */
#endif /* __temporal_tempo_h__ */

View File

@ -24,7 +24,7 @@ APPNAME = 'temporal'
VERSION = TEMPORAL_VERSION
I18N_PACKAGE = 'libtemporal'
temporal_sources = [ 'time.cc', 'bbt_time.cc' ]
temporal_sources = [ 'time.cc', 'bbt_time.cc', 'tempo.cc' ]
def options(opt):
autowaf.set_options(opt)
@ -50,7 +50,9 @@ def build(bld):
obj.target = 'temporal'
obj.vnum = TEMPORAL_LIB_VERSION
obj.install_path = bld.env['LIBDIR']
obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ]
obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ]
obj.uselib = [ 'GLIBMM', 'XML' ]
obj.use = [ 'libpbd' ]
def shutdown():
autowaf.shutdown()