-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathControlGroups.cpp
More file actions
131 lines (121 loc) · 4.57 KB
/
Copy pathControlGroups.cpp
File metadata and controls
131 lines (121 loc) · 4.57 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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-FileCopyrightText: 2026 Anthony Green <green@moxielogic.com>
// SPDX-License-Identifier: AGPL-3.0-only
// Control groups (VCA-lite) — a named group whose fader SCALES the volumes of its
// member inserts, without any extra audio routing. Each member insert carries the
// group's name on its MixerTrack (so membership survives insert re-indexing); the
// group holds the shared gain + mute. The mix loop (renderBlock, MainComponent.cpp)
// multiplies each insert's volume by its group's gain and silences it if the group
// is muted. This is *control scaling* (like an Ardour VCA / a console group fader),
// distinct from a bus, which is real summing routing. Serialised in
// toValueTree/loadFromTree and the composition (Composition.cpp).
#include "MainComponent.h"
MainComponent::ControlGroup* MainComponent::findControlGroup (const juce::String& name)
{
for (auto& g : controlGroups)
if (g->name == name) return g.get();
return nullptr;
}
bool MainComponent::apiDefineControlGroup (const juce::String& name, float gain)
{
if (name.trim().isEmpty()) return false;
return callOnMessageThread ([&] () -> bool
{
pushUndoSnapshot();
const juce::ScopedLock sl (engineLock);
if (auto* existing = findControlGroup (name))
existing->gain.store (juce::jlimit (0.0f, 1.0f, gain));
else
{
auto ng = std::make_unique<ControlGroup>();
ng->name = name;
ng->gain.store (juce::jlimit (0.0f, 1.0f, gain));
controlGroups.push_back (std::move (ng));
}
return true;
});
}
bool MainComponent::apiSetControlGroupGain (const juce::String& name, float gain)
{
return callOnMessageThread ([&] () -> bool
{
const juce::ScopedLock sl (engineLock);
auto* g = findControlGroup (name);
if (g == nullptr) return false;
g->gain.store (juce::jlimit (0.0f, 1.0f, gain));
return true;
});
}
bool MainComponent::apiSetControlGroupMute (const juce::String& name, bool mute)
{
return callOnMessageThread ([&] () -> bool
{
const juce::ScopedLock sl (engineLock);
auto* g = findControlGroup (name);
if (g == nullptr) return false;
g->mute.store (mute);
return true;
});
}
bool MainComponent::apiSetControlGroupSolo (const juce::String& name, bool solo)
{
return callOnMessageThread ([&] () -> bool
{
const juce::ScopedLock sl (engineLock);
auto* g = findControlGroup (name);
if (g == nullptr) return false;
g->solo.store (solo);
return true;
});
}
// Assign an insert to a group (group="" clears its membership). Creating a reference
// to a not-yet-defined group defines it (unity gain), so a single call both makes a
// group and adds its first member.
bool MainComponent::apiAssignInsertToGroup (int insert, const juce::String& group)
{
return callOnMessageThread ([&] () -> bool
{
pushUndoSnapshot();
const juce::ScopedLock sl (engineLock);
if (! juce::isPositiveAndBelow (insert, (int) mixerTracks.size())) return false;
const juce::String g = group.trim();
if (g.isNotEmpty() && findControlGroup (g) == nullptr)
{
auto ng = std::make_unique<ControlGroup>();
ng->name = g;
controlGroups.push_back (std::move (ng));
}
mixerTracks[(size_t) insert]->group = g;
return true;
});
}
bool MainComponent::apiRemoveControlGroup (const juce::String& name)
{
return callOnMessageThread ([&] () -> bool
{
pushUndoSnapshot();
const juce::ScopedLock sl (engineLock);
const auto before = controlGroups.size();
for (auto& mt : mixerTracks)
if (mt->group == name) mt->group.clear();
controlGroups.erase (std::remove_if (controlGroups.begin(), controlGroups.end(),
[&] (const std::unique_ptr<ControlGroup>& g) { return g->name == name; }),
controlGroups.end());
return controlGroups.size() != before;
});
}
std::vector<MainComponent::ControlGroupInfo> MainComponent::apiListControlGroups()
{
return callOnMessageThread ([&]
{
const juce::ScopedLock sl (engineLock);
std::vector<ControlGroupInfo> out;
for (auto& g : controlGroups)
{
int members = 0;
for (auto& mt : mixerTracks)
if (mt->group == g->name) ++members;
out.push_back ({ g->name, g->gain.load(), g->mute.load(), g->solo.load(), members });
}
return out;
});
}