Ardour  9.0-pre0-1592-g5ffc3aa28b
scope.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2025 Paul Davis <paul@linuxaudiosystems.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #pragma once
20 
21 #include <cstdint>
22 #include <memory>
23 
24 #include "pbd/compose.h"
25 
26 #include "temporal/debug.h"
27 #include "temporal/tempo.h"
28 #include "temporal/visibility.h"
29 
30 namespace Temporal {
31 
32 class TempoMap;
33 
34 /* This object aand the use of the EC_LOCAL_TEMPO_MAP_SCOPE allows derived
35  * classes to have a "local" (i.e. non-global) tempo map in use. This is
36  * intended for custom editing contexts where all conversion between
37  * sample/pixel and beat time should use a local tempo map, rather than the
38  * global one.
39  *
40  * The downside is a bit ugly: every method of a derived class should have a
41  * call to EC_LOCAL_TEMPO_MAP_SCOPE before anything else. However, in C++ there
42  * is no other way to accomplish this, since there is no method-based code
43  * injection. This macro uses an RAII technique (via TempoMapScope) to call the
44  * ::in() and ::out() methods of the ScopedTempoMapOwner on entry and exist
45  * from the method scope.
46  *
47  * A derived class will call start_local_tempo_map() to provide the map it
48  * should be using, and end_local_tempo_map() when for whatever reason that map
49  * is no longer relevant.
50  *
51  * start_local_tempo_map() will set the local_tempo_map (shared) ptr, which
52  * gives us an indication as we enter and leave the scope of class methods that
53  * there is a map which should be in use. There is also a depth counter so that
54  * we only set the thread-local tempo map pointer when we transition from
55  * depth==0 to depth==1. See notes in the method definition for some important
56  * caveats.
57  *
58  * end_local_tempo_map() is called when the object's methods should no longer
59  * use the previously provided local tempo map.
60  *
61  * The cost of this is low (but not zero, obviously):
62  *
63  * - extra pointer on the stack (the scope member of TempoMapScope)
64  * - two conditionals, the first of which will frequently fail
65  * - writes to the thread-local pointer whenever the method dept
66  * - transitions between 0 and 1 (in either direction).
67  */
68 
70 {
71  public:
72  ScopedTempoMapOwner () : local_tempo_map_depth (0) {}
73  virtual ~ScopedTempoMapOwner () {}
74 
75  void start_local_tempo_map (std::shared_ptr<Temporal::TempoMap> map);
77 
78  uint64_t depth() const { return local_tempo_map_depth; }
79 
80  virtual std::string scope_name() const = 0;
81 
82  protected:
83  mutable std::shared_ptr<Temporal::TempoMap> _local_tempo_map;
84  mutable uint64_t local_tempo_map_depth;
85 
86  private:
87  friend class TempoMapScope;
88 
89  void in () const {
90  if (_local_tempo_map && local_tempo_map_depth++ == 0 ) {
91  DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: in to local tempo %2, TMAP set\n", scope_name(), local_tempo_map_depth));
92  Temporal::TempoMap::set (_local_tempo_map);
93  } else {
94  DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: in to local tempo %2, no tm set\n", scope_name(), local_tempo_map_depth));
95  }
96  }
97 
98  void out () const {
99  DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: out with local tempo %2\n", scope_name(), local_tempo_map_depth));
100  if (local_tempo_map_depth && --local_tempo_map_depth == 0) {
101  DEBUG_TRACE (PBD::DEBUG::ScopedTempoMap, string_compose ("%1: done with local tempo, depth now %2\n", scope_name(), local_tempo_map_depth));
102  Temporal::TempoMap::fetch (); /* get current global map into thread-local pointer */
103  }
104  }
105 
106 };
107 
108 
111  : scope (sco)
112  {
113  scope.in ();
114  }
115 
117  scope.out ();
118  }
119 
121 };
122 
123 } // namespace
124 
125 #define EC_LOCAL_TEMPO_SCOPE Temporal::TempoMapScope __tms (*this);
virtual std::string scope_name() const =0
virtual ~ScopedTempoMapOwner()
Definition: scope.h:73
uint64_t local_tempo_map_depth
Definition: scope.h:84
uint64_t depth() const
Definition: scope.h:78
std::shared_ptr< Temporal::TempoMap > _local_tempo_map
Definition: scope.h:83
void start_local_tempo_map(std::shared_ptr< Temporal::TempoMap > map)
static void set(SharedPtr new_map)
static SharedPtr fetch()
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:246
#define DEBUG_TRACE(bits, str)
DebugBits ScopedTempoMap
ScopedTempoMapOwner const & scope
Definition: scope.h:120
TempoMapScope(ScopedTempoMapOwner const &sco)
Definition: scope.h:110
#define LIBTEMPORAL_API