Skip to content

Commit 41e5f1d

Browse files
committed
Racks: Fixed an issue that meant Racks with Modifiers would get their inputs passed through to their outputs
1 parent 1e04be8 commit 41e5f1d

File tree

8 files changed

+104
-7
lines changed

8 files changed

+104
-7
lines changed

modules/tracktion_engine/model/automation/tracktion_Modifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ void Modifier::baseClassDeinitialise()
171171
void Modifier::baseClassApplyToBuffer (const PluginRenderContext& prc)
172172
{
173173
applyToBuffer (prc);
174+
174175
jassert (valueFifo);
175176
const float v = getCurrentValue();
176177
valueFifo->addValue (prc.bufferNumSamples, getCurrentValue());

modules/tracktion_engine/playback/graph/tracktion_EditNodeBuilder.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1334,7 +1334,8 @@ std::unique_ptr<tracktion::graph::Node> createModifierNodeForList (ModifierList*
13341334
continue;
13351335

13361336
node = makeNode<ModifierNode> (std::move (node), modifier, params.sampleRate, params.blockSize,
1337-
trackMuteState, playHeadState, params.forRendering);
1337+
trackMuteState, playHeadState, params.forRendering,
1338+
ModifierNode::ClearOutputs::no);
13381339
}
13391340
}
13401341

modules/tracktion_engine/playback/graph/tracktion_ModifierNode.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ ModifierNode::ModifierNode (std::unique_ptr<Node> inputNode,
1818
tracktion::engine::Modifier::Ptr modifierToProcess,
1919
double sampleRateToUse, int blockSizeToUse,
2020
const TrackMuteState* trackMuteStateToUse,
21-
tracktion::graph::PlayHeadState& playHeadStateToUse, bool rendering)
21+
tracktion::graph::PlayHeadState& playHeadStateToUse,
22+
bool rendering, ClearOutputs clearOutputs_)
2223
: input (std::move (inputNode)),
2324
modifier (std::move (modifierToProcess)),
2425
trackMuteState (trackMuteStateToUse),
2526
playHeadState (&playHeadStateToUse),
26-
isRendering (rendering)
27+
isRendering (rendering),
28+
clearOutputs (clearOutputs_)
2729
{
2830
jassert (input != nullptr);
2931
jassert (modifier != nullptr);
@@ -116,8 +118,15 @@ void ModifierNode::process (ProcessContext& pc)
116118
if (shouldProcess)
117119
modifier->baseClassApplyToBuffer (getPluginRenderContext (pc.referenceSampleRange, outputAudioBuffer));
118120

119-
// Then copy the buffers to the outputs
120-
outputBuffers.midi.copyFrom (midiMessageArray);
121+
if (clearOutputs == ClearOutputs::yes)
122+
{
123+
outputAudioBlock.clear();
124+
}
125+
else
126+
{
127+
// Then copy the buffers to the outputs
128+
outputBuffers.midi.copyFrom (midiMessageArray);
129+
}
121130
}
122131

123132
//==============================================================================

modules/tracktion_engine/playback/graph/tracktion_ModifierNode.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,26 @@ class ModifierNode final : public tracktion::graph::Node
2222
{
2323
public:
2424
//==============================================================================
25+
enum class ClearOutputs : bool
26+
{
27+
no,
28+
yes
29+
};
30+
2531
/** Creates a ModifierNode to process a Modifier.
2632
@param const TrackMuteState* The optional TrackMuteState to use
2733
@param PlayHeadState The PlayHeadState to monitor for jumps
2834
@param rendering Should be true if this is an offline render
35+
@param ClearOutputs Whether to clear the outputs or pass them through
2936
3037
*/
3138
ModifierNode (std::unique_ptr<Node> input,
3239
tracktion::engine::Modifier::Ptr,
3340
double sampleRateToUse, int blockSizeToUse,
3441
const TrackMuteState*,
35-
tracktion::graph::PlayHeadState&, bool rendering);
42+
tracktion::graph::PlayHeadState&,
43+
bool rendering,
44+
ClearOutputs);
3645

