Audacity 3.2.0
SnapUtils.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 SnapUtils.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11
12#include "SnapUtils.h"
13
14#include <algorithm>
15#include <cmath>
16#include <unordered_map>
17
18
19namespace
20{
21const wxString SnapModeKey = L"/Snap/Mode";
22const wxString SnapToKey = L"/Snap/To";
23const wxString OldSnapToKey = L"/SnapTo";
24const wxString SelectionFormatKey = L"/SelectionFormat";
25
26const auto PathStart = L"SnapFunctions";
27} // namespace
28
30
33 EnumValueSymbols { L"OFF", L"NEAREST", L"PRIOR" },
34 0,
36};
37
39{
41 return SnapModeSetting.ReadEnum();
42
43 return static_cast<SnapMode>(gPrefs->Read(OldSnapToKey, 0L));
44}
45
46wxString DeduceSnapTo()
47{
48 const auto& defaultSnapTo = SnapToSetting.GetDefault();
49
51 return defaultSnapTo;
52
53 // It appears that we are migrating from an older version of Audacity
54 // where snapping was controlled by the "/SelectionFormat".
55 // We are only trying to match the known values from the
56 // config to the appropriate snapping function, this matching
57 // does not introduce any kind of dependency to the lib-numeric-format.
58
59 const auto selectionFormat = gPrefs->Read(SelectionFormatKey);
60
61 static const std::unordered_map<wxString, wxString> selectionFormatLookup = {
62 { L"seconds", L"seconds" },
63 { L"seconds + milliseconds", L"milliseconds" },
64 { L"hh:mm:ss", L"seconds" },
65 { L"dd:hh:mm:ss", L"seconds" },
66 { L"hh:mm:ss + hundredths", L"centiseconds" },
67 { L"hh:mm:ss + milliseconds", L"milliseconds" },
68 { L"hh:mm:ss + samples", L"samples" },
69 { L"samples", L"samples" },
70 { L"hh:mm:ss + film frames (24 fps)", L"film_24_fps" },
71 { L"film frames (24 fps)", L"film_24_fps" },
72 { L"hh:mm:ss + NTSC drop frames", L"ntsc_29.97_fps" },
73 // Well, not really. Snapping with the "bent" time sounds funny anyway.
74 { L"hh:mm:ss + NTSC non-drop frames", L"ntsc_30_fps" },
75 { L"NTSC frames", L"ntsc_29.97_fps" },
76 { L"hh:mm:ss + PAL frames (25 fps)", L"film_25_fps" },
77 { L"PAL frames (25 fps)", L"film_25_fps" },
78 { L"hh:mm:ss + CDDA frames (75 fps)", L"cd_75_fps" },
79 { L"CDDA frames (75 fps)", L"cd_75_fps" },
80 };
81
82 auto it = selectionFormatLookup.find(selectionFormat);
83
84 if (it != selectionFormatLookup.end())
85 return it->second;
86
87 return defaultSnapTo;
88}
89
91{
93 return SnapToSetting.Read();
94
95 // Try to perform the config migration once
96 const auto snapTo = DeduceSnapTo();
97 SnapToSetting.Write(snapTo);
98 gPrefs->Flush();
99
100
101 return snapTo;
102}
103
105{
107 return registry;
108}
109
111{
113 PathStart,
114 { { L"", L"beats,triplets,time,video,cd" } },
115 };
116
118 Registry::VisitWithFunctions(visitor, &top, &Registry());
119}
120
122{
123 using Cache = std::unordered_map<Identifier, const SnapRegistryItem*>;
124 static Cache cache;
125
126 auto it = cache.find(id);
127 if (it != cache.end())
128 return it->second;
129
130 auto visitor = [&](const SnapRegistryItem &item, auto&) {
131 auto it = cache.find(item.name);
132
133 if (it == cache.end())
134 {
135 cache.insert({ item.name, &item });
136 }
137 };
138
139 Registry::Visit(visitor, &Registry());
140
141 it = cache.find(id);
142
143 return it != cache.end() ? it->second : nullptr;
144}
145
147 const Identifier& id, const AudacityProject& project, double time,
148 bool nearest)
149{
150 auto item = Find(id);
151
152 if (item == nullptr)
153 return SnapResult { time, false };
154
155 return item->Snap(project, time, nearest);
156}
157
159 const Identifier& id, const AudacityProject& project, double time,
160 bool upwards)
161{
162 auto item = Find(id);
163
164 if (item == nullptr)
165 return SnapResult { time, false };
166
167 return item->SingleStep(project, time, upwards);
168}
169
171{
172}
173
175 const Identifier& internalName, const TranslatableString& _label)
176 : SingleItem { internalName }
177 , label { _label }
178{
179}
180
182{
183}
184
185namespace
186{
187SnapResult SnapWithMultiplier (double value, double multiplier, bool nearest)
188{
189 if (multiplier <= 0.0)
190 return SnapResult { value, false };
191
192 auto result = nearest ? std::round(value * multiplier) / multiplier :
193 std::floor(value * multiplier) / multiplier;
194
195 return SnapResult { result, true };
196}
197
199{
200public:
202 const Identifier& internalName, const TranslatableString& label,
203 double multiplier)
204 : SnapRegistryItem { internalName, label }
205 , mMultiplier { multiplier }
206 {
207 assert(mMultiplier > 0.0);
208 }
209
211 Snap(const AudacityProject&, double time, bool nearest) const override
212 {
213 return SnapWithMultiplier(time, mMultiplier, nearest);
214 }
215
217 const AudacityProject& project, double time, bool upwards) const override
218 {
219 const auto step = (upwards ? 1.0 : -1.0) / mMultiplier;
220 const double result = time + step;
221
222 if (result < 0.0)
223 return { 0.0, false };
224
225 return SnapWithMultiplier(result, mMultiplier, true);
226 }
227
228private:
229 const double mMultiplier;
230};
231
233{
234public:
236 const Identifier& internalName, const TranslatableString& label,
237 MultiplierFunctor functor)
238 : SnapRegistryItem { internalName, label }
239 , mMultiplierFunctor { std::move(functor) }
240 {
241 assert(mMultiplierFunctor);
242 }
243
245 Snap(const AudacityProject& project, double time, bool nearest) const override
246 {
247 if (!mMultiplierFunctor)
248 return { time, false };
249 return SnapWithMultiplier(time, mMultiplierFunctor(project), nearest);
250 }
251
253 const AudacityProject& project, double time, bool upwards) const override
254 {
255 if (!mMultiplierFunctor)
256 return { time, false };
257
258 const auto multiplier = mMultiplierFunctor(project);
259
260 const auto eps =
261 std::max(1.0, time) * std::numeric_limits<double>::epsilon();
262
263 const auto current = static_cast<int>(std::floor(time * (1.0 + eps) * multiplier));
264 const auto next = upwards ? current + 1 : current - 1;
265
266 double result = next / multiplier;
267
268 if (result < 0.0)
269 return { 0.0, false };
270
271 while (static_cast<int>(std::floor(result * multiplier)) < next)
272 result += eps;
273
274 while (static_cast<int>(std::floor(result * multiplier)) > next)
275 result -= eps;
276
277 return { result, true };
278 }
279
280private:
282};
283
284}
285
286std::unique_ptr<SnapRegistryItem> TimeInvariantSnapFunction(
287 const Identifier& functionId, const TranslatableString& label,
288 MultiplierFunctor functor)
289{
290 return std::make_unique<ProjectDependentMultiplierSnapItem>(
291 functionId, label, std::move(functor));
292}
293
294std::unique_ptr<SnapRegistryItem> TimeInvariantSnapFunction(
295 const Identifier& functionId, const TranslatableString& label,
296 double multiplier)
297{
298 return std::make_unique<ConstantMultiplierSnapItem>(
299 functionId, label, multiplier);
300}
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
StringSetting SnapToSetting
Definition: SnapUtils.cpp:29
Identifier ReadSnapTo()
Definition: SnapUtils.cpp:90
wxString DeduceSnapTo()
Definition: SnapUtils.cpp:46
SnapMode ReadSnapMode()
Definition: SnapUtils.cpp:38
std::unique_ptr< SnapRegistryItem > TimeInvariantSnapFunction(const Identifier &functionId, const TranslatableString &label, MultiplierFunctor functor)
Definition: SnapUtils.cpp:286
EnumSetting< SnapMode > SnapModeSetting
Definition: SnapUtils.cpp:31
std::function< double(const AudacityProject &)> MultiplierFunctor
Definition: SnapUtils.h:92
SnapMode
Definition: SnapUtils.h:21
TranslatableString label
Definition: TagsEditor.cpp:165
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Adapts EnumSettingBase to a particular enumeration type.
Definition: Prefs.h:514
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
const T & GetDefault() const
Definition: Prefs.h:199
Specialization of Setting for strings.
Definition: Prefs.h:370
Holds a msgid for the translation catalog; may also bind format arguments.
SnapResult Snap(const AudacityProject &, double time, bool nearest) const override
Definition: SnapUtils.cpp:211
ConstantMultiplierSnapItem(const Identifier &internalName, const TranslatableString &label, double multiplier)
Definition: SnapUtils.cpp:201
SnapResult SingleStep(const AudacityProject &project, double time, bool upwards) const override
Definition: SnapUtils.cpp:216
SnapResult SingleStep(const AudacityProject &project, double time, bool upwards) const override
Definition: SnapUtils.cpp:252
SnapResult Snap(const AudacityProject &project, double time, bool nearest) const override
Definition: SnapUtils.cpp:245
ProjectDependentMultiplierSnapItem(const Identifier &internalName, const TranslatableString &label, MultiplierFunctor functor)
Definition: SnapUtils.cpp:235
virtual bool Flush() noexcept=0
virtual bool HasEntry(const wxString &key) const =0
Checks whether specified key exists within the current group.
virtual bool Read(const wxString &key, bool *value) const =0
void Visit(const Visitors &visitors, const GroupItem< RegistryTraits > *pTopItem, const GroupItem< RegistryTraits > *pRegistry={}, typename RegistryTraits::ComputedItemContextType &computedItemContext=RegistryTraits::ComputedItemContextType::Instance)
Definition: Registry.h:609
void VisitWithFunctions(const VisitorFunctions< RegistryTraits > &visitors, const GroupItem< RegistryTraits > *pTopItem, const GroupItem< RegistryTraits > *pRegistry={}, typename RegistryTraits::ComputedItemContextType &computedItemContext=RegistryTraits::ComputedItemContextType::Instance)
Definition: Registry.h:623
SnapResult SnapWithMultiplier(double value, double multiplier, bool nearest)
Definition: SnapUtils.cpp:187
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
STL namespace.
const Identifier name
Definition: Registry.h:86
static const SnapRegistryItem * Find(const Identifier &id)
Definition: SnapUtils.cpp:121
static void Visit(const SnapRegistryVisitor &visitor)
Definition: SnapUtils.cpp:110
static SnapResult Snap(const Identifier &id, const AudacityProject &project, double time, bool nearest)
Definition: SnapUtils.cpp:146
static Registry::GroupItem< SnapRegistryTraits > & Registry()
Definition: SnapUtils.cpp:104
static SnapResult SingleStep(const Identifier &id, const AudacityProject &project, double time, bool upwards)
Definition: SnapUtils.cpp:158
~SnapRegistryGroup() override
Definition: SnapUtils.cpp:170
SnapRegistryItem(const Identifier &internalName, const TranslatableString &label)
Definition: SnapUtils.cpp:174
~SnapRegistryItem() override
Definition: SnapUtils.cpp:181