-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLocations.cpp
More file actions
69 lines (62 loc) · 2.88 KB
/
Copy pathLocations.cpp
File metadata and controls
69 lines (62 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// SPDX-FileCopyrightText: 2026 Anthony Green <green@moxielogic.com>
// SPDX-License-Identifier: AGPL-3.0-only
// Timeline locations — named points and ranges on the timeline (markers, sections,
// loop/punch/export ranges). A single model so the ruler, the control API, and
// render/export presets all refer to the same named spots ("intro", "chorus-2",
// "loop-a") instead of re-passing raw beat numbers. Stored in the composition
// (see Composition.cpp) and serialised in toValueTree/loadFromTree (MainComponent.cpp).
#include "MainComponent.h"
using gloopy::time::BeatPosition;
bool MainComponent::apiAddLocation (const juce::String& name, const juce::String& kind,
double startBeat, double endBeat)
{
if (name.trim().isEmpty()) return false;
return callOnMessageThread ([&] () -> bool
{
const double s = juce::jmax (0.0, startBeat);
const double e = juce::jmax (s, endBeat); // markers pass end<=start -> collapses to a point
pushUndoSnapshot();
{
const juce::ScopedLock sl (engineLock);
auto it = std::find_if (locations.begin(), locations.end(),
[&] (const TimelineLocation& l) { return l.name == name; });
if (it != locations.end())
*it = { name, kind, BeatPosition { s }, BeatPosition { e } }; // upsert
else
locations.push_back ({ name, kind, BeatPosition { s }, BeatPosition { e } });
}
std::cout << "[loc] " << kind << " '" << name << "' [" << s << ".." << e << "]" << std::endl;
return true;
});
}
std::vector<MainComponent::TimelineLocation> MainComponent::apiListLocations()
{
return callOnMessageThread ([&] { const juce::ScopedLock sl (engineLock); return locations; });
}
bool MainComponent::apiRemoveLocation (const juce::String& name)
{
return callOnMessageThread ([&] () -> bool
{
pushUndoSnapshot();
const juce::ScopedLock sl (engineLock);
const auto before = locations.size();
locations.erase (std::remove_if (locations.begin(), locations.end(),
[&] (const TimelineLocation& l) { return l.name == name; }),
locations.end());
return locations.size() != before;
});
}
// Resolve a named range/section to a beat span, for render/export targeting.
// A zero-length location (a marker) is rejected — a range must span time.
bool MainComponent::apiResolveRange (const juce::String& name, double& startBeat, double& endBeat)
{
const juce::ScopedLock sl (engineLock);
for (auto& l : locations)
if (l.name == name && l.endBeat > l.startBeat) // typed position comparison (Time.h)
{
startBeat = l.startBeat.inBeats();
endBeat = l.endBeat.inBeats();
return true;
}
return false;
}