I’m working on making a dub siren plugin loosely based on the AudioProcessorValueTreeState tutorial and the AudioProcessorGraph tutorial.
I currently have one oscillator in the graph with the frequency controlled by a slider. However, it sounds as if the oscillator’s output is being modulated by something - rather than a pure sine wave I get a wobbling sound. If I put the frequency right down to 1Hz rather than hearing nothing I hear a distorted oscillation with the volume going up and down at 1Hz. The output at, say, 600Hz will sound different if I move the slider to a different value and then bring it back to 600Hz.
Below is what is hopefully only the relevant bits of code (I realise the frequency range is too high for a real LFO but I don’t think that’s relevant). As you can see they are fairly similar to the tutorials so I am at a loss what to look at next. Both tutorials build and sound fine.
I thought that the problem might be making the LFO
a subclass of AudioProcessorValueTreeState::Listener
, but commenting out the relevant bits of code made no difference.
Possibly relevant is that I get a lot of the following error in the console:
2018-06-30 17:52:05.153012+0100 AudioPluginHost[21997:4770486] dynamic_cast error 1: Both of the following type_info's should have public visibility. At least one of them is hidden. N4juce23AudioProcessorParameterE, N4juce28AudioProcessorValueTreeState9ParameterE.
Removing the call to createAndAddParameters
in the constructor removes this error but I don’t see why it should cause problems.
I would be grateful for any suggestions.
// LFO.h ================
class LFO : public ProcessorBase, public AudioProcessorValueTreeState::Listener
{
public:
LFO();
const String getName() const override { return "LFO"; }
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
void processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override;
void reset() override;
void parameterChanged(const String& parameterID, float newValue) override;
private:
dsp::Oscillator<float> oscillator;
};
// LFO.cpp ==============
LFO::LFO()
{
oscillator.setFrequency(440.0f);
oscillator.initialise([] (float x) { return std::sin(x); });
}
void LFO::prepareToPlay(double sampleRate, int samplesPerBlock)
{
dsp::ProcessSpec spec { sampleRate, static_cast<uint32>(samplesPerBlock) };
oscillator.prepare(spec);
}
void LFO::processBlock(AudioSampleBuffer &buffer, juce::MidiBuffer &midiMessages)
{
dsp::AudioBlock<float> block(buffer);
dsp::ProcessContextReplacing<float> context(block);
oscillator.process(context);
}
void LFO::reset()
{
oscillator.reset();
}
void LFO::parameterChanged(const String& parameterID, float newValue)
{
if (parameterID == "lfo_freq") {
oscillator.setFrequency(newValue);
}
}
// PluginProcessor.cpp ========
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
DubSirenAudioProcessor::DubSirenAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
: AudioProcessor (BusesProperties()
.withOutput ("Output", AudioChannelSet::stereo(), true)
),
mainProcessor(new AudioProcessorGraph()),
parameters(*this, nullptr)
#endif
{
parameters.createAndAddParameter("lfo_freq", "LFO Freq", String(), NormalisableRange<float>(1.0f, 10000.0f), 440.0f, nullptr, nullptr);
parameters.state = ValueTree(Identifier("LFOFreq"));
}
...
void DubSirenAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
if (lfoNode == nullptr) {
mainProcessor->setPlayConfigDetails(getMainBusNumInputChannels(),
getMainBusNumOutputChannels(),
sampleRate, samplesPerBlock);
mainProcessor->prepareToPlay(sampleRate, samplesPerBlock);
initialiseGraph();
}
}
void DubSirenAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); i++)
{
buffer.clear(i, 0, buffer.getNumSamples());
}
mainProcessor->processBlock(buffer, midiMessages);
}
void DubSirenAudioProcessor::initialiseGraph()
{
mainProcessor->clear();
audioOutputNode = mainProcessor->addNode(new AudioGraphIOProcessor(AudioGraphIOProcessor::audioOutputNode));
midiInputNode = mainProcessor->addNode(new AudioGraphIOProcessor(AudioGraphIOProcessor::midiInputNode));
midiOutputNode = mainProcessor->addNode(new AudioGraphIOProcessor(AudioGraphIOProcessor::midiOutputNode));
connectAudioNodes();
connectMidiNodes();
}
void DubSirenAudioProcessor::connectAudioNodes()
{
lfoNode = mainProcessor->addNode(new LFO());
parameters.addParameterListener("lfo_freq", static_cast<LFO*>(lfoNode->getProcessor()));
for (int channel = 0; channel < 2; channel++)
{
mainProcessor->addConnection({
{ lfoNode->nodeID, channel },
{ audioOutputNode->nodeID, channel }
});
}
}
void DubSirenAudioProcessor::connectMidiNodes()
{
mainProcessor->addConnection({
{ midiInputNode->nodeID, AudioProcessorGraph::midiChannelIndex},
{ midiOutputNode->nodeID, AudioProcessorGraph::midiChannelIndex}
});
}
// PluginEditor.cpp =======
DubSirenAudioProcessorEditor::DubSirenAudioProcessorEditor (DubSirenAudioProcessor& p, AudioProcessorValueTreeState& vts)
: AudioProcessorEditor (p), processor (p), valueTreeState(vts)
{
setSize (400, 300);
lfoFreqLabel.setText("LFO Freq", dontSendNotification);
addAndMakeVisible(lfoFreqLabel);
addAndMakeVisible(lfoFreq);
lfoFreqAttachment.reset(new SliderAttachment(valueTreeState, "lfo_freq", lfoFreq));
}