-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPluginProcessor.cpp
More file actions
129 lines (108 loc) · 4.97 KB
/
Copy pathPluginProcessor.cpp
File metadata and controls
129 lines (108 loc) · 4.97 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
#include "PluginProcessor.h"
#include "PluginEditor.h"
// =============================================================================
PluginProcessor::PluginProcessor ()
: AudioProcessor (BusesProperties ()
.withInput ("Input", juce::AudioChannelSet::stereo (), true)
.withOutput ("Output", juce::AudioChannelSet::stereo (), true))
{
}
PluginProcessor::~PluginProcessor ()
{
// Editor should already be null here (JUCE deletes the editor before the
// processor), but guard defensively in case of unusual host teardown order.
activeEditor.store (nullptr, std::memory_order_release);
}
// =============================================================================
bool PluginProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
// Accept any combination that has at least one input channel.
// The waterfall is an analyser; it passes audio through unchanged.
if (layouts.getMainInputChannelSet () == juce::AudioChannelSet::disabled () ||
layouts.getMainOutputChannelSet () == juce::AudioChannelSet::disabled ())
return false;
// Outputs must match inputs so we can do a transparent pass-through.
return layouts.getMainOutputChannelSet () == layouts.getMainInputChannelSet ();
}
// =============================================================================
void PluginProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
// Pre-allocate the mono scratch on the message thread (prepareToPlay is
// not called on the RT thread) so processBlock never allocates.
monoScratch.assign (static_cast<std::size_t> (samplesPerBlock), 0.0f);
// Forward sample rate to the waterfall if the editor already exists.
// If it doesn't exist yet, PluginEditor's constructor will call
// setSampleRate itself after reading back getTotalNumInputChannels().
if (auto* e = activeEditor.load (std::memory_order_acquire))
e->getWaterfall ().setSampleRate (sampleRate);
}
void PluginProcessor::releaseResources ()
{
monoScratch.clear ();
}
// =============================================================================
void PluginProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
{
juce::ScopedNoDenormals noDenormals;
// ---- Transparent pass-through -------------------------------------------
// This plugin is a pure analyser; it does not modify the audio signal.
// (Remove these lines if you want a silent / zero-latency analyser.)
auto totalNumInputChannels = getTotalNumInputChannels ();
auto totalNumOutputChannels = getTotalNumOutputChannels ();
for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples ());
// ---- Push mono mix to the waterfall -------------------------------------
auto* editor = activeEditor.load (std::memory_order_acquire);
if (editor == nullptr)
return;
const int numSamples = buffer.getNumSamples ();
const int numChans = totalNumInputChannels;
// Safety: samplesPerBlock can exceed what prepareToPlay allocated if the
// host is misbehaving. Guard without allocating; just truncate.
const int n = juce::jmin (numSamples, (int)monoScratch.size ());
if (numChans <= 0)
return;
const float inv = 1.0f / (float)numChans;
// Copy channel 0 scaled — avoids a separate zero-init pass.
const float* src0 = buffer.getReadPointer (0);
for (int i = 0; i < n; ++i)
monoScratch[i] = src0[i] * inv;
// Accumulate remaining channels.
for (int c = 1; c < numChans; ++c)
{
const float* src = buffer.getReadPointer (c);
for (int i = 0; i < n; ++i)
monoScratch[i] += src[i] * inv;
}
const int accepted = editor->getWaterfall ().pushAudioBlock (monoScratch.data (), n);
if (accepted < n)
{
DBG ("[Waterfall] FIFO full — dropped " << (n - accepted) << " samples");
}
}
// =============================================================================
juce::AudioProcessorEditor* PluginProcessor::createEditor ()
{
auto* e = new PluginEditor (*this);
registerEditor (e);
return e;
}
// =============================================================================
void PluginProcessor::registerEditor (PluginEditor* e)
{
activeEditor.store (e, std::memory_order_release);
}
void PluginProcessor::unregisterEditor (PluginEditor* e)
{
// Only clear if it is still us — guards against a race where a second
// editor was created before the first was fully destroyed (unusual, but
// some hosts do this during plugin scanning).
PluginEditor* expected = e;
activeEditor.compare_exchange_strong (expected, nullptr, std::memory_order_acq_rel);
}
// =============================================================================
// This macro tells JUCE's plugin wrapper how to instantiate the processor.
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter ()
{
return new PluginProcessor ();
}