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);
}
Temporal::TempoMap::SharedPtr current_map = Temporal::TempoMap::read ();
Temporal::TempoMap::WritableSharedPtr current_map = Temporal::TempoMap::read ();
if (current_map != Temporal::TempoMap::use()) {
Temporal::TempoMap::set (current_map);
if (_session) {

View File

@ -7368,12 +7368,13 @@ Session::listening () const
void
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) {
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())) {
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);
} 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") {
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;
} else {
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 */
TempoMap::update (tmap); /* update the global tempo map manager */
} catch (...) {

View File

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

View File

@ -38,7 +38,7 @@ ArdourTransport::set_tempo (double 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 ());

View File

@ -44,7 +44,7 @@ std::string Tempo::xml_node_name = X_("Tempo");
std::string Meter::xml_node_name = X_("Meter");
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;
void
@ -1802,7 +1802,7 @@ TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp,
}
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
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
TempoMap::count_bars (Beats const & start, Beats const & end)
TempoMap::count_bars (Beats const & start, Beats const & end) const
{
TempoMapPoints bar_grid;
superclock_t s (superclock_at (start));
@ -3181,7 +3181,7 @@ void
TempoMap::MementoBinder::set_state (XMLNode const & node, int version) const
{
/* 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 */
map->set_state (node, version);
/* 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
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);
fetch ();
}
TempoMap::SharedPtr
TempoMap::WritableSharedPtr
TempoMap::write_copy()
{
return _map_mgr.write_copy();
}
int
TempoMap::update (TempoMap::SharedPtr m)
TempoMap::update (TempoMap::WritableSharedPtr m)
{
if (!_map_mgr.update (m)) {
return -1;
@ -3231,7 +3231,7 @@ TempoMap::abort_update ()
}
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).
*

View File

@ -598,7 +598,7 @@ class /*LIBTEMPORAL_API*/ MusicTimePoint : public bartime_hook, public virtual
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)
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) {}
~TempoMapPoint () {}
@ -658,24 +658,35 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
*
*/
public:
typedef boost::shared_ptr<TempoMap> SharedPtr;
typedef boost::shared_ptr<TempoMap const> SharedPtr;
typedef boost::shared_ptr<TempoMap> WritableSharedPtr;
private:
static thread_local SharedPtr _tempo_map_p;
static thread_local WritableSharedPtr _tempo_map_p;
static SerializedRCUManager<TempoMap> _map_mgr;
public:
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 fetch() { update_thread_tempo_map(); return use(); }
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 fetch() { update_thread_tempo_map(); return _tempo_map_p; }
LIBTEMPORAL_API static SharedPtr write_copy();
LIBTEMPORAL_API static void fetch_writable() { _tempo_map_p = write_copy(); }
LIBTEMPORAL_API static int update (SharedPtr m);
/* Used only by the ARDOUR::AudioEngine API to reset the process thread
* tempo map only when it has changed.
*/
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 ();
/* 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 ... */
public:
@ -727,11 +738,11 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
* offer one that uses an STL container instead.
*/
typedef std::list<Point*> Metrics;
typedef std::list<Point const *> Metrics;
void get_metrics (Metrics& m) {
for (Points::iterator t = _points.begin(); t != _points.end(); ++t) {
m.push_back (&*t);
void get_metrics (Metrics& m) const {
for (auto const & p : _points) {
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 void get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0);
LIBTEMPORAL_API uint32_t count_bars (Beats const & start, Beats const & end);
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) const;
struct EmptyTempoMapException : public std::exception {
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_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:
Tempos _tempos;