I’m attempting to make my PluginEditor an AudioSource, so that I can play multiple tracks over themselves, independent of the DAW. I’ve got the sources playing, and everything seems okay, until I close the plugin, I get this exception:
I get the exception, even if I close the plugin BEFORE loading any sources into the mixerAudioSource.
No line numbers… haha. When I comment out the line:
//setup audio device manager with two channels, sterio
audioDeviceManager.initialiseWithDefaultDevices(2, 2);
closing the plugin works without exception.
Here’s the call stack… Seems to be happening when mixerAudioSource is releasing it’s resources. I’m attempting to do this in the editors releaseResources, but that doesn’t help. And again, this error occurs whether I’ve actually loaded anything into mixerAudioSource or not.
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin editor.
==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
PolyEchoAudioProcessorEditor::PolyEchoAudioProcessorEditor(PolyEchoAudioProcessor& p)
: AudioProcessorEditor(&p), audioProcessor(p), juce::AudioProcessorValueTreeState::Listener()
{
mWalletIdTextbox.reset(new juce::TextEditor("Wallet Id"));
addAndMakeVisible(mWalletIdTextbox.get());
mWalletIdTextbox->setMultiLine(false);
mWalletIdTextbox->setReturnKeyStartsNewLine(false);
mWalletIdTextbox->setReadOnly(false);
mWalletIdTextbox->setScrollbarsShown(true);
mWalletIdTextbox->setCaretVisible(true);
mWalletIdTextbox->setPopupMenuEnabled(true);
mWalletIdTextbox->setColour(juce::TextEditor::backgroundColourId, juce::Colour(0xffaeaeae));
mWalletIdTextbox->setText(juce::String());
mWalletIdTextbox->setBounds(206, 20, 360, 24);
mAddressLabel.reset(new juce::Label(juce::String(),
TRANS("Address")));
addAndMakeVisible(mAddressLabel.get());
mAddressLabel->setFont(juce::Font(15.00f, juce::Font::plain).withTypefaceStyle("Regular"));
mAddressLabel->setJustificationType(juce::Justification::centredLeft);
mAddressLabel->setEditable(false, false, false);
mAddressLabel->setColour(juce::TextEditor::textColourId, juce::Colours::black);
mAddressLabel->setColour(juce::TextEditor::backgroundColourId, juce::Colour(0x00000000));
mAddressLabel->setBounds(128, 20, 71, 24);
mProjectsComboBox.reset(new juce::ComboBox("Projects Combo Box"));
addAndMakeVisible(mProjectsComboBox.get());
mProjectsComboBox->setEditableText(false);
mProjectsComboBox->setJustificationType(juce::Justification::centredLeft);
mProjectsComboBox->setTextWhenNothingSelected(juce::String());
mProjectsComboBox->setTextWhenNoChoicesAvailable(TRANS("(no choices)"));
mProjectsComboBox->addListener(this);
mProjectsComboBox->setBounds(206, 48, 360, 24);
mProjectLabel.reset(new juce::Label("Project Label",
TRANS("Project")));
addAndMakeVisible(mProjectLabel.get());
mProjectLabel->setFont(juce::Font(15.00f, juce::Font::plain).withTypefaceStyle("Regular"));
mProjectLabel->setJustificationType(juce::Justification::centredLeft);
mProjectLabel->setEditable(false, false, false);
mProjectLabel->setColour(juce::TextEditor::textColourId, juce::Colours::black);
mProjectLabel->setColour(juce::TextEditor::backgroundColourId, juce::Colour(0x00000000));
mProjectLabel->setBounds(129, 48, 56, 24);
mLoadingProjectLabel.reset(new juce::Label("Loading Project Label",
TRANS("Loading Project...")));
addAndMakeVisible(mLoadingProjectLabel.get());
mLoadingProjectLabel->setFont(juce::Font(61.90f, juce::Font::plain).withTypefaceStyle("Regular"));
mLoadingProjectLabel->setJustificationType(juce::Justification::centredLeft);
mLoadingProjectLabel->setEditable(false, false, false);
mLoadingProjectLabel->setColour(juce::TextEditor::textColourId, juce::Colours::black);
mLoadingProjectLabel->setColour(juce::TextEditor::backgroundColourId, juce::Colour(0x00000000));
mLoadingProjectLabel->setBounds(280, 248, 248, 184);
mPlayButton.reset(new juce::TextButton("new button"));
addAndMakeVisible(mPlayButton.get());
mPlayButton->setButtonText(TRANS("Play"));
mPlayButton->addListener(this);
mPlayButton->setBounds(656, 24, 56, 48);
mStopButton.reset(new juce::TextButton("Stop Button"));
addAndMakeVisible(mStopButton.get());
mStopButton->setButtonText(TRANS("Stop"));
mStopButton->addListener(this);
mStopButton->setBounds(592, 24, 56, 48);
cachedImage_polyechologo_png_1 = juce::ImageCache::getFromMemory(polyechologo_png, polyechologo_pngSize);
//[UserPreSize]
//[/UserPreSize]
setSize(800, 800);
//END GUI definitions
// turn off Loading label
mLoadingProjectLabel->setVisible(false);
// init PolyEcho Service
pes = std::make_unique<PolyEchoService>();
audioSourcePlayer.setSource(&mixerAudioSource);
//setup audio device manager with two channels, sterio
audioDeviceManager.initialiseWithDefaultDevices(2, 2);
audioDeviceManager.addAudioCallback(&audioSourcePlayer);
//load project name from API into selection combobox
loadProjects();
//
readyToPlay = false;
}
PolyEchoAudioProcessorEditor::~PolyEchoAudioProcessorEditor()
{
}
//==============================================================================
void PolyEchoAudioProcessorEditor::paint(juce::Graphics& g)
{
g.fillAll(juce::Colour(0xff538999));
{
int x = 21, y = 22, width = 100, height = 50;
//[UserPaintCustomArguments] Customize the painting arguments here..
//[/UserPaintCustomArguments]
g.setColour(juce::Colours::black);
g.drawImageWithin(cachedImage_polyechologo_png_1,
x, y, width, height,
juce::RectanglePlacement::centred,
false);
}
//g.setColour (juce::Colours::white);
//g.setFont (15.0f);
//g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
}
void PolyEchoAudioProcessorEditor::resized()
{
// This is generally where you'll want to lay out the positions of any
// subcomponents in your editor..
}
void PolyEchoAudioProcessorEditor::parameterChanged(const juce::String& parameterID, float newValue)
{
//Used to update stuff on chagnes of parameters
if (parameterID == "CONFIGWALLET")
{
std::cout << "Hello Wallet";
}
}
//===============================================================================
void PolyEchoAudioProcessorEditor::loadProjects()
{
mProjectsJson = pes->getProjects();
if (mProjectsJson == nullptr)
return;
int counter = 1;
for (auto project : mProjectsJson.at("data"))
{
auto projectName = project["/name"_json_pointer].get<std::string>();
mProjectsComboBox->addItem(projectName, counter);
counter++;
}
}
void PolyEchoAudioProcessorEditor::onConfigWalletClicked()
{
std::cout << "askfdjasldfjas;lf";
}
bool PolyEchoAudioProcessorEditor::isInterestedInFileDrag(const juce::StringArray& files)
{
// this happens when the mouse is hovering in the plugin, don't process anything here
return true;
}
void PolyEchoAudioProcessorEditor::filesDropped(const juce::StringArray& files, int x, int y)
{
if (files.size() != 0)
{
auto filePath = files.begin()->toStdString();
auto currentProjectName = mProjectsComboBox->getItemText(mProjectsComboBox->getSelectedItemIndex());
mCurrentProject->addStem(filePath, StemType::Bass);
}
}
void PolyEchoAudioProcessorEditor::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
{
mixerAudioSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
}
void PolyEchoAudioProcessorEditor::releaseResources()
{
mixerAudioSource.releaseResources();
}
void PolyEchoAudioProcessorEditor::getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill)
{
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
{
if (stem->getReaderSource() == nullptr)
{
bufferToFill.clearActiveBufferRegion();
return;
}
}
mixerAudioSource.getNextAudioBlock(bufferToFill);
}
void PolyEchoAudioProcessorEditor::changeListenerCallback(juce::ChangeBroadcaster* source)
{
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
{
if (source == stem->getTransportSource())
{
if (stem->getTransportSource()->isPlaying())
changeState(Playing);
else if ((state == Stopping) || (state == Playing))
changeState(Stopped);
else if (Pausing == state)
changeState(Paused);
}
}
}
void PolyEchoAudioProcessorEditor::changeState(PlaybackState newState)
{
if (state != newState)
{
state = newState;
switch (state)
{
case Stopped:
mPlayButton->setButtonText("Play");
mStopButton->setButtonText("Stop");
mStopButton->setEnabled(false);
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
{
stem->getTransportSource()->setPosition(0.0);
stem->startPositionIndicatorTimer();
}
break;
case Starting:
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
{
stem->getTransportSource()->start();
stem->startPositionIndicatorTimer();
}
break;
case Playing:
mPlayButton->setButtonText("Pause");
mStopButton->setButtonText("Stop");
mStopButton->setEnabled(true);
break;
case Pausing:
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
stem->getTransportSource()->stop();
break;
case Paused:
mPlayButton->setButtonText("Resume");
mStopButton->setButtonText("Return to Zero");
break;
case Stopping:
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
stem->getTransportSource()->stop();
break;
}
}
}
void PolyEchoAudioProcessorEditor::buttonClicked(juce::Button* buttonThatWasClicked)
{
if (readyToPlay)
{
if (buttonThatWasClicked == mPlayButton.get())
{
if ((state == Stopped) || (state == Paused))
changeState(Starting);
else if (state == Playing)
changeState(Pausing);
}
else if (buttonThatWasClicked == mStopButton.get())
{
if (state == Paused)
changeState(Stopped);
else
changeState(Stopping);
}
}
}
void PolyEchoAudioProcessorEditor::comboBoxChanged(juce::ComboBox* comboBoxThatHasChanged)
{
//mLoadingProjectLabel->setVisible(true); // not working because the thread is held up TODO: get it working.
// load current project
mCurrentProject.reset(new Project(comboBoxThatHasChanged->getText(), mProjectsJson));
addAndMakeVisible(mCurrentProject.get());
auto xMargin = 24;
auto yMargin = 100;
//set audio player to new sources
for (std::unique_ptr<Stem>& stem : mCurrentProject->mStems)
{
mixerAudioSource.addInputSource(stem->getTransportSource(), false);
stem->getTransportSource()->addChangeListener(this);
}
// set main margins for project componenet
mCurrentProject->setBounds(xMargin, 100, getWidth() - (xMargin * 2), getHeight() - (yMargin + 24));
//reset Playback state
state = PlaybackState::Stopped;
readyToPlay = true;
}