kopia lustrzana https://github.com/jameshball/osci-render
Refactor how syphon/spout input works to be cleaner and less buggy
rodzic
9e93ffc260
commit
67650b682e
|
@ -22,9 +22,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
juce::FileBrowserComponent::canSelectFiles;
|
||||
|
||||
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType syphonLock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parsersLock(audioProcessor.parsersLock);
|
||||
bool fileAdded = false;
|
||||
for (auto& file : chooser.getResults()) {
|
||||
|
@ -66,9 +63,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
|
||||
addAndMakeVisible(leftArrow);
|
||||
leftArrow.onClick = [this] {
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parserLock(audioProcessor.parsersLock);
|
||||
juce::SpinLock::ScopedLockType effectsLock(audioProcessor.effectsLock);
|
||||
|
||||
|
@ -83,9 +77,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
|
||||
addAndMakeVisible(rightArrow);
|
||||
rightArrow.onClick = [this] {
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parserLock(audioProcessor.parsersLock);
|
||||
juce::SpinLock::ScopedLockType effectsLock(audioProcessor.effectsLock);
|
||||
|
||||
|
@ -108,9 +99,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
|
|||
addAndMakeVisible(createFile);
|
||||
|
||||
createFile.onClick = [this] {
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType syphonLock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parsersLock(audioProcessor.parsersLock);
|
||||
auto fileNameText = fileName.getText();
|
||||
auto fileTypeText = fileType.getText();
|
||||
|
@ -173,8 +161,8 @@ void MainComponent::updateFileLabel() {
|
|||
|
||||
{
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
if (audioProcessor.isSyphonInputActive()) {
|
||||
fileLabel.setText(audioProcessor.getSyphonSourceName(), juce::dontSendNotification);
|
||||
if (audioProcessor.syphonInputActive) {
|
||||
fileLabel.setText(pluginEditor.getSyphonSourceName(), juce::dontSendNotification);
|
||||
} else
|
||||
#endif
|
||||
if (audioProcessor.objectServerRendering) {
|
||||
|
|
|
@ -54,9 +54,6 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
|
|||
colourScheme = lookAndFeel.getDefaultColourScheme();
|
||||
|
||||
{
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType syphonLock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
|
||||
initialiseCodeEditors();
|
||||
}
|
||||
|
@ -143,9 +140,6 @@ void OscirenderAudioProcessorEditor::filesDropped(const juce::StringArray& files
|
|||
if (file.hasFileExtension("osci")) {
|
||||
openProject(file);
|
||||
} else {
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType syphonLock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parsersLock(audioProcessor.parsersLock);
|
||||
juce::SpinLock::ScopedLockType effectsLock(audioProcessor.effectsLock);
|
||||
|
||||
|
@ -379,9 +373,6 @@ void OscirenderAudioProcessorEditor::handleAsyncUpdate() {
|
|||
void OscirenderAudioProcessorEditor::changeListenerCallback(juce::ChangeBroadcaster* source) {
|
||||
if (source == &audioProcessor.broadcaster) {
|
||||
{
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType syphonLock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parsersLock(audioProcessor.parsersLock);
|
||||
initialiseCodeEditors();
|
||||
settings.update();
|
||||
|
@ -389,9 +380,6 @@ void OscirenderAudioProcessorEditor::changeListenerCallback(juce::ChangeBroadcas
|
|||
resized();
|
||||
repaint();
|
||||
} else if (source == &audioProcessor.fileChangeBroadcaster) {
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType syphonLock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parsersLock(audioProcessor.parsersLock);
|
||||
// triggered when the audioProcessor changes the current file (e.g. to Blender)
|
||||
settings.fileUpdated(audioProcessor.getCurrentFileName());
|
||||
|
@ -462,9 +450,6 @@ void OscirenderAudioProcessorEditor::updateCodeDocument() {
|
|||
bool OscirenderAudioProcessorEditor::keyPressed(const juce::KeyPress& key) {
|
||||
bool consumeKey = false;
|
||||
{
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
|
||||
#endif
|
||||
juce::SpinLock::ScopedLockType parserLock(audioProcessor.parsersLock);
|
||||
juce::SpinLock::ScopedLockType effectsLock(audioProcessor.effectsLock);
|
||||
|
||||
|
@ -527,13 +512,12 @@ void OscirenderAudioProcessorEditor::openVisualiserSettings() {
|
|||
void OscirenderAudioProcessorEditor::openSyphonInputDialog() {
|
||||
SyphonInputSelectorComponent* selector = nullptr;
|
||||
{
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
|
||||
selector = new SyphonInputSelectorComponent(
|
||||
sharedTextureManager,
|
||||
[this](const juce::String& server, const juce::String& app) { onSyphonInputSelected(server, app); },
|
||||
[this]() { onSyphonInputDisconnected(); },
|
||||
audioProcessor.isSyphonInputActive(),
|
||||
audioProcessor.getSyphonSourceName());
|
||||
[this](const juce::String& server, const juce::String& app) { connectSyphonInput(server, app); },
|
||||
[this]() { disconnectSyphonInput(); },
|
||||
syphonFrameGrabber && syphonFrameGrabber->isActive(),
|
||||
getSyphonSourceName());
|
||||
}
|
||||
juce::DialogWindow::LaunchOptions options;
|
||||
options.content.setOwned(selector);
|
||||
|
@ -546,13 +530,36 @@ void OscirenderAudioProcessorEditor::openSyphonInputDialog() {
|
|||
options.launchAsync();
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessorEditor::onSyphonInputSelected(const juce::String& server, const juce::String& app) {
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
|
||||
audioProcessor.connectSyphonInput(server, app);
|
||||
void OscirenderAudioProcessorEditor::connectSyphonInput(const juce::String& server, const juce::String& app) {
|
||||
juce::SpinLock::ScopedLockType lock(syphonLock);
|
||||
if (!syphonFrameGrabber) {
|
||||
syphonFrameGrabber = std::make_unique<SyphonFrameGrabber>(sharedTextureManager, server, app, audioProcessor.syphonImageParser);
|
||||
audioProcessor.syphonInputActive = true;
|
||||
{
|
||||
juce::MessageManagerLock lock;
|
||||
audioProcessor.fileChangeBroadcaster.sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OscirenderAudioProcessorEditor::onSyphonInputDisconnected() {
|
||||
juce::SpinLock::ScopedLockType lock(audioProcessor.syphonLock);
|
||||
audioProcessor.disconnectSyphonInput();
|
||||
void OscirenderAudioProcessorEditor::disconnectSyphonInput() {
|
||||
juce::SpinLock::ScopedLockType lock(syphonLock);
|
||||
if (!syphonFrameGrabber) {
|
||||
return;
|
||||
}
|
||||
audioProcessor.syphonInputActive = false;
|
||||
syphonFrameGrabber.reset();
|
||||
{
|
||||
juce::MessageManagerLock lock;
|
||||
audioProcessor.fileChangeBroadcaster.sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
juce::String OscirenderAudioProcessorEditor::getSyphonSourceName() const {
|
||||
juce::SpinLock::ScopedLockType lock(syphonLock);
|
||||
if (syphonFrameGrabber) {
|
||||
return syphonFrameGrabber->getSourceName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include "CommonPluginEditor.h"
|
||||
#include "LookAndFeel.h"
|
||||
#include "MidiComponent.h"
|
||||
#include "PluginProcessor.h"
|
||||
#include "SettingsComponent.h"
|
||||
#include "MidiComponent.h"
|
||||
#include "components/OsciMainMenuBarModel.h"
|
||||
#include "LookAndFeel.h"
|
||||
#include "components/ErrorCodeEditorComponent.h"
|
||||
#include "components/LuaConsole.h"
|
||||
#include "components/OsciMainMenuBarModel.h"
|
||||
#include "visualiser/VisualiserSettings.h"
|
||||
#include "CommonPluginEditor.h"
|
||||
|
||||
class OscirenderAudioProcessorEditor : public CommonPluginEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater, public juce::ChangeListener, public juce::FileDragAndDropTarget {
|
||||
public:
|
||||
|
@ -18,7 +19,7 @@ public:
|
|||
|
||||
void paint(juce::Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
|
||||
bool isBinaryFile(juce::String name);
|
||||
void initialiseCodeEditors();
|
||||
void addCodeEditor(int index);
|
||||
|
@ -37,15 +38,15 @@ private:
|
|||
void registerFileRemovedCallback();
|
||||
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
public:
|
||||
|
||||
public:
|
||||
const double CLOSED_PREF_SIZE = 30.0;
|
||||
const double RESIZER_BAR_SIZE = 7.0;
|
||||
|
||||
std::atomic<bool> editingCustomFunction = false;
|
||||
|
||||
SettingsComponent settings{audioProcessor, *this};
|
||||
|
||||
|
||||
#if !OSCI_PREMIUM
|
||||
juce::TextButton upgradeButton{"Upgrade to premium!"};
|
||||
#endif
|
||||
|
@ -62,7 +63,7 @@ public:
|
|||
juce::CodeEditorComponent::ColourScheme colourScheme;
|
||||
juce::LuaTokeniser luaTokeniser;
|
||||
juce::XmlTokeniser xmlTokeniser;
|
||||
juce::ShapeButton collapseButton;
|
||||
juce::ShapeButton collapseButton;
|
||||
std::shared_ptr<juce::CodeDocument> customFunctionCodeDocument = std::make_shared<juce::CodeDocument>();
|
||||
std::shared_ptr<OscirenderCodeEditorComponent> customFunctionCodeEditor = std::make_shared<OscirenderCodeEditorComponent>(*customFunctionCodeDocument, &luaTokeniser, audioProcessor, CustomEffect::UNIQUE_ID, CustomEffect::FILE_NAME);
|
||||
|
||||
|
@ -76,8 +77,8 @@ public:
|
|||
|
||||
std::atomic<bool> updatingDocumentsWithParserLock = false;
|
||||
|
||||
void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override;
|
||||
void codeDocumentTextDeleted(int startIndex, int endIndex) override;
|
||||
void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override;
|
||||
void codeDocumentTextDeleted(int startIndex, int endIndex) override;
|
||||
void updateCodeDocument();
|
||||
void updateCodeEditor(bool binaryFile, bool shouldOpenEditor = false);
|
||||
void setCodeEditorVisible(std::optional<bool> visible);
|
||||
|
@ -86,10 +87,16 @@ public:
|
|||
void mouseDown(const juce::MouseEvent& event) override;
|
||||
void mouseMove(const juce::MouseEvent& event) override;
|
||||
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
// Syphon/Spout input dialog
|
||||
void openSyphonInputDialog();
|
||||
void onSyphonInputSelected(const juce::String& server, const juce::String& app);
|
||||
void onSyphonInputDisconnected();
|
||||
void connectSyphonInput(const juce::String& server, const juce::String& app);
|
||||
void disconnectSyphonInput();
|
||||
juce::String getSyphonSourceName() const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessorEditor)
|
||||
juce::SpinLock syphonLock;
|
||||
std::unique_ptr<SyphonFrameGrabber> syphonFrameGrabber;
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OscirenderAudioProcessorEditor)
|
||||
};
|
||||
|
|
|
@ -505,8 +505,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
|
|||
|
||||
{
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
juce::SpinLock::ScopedLockType sLock(syphonLock);
|
||||
if (isSyphonInputActive()) {
|
||||
if (syphonInputActive) {
|
||||
for (int sample = 0; sample < outputBuffer3d.getNumSamples(); sample++) {
|
||||
osci::Point point = syphonImageParser.getSample();
|
||||
outputBuffer3d.setSample(0, sample, point.x);
|
||||
|
@ -514,7 +513,7 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
|
|||
}
|
||||
} else
|
||||
#endif
|
||||
if (usingInput && totalNumInputChannels >= 1) {
|
||||
if (usingInput && totalNumInputChannels >= 1) {
|
||||
if (totalNumInputChannels >= 2) {
|
||||
for (auto channel = 0; channel < juce::jmin(2, totalNumInputChannels); channel++) {
|
||||
outputBuffer3d.copyFrom(channel, 0, inputBuffer, channel, 0, buffer.getNumSamples());
|
||||
|
@ -898,49 +897,6 @@ void OscirenderAudioProcessor::envelopeChanged(EnvelopeComponent* changedEnvelop
|
|||
}
|
||||
}
|
||||
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
// Syphon/Spout input management
|
||||
|
||||
// syphonLock must be held when calling this function
|
||||
bool OscirenderAudioProcessor::isSyphonInputActive() const {
|
||||
return syphonFrameGrabber != nullptr && syphonFrameGrabber->isActive();
|
||||
}
|
||||
|
||||
// syphonLock must be held when calling this function
|
||||
bool OscirenderAudioProcessor::isSyphonInputStarted() const {
|
||||
return syphonFrameGrabber != nullptr;
|
||||
}
|
||||
|
||||
// syphonLock must be held when calling this function
|
||||
void OscirenderAudioProcessor::connectSyphonInput(const juce::String& server, const juce::String& app) {
|
||||
auto editor = dynamic_cast<OscirenderAudioProcessorEditor*>(getActiveEditor());
|
||||
if (!syphonFrameGrabber && editor) {
|
||||
syphonFrameGrabber = std::make_unique<SyphonFrameGrabber>(editor->sharedTextureManager, server, app, syphonImageParser);
|
||||
{
|
||||
juce::MessageManagerLock lock;
|
||||
fileChangeBroadcaster.sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// syphonLock must be held when calling this function
|
||||
void OscirenderAudioProcessor::disconnectSyphonInput() {
|
||||
syphonFrameGrabber.reset();
|
||||
{
|
||||
juce::MessageManagerLock lock;
|
||||
fileChangeBroadcaster.sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// syphonLock must be held when calling this function
|
||||
juce::String OscirenderAudioProcessor::getSyphonSourceName() const {
|
||||
if (syphonFrameGrabber) {
|
||||
return syphonFrameGrabber->getSourceName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
|
||||
return new OscirenderAudioProcessor();
|
||||
}
|
||||
|
|
|
@ -282,19 +282,12 @@ private:
|
|||
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
public:
|
||||
bool isSyphonInputActive() const;
|
||||
bool isSyphonInputStarted() const;
|
||||
void connectSyphonInput(const juce::String& server, const juce::String& app);
|
||||
void disconnectSyphonInput();
|
||||
juce::String getSyphonSourceName() const;
|
||||
std::atomic<bool> syphonInputActive = false;
|
||||
|
||||
juce::SpinLock syphonLock;
|
||||
|
||||
private:
|
||||
ImageParser syphonImageParser = ImageParser(*this);
|
||||
std::unique_ptr<SyphonFrameGrabber> syphonFrameGrabber;
|
||||
#endif
|
||||
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(OscirenderAudioProcessor)
|
||||
};
|
||||
|
|
|
@ -84,7 +84,7 @@ void SettingsComponent::fileUpdated(juce::String fileName) {
|
|||
// Check if the file is an image based on extension or Syphon/Spout input
|
||||
bool isSyphonActive = false;
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
isSyphonActive = audioProcessor.isSyphonInputStarted();
|
||||
isSyphonActive = audioProcessor.syphonInputActive;
|
||||
#endif
|
||||
|
||||
bool isImage = isSyphonActive ||
|
||||
|
|
|
@ -64,11 +64,12 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
|
|||
|
||||
#if (JUCE_MAC || JUCE_WINDOWS) && OSCI_PREMIUM
|
||||
// Add Syphon/Spout input menu item under Recording
|
||||
addMenuItem(2, audioProcessor.isSyphonInputActive() ? "Disconnect Syphon/Spout Input" : "Select Syphon/Spout Input...", [this] {
|
||||
if (audioProcessor.isSyphonInputActive())
|
||||
disconnectSyphonInput();
|
||||
else
|
||||
addMenuItem(2, audioProcessor.syphonInputActive ? "Disconnect Syphon/Spout Input" : "Select Syphon/Spout Input...", [this] {
|
||||
if (audioProcessor.syphonInputActive) {
|
||||
editor.disconnectSyphonInput();
|
||||
} else {
|
||||
openSyphonInputDialog();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
|
@ -83,8 +84,4 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
|
|||
void OsciMainMenuBarModel::openSyphonInputDialog() {
|
||||
editor.openSyphonInputDialog();
|
||||
}
|
||||
|
||||
void OsciMainMenuBarModel::disconnectSyphonInput() {
|
||||
audioProcessor.disconnectSyphonInput();
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
class SyphonFrameGrabber : private juce::Thread, public juce::Component {
|
||||
public:
|
||||
SyphonFrameGrabber(SharedTextureManager& manager, juce::String server, juce::String app, ImageParser& parser, int pollMs = 16)
|
||||
SyphonFrameGrabber(SharedTextureManager& manager, juce::String server, juce::String app, ImageParser& parser, int pollMs = 8)
|
||||
: juce::Thread("SyphonFrameGrabber"), pollIntervalMs(pollMs), manager(manager), parser(parser) {
|
||||
// Create the invisible OpenGL context component
|
||||
glContextComponent = std::make_unique<InvisibleOpenGLContextComponent>();
|
||||
|
@ -30,6 +30,8 @@ public:
|
|||
}
|
||||
|
||||
~SyphonFrameGrabber() override {
|
||||
juce::CriticalSection::ScopedLockType lock(openGLLock);
|
||||
|
||||
stopThread(500);
|
||||
if (receiver) {
|
||||
manager.removeReceiver(receiver);
|
||||
|
@ -41,6 +43,8 @@ public:
|
|||
void run() override {
|
||||
while (!threadShouldExit()) {
|
||||
{
|
||||
juce::CriticalSection::ScopedLockType lock(openGLLock);
|
||||
|
||||
bool activated = false;
|
||||
if (glContextComponent) {
|
||||
activated = glContextComponent->getContext().makeActive();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginWantsMidiIn"
|
||||
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
||||
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
||||
version="2.5.0.0" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
version="2.5.0.1" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
companyEmail="james@ball.sh" defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 OSCI_PREMIUM=1 JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 JUCE_MODAL_LOOPS_PERMITTED=1"
|
||||
pluginAUMainType="'aumf'">
|
||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||
|
|
Ładowanie…
Reference in New Issue