scoped tempo maps: logic fixes and some comment-documentation

This commit is contained in:
Paul Davis 2025-08-12 07:53:30 -06:00
parent 2fc669964e
commit b205c0bc4b
3 changed files with 112 additions and 27 deletions

View file

@ -25,35 +25,59 @@
#include "temporal/debug.h"
#include "temporal/tempo.h"
#include "temporal/visibility.h"
namespace Temporal {
class ScopedTempoMapOwner
class TempoMap;
/* This object aand the use of the EC_LOCAL_TEMPO_MAP_SCOPE allows derived
* classes to have a "local" (i.e. non-global) tempo map in use. This is
* intended for custom editing contexts where all conversion between
* sample/pixel and beat time should use a local tempo map, rather than the
* global one.
*
* The downside is a bit ugly: every method of a derived class should have a
* call to EC_LOCAL_TEMPO_MAP_SCOPE before anything else. However, in C++ there
* is no other way to accomplish this, since there is no method-based code
* injection. This macro uses an RAII technique (via TempoMapScope) to call the
* ::in() and ::out() methods of the ScopedTempoMapOwner on entry and exist
* from the method scope.
*
* A derived class will call start_local_tempo_map() to provide the map it
* should be using, and end_local_tempo_map() when for whatever reason that map
* is no longer relevant.
*
* start_local_tempo_map() will set the local_tempo_map (shared) ptr, which
* gives us an indication as we enter and leave the scope of class methods that
* there is a map which should be in use. There is also a depth counter so that
* we only set the thread-local tempo map pointer when we transition from
* depth==0 to depth==1. See notes in the method definition for some important
* caveats.
*
* end_local_tempo_map() is called when the object's methods should no longer
* use the previously provided local tempo map.
*
* The cost of this is low (but not zero, obviously):
*
* - extra pointer on the stack (the scope member of TempoMapScope)
* - two conditionals, the first of which will frequently fail
* - writes to the thread-local pointer whenever the method dept
* - transitions between 0 and 1 (in either direction).
*/
class LIBTEMPORAL_API ScopedTempoMapOwner
{
public:
ScopedTempoMapOwner () : local_tempo_map_depth (0) {}
virtual ~ScopedTempoMapOwner () {}
LIBTEMPORAL_API ScopedTempoMapOwner () : local_tempo_map_depth (0) {}
virtual LIBTEMPORAL_API ~ScopedTempoMapOwner () {}
void start_local_tempo_map (std::shared_ptr<Temporal::TempoMap> map) {
DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: starting local tempo scope\n", scope_name()));
map->set_scope_owner (*this);
_local_tempo_map = map;
Temporal::TempoMap::set (_local_tempo_map);
local_tempo_map_depth = 1;
}
void LIBTEMPORAL_API start_local_tempo_map (std::shared_ptr<Temporal::TempoMap> map);
void LIBTEMPORAL_API end_local_tempo_map ();
void end_local_tempo_map () {
DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: ending local tempo scope\n", scope_name()));
assert (_local_tempo_map);
local_tempo_map_depth = 0;
_local_tempo_map->clear_scope_owner ();
_local_tempo_map.reset ();
Temporal::TempoMap::fetch ();
}
uint64_t LIBTEMPORAL_API depth() const { return local_tempo_map_depth; }
uint64_t depth() const { return local_tempo_map_depth; }
virtual std::string scope_name() const = 0;
virtual LIBTEMPORAL_API std::string scope_name() const = 0;
protected:
mutable std::shared_ptr<Temporal::TempoMap> _local_tempo_map;
@ -64,8 +88,10 @@ class ScopedTempoMapOwner
void in () const {
if (_local_tempo_map && local_tempo_map_depth++ == 0 ) {
DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: in to local tempo %2\n", scope_name(), local_tempo_map_depth));
DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: in to local tempo %2, TMAP set\n", scope_name(), local_tempo_map_depth));
Temporal::TempoMap::set (_local_tempo_map);
} else {
DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: in to local tempo %2, no tm set\n", scope_name(), local_tempo_map_depth));
}
}
@ -96,5 +122,4 @@ struct TempoMapScope {
} // namespace
// #define EC_LOCAL_TEMPO_SCOPE Temporal::TempoMapScope __tms (*this);
#define EC_LOCAL_TEMPO_SCOPE
#define EC_LOCAL_TEMPO_SCOPE Temporal::TempoMapScope __tms (*this);