Hi, I created for my needings a plugin that is able to get audio from another AudioDevice and stream it inside the Daw or that send incoming audio to another AudioDevice.
Those two looks like this:
My Daw crashes if I has more than 2 plugin with same Audio Device streams audio in input (for example in my graph that is streaming audio with AudioDevice “A” all works if I has only 1 instance of “External Audio Inputs” connected to AudioDevice “B”, another connected to device “B” causes crash).
According to you what should be the problem ?
Also sometimes App causes glitches when I interact with another instance plugin or under 128 samples, Need to use some stuffs as AbstractFifo? there’s need to add a queue to buffer to avoid glitches (and in this case how can I do? and what should be the minor BlockSize to add to avoid to many latency?)?
I’m not expert in those stuffs so any help is really appreciated ![]()
Here the code:
.h
/*
class ExternalAudioIO;
/*
===================================================
MARK: ======== WrappedAudioProcessor ========
==================================================
*/
const int SAFETY_BLOCK_SIZE = 0;
class WrappedAudioProcessor : public AudioProcessor
{
public:
WrappedAudioProcessor(ExternalAudioIO* o, const BusesProperties& ioLayouts) : AudioProcessor(ioLayouts),
owner(o) {}
~WrappedAudioProcessor() override {}
void prepareToPlay (double sampleRate, int samplesPerBlock) override { numProcessBlock = 0; }
void reset() override {}
void releaseResources() override {}
void processBlock (AudioBuffer<float>& audioBuff, MidiBuffer&) override;
const String getName() const override { return "WrappedAudioProcessor"; }
double getTailLengthSeconds() const override { return 0.0; }
bool acceptsMidi() const override { return false; }
bool producesMidi() const override { return false; }
bool isMidiEffect() const override { return false; }
bool hasEditor() const override { return false; }
AudioProcessorEditor* createEditor() override { return nullptr; }
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
void getStateInformation (MemoryBlock&) override {}
void setStateInformation (const void*, int) override {}
bool isBusesLayoutSupported (const BusesLayout& layouts) const override { return true; }
bool canAddBus (bool isInput) const override { return true; }
bool canRemoveBus (bool isInput) const override { return true; }
int numProcessBlock = 0;
private:
ExternalAudioIO* owner;
juce::CriticalSection buffersLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WrappedAudioProcessor)
};
/*
===================================================
MARK: ======== ExternalAudioIOs ========
==================================================
*/
class ExternalAudioIO : public InternalAudioProcessor, public ChangeListener
{
public:
enum IODeviceType { input, output };
ExternalAudioIO(IODeviceType t);
~ExternalAudioIO() override;
void prepareToPlay (double, int) override;
void reset() override;
void releaseResources() override;
void processBuffer (AudioBuffer<float>& audioBuff, MidiBuffer&) override;
static String getIdentifier(IODeviceType deviceType, bool name = false);
const String getName() const override { return getIdentifier(deviceType, true); }
double getTailLengthSeconds() const override { return 0.0; }
bool acceptsMidi() const override { return false; }
bool producesMidi() const override { return false; }
bool isMidiEffect() const override { return false; }
AudioProcessorEditor* createEditor() override;
bool hasEditor() const override { return true; }
int getNumPrograms() override { return 1; }
int getCurrentProgram() override { return 0; }
void setCurrentProgram (int) override {}
const String getProgramName (int) override { return {}; }
void changeProgramName (int, const String&) override {}
void getStateInformation (MemoryBlock&) override;
void setStateInformation (const void*, int) override;
IODeviceType getType() const { return deviceType; }
bool isBusesLayoutSupported (const BusesLayout& layouts) const override { return true; }
void updateBusLayout();
void updatePair();
bool canAddBus (bool isInput) const override { return true; }
bool canRemoveBus (bool isInput) const override { return true; }
//==============================================================================
void parameterUpdated(const String& parameterID, float newValue) override {}
//==============================================================================
AudioDeviceManager deviceManager {};
AudioProcessorPlayer processorPlayer {};
//==============================================================================
bool initialized = false;
private:
friend class ExternalAudioIOEditor;
friend class WrappedAudioProcessor;
IODeviceType deviceType;
bool pair = true; //Not yet Implemented
WrappedAudioProcessor wrappedAudioProcessor;
std::unique_ptr<juce::XmlElement> savedState;
juce::CriticalSection buffersLock;
//==============================================================================
AudioBuffer<float> audioBuffer;
void changeListenerCallback (ChangeBroadcaster*) override;
//==============================================================================
void deinitAudioDevice();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExternalAudioIO)
};
//MARK: ExternalAudioIOEditor ==============================================================================
class ExternalAudioIOEditor : public InternalAudioProcessorEditor
{
public:
ExternalAudioIOEditor (ExternalAudioIO& processor);
~ExternalAudioIOEditor() override;
void resizedEditor() override;
void updateEditorCallback(InternalAudioProcessor* processor) override;
private:
ExternalAudioIO& audioIOProcessor;
std::unique_ptr<ExternalAudioDeviceSelectorComponent> audioSelectorComponent;
TextButton pairBtn { "Mono" }; //Not yet Implemented
void update();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExternalAudioIOEditor)
};
.cpp
//MARK: WrappedAudioProcessor ==============================================================================
void WrappedAudioProcessor::processBlock (AudioBuffer<float>& audioBuff, MidiBuffer&)
{
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
int numChannels = audioBuff.getNumChannels();
auto numSamples = audioBuff.getNumSamples();
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
audioBuff.clear(i, 0, numSamples);
if (!owner) { return; }
if (!owner->initialized) { return; }
const ScopedLock lock(buffersLock);
bool isInput = (owner->getType() == ExternalAudioIO::IODeviceType::input);
for (int chan = 0; chan < numChannels; ++chan)
{
auto* src = isInput ? audioBuff.getReadPointer(chan) : owner->audioBuffer.getReadPointer(chan);
auto* dest = isInput ? owner->audioBuffer.getWritePointer(chan) : audioBuff.getWritePointer(chan);
for (int i = 0; i < numSamples; ++i)
dest[i] = src[i];
}
}
//MARK: ExternalAudioIO ==============================================================================
ExternalAudioIO::ExternalAudioIO(IODeviceType t) : InternalAudioProcessor ((t == IODeviceType::output) ?
BusesProperties().withInput("Input", juce::AudioChannelSet::stereo(), true) :
BusesProperties().withOutput("Output", juce::AudioChannelSet::stereo(), true)),
deviceType(t),
wrappedAudioProcessor(this, (BusesProperties().withInput("Input", juce::AudioChannelSet::stereo(), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true)))
{
}
ExternalAudioIO::~ExternalAudioIO()
{
initialized = false;
deinitAudioDevice();
}
void ExternalAudioIO::deinitAudioDevice()
{
deviceManager.closeAudioDevice();
deviceManager.removeAudioCallback (&processorPlayer);
processorPlayer.setProcessor (nullptr);
}
String ExternalAudioIO::getIdentifier(IODeviceType deviceType, bool name)
{
String separator = name ? " " : "_";
if (deviceType == ExternalAudioIO::IODeviceType::input)
{
return "EXTERNAL" + separator + "AUDIO" + separator + "INPUTS";
} else {
return "EXTERNAL" + separator + "AUDIO" + separator + "OUTPUTS";
}
}
void ExternalAudioIO::updateBusLayout()
{
}
void ExternalAudioIO::updatePair()
{
pair = !pair;
updateEditorAsync();
}
//=======================================================================================
void ExternalAudioIO::prepareToPlay(double sampleRate, int samplesPerBlock)
{
const ScopedLock lock(buffersLock);
bool isInput = deviceType == IODeviceType::input;
AudioDeviceManager::AudioDeviceSetup audioDeviceSetup {};
audioDeviceSetup.bufferSize = samplesPerBlock;
audioDeviceSetup.sampleRate = sampleRate;
if (isInput)
audioDeviceSetup.outputDeviceName = String();
else
audioDeviceSetup.inputDeviceName = String();
deviceManager.initialise(isInput ? 0 : getTotalNumInputChannels(),
isInput ? 0 : getTotalNumOutputChannels(),
savedState.get() ? savedState.get() : nullptr,
false, juce::String(),
&audioDeviceSetup);
deviceManager.addAudioCallback (&processorPlayer);
deviceManager.addChangeListener (this);
processorPlayer.setProcessor(&wrappedAudioProcessor);
const int numChannels = isInput ? getTotalNumOutputChannels() : getTotalNumInputChannels();
audioBuffer.setSize(numChannels, samplesPerBlock, false, true, false);
wrappedAudioProcessor.prepareToPlay(sampleRate, samplesPerBlock);
initialized = true;
}
void ExternalAudioIO::processBuffer (AudioBuffer<float>& audioBuff, MidiBuffer& midiBuff)
{
midiBuff.clear();
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
auto numSamples = audioBuff.getNumSamples();
int numChannels = audioBuff.getNumChannels();
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
audioBuff.clear(i, 0, numSamples);
bool isInput = (deviceType == IODeviceType::input);
const ScopedLock lock(buffersLock);
for (int chan = 0; chan < numChannels; ++chan)
{
auto* src = isInput ? audioBuffer.getReadPointer(chan) : audioBuff.getReadPointer(chan);
auto* dest = isInput ? audioBuff.getWritePointer(chan) : audioBuffer.getWritePointer(chan);
for (int i = 0; i < numSamples; ++i)
dest[i] = src[i];
}
}
void ExternalAudioIO::releaseResources()
{
const ScopedLock lock(buffersLock);
wrappedAudioProcessor.releaseResources();
audioBuffer.clear();
initialized = false;
deinitAudioDevice();
}
void ExternalAudioIO::reset()
{
const ScopedLock lock(buffersLock);
wrappedAudioProcessor.reset();
initialized = false;
}
void ExternalAudioIO::changeListenerCallback (ChangeBroadcaster* source)
{
if (source == &deviceManager)
{
updateBusLayout();
}
}
//MARK: AudioProcessorEditor =======================================================
AudioProcessorEditor* ExternalAudioIO::createEditor()
{
InternalAudioProcessorEditor* editor = new ExternalAudioIOEditor(*this);
updateActiveEditor(editor);
return editor;
}
void ExternalAudioIO::getStateInformation (juce::MemoryBlock& destData)
{
}
void ExternalAudioIO::setStateInformation (const void* data, int sizeInBytes)
{
}
//MARK: ExternalAudioIOEditor ==============================================================================
ExternalAudioIOEditor::ExternalAudioIOEditor (ExternalAudioIO& processor) : InternalAudioProcessorEditor(processor), audioIOProcessor(processor)
{
addAndMakeVisible(pairBtn);
pairBtn.onClick = [this]() { audioIOProcessor.updateBusLayout(); };
setSize(400, 200);
update();
}
ExternalAudioIOEditor::~ExternalAudioIOEditor()
{
}
void ExternalAudioIOEditor::resizedEditor()
{
Rectangle<int> localBounds = getPanelArea();
Rectangle<int> bottomArea = localBounds.removeFromBottom(24).reduced(24, 0);
pairBtn.setBounds(bottomArea.removeFromRight(100));
localBounds.removeFromBottom(12);
if (!audioSelectorComponent.get()) { return; }
audioSelectorComponent->setBounds(localBounds);
}
void ExternalAudioIOEditor::update()
{
if (audioIOProcessor.pair != pairBtn.getToggleState())
{
pairBtn.setToggleState(audioIOProcessor.pair, NotificationType::dontSendNotification);
pairBtn.setButtonText((audioIOProcessor.pair) ? "Stereo" : "Mono");
removeChildComponent(audioSelectorComponent.get());
audioSelectorComponent.reset(new ExternalAudioDeviceSelectorComponent(audioIOProcessor.deviceManager,
0, (audioIOProcessor.deviceType == ExternalAudioIO::IODeviceType::input) ? 256 : 0,
0, (audioIOProcessor.deviceType == ExternalAudioIO::IODeviceType::output) ? 256 : 0,
audioIOProcessor.pair));
addAndMakeVisible(audioSelectorComponent.get());
resized();
}
}
void ExternalAudioIOEditor::updateEditorCallback(InternalAudioProcessor* processor)
{
if (processor == &audioIOProcessor)
update();
}

