temporal: TempoMap::use() returns a const ptr to enforce semantics (library version)

This commit leaves two issues outstanding:

1. unclear/ugly semantics for drag operations that reset the GUI thread's tempo map to the writable copy
2. undo/redo for the tempo map

These will be addressed in future commits
This commit is contained in:
Paul Davis 2022-04-08 09:23:19 -06:00
parent 3d395585c1
commit 7c3268d12f
8 changed files with 45 additions and 31 deletions

View file

@ -289,7 +289,7 @@ AudioEngine::process_callback (pframes_t nframes)
thread_init_callback (NULL); thread_init_callback (NULL);
} }
Temporal::TempoMap::SharedPtr current_map = Temporal::TempoMap::read (); Temporal::TempoMap::WritableSharedPtr current_map = Temporal::TempoMap::read ();
if (current_map != Temporal::TempoMap::use()) { if (current_map != Temporal::TempoMap::use()) {
Temporal::TempoMap::set (current_map); Temporal::TempoMap::set (current_map);
if (_session) { if (_session) {

View file

@ -7368,12 +7368,13 @@ Session::listening () const
void void
Session::maybe_update_tempo_from_midiclock_tempo (float bpm) Session::maybe_update_tempo_from_midiclock_tempo (float bpm)
{ {
TempoMap::SharedPtr tmap (TempoMap::use()); TempoMap::WritableSharedPtr tmap (TempoMap::write_copy());
if (tmap->n_tempos() == 1) { if (tmap->n_tempos() == 1) {
Temporal::TempoMetric const & metric (tmap->metric_at (0)); Temporal::TempoMetric const & metric (tmap->metric_at (0));
if (fabs (metric.tempo().note_types_per_minute() - bpm) > (0.01 * metric.tempo().note_types_per_minute())) { if (fabs (metric.tempo().note_types_per_minute() - bpm) > (0.01 * metric.tempo().note_types_per_minute())) {
tmap->change_tempo (metric.get_editable_tempo(), Tempo (bpm, 4.0, bpm)); tmap->change_tempo (metric.get_editable_tempo(), Tempo (bpm, 4.0, bpm));
TempoMap::update (tmap);
} }
} }
} }

View file

@ -111,7 +111,9 @@ Session::memento_command_factory(XMLNode *n)
return new MementoCommand<Locations>(*_locations, before, after); return new MementoCommand<Locations>(*_locations, before, after);
} else if (type_name == "Temporal::TempoMap") { } else if (type_name == "Temporal::TempoMap") {
return new MementoCommand<TempoMap>(*TempoMap::use(), before, after);
#warning NUTEMPO how to reconstruct memento commands for a tempo map
// return new MementoCommand<TempoMap>(*TempoMap::fetch(), before, after);
} else if (type_name == "ARDOUR::Playlist" || type_name == "ARDOUR::AudioPlaylist" || type_name == "ARDOUR::MidiPlaylist") { } else if (type_name == "ARDOUR::Playlist" || type_name == "ARDOUR::AudioPlaylist" || type_name == "ARDOUR::MidiPlaylist") {
if (boost::shared_ptr<Playlist> pl = _playlists->by_name(child->property("name")->value())) { if (boost::shared_ptr<Playlist> pl = _playlists->by_name(child->property("name")->value())) {

View file

@ -1589,7 +1589,7 @@ Session::set_state (const XMLNode& node, int version)
goto out; goto out;
} else { } else {
try { try {
TempoMap::SharedPtr tmap = TempoMap::write_copy (); /* get writable copy of current tempo map */ TempoMap::WritableSharedPtr tmap = TempoMap::write_copy (); /* get writable copy of current tempo map */
tmap->set_state (*child, version); /* reset its state */ tmap->set_state (*child, version); /* reset its state */
TempoMap::update (tmap); /* update the global tempo map manager */ TempoMap::update (tmap); /* update the global tempo map manager */
} catch (...) { } catch (...) {

View file

@ -99,7 +99,7 @@ TempoMapImporter::_cancel_move ()
void void
TempoMapImporter::_move () TempoMapImporter::_move ()
{ {
TempoMap::SharedPtr tmap (TempoMap::write_copy()); TempoMap::WritableSharedPtr tmap (TempoMap::write_copy());
tmap->set_state (xml_tempo_map, Stateful::current_state_version); tmap->set_state (xml_tempo_map, Stateful::current_state_version);
TempoMap::update (tmap); TempoMap::update (tmap);
} }

View file

@ -38,7 +38,7 @@ ArdourTransport::set_tempo (double bpm)
{ {
bpm = std::max (0.01, bpm); bpm = std::max (0.01, bpm);
TempoMap::SharedPtr tmap (TempoMap::write_copy()); TempoMap::WritableSharedPtr tmap (TempoMap::write_copy());
Tempo tempo (bpm, tmap->metric_at (0).tempo().note_type ()); Tempo tempo (bpm, tmap->metric_at (0).tempo().note_type ());

View file

@ -44,7 +44,7 @@ std::string Tempo::xml_node_name = X_("Tempo");
std::string Meter::xml_node_name = X_("Meter"); std::string Meter::xml_node_name = X_("Meter");
SerializedRCUManager<TempoMap> TempoMap::_map_mgr (0); SerializedRCUManager<TempoMap> TempoMap::_map_mgr (0);
thread_local TempoMap::SharedPtr TempoMap::_tempo_map_p; thread_local TempoMap::WritableSharedPtr TempoMap::_tempo_map_p;
PBD::Signal0<void> TempoMap::MapChanged; PBD::Signal0<void> TempoMap::MapChanged;
void void
@ -1802,7 +1802,7 @@ TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp,
} }
void void
TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod) TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod) const
{ {
/* note: @param bar_mod is "bar modulo", and describes the N in "give /* note: @param bar_mod is "bar modulo", and describes the N in "give
me every Nth bar". If the caller wants every 4th bar, bar_mod == me every Nth bar". If the caller wants every 4th bar, bar_mod ==
@ -2119,7 +2119,7 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
} }
uint32_t uint32_t
TempoMap::count_bars (Beats const & start, Beats const & end) TempoMap::count_bars (Beats const & start, Beats const & end) const
{ {
TempoMapPoints bar_grid; TempoMapPoints bar_grid;
superclock_t s (superclock_at (start)); superclock_t s (superclock_at (start));
@ -3181,7 +3181,7 @@ void
TempoMap::MementoBinder::set_state (XMLNode const & node, int version) const TempoMap::MementoBinder::set_state (XMLNode const & node, int version) const
{ {
/* fetch a writable copy of this thread's tempo map */ /* fetch a writable copy of this thread's tempo map */
TempoMap::SharedPtr map (write_copy()); TempoMap::WritableSharedPtr map (write_copy());
/* change the state of the copy */ /* change the state of the copy */
map->set_state (node, version); map->set_state (node, version);
/* do the update step of RCU. This will also update this thread's map pointer */ /* do the update step of RCU. This will also update this thread's map pointer */
@ -3191,19 +3191,19 @@ TempoMap::MementoBinder::set_state (XMLNode const & node, int version) const
void void
TempoMap::init () TempoMap::init ()
{ {
SharedPtr new_map (new TempoMap (Tempo (120, 4), Meter (4, 4))); WritableSharedPtr new_map (new TempoMap (Tempo (120, 4), Meter (4, 4)));
_map_mgr.init (new_map); _map_mgr.init (new_map);
fetch (); fetch ();
} }
TempoMap::SharedPtr TempoMap::WritableSharedPtr
TempoMap::write_copy() TempoMap::write_copy()
{ {
return _map_mgr.write_copy(); return _map_mgr.write_copy();
} }
int int
TempoMap::update (TempoMap::SharedPtr m) TempoMap::update (TempoMap::WritableSharedPtr m)
{ {
if (!_map_mgr.update (m)) { if (!_map_mgr.update (m)) {
return -1; return -1;
@ -3231,7 +3231,7 @@ TempoMap::abort_update ()
} }
void void
TempoMap::midi_clock_beat_at_or_after (samplepos_t const pos, samplepos_t& clk_pos, uint32_t& clk_beat) TempoMap::midi_clock_beat_at_or_after (samplepos_t const pos, samplepos_t& clk_pos, uint32_t& clk_beat) const
{ {
/* Sequences are always assumed to start on a MIDI Beat of 0 (ie, the downbeat). /* Sequences are always assumed to start on a MIDI Beat of 0 (ie, the downbeat).
* *

View file

@ -598,7 +598,7 @@ class /*LIBTEMPORAL_API*/ MusicTimePoint : public bartime_hook, public virtual
class LIBTEMPORAL_API TempoMapPoint : public Point, public TempoMetric class LIBTEMPORAL_API TempoMapPoint : public Point, public TempoMetric
{ {
public: public:
TempoMapPoint (TempoMap & map, TempoMetric const & tm, superclock_t sc, Beats const & q, BBT_Time const & bbt) TempoMapPoint (TempoMap const & map, TempoMetric const & tm, superclock_t sc, Beats const & q, BBT_Time const & bbt)
: Point (map, sc, q, bbt), TempoMetric (tm), _floating (false) {} : Point (map, sc, q, bbt), TempoMetric (tm), _floating (false) {}
~TempoMapPoint () {} ~TempoMapPoint () {}
@ -658,24 +658,35 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
* *
*/ */
public: public:
typedef boost::shared_ptr<TempoMap> SharedPtr; typedef boost::shared_ptr<TempoMap const> SharedPtr;
typedef boost::shared_ptr<TempoMap> WritableSharedPtr;
private: private:
static thread_local SharedPtr _tempo_map_p; static thread_local WritableSharedPtr _tempo_map_p;
static SerializedRCUManager<TempoMap> _map_mgr; static SerializedRCUManager<TempoMap> _map_mgr;
public: public:
LIBTEMPORAL_API static void init (); LIBTEMPORAL_API static void init ();
LIBTEMPORAL_API static void update_thread_tempo_map() { _tempo_map_p = _map_mgr.reader(); } LIBTEMPORAL_API static void update_thread_tempo_map() { _tempo_map_p = _map_mgr.reader(); }
LIBTEMPORAL_API static SharedPtr use() { assert (_tempo_map_p); return _tempo_map_p; } LIBTEMPORAL_API static SharedPtr use() { assert (_tempo_map_p); return _tempo_map_p; }
LIBTEMPORAL_API static SharedPtr fetch() { update_thread_tempo_map(); return use(); } LIBTEMPORAL_API static SharedPtr fetch() { update_thread_tempo_map(); return _tempo_map_p; }
LIBTEMPORAL_API static SharedPtr read() { return _map_mgr.reader(); }
LIBTEMPORAL_API static void set (SharedPtr new_map) { _tempo_map_p = new_map; /* new_map must have been fetched with read() */ }
LIBTEMPORAL_API static SharedPtr write_copy(); /* Used only by the ARDOUR::AudioEngine API to reset the process thread
LIBTEMPORAL_API static void fetch_writable() { _tempo_map_p = write_copy(); } * tempo map only when it has changed.
LIBTEMPORAL_API static int update (SharedPtr m); */
LIBTEMPORAL_API static WritableSharedPtr read() { return _map_mgr.reader(); }
LIBTEMPORAL_API static void set (WritableSharedPtr new_map) { _tempo_map_p = new_map; /* new_map must have been fetched with read() */ }
/* API for typical tempo map changes */
LIBTEMPORAL_API static WritableSharedPtr write_copy();
LIBTEMPORAL_API static int update (WritableSharedPtr m);
LIBTEMPORAL_API static void abort_update (); LIBTEMPORAL_API static void abort_update ();
/* API to be reviewed */
LIBTEMPORAL_API static WritableSharedPtr fetch_writable() { _tempo_map_p = write_copy(); return _tempo_map_p; }
/* and now on with the rest of the show ... */ /* and now on with the rest of the show ... */
public: public:
@ -727,11 +738,11 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
* offer one that uses an STL container instead. * offer one that uses an STL container instead.
*/ */
typedef std::list<Point*> Metrics; typedef std::list<Point const *> Metrics;
void get_metrics (Metrics& m) { void get_metrics (Metrics& m) const {
for (Points::iterator t = _points.begin(); t != _points.end(); ++t) { for (auto const & p : _points) {
m.push_back (&*t); m.push_back (&p);
} }
} }
@ -845,8 +856,8 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
LIBTEMPORAL_API BBT_Time bbt_walk (BBT_Time const &, BBT_Offset const &) const; LIBTEMPORAL_API BBT_Time bbt_walk (BBT_Time const &, BBT_Offset const &) const;
LIBTEMPORAL_API void get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0); LIBTEMPORAL_API void get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0) const;
LIBTEMPORAL_API uint32_t count_bars (Beats const & start, Beats const & end); LIBTEMPORAL_API uint32_t count_bars (Beats const & start, Beats const & end) const;
struct EmptyTempoMapException : public std::exception { struct EmptyTempoMapException : public std::exception {
virtual const char* what() const throw() { return "TempoMap is empty"; } virtual const char* what() const throw() { return "TempoMap is empty"; }
@ -879,7 +890,7 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
LIBTEMPORAL_API Beats quarters_at_sample (samplepos_t sc) const { return quarters_at_superclock (samples_to_superclock (sc, TEMPORAL_SAMPLE_RATE)); } LIBTEMPORAL_API Beats quarters_at_sample (samplepos_t sc) const { return quarters_at_superclock (samples_to_superclock (sc, TEMPORAL_SAMPLE_RATE)); }
LIBTEMPORAL_API Beats quarters_at_superclock (superclock_t sc) const; LIBTEMPORAL_API Beats quarters_at_superclock (superclock_t sc) const;
LIBTEMPORAL_API void midi_clock_beat_at_or_after (samplepos_t const pos, samplepos_t& clk_pos, uint32_t& clk_beat); LIBTEMPORAL_API void midi_clock_beat_at_or_after (samplepos_t const pos, samplepos_t& clk_pos, uint32_t& clk_beat) const;
private: private:
Tempos _tempos; Tempos _tempos;