tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_VolumeAndPan.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
15{
16 VolAutomatableParameter (const juce::String& xmlTag, const juce::String& name,
17 Plugin& owner, juce::Range<float> r)
18 : AutomatableParameter (xmlTag, name, owner, r)
19 {
20 }
21
23 {
24 notifyListenersOfDeletion();
25 }
26
27 juce::String valueToString (float value) override
28 {
29 return juce::Decibels::toString (volumeFaderPositionToDB (value) + 0.001);
30 }
31
32 float stringToValue (const juce::String& str) override
33 {
34 return decibelsToVolumeFaderPosition (dbStringToDb (str));
35 }
36};
37
38//==============================================================================
40{
41 PanAutomatableParameter (const juce::String& xmlTag, const juce::String& name,
42 Plugin& owner, juce::Range<float> r)
43 : AutomatableParameter (xmlTag, name, owner, r)
44 {
45 }
46
48 {
49 notifyListenersOfDeletion();
50 }
51
52 juce::String valueToString (float value) override
53 {
54 return getPanString (value);
55 }
56
57 float stringToValue (const juce::String& str) override
58 {
59 const float v = str.retainCharacters ("0123456789.-").getFloatValue();
60 return str.contains (TRANS("Left")) ? -v : v;
61 }
62};
63
64//==============================================================================
66{
67 MasterVolParameter (const juce::String& xmlTag, const juce::String& name,
68 Plugin& owner, juce::Range<float> r)
69 : VolAutomatableParameter (xmlTag, name, owner, r)
70 {
71 }
72
73 ~MasterVolParameter() override
74 {
75 notifyListenersOfDeletion();
76 }
77
78 juce::String getPluginAndParamName() const override { return getParameterName(); }
79 juce::String getFullName() const override { return getParameterName(); }
80};
81
82//==============================================================================
84{
85 MasterPanParameter (const juce::String& xmlTag, const juce::String& name,
86 Plugin& owner, juce::Range<float> r)
87 : PanAutomatableParameter (xmlTag, name, owner, r)
88 {
89 }
90
91 ~MasterPanParameter() override
92 {
93 notifyListenersOfDeletion();
94 }
95
96 juce::String getPluginAndParamName() const override { return getParameterName(); }
97 juce::String getFullName() const override { return getParameterName(); }
98};
99
100//==============================================================================
101VolumeAndPanPlugin::VolumeAndPanPlugin (PluginCreationInfo info, bool isMasterVolumeNode)
102 : Plugin (info),
103 isMasterVolume (isMasterVolumeNode)
104{
105 auto um = getUndoManager();
106
107 volume.referTo (state, IDs::volume, um, decibelsToVolumeFaderPosition (0));
108 pan.referTo (state, IDs::pan, um);
109 applyToMidi.referTo (state, IDs::applyToMidi, um);
110 ignoreVca.referTo (state, IDs::ignoreVca, um);
111 polarity.referTo (state, IDs::polarity, um);
112 panLaw.referTo (state, IDs::panLaw, um);
113
114 if (isMasterVolumeNode)
115 {
116 addAutomatableParameter (volParam = new MasterVolParameter ("master volume", TRANS("Master volume"), *this, { 0.0f, 1.0f }));
117 addAutomatableParameter (panParam = new MasterPanParameter ("master pan", TRANS("Master pan"), *this, { -1.0f, 1.0f }));
118 }
119 else
120 {
121 addAutomatableParameter (volParam = new VolAutomatableParameter ("volume", TRANS("Volume"), *this, { 0.0f, 1.0f }));
122 addAutomatableParameter (panParam = new PanAutomatableParameter ("pan", TRANS("Pan"), *this, { -1.0f, 1.0f }));
123 }
124
125 volParam->attachToCurrentValue (volume);
126 panParam->attachToCurrentValue (pan);
127}
128
129VolumeAndPanPlugin::VolumeAndPanPlugin (Edit& ed, const juce::ValueTree& v, bool isMaster)
130 : VolumeAndPanPlugin (PluginCreationInfo (ed, v, false), isMaster)
131{
132}
133
134VolumeAndPanPlugin::~VolumeAndPanPlugin()
135{
136 notifyListenersOfDeletion();
137
138 volParam->detachFromCurrentValue();
139 panParam->detachFromCurrentValue();
140}
141
142juce::ValueTree VolumeAndPanPlugin::create()
143{
144 juce::ValueTree v (IDs::PLUGIN);
145 v.setProperty (IDs::type, xmlTypeName, nullptr);
146 return v;
147}
148
149const char* VolumeAndPanPlugin::xmlTypeName = "volume";
150
151//==============================================================================
152void VolumeAndPanPlugin::initialise (const PluginInitialisationInfo&)
153{
154 refreshVCATrack();
155
156 auto sliderPos = getSliderPos();
157 getGainsFromVolumeFaderPositionAndPan (sliderPos, getPan(), getPanLaw(), lastGainL, lastGainR);
158 lastGainS = volumeFaderPositionToGain (sliderPos);
159}
160
162{
163 TRACKTION_ASSERT_MESSAGE_THREAD
164 refreshVCATrack();
165}
166
168{
170
171 {
172 const std::scoped_lock sl (vcaTrackLock);
173 std::swap (vcaTrack, deleter);
174 }
175}
176
177//==============================================================================
178static float getParentVcaDb (Track& track, TimePosition time)
179{
180 float posOffset = 0.0f;
181
182 for (FolderTrack::Ptr p = track.getParentFolderTrack(); p != nullptr; p = p->getParentFolderTrack())
183 {
184 float db = p->getVcaDb (time);
185
186 if (db < -96.0f)
187 return -100.0f;
188
189 posOffset += decibelsToVolumeFaderPosition (db)
190 - decibelsToVolumeFaderPosition (0.0f);
191 }
192
193 return volumeFaderPositionToDB (decibelsToVolumeFaderPosition (0) + posOffset);
194}
195
197{
198 if (isEnabled())
199 {
200 SCOPED_REALTIME_CHECK
201
202 if (fc.destBuffer != nullptr)
203 {
204 const int numChansIn = fc.destBuffer->getNumChannels();
205 float vcaPosDelta = 0.0f;
206
207 {
208 const std::scoped_lock sl (vcaTrackLock);
209 vcaPosDelta = vcaTrack != nullptr
210 ? decibelsToVolumeFaderPosition (getParentVcaDb (*vcaTrack, fc.editTime.getStart()))
211 - decibelsToVolumeFaderPosition (0.0f)
212 : 0.0f;
213 }
214
215 float lgain, rgain;
216 getGainsFromVolumeFaderPositionAndPan (getSliderPos() + vcaPosDelta, getPan(), getPanLaw(), lgain, rgain);
217 lgain *= (polarity ? -1 : 1);
218 rgain *= (polarity ? -1 : 1);
219
220 fc.destBuffer->applyGainRamp (0, fc.bufferStartSample, fc.bufferNumSamples, lastGainL, lgain);
221
222 if (numChansIn > 1)
223 fc.destBuffer->applyGainRamp (1, fc.bufferStartSample, fc.bufferNumSamples, lastGainR, rgain);
224
225 lastGainL = lgain;
226 lastGainR = rgain;
227
228 // If the number of channels is greater than two, just apply volume
229 if (numChansIn > 2)
230 {
231 const float gain = volumeFaderPositionToGain (getSliderPos() + vcaPosDelta) * (polarity ? -1 : 1);
232
233 for (int i = 2; i < numChansIn; ++i)
234 fc.destBuffer->applyGainRamp (i, fc.bufferStartSample, fc.bufferNumSamples, lastGainS, gain);
235
236 lastGainS = gain;
237 }
238 }
239
240 if (applyToMidi && fc.bufferForMidiMessages != nullptr)
241 fc.bufferForMidiMessages->multiplyVelocities (volumeFaderPositionToGain (getSliderPos()));
242 }
243}
244
245void VolumeAndPanPlugin::refreshVCATrack()
246{
247 juce::ReferenceCountedObjectPtr<AudioTrack> newVcaTrack (ignoreVca ? nullptr : dynamic_cast<AudioTrack*> (getOwnerTrack()));
248
249 {
250 const std::scoped_lock sl (vcaTrackLock);
251 std::swap (vcaTrack, newVcaTrack);
252 }
253}
254
255float VolumeAndPanPlugin::getVolumeDb() const
256{
257 return volumeFaderPositionToDB (volParam->getCurrentValue());
258}
259
260void VolumeAndPanPlugin::setVolumeDb (float vol)
261{
262 setSliderPos (decibelsToVolumeFaderPosition (vol));
263}
264
265void VolumeAndPanPlugin::setSliderPos (float newV)
266{
267 volParam->setParameter (juce::jlimit (0.0f, 1.0f, newV), juce::sendNotification);
268}
269
270void VolumeAndPanPlugin::setPan (float p)
271{
272 if (p >= -0.005f && p <= 0.005f)
273 p = 0.0f;
274
275 panParam->setParameter (juce::jlimit (-1.0f, 1.0f, p), juce::sendNotification);
276}
277
278PanLaw VolumeAndPanPlugin::getPanLaw() const noexcept
279{
280 PanLaw p = (PanLaw) panLaw.get();
281 return p == PanLawDefault ? getDefaultPanLaw() : p;
282}
283
284//==============================================================================
285void VolumeAndPanPlugin::setAppliedToMidiVolumes (bool b)
286{
287 if (b != applyToMidi)
288 {
289 applyToMidi = b;
290 changed();
291 }
292}
293
294void VolumeAndPanPlugin::muteOrUnmute()
295{
296 if (getVolumeDb() > -90.0f)
297 {
298 lastVolumeBeforeMute = getVolumeDb();
299 setVolumeDb (lastVolumeBeforeMute - 0.01f); // needed so that automation is recorded correctly
300 setVolumeDb (-100.0f);
301 }
302 else
303 {
304 if (lastVolumeBeforeMute < -100.0f)
305 lastVolumeBeforeMute = 0.0f;
306
307 setVolumeDb (getVolumeDb() + 0.01f); // needed so that automation is recorded correctly
308 setVolumeDb (lastVolumeBeforeMute);
309 }
310}
311
312void VolumeAndPanPlugin::restorePluginStateFromValueTree (const juce::ValueTree& v)
313{
314 copyPropertiesToCachedValues (v, volume, pan, panLaw, applyToMidi, ignoreVca, polarity);
315
316 for (auto p : getAutomatableParameters())
317 p->updateFromAttachedValue();
318}
319
320}} // namespace tracktion { inline namespace engine
int getNumChannels() const noexcept
void applyGainRamp(int channel, int startSample, int numSamples, Type startGain, Type endGain) noexcept
Type get() const noexcept
static String toString(Type decibels, int decimalPlaces=2, Type minusInfinityDb=Type(defaultMinusInfinitydB), bool shouldIncludeSuffix=true, StringRef customMinusInfinityString={})
float getFloatValue() const noexcept
String retainCharacters(StringRef charactersToRetain) const
bool contains(StringRef text) const noexcept
void changed() override
method from Selectable, that's been overridden here to also tell the edit that it's changed.
Track * getOwnerTrack() const
Returns the track if it's a track or clip plugin.
Base class for tracks which contain clips and plugins and can be added to Edit[s].
void deinitialise() override
Called after play stops to release resources.
void initialiseWithoutStopping(const PluginInitialisationInfo &) override
Tells the plugin that the audio graph has changed but the plugin isn't being re-initialised - i....
void applyToBuffer(const PluginRenderContext &) override
Process the next block of data.
#define TRANS(stringLiteral)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
sendNotification
PanLaw
All laws have been designed to be equal-power, excluding linear respectively.
Passed into Plugins when they are being initialised, to give them useful contextual information that ...
Represents a position in real-life time.
The context passed to plugin render methods to provide it with buffers to fill.
int bufferNumSamples
The number of samples to write into the audio buffer.
MidiMessageArray * bufferForMidiMessages
A buffer of MIDI events to process.
juce::AudioBuffer< float > * destBuffer
The target audio buffer which needs to be filled.
int bufferStartSample
The index of the start point in the audio buffer from which data must be written.
TimeRange editTime
The edit time range this context represents.
T swap(T... args)