diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 16be1b7..cc6ecde 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -54,7 +54,7 @@ public: VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters); SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings"); - VisualiserComponent visualiser{audioProcessor.threadManager, visualiserSettings, nullptr}; + VisualiserComponent visualiser{audioProcessor.haltRecording, audioProcessor.threadManager, visualiserSettings, nullptr}; SettingsComponent settings{audioProcessor, *this}; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 8cd455f..801832e 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -776,6 +776,14 @@ juce::AudioProcessorEditor* OscirenderAudioProcessor::createEditor() { //============================================================================== void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData) { + // we need to stop recording the visualiser when saving the state, otherwise + // there are issues. This is the only place we can do this because there is + // no callback when closing the standalone app except for this. + + if (haltRecording != nullptr && juce::JUCEApplicationBase::isStandaloneApp()) { + haltRecording(); + } + juce::SpinLock::ScopedLockType lock1(parsersLock); juce::SpinLock::ScopedLockType lock2(effectsLock); diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 4a9f460..7a716e5 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -239,6 +239,8 @@ public: juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), 1.0f, juce::Font::plain); ShapeSound::Ptr objectServerSound = new ShapeSound(); + + std::function haltRecording; void addLuaSlider(); void updateEffectPrecedence(); diff --git a/Source/components/StopwatchComponent.h b/Source/components/StopwatchComponent.h index e24be54..2bcbec1 100644 --- a/Source/components/StopwatchComponent.h +++ b/Source/components/StopwatchComponent.h @@ -14,22 +14,23 @@ class StopwatchComponent : public juce::Component, public juce::Timer { } void start() { - startTime = juce::Time::getCurrentTime(); startTimerHz(60); } + void addTime(juce::RelativeTime time) { + elapsedTime += time; + } + void stop() { stopTimer(); } void reset() { - startTime = juce::Time::getCurrentTime(); + elapsedTime = juce::RelativeTime(); timerCallback(); } void timerCallback() override { - juce::Time currentTime = juce::Time::getCurrentTime(); - juce::RelativeTime elapsedTime = currentTime - startTime; int hours = elapsedTime.inHours(); int minutes = (int) elapsedTime.inMinutes() % 60; int seconds = (int) elapsedTime.inSeconds() % 60; @@ -45,7 +46,7 @@ class StopwatchComponent : public juce::Component, public juce::Timer { private: juce::Label label; - juce::Time startTime; + juce::RelativeTime elapsedTime; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StopwatchComponent) }; diff --git a/Source/concurrency/BufferConsumer.h b/Source/concurrency/BufferConsumer.h index 9201820..b90a4a7 100644 --- a/Source/concurrency/BufferConsumer.h +++ b/Source/concurrency/BufferConsumer.h @@ -111,7 +111,12 @@ public: sema.release(); } else { OsciPoint item; + // We dequeue an item so that the audio thread is unblocked + // if it's trying to wait until the queue is no longer full. queue->try_dequeue(item); + // We enqueue an item so that the consumer is unblocked + // if it's trying to wait until the queue is no longer empty. + queue->try_enqueue(item); } } diff --git a/Source/visualiser/VisualiserComponent.cpp b/Source/visualiser/VisualiserComponent.cpp index 67c4c56..b5a89ff 100644 --- a/Source/visualiser/VisualiserComponent.cpp +++ b/Source/visualiser/VisualiserComponent.cpp @@ -11,12 +11,16 @@ #include "TexturedFragmentShader.glsl" #include "TexturedVertexShader.glsl" -VisualiserComponent::VisualiserComponent(AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool visualiserOnly) : settings(settings), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent", threadManager), parent(parent) { +VisualiserComponent::VisualiserComponent(std::function& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool visualiserOnly) : haltRecording(haltRecording), settings(settings), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), threadManager), parent(parent) { + haltRecording = [this] { + setRecording(false); + }; + addAndMakeVisible(record); record.setPulseAnimation(true); record.onClick = [this] { - toggleRecording(); + setRecording(record.getToggleState()); stopwatch.stop(); stopwatch.reset(); @@ -61,6 +65,10 @@ VisualiserComponent::VisualiserComponent(AudioBackgroundThreadManager& threadMan } VisualiserComponent::~VisualiserComponent() { + setRecording(false); + if (parent == nullptr) { + haltRecording = nullptr; + } openGLContext.detach(); setShouldBeRunning(false, [this] { renderingSemaphore.release(); @@ -137,7 +145,7 @@ void VisualiserComponent::setPaused(bool paused) { } void VisualiserComponent::mouseDown(const juce::MouseEvent& event) { - if (event.mods.isLeftButtonDown() && child == nullptr) { + if (event.mods.isLeftButtonDown() && child == nullptr && !record.getToggleState()) { setPaused(active); } } @@ -155,12 +163,12 @@ bool VisualiserComponent::keyPressed(const juce::KeyPress& key) { void VisualiserComponent::setFullScreen(bool fullScreen) {} -void VisualiserComponent::toggleRecording() { - setBlockOnAudioThread(record.getToggleState()); -} - -void VisualiserComponent::haltRecording() { - record.setToggleState(false, juce::NotificationType::dontSendNotification); +void VisualiserComponent::setRecording(bool recording) { + record.setToggleState(recording, juce::NotificationType::dontSendNotification); + if (recording) { + setPaused(false); + } + setBlockOnAudioThread(recording); } void VisualiserComponent::resized() { @@ -185,12 +193,12 @@ void VisualiserComponent::resized() { } void VisualiserComponent::popoutWindow() { - haltRecording(); - auto visualiser = new VisualiserComponent(threadManager, settings, this); + setRecording(false); + record.setEnabled(false); + auto visualiser = new VisualiserComponent(haltRecording, threadManager, settings, this); visualiser->settings.setLookAndFeel(&getLookAndFeel()); visualiser->openSettings = openSettings; visualiser->closeSettings = closeSettings; - visualiser->recordingHalted = recordingHalted; child = visualiser; childUpdated(); visualiser->setSize(300, 325); @@ -206,6 +214,17 @@ void VisualiserComponent::popoutWindow() { void VisualiserComponent::childUpdated() { popOutButton.setVisible(child == nullptr); + record.setEnabled(child == nullptr); + if (child != nullptr) { + haltRecording = [this] { + setRecording(false); + child->setRecording(false); + }; + } else { + haltRecording = [this] { + setRecording(false); + }; + } } void VisualiserComponent::newOpenGLContextCreated() { @@ -295,6 +314,7 @@ void VisualiserComponent::renderOpenGL() { renderScope(xSamples, ySamples, zSamples); } renderingSemaphore.release(); + stopwatch.addTime(juce::RelativeTime::seconds(1.0 / FRAME_RATE)); } // render texture to screen diff --git a/Source/visualiser/VisualiserComponent.h b/Source/visualiser/VisualiserComponent.h index dd7eca6..c7f32e7 100644 --- a/Source/visualiser/VisualiserComponent.h +++ b/Source/visualiser/VisualiserComponent.h @@ -28,7 +28,7 @@ struct Texture { class VisualiserWindow; class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater { public: - VisualiserComponent(AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool visualiserOnly = false); + VisualiserComponent(std::function& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool visualiserOnly = false); ~VisualiserComponent() override; std::function openSettings; @@ -49,8 +49,7 @@ public: void renderOpenGL() override; void openGLContextClosing() override; void setFullScreen(bool fullScreen); - void toggleRecording(); - void haltRecording(); + void setRecording(bool recording); void childUpdated(); VisualiserComponent* parent = nullptr; @@ -58,14 +57,13 @@ public: std::unique_ptr popout = nullptr; std::atomic active = true; - - std::function recordingHalted; private: float intensity; const double FRAME_RATE = 60.0; bool visualiserOnly; + std::function& haltRecording; AudioBackgroundThreadManager& threadManager;