diff --git a/libs/ardour/ardour/automation_list.h b/libs/ardour/ardour/automation_list.h index 9beca77485..22ac87866f 100644 --- a/libs/ardour/ardour/automation_list.h +++ b/libs/ardour/ardour/automation_list.h @@ -44,7 +44,7 @@ namespace ARDOUR { class AutomationList; -class DoubleBeatsSamplesConverter; +class BeatsSamplesConverter; /** A SharedStatefulProperty for AutomationLists */ class LIBARDOUR_API AutomationListProperty : public PBD::SharedStatefulProperty @@ -86,7 +86,7 @@ public: AutomationList& operator= (const AutomationList&); void thaw (); - bool paste (const ControlList&, double, DoubleBeatsSamplesConverter const&); + bool paste (const ControlList&, double, BeatsSamplesConverter const&); void set_automation_state (AutoState); AutoState automation_state() const; diff --git a/libs/ardour/ardour/beats_samples_converter.h b/libs/ardour/ardour/beats_samples_converter.h index 754c467f91..474772c759 100644 --- a/libs/ardour/ardour/beats_samples_converter.h +++ b/libs/ardour/ardour/beats_samples_converter.h @@ -16,6 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include + #include "temporal/beats.h" #include "evoral/TimeConverter.h" @@ -67,6 +69,7 @@ private: const TempoMap& _tempo_map; }; + } /* namespace ARDOUR */ #endif /* __ardour_beats_samples_converter_h__ */ diff --git a/libs/ardour/ardour/evoral_types_convert.h b/libs/ardour/ardour/evoral_types_convert.h index 26315e58f0..dd7c9cff2f 100644 --- a/libs/ardour/ardour/evoral_types_convert.h +++ b/libs/ardour/ardour/evoral_types_convert.h @@ -29,39 +29,6 @@ namespace PBD { DEFINE_ENUM_CONVERT(Evoral::ControlList::InterpolationStyle) -template <> -inline bool to_string (Temporal::Beats beats, std::string& str) -{ - return double_to_string (beats.to_double (), str); -} - -template <> -inline bool string_to (const std::string& str, Temporal::Beats& beats) -{ - double tmp; - if (!string_to_double (str, tmp)) { - return false; - } - beats = Temporal::Beats(tmp); - return true; -} - -template <> -inline std::string to_string (Temporal::Beats beats) -{ - std::string tmp; - double_to_string (beats.to_double (), tmp); - return tmp; -} - -template <> -inline Temporal::Beats string_to (const std::string& str) -{ - double tmp; - string_to_double (str, tmp); - return Temporal::Beats (tmp); -} - } // namespace PBD #endif // ARDOUR_EVORAL_TYPES_CONVERT_H diff --git a/libs/ardour/ardour/quantize.h b/libs/ardour/ardour/quantize.h index 6d0cf2d3b1..ba38ea2018 100644 --- a/libs/ardour/ardour/quantize.h +++ b/libs/ardour/ardour/quantize.h @@ -30,7 +30,7 @@ class LIBARDOUR_API Quantize : public MidiOperator { public: Quantize (bool snap_start, bool snap_end, double start_grid, double end_grid, - float strength, float swing, float threshold); + float strength, float swing, Temporal::Beats const & threshold); ~Quantize (); Command* operator() (boost::shared_ptr, @@ -45,7 +45,7 @@ private: double _end_grid; float _strength; float _swing; - float _threshold; + Temporal::Beats _threshold; }; } /* namespace */ diff --git a/libs/ardour/automation_list.cc b/libs/ardour/automation_list.cc index 4d03de9686..2208cc8924 100644 --- a/libs/ardour/automation_list.cc +++ b/libs/ardour/automation_list.cc @@ -315,7 +315,7 @@ AutomationList::thaw () } bool -AutomationList::paste (const ControlList& alist, double pos, DoubleBeatsSamplesConverter const& bfc) +AutomationList::paste (const ControlList& alist, double pos, BeatsSamplesConverter const& bfc) { AutomationType src_type = (AutomationType)alist.parameter().type(); AutomationType dst_type = (AutomationType)_parameter.type(); @@ -330,9 +330,9 @@ AutomationList::paste (const ControlList& alist, double pos, DoubleBeatsSamplesC for (const_iterator i = alist.begin ();i != alist.end (); ++i) { double when = (*i)->when; if (to_sample) { - when = bfc.to ((*i)->when); + when = bfc.to (Temporal::Beats::from_double ((*i)->when)); } else { - when = bfc.from ((*i)->when); + when = bfc.from ((*i)->when).to_double (); } cl.fast_simple_add (when, (*i)->value); } diff --git a/libs/ardour/import_pt.cc b/libs/ardour/import_pt.cc index 226d8b18fe..30fcc4c8d3 100644 --- a/libs/ardour/import_pt.cc +++ b/libs/ardour/import_pt.cc @@ -453,8 +453,8 @@ no_audio_tracks: for (vector::const_iterator j = a->reg.midi.begin (); j != a->reg.midi.end (); ++j) { //printf(" : MIDI : pos=%f len=%f\n", (float)j->pos / 960000., (float)j->length / 960000.); - Temporal::Beats start = (Temporal::Beats)(j->pos / 960000.); - Temporal::Beats len = (Temporal::Beats)(j->length / 960000.); + Temporal::Beats start = Temporal::Beats::from_double (j->pos / 960000.); + Temporal::Beats len = Temporal::Beats::from_double(j->length / 960000.); /* PT C-2 = 0, Ardour C-1 = 0, subtract twelve to convert ? */ midicmd->add (boost::shared_ptr > (new Evoral::Note ((uint8_t)1, start, len, j->note, j->velocity))); } diff --git a/libs/ardour/legatize.cc b/libs/ardour/legatize.cc index 6e424bb7c9..d922bf3f8f 100644 --- a/libs/ardour/legatize.cc +++ b/libs/ardour/legatize.cc @@ -43,7 +43,7 @@ Legatize::operator()(boost::shared_ptr model, break; } - const Temporal::Beats new_end = (*next)->time() - Temporal::Beats::tick(); + const Temporal::Beats new_end = (*next)->time() - Temporal::Beats::one_tick(); if ((*i)->end_time() > new_end || (!_shrink_only && (*i)->end_time() < new_end)) { const Temporal::Beats new_length(new_end - (*i)->time()); diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index e94a3dbf2c..3191581b0f 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -200,7 +200,6 @@ CLASSKEYS(std::list); CLASSKEYS(ARDOUR::AudioEngine); CLASSKEYS(ARDOUR::BeatsSamplesConverter); -CLASSKEYS(ARDOUR::DoubleBeatsSamplesConverter); CLASSKEYS(ARDOUR::BufferSet); CLASSKEYS(ARDOUR::ChanCount); CLASSKEYS(ARDOUR::ChanMapping); @@ -557,8 +556,7 @@ LuaBindings::common (lua_State* L) .endClass () .beginClass ("Beats") - .addConstructor () - .addFunction ("to_double", &Temporal::Beats::to_double) + /* XXX need some way to construct beats in Lua */ .endClass () .beginClass ("Parameter") @@ -1905,12 +1903,6 @@ LuaBindings::common (lua_State* L) .addFunction ("from", &BeatsSamplesConverter::from) .endClass () - .beginClass ("DoubleBeatsSamplesConverter") - .addConstructor () - .addFunction ("to", &DoubleBeatsSamplesConverter::to) - .addFunction ("from", &DoubleBeatsSamplesConverter::from) - .endClass () - .beginClass ("TempoMap") .addFunction ("add_tempo", &TempoMap::add_tempo) .addFunction ("add_meter", &TempoMap::add_meter) diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index f7912a8b8f..dc666387b2 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -1776,6 +1776,15 @@ LV2Plugin::write_to_ui(uint32_t index, } return true; } +/* this used to be computed by Temporal::Beats::to_double() but that + * method has been hidden as of February 2017 to prevent inadvertent + * use of floating point musical time. + */ +inline static double +beats_to_double (Temporal::Beats const & b) +{ + return (double) b.get_beats() + (b.get_ticks() / (double) Temporal::ticks_per_beat); +} static void forge_variant(LV2_Atom_Forge* forge, const Variant& value) @@ -1785,7 +1794,7 @@ forge_variant(LV2_Atom_Forge* forge, const Variant& value) break; case Variant::BEATS: // No atom type for this, just forge a double - lv2_atom_forge_double(forge, value.get_beats().to_double()); + lv2_atom_forge_double(forge, beats_to_double (value.get_beats())); break; case Variant::BOOL: lv2_atom_forge_bool(forge, value.get_bool()); diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 3dacd09bcd..cd952ef8a3 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -66,7 +66,6 @@ using namespace PBD; MidiSource::MidiSource (Session& s, string name, Source::Flag flags) : Source(s, DataType::MIDI, name, flags) , _writing(false) - , _length_beats(0.0) , _capture_length(0) , _capture_loop_length(0) { @@ -75,7 +74,6 @@ MidiSource::MidiSource (Session& s, string name, Source::Flag flags) MidiSource::MidiSource (Session& s, const XMLNode& node) : Source(s, node) , _writing(false) - , _length_beats(0.0) , _capture_length(0) , _capture_loop_length(0) { diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index 04955c7354..c9ea4de9e3 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -191,7 +191,7 @@ MidiStateTracker::resolve_notes (MidiSource& src, const MidiSource::Lock& lock, this, (int) note, (int) channel, time)); _active_notes[note + 128 * channel]--; /* don't stack events up at the same time */ - time += Temporal::Beats::tick(); + time += Temporal::Beats::one_tick(); } } } diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 3a6c372bfc..f78cb385b9 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -452,7 +452,7 @@ MidiTrack::non_realtime_locate (samplepos_t pos) (rcontrol = region->control(tcontrol->parameter()))) { const Temporal::Beats pos_beats = bfc.from(pos - origin); if (rcontrol->list()->size() > 0) { - tcontrol->set_value(rcontrol->list()->eval(pos_beats.to_double()), Controllable::NoGroup); + tcontrol->set_value(rcontrol->list()->eval(pos_beats), Controllable::NoGroup); } } } diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index 28a86a65e7..0a7bb1b48a 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -39,7 +39,7 @@ using namespace ARDOUR; Quantize::Quantize (bool snap_start, bool snap_end, double start_grid, double end_grid, - float strength, float swing, float threshold) + float strength, float swing, Temporal::Beats const & threshold) : _snap_start (snap_start) , _snap_end (snap_end) , _start_grid(start_grid) @@ -122,8 +122,8 @@ Quantize::operator () (boost::shared_ptr model, to quantize relative to actual session beats (etc.) rather than from the start of the model. */ - const double round_pos = round(position.to_double() / _start_grid) * _start_grid; - const double offset = round_pos - position.to_double(); + const double round_pos = (position / _start_grid) * _start_grid; + const double offset = round_pos - position; MidiModel::NoteDiffCommand* cmd = new MidiModel::NoteDiffCommand (model, "quantize"); @@ -138,8 +138,8 @@ Quantize::operator () (boost::shared_ptr model, * guaranteed to precisely align with the quantize grid(s). */ - double new_start = round (((*i)->time().to_double() - offset) / _start_grid) * _start_grid; - double new_end = round (((*i)->end_time().to_double() - offset) / _end_grid) * _end_grid; + Temporal::Beats new_start = (((*i)->time() - offset) / _start_grid) * _start_grid; + Temporal::Beats new_end = (((*i)->end_time() - offset) / _end_grid) * _end_grid; if (_swing) { @@ -154,25 +154,25 @@ Quantize::operator () (boost::shared_ptr model, new_end += offset; } - double delta = new_start - (*i)->time().to_double(); + Temporal::Beats delta = new_start - (*i)->time(); - - if (fabs (delta) >= _threshold) { + if (delta.abs() >= _threshold) { if (_snap_start) { - delta *= _strength; + delta = delta * _strength; cmd->change ((*i), MidiModel::NoteDiffCommand::StartTime, (*i)->time() + delta); } } if (_snap_end) { - delta = new_end - (*i)->end_time().to_double(); + delta = new_end - (*i)->end_time(); + + if (delta.abs() >= _threshold) { - if (fabs (delta) >= _threshold) { Temporal::Beats new_dur(new_end - new_start); if (!new_dur) { - new_dur = Temporal::Beats(_end_grid); + new_dur = Temporal::Beats::from_double (_end_grid); } cmd->change ((*i), MidiModel::NoteDiffCommand::Length, new_dur); diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 6eca986671..a5f4b8b7d9 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -67,7 +67,6 @@ SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags) , FileSource(s, DataType::MIDI, path, string(), flags) , Evoral::SMF() , _open (false) - , _last_ev_time_beats(0.0) , _last_ev_time_samples(0) , _smf_last_read_end (0) , _smf_last_read_time (0) @@ -103,7 +102,6 @@ SMFSource::SMFSource (Session& s, const string& path) , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0)) , Evoral::SMF() , _open (false) - , _last_ev_time_beats(0.0) , _last_ev_time_samples(0) , _smf_last_read_end (0) , _smf_last_read_time (0) @@ -135,7 +133,6 @@ SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist) , MidiSource(s, node) , FileSource(s, node, must_exist) , _open (false) - , _last_ev_time_beats(0.0) , _last_ev_time_samples(0) , _smf_last_read_end (0) , _smf_last_read_time (0) @@ -419,7 +416,7 @@ SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock, Temporal::Beats time = ev.time(); if (time < _last_ev_time_beats) { const Temporal::Beats difference = _last_ev_time_beats - time; - if (difference.to_double() / (double)ppqn() < 1.0) { + if (difference < Temporal::Beats::ticks (ppqn())) { /* Close enough. This problem occurs because Sequence is not actually ordered due to fuzzy time comparison. I'm pretty sure this is inherently a bad idea which causes problems all over the @@ -428,7 +425,7 @@ SMFSource::append_event_beats (const Glib::Threads::Mutex::Lock& lock, } else { /* Out of order by more than a tick. */ warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"), - ev.time(), _last_ev_time_beats, difference, difference.to_double() / (double)ppqn()) + ev.time(), _last_ev_time_beats, difference, difference) << endmsg; return; } diff --git a/libs/temporal/temporal/beats.h b/libs/temporal/temporal/beats.h index 112da15008..dc8e72dfc6 100644 --- a/libs/temporal/temporal/beats.h +++ b/libs/temporal/temporal/beats.h @@ -28,14 +28,44 @@ #include #include +#include "pbd/compose.h" +#include "pbd/failed_constructor.h" +#include "pbd/string_convert.h" + + #include "temporal/visibility.h" +#include "temporal/types.h" + +namespace ARDOUR { +class Variant; /* Can stay since LV2 has no way to exchange beats as anything except double */ +/* these all need fixing to not use ::to_double() */ +class TempoMap; +class Track; +class MidiStretch; +class MidiModel; +class AutomationList; +class MidiSource; +class MidiRegion; +/* these use ::to_double() but should be removed */ +class DoubleBeatsSamplesConverter; +} + +namespace Evoral { +template class Sequence; +} + +/* XXX hack friends for ::do_double() access ... remove */ + +class QuantizeDialog; +class NoteDrag; +class NoteCreateDrag; namespace Temporal { /** Musical time in beats. */ class /*LIBTEMPORAL_API*/ Beats { public: - LIBTEMPORAL_API static const int32_t PPQN = 1920; + LIBTEMPORAL_API static const int32_t PPQN = Temporal::ticks_per_beat; Beats() : _beats(0), _ticks(0) {} Beats(const Beats& other) : _beats(other._beats), _ticks(other._ticks) {} @@ -72,18 +102,16 @@ public: _ticks = sign * ticks; } - /** Create from a precise BT time. */ + /** Create from a precise beats:ticks pair. */ explicit Beats(int32_t b, int32_t t) : _beats(b), _ticks(t) { normalize(); } /** Create from a real number of beats. */ - explicit Beats(double time) { + static Beats from_double (double beats) { double whole; - const double frac = modf(time, &whole); - - _beats = whole; - _ticks = frac * PPQN; + const double frac = modf (beats, &whole); + return Beats (whole, (int32_t) rint (frac * PPQN)); } /** Create from an integer number of beats. */ @@ -107,6 +135,12 @@ public: return Beats(ticks / ppqn, (ticks % ppqn) * PPQN / ppqn); } + 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); } + + int32_t get_beats() const { return _beats; } + int32_t get_ticks() const { return _ticks; } + Beats& operator=(double time) { double whole; const double frac = modf(time, &whole); @@ -134,9 +168,123 @@ public: return Beats(_beats, 0); } - Beats snap_to(const Temporal::Beats& snap) const { + + Beats prev_beat() const { + /* always moves backwards even if currently on beat */ + return Beats (_beats-1, 0); + } + + Beats next_beat() const { + /* always moves forwards even if currently on beat */ + return Beats (_beats+1, 0); + } + + Beats round_to_subdivision (int subdivision, RoundMode dir) const { + uint32_t ticks = to_ticks(); + const uint32_t ticks_one_subdivisions_worth = ticks_per_beat / subdivision; + uint32_t mod = ticks % ticks_one_subdivisions_worth; + uint32_t beats = _beats; + + if (dir > 0) { + + if (mod == 0 && dir == RoundUpMaybe) { + /* right on the subdivision, which is fine, so do nothing */ + + } else if (mod == 0) { + /* right on the subdivision, so the difference is just the subdivision ticks */ + ticks += ticks_one_subdivisions_worth; + + } else { + /* not on subdivision, compute distance to next subdivision */ + + ticks += ticks_one_subdivisions_worth - mod; + } + + // NOTE: this code intentionally limits the rounding so we don't advance to the next beat. + // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat. + // And since the "prev" direction DOES move beats, I assume this code is unintended. + // But I'm keeping it around, until we determine there are no terrible consequences. + // if (ticks >= BBT_Time::ticks_per_beat) { + // ticks -= BBT_Time::ticks_per_beat; + // } + + } else if (dir < 0) { + + /* round to previous (or same iff dir == RoundDownMaybe) */ + + uint32_t difference = ticks % ticks_one_subdivisions_worth; + + if (difference == 0 && dir == RoundDownAlways) { + /* right on the subdivision, but force-rounding down, + so the difference is just the subdivision ticks */ + difference = ticks_one_subdivisions_worth; + } + + if (ticks < difference) { + ticks = ticks_per_beat - ticks; + } else { + ticks -= difference; + } + + } else { + /* round to nearest */ + double rem; + + /* compute the distance to the previous and next subdivision */ + + if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) { + + /* closer to the next subdivision, so shift forward */ + + ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem)); + + //DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks)); + + if (ticks > ticks_per_beat) { + ++beats; + ticks -= ticks_per_beat; + //DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats)); + } + + } else if (rem > 0) { + + /* closer to previous subdivision, so shift backward */ + + if (rem > ticks) { + if (beats == 0) { + /* can't go backwards past zero, so ... */ + return *this; + } + /* step back to previous beat */ + --beats; + ticks = lrint (ticks_per_beat - rem); + //DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats)); + } else { + ticks = lrint (ticks - rem); + //DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks)); + } + } else { + /* on the subdivision, do nothing */ + } + } + + return Beats::ticks (ticks); + } + + Beats snap_to (Temporal::Beats const & snap) const { const double snap_time = snap.to_double(); - return Beats(ceil(to_double() / snap_time) * snap_time); + return Beats::from_double (ceil(to_double() / snap_time) * snap_time); + } + + Beats abs () const { + return Beats (::abs (_beats), ::abs (_ticks)); + } + + Beats diff (Beats const & other) const { + if (other > *this) { + return other - *this; + } + return *this - other; } inline bool operator==(const Beats& b) const { @@ -264,6 +412,11 @@ public: _beats = B._beats; _ticks = B._ticks; return *this; + + /* avoids calling ::to_double() to compute ratios of two Beat distances + */ + double operator/ (Beats const & other) { + return (double) to_ticks() / (double) other.to_ticks(); } Beats& operator+=(const Beats& b) { @@ -280,20 +433,43 @@ public: return *this; } - double to_double() const { return (double)_beats + (_ticks / (double)PPQN); } - 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); } - - int32_t get_beats() const { return _beats; } - int32_t get_ticks() const { return _ticks; } - bool operator!() const { return _beats == 0 && _ticks == 0; } + operator bool () const { return _beats != 0 || _ticks != 0; } - static Beats tick() { return Beats(0, 1); } + static Beats one_tick() { return Beats(0, 1); } private: int32_t _beats; int32_t _ticks; + + /* almost nobody should ever be allowed to use this method */ + friend class TempoPoint; + friend class ARDOUR::TempoMap; + friend class ARDOUR::Track; + friend class ARDOUR::Variant; + friend class ARDOUR::MidiStretch; + friend class ARDOUR::MidiModel; + friend class ARDOUR::AutomationList; + friend class ARDOUR::MidiSource; + friend class ARDOUR::MidiRegion; + friend class ARDOUR::DoubleBeatsSamplesConverter; + friend class ::QuantizeDialog; + friend class ::NoteDrag; + friend class ::NoteCreateDrag; + double to_double() const { return (double)_beats + (_ticks / (double)PPQN); } + + /* this needs to exist because Evoral::Sequence is templated, and some + * other possible template types cannot provide ::from_double + */ + + friend class Evoral::Sequence; + explicit Beats (double beats) { + double whole; + const double frac = modf (beats, &whole); + + _beats = whole; + _ticks = frac * PPQN; + } }; /* @@ -309,17 +485,18 @@ private: inline std::ostream& operator<<(std::ostream& os, const Beats& t) { - os << t.get_beats() << '.' << t.get_ticks(); + os << t.get_beats() << ':' << t.get_ticks(); return os; } inline std::istream& -operator>>(std::istream& is, Beats& t) +operator>>(std::istream& istr, Beats& b) { - double beats; - is >> beats; - t = Beats(beats); - return is; + int32_t beats, ticks; + char d; /* delimiter, whatever it is */ + istr >> beats >> d >> ticks; + b = Beats (beats, ticks); + return istr; } } // namespace Evoral @@ -347,4 +524,29 @@ namespace std { }; } +namespace PBD { + namespace DEBUG { + LIBTEMPORAL_API extern uint64_t Beats; + } + +template<> +inline bool to_string (Temporal::Beats val, std::string & str) +{ + std::ostringstream ostr; + ostr << val; + str = ostr.str(); + return true; +} + +template<> +inline bool string_to (std::string const & str, Temporal::Beats & val) +{ + std::istringstream istr (str); + istr >> val; + return (bool) istr; +} + +} /* end namsepace PBD */ + + #endif // TEMPORAL_BEATS_HPP diff --git a/libs/temporal/temporal/types.h b/libs/temporal/temporal/types.h index f4e690da41..3907fb0c4e 100644 --- a/libs/temporal/temporal/types.h +++ b/libs/temporal/temporal/types.h @@ -41,6 +41,40 @@ typedef int64_t samplecnt_t; static const samplepos_t max_samplepos = INT64_MAX; static const samplecnt_t max_samplecnt = INT64_MAX; +/* 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. +*/ + +static const int32_t ticks_per_beat = 1920; + +enum TimeDomain { + /* simple ordinals, since these are mutually exclusive */ + AudioTime = 0, + BeatTime = 1, + BarTime = 2, +}; + +enum Dirty { + /* combinable */ + SampleDirty = 0x1, + BeatsDirty = 0x2, + BBTDirty = 0x4 +}; + +enum RoundMode { + RoundDownMaybe = -2, ///< Round down only if necessary + RoundDownAlways = -1, ///< Always round down, even if on a division + RoundNearest = 0, ///< Round to nearest + RoundUpAlways = 1, ///< Always round up, even if on a division + RoundUpMaybe = 2 ///< Round up only if necessary +}; + +extern void setup_enum_writer (); + } #endif /* __libpbd_position_types_h__ */