/******************************************************************************* The block below describes the properties of this PIP. A PIP is a short snippet of code that can be read by the Projucer and used to generate a JUCE project. BEGIN_JUCE_PIP_METADATA name: RetrospectiveRecordBufferSyncHang version: 1.0.0 description: A simple Tracktion Engine demo project that shows how loading a .tracktionedit file, calling play on it, and then deleting it to reload can sometimes cause an infinite lock when the RetrospectiveRecordBuffer of the WaveInputDevice attempts to sync playhead position. dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats, juce_audio_processors, juce_audio_utils, juce_core, juce_data_structures, juce_dsp, juce_events, juce_graphics, juce_gui_basics, juce_gui_extra, juce_osc, tracktion_engine, tracktion_graph exporters: VS2019, XCODE_MAC moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1 defines: JUCE_MODAL_LOOPS_PERMITTED=1 type: Component mainClass: MainComponent END_JUCE_PIP_METADATA *******************************************************************************/ #pragma once namespace te = tracktion_engine; //============================================================================== class MainComponent : public juce::Component, private juce::Timer { public: //============================================================================== MainComponent() { addAndMakeVisible(pluginListButton); pluginListButton.onClick = [this] { showPluginList(); }; addAndMakeVisible(pathEditor); pathEditor.setTextToShowWhenEmpty("path to *.tracktionedit to reload", Colours::white.darker(0.25f)); addAndMakeVisible(startButton); startButton.onClick = [this] { numReloads = 0; pathEditor.setEnabled(false); startButton.setEnabled(false); stopButton.setEnabled(true); startTimerHz(reloadsPerSecond); }; addAndMakeVisible(stopButton); stopButton.setEnabled(false); stopButton.onClick = [this] { pathEditor.setEnabled(true); startButton.setEnabled(true); stopButton.setEnabled(false); stopTimer(); }; addAndMakeVisible(reloadsLabel); reloadsLabel.setJustificationType(juce::Justification::centred); setSize(420, 192); } ~MainComponent() override { if (edit != nullptr) edit.reset(nullptr); engine.getTemporaryFileManager().getTempDirectory().deleteRecursively(); } //============================================================================== void paint(juce::Graphics& g) override { g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId)); } void resized() override { auto b = getLocalBounds(); int rowHeight = 20; int x = b.getX(); int y = b.getY(); int width = b.getWidth(); pathEditor.setBounds({ x, y, width, rowHeight }); startButton.setBounds({ x, y + rowHeight, width, rowHeight }); stopButton.setBounds({ x, y + rowHeight * 2, width, rowHeight }); pluginListButton.setBounds({ x, y + rowHeight * 3, width, rowHeight }); reloadsLabel.setBounds({ x, y + rowHeight * 6, width, rowHeight }); } private: int numReloads = 0; const int reloadsPerSecond = 4; te::Engine engine{ ProjectInfo::projectName, nullptr, nullptr }; std::unique_ptr edit = nullptr; TextEditor pathEditor{ "Edit Path" }; TextButton startButton{ "Start Reloading", "Start Reloading" }; TextButton stopButton{ "Stop Reloading", "Stop Reloading" }; TextButton pluginListButton{ "Plugin List", "Plugin List" }; Label reloadsLabel{ "Reloads", "Reloads: 0" }; void timerCallback() override { reloadsLabel.setText("Reloads: " + juce::String(++numReloads), juce::NotificationType::sendNotification); reloadEdit(); } void reloadEdit() { String editPath = pathEditor.getText().trim(); auto editFile = File(editPath); jassert(editFile.existsAsFile()); if (edit != nullptr) edit.reset(); edit = std::move(te::loadEditFromFile(engine, editFile)); // Comment out the line below and the issue never happens edit->getTransport().play(true); } void showPluginList() { DialogWindow::LaunchOptions o; o.dialogTitle = TRANS("Plugins"); o.dialogBackgroundColour = Colours::black; o.escapeKeyTriggersCloseButton = true; o.useNativeTitleBar = true; o.resizable = true; o.useBottomRightCornerResizer = true; auto v = new PluginListComponent(engine.getPluginManager().pluginFormatManager, engine.getPluginManager().knownPluginList, engine.getTemporaryFileManager().getTempFile("PluginScanDeadMansPedal"), te::getApplicationSettings()); v->setSize(800, 600); o.content.setOwned(v); o.launchAsync(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent) };