3746
/** Creates a ModifierNode to process a plugin on in a Rack with an InputProvider.
3847
@param InputProvider The InputProvider to provide inputs and a PluginRenderContext
@@ -65,6 +74,7 @@ class ModifierNode final : public tracktion::graph::Node
6574
const TrackMuteState* trackMuteState = nullptr;
6675
tracktion::graph::PlayHeadState* playHeadState = nullptr;
6776
bool isRendering = false;
77+
ClearOutputs clearOutputs = ClearOutputs::no;
6878

6979
bool isInitialised = false;
7080
double sampleRate = 44100.0;

modules/tracktion_engine/playback/graph/tracktion_RackNode.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@ namespace RackNodeBuilder
401401
for (auto m : rack.getModifierList().getModifiers())
402402
itemNodes[m->itemID] = makeNode<ModifierNode> (makeNode<ConnectedNode> ((size_t) m->itemID.getRawID()),
403403
m, sampleRate, blockSize, nullptr,
404-
processState.playHeadState, isRendering);
404+
processState.playHeadState, isRendering,
405+
ModifierNode::ClearOutputs::yes);
405406

406407
// Create an input node and an output summing node
407408
auto outputNode = std::make_unique<ConnectedNode>();

modules/tracktion_engine/plugins/tracktion_Plugins.test.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,51 @@ class ModifiedParameterValuesTests : public juce::UnitTest
363363

364364
static ModifiedParameterValuesTests modifiedParameterValuesTests;
365365

366+
367+
//==============================================================================
368+
//==============================================================================
369+
TEST_SUITE ("tracktion_engine")
370+
{
371+
TEST_CASE ("Modifiers in Racks")
372+
{
373+
// Add a Modifier to a Rack and ensure the input isn't present on the output
374+
auto& engine = *Engine::getEngines()[0];
375+
auto edit = engine::test_utilities::createTestEdit (engine, 1, Edit::forEditing);
376+
377+
auto rackType = edit->getRackList().addNewRack();
378+
auto modifier = rackType->getModifierList().insertModifier (juce::ValueTree (IDs::ENVELOPEFOLLOWER), 0, nullptr);
379+
CHECK(rackType->addConnection (EditItemID(), 1, modifier->itemID, 1));
380+
CHECK(rackType->addConnection (EditItemID(), 2, modifier->itemID, 2));
381+
382+
auto& track = *getAudioTracks (*edit)[0];
383+
auto fileLength = 5_td;
384+
auto squareFile = graph::test_utilities::getSquareFile<juce::WavAudioFormat> (44100.0, fileLength.inSeconds());
385+
auto squareAudioFile = AudioFile (engine, squareFile->getFile());
386+
insertWaveClip (track, {}, squareFile->getFile(),
387+
ClipPosition {{ 0_tp, fileLength }}, DeleteExistingClips::no);
388+
389+
// Check square clip is audible
390+
{
391+
auto player = test_utilities::createEnginePlayer (*edit, {}, { squareAudioFile });
392+
auto buffer = process (*player, fileLength);
393+
394+
CHECK_GT(getRMS (toBufferView (buffer).getChannel (0)), 0.99f);
395+
}
396+
397+
// Now add Rack and clip shouldn't be heard
398+
{
399+
[[maybe_unused]] auto rackInstance = track.pluginList.insertPlugin (RackInstance::create (*rackType), 0);
400+
401+
edit->getTransport().setPosition (0_tp);
402+
auto player = test_utilities::createEnginePlayer (*edit, {}, { squareAudioFile });
403+
auto buffer = process (*player, fileLength);
404+
405+
CHECK_LT(getRMS (toBufferView (buffer).getChannel (0)), 0.01f);
406+
}
407+
}
408+
}
409+
410+
366411
#endif
367412

368413

modules/tracktion_engine/testing/tracktion_EnginePlayer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ class EnginePlayer
118118
int numSamplesProcessed = 0;
119119
};
120120

121+
//==============================================================================
122+
//==============================================================================
123+
/** Process a duration. */
124+
inline juce::AudioBuffer<float> process (EnginePlayer& player, TimeDuration td)
125+
{
126+
auto numSamples = toSamples (td, player.getParams().sampleRate);
127+
return player.process (numSamples);
128+
}
121129

122130
//==============================================================================
123131
//==============================================================================

modules/tracktion_engine/utilities/tracktion_AudioUtilities.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ inline choc::buffer::BufferView<SampleType, choc::buffer::SeparateChannelLayout>
8383
(choc::buffer::FrameCount) buffer.getNumSamples());
8484
}
8585

86+
/** Returns the RMS of a single channel buffer. */
87+
template<typename SampleType>
88+
SampleType getRMS (choc::buffer::MonoView<SampleType>);
89+
8690
//==============================================================================
8791
/** All laws have been designed to be equal-power, excluding linear respectively */
8892
enum PanLaw
@@ -255,6 +259,24 @@ inline void clearChannels (juce::AudioBuffer<float>& buffer, int startChannel, i
255259
buffer.clear (ch, startSample, endSample);
256260
}
257261

262+
263+
//==============================================================================
264+
template<typename SampleType>
265+
inline SampleType getRMS (choc::buffer::MonoView<SampleType> view)
266+
{
267+
auto numSamples = view.getNumFrames();
268+
double sum = 0.0;
269+
270+
for (choc::buffer::FrameCount i = 0; i < numSamples; ++i)
271+
{
272+
auto sample = view.getSample (0, i);
273+
sum += sample * sample;
274+
}
275+
276+
return static_cast<SampleType> (std::sqrt (sum / numSamples));
277+
}
278+
279+
258280
}} // namespace tracktion { inline namespace engine
259281

260282
namespace juce

0 commit comments

Comments
 (0)