diff --git a/Source/CustomStandaloneFilterApp.cpp b/Source/CustomStandaloneFilterApp.cpp index 2f391ad..af65cc2 100644 --- a/Source/CustomStandaloneFilterApp.cpp +++ b/Source/CustomStandaloneFilterApp.cpp @@ -81,7 +81,18 @@ public: const String getApplicationName() override { return JucePlugin_Name; } const String getApplicationVersion() override { return JucePlugin_VersionString; } bool moreThanOneInstanceAllowed() override { return true; } - void anotherInstanceStarted (const String&) override {} + void anotherInstanceStarted (const String& cmdline) override { + + DBG("Another instance started: " << cmdline); + + } + + void urlOpened(const URL& url) override { + + DBG("URL opened: " << url.toString(false)); + if (mainWindow.get() != nullptr) + mainWindow->pluginHolder->urlOpened(url); + } CustomLookAndFeel sonoLNF; diff --git a/Source/CustomStandaloneFilterWindow.h b/Source/CustomStandaloneFilterWindow.h index 940d8b9..9993e82 100644 --- a/Source/CustomStandaloneFilterWindow.h +++ b/Source/CustomStandaloneFilterWindow.h @@ -423,6 +423,14 @@ public: } #endif + void urlOpened(const URL& url) { + if (urlOpenedCallback) + urlOpenedCallback(url); + } + + std::function urlOpenedCallback; + + static StandalonePluginHolder* getInstance(); //============================================================================== @@ -783,6 +791,10 @@ private: if (auto * sonoeditor = dynamic_cast(editor.get())) { sonoeditor->getAudioDeviceManager = [this]() { return &owner.getDeviceManager(); }; sonoeditor->showAudioSettingsDialog = [this](Component* calloutTarget, Component* calloutParent) { owner.pluginHolder->showAudioSettingsDialog(calloutTarget, calloutParent); }; + + owner.pluginHolder->urlOpenedCallback = [sonoeditor](const URL& url) { + sonoeditor->urlOpened(url); + }; } editor->addComponentListener (this); diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 51b54ec..600ead5 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -77,7 +77,11 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(Pau addAndMakeVisible(&m_perfmeter); addAndMakeVisible(&m_import_button); - m_import_button.setButtonText("Show browser"); +#if JUCE_IOS + m_import_button.setButtonText("Load Audio..."); +#else + m_import_button.setButtonText("Show browser"); +#endif m_import_button.onClick = [this]() { toggleFileBrowser(); @@ -169,7 +173,13 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(Pau m_stretchgroup = std::make_unique("", -1, &processor, false); m_stretchgroup->setBackgroundColor(Colour(0xff332244)); - m_stretchgroup->addParameterComponent(m_parcomps[cpi_stretchamount].get()); + if (*processor.getBoolParameter(cpi_bypass_stretch)) { + m_stretchgroup->addParameterComponent(m_parcomps[cpi_dryplayrate].get()); + removeChildComponent(m_parcomps[cpi_stretchamount].get()); + } else { + m_stretchgroup->addParameterComponent(m_parcomps[cpi_stretchamount].get()); + removeChildComponent(m_parcomps[cpi_dryplayrate].get()); + } m_stretchgroup->addParameterComponent(m_parcomps[cpi_fftsize].get()); addAndMakeVisible(m_stretchgroup.get()); @@ -282,6 +292,15 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(Pau m_spec_order_ed.ModuleSelectedCallback = [this](int id) { + auto nowtime = Time::getMillisecondCounterHiRes() * 1e-3; + + if (m_lastspec_select_group == id && nowtime < m_lastspec_select_time + 0.5) { + // double click toggles enabled + setSpectrumProcGroupEnabled(id, !isSpectrumProcGroupEnabled(id)); + } + m_lastspec_select_group = id; + m_lastspec_select_time = nowtime; + if (id == FreeFilterGroup) { if (isSpectrumProcGroupEnabled(id)) { m_wavefilter_tab.setCurrentTabIndex(2); @@ -367,14 +386,18 @@ PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(Pau //m_wavefilter_tab.addTab("Spectrum", Colours::white, &m_sonogram, false); addAndMakeVisible(&m_wavefilter_tab); - setSize (1200, 320+14*25); + + auto defbounds = processor.getLastPluginBounds(); + + setSize (defbounds.getWidth(), defbounds.getHeight()); + startTimer(1, 100); startTimer(2, 1000); startTimer(3, 200); m_wavecomponent.startTimer(100); - setResizeLimits(320, 570, 40000, 4000); + setResizeLimits(320, 430, 40000, 4000); setResizable(true, !JUCEApplicationBase::isStandaloneApp()); @@ -401,6 +424,19 @@ bool PaulstretchpluginAudioProcessorEditor::isSpectrumProcGroupEnabled(int group return false; } +void PaulstretchpluginAudioProcessorEditor::setSpectrumProcGroupEnabled(int groupid, bool enabled) +{ + auto order = processor.getStretchSource()->getSpectrumProcessOrder(); + for (int i=0; i < order.size(); ++i) { + if (order[i].m_index == groupid) { + *(order[i].m_enabled) = enabled; //->setValueNotifyingHost(enabled ? 1.0f : 0.0f); + return; + } + } + return; +} + + void PaulstretchpluginAudioProcessorEditor::showRenderDialog() { @@ -510,6 +546,10 @@ void PaulstretchpluginAudioProcessorEditor::resized() minh = 40; #endif + DBG("Resized: " << getWidth() << " " << getHeight()); + + processor.setLastPluginBounds(getLocalBounds()); + FlexBox mainbox; mainbox.flexDirection = FlexBox::Direction::column; @@ -653,6 +693,7 @@ void PaulstretchpluginAudioProcessorEditor::resized() #if JUCE_IOS tabminh = 234; + orderminh = 44; #endif int totminh = vpminh + orderminh + tabminh + topboxh + toggleh + volh + stretchH; @@ -798,9 +839,17 @@ void PaulstretchpluginAudioProcessorEditor::timerCallback(int id) processor.m_free_filter_envelope->updateMinMaxValues(); m_free_filter_component.repaint(); m_spec_order_ed.repaint(); - m_parcomps[cpi_dryplayrate]->setVisible(*processor.getBoolParameter(cpi_bypass_stretch)); - m_parcomps[cpi_stretchamount]->setVisible(!(*processor.getBoolParameter(cpi_bypass_stretch))); - //if (m_wavefilter_tab.getCurrentTabIndex() != processor.m_cur_tab_index) + + if (*processor.getBoolParameter(cpi_bypass_stretch)) { + m_stretchgroup->replaceParameterComponent(m_parcomps[cpi_stretchamount].get(), m_parcomps[cpi_dryplayrate].get()); + } else { + m_stretchgroup->replaceParameterComponent(m_parcomps[cpi_dryplayrate].get(), m_parcomps[cpi_stretchamount].get()); + } + + //m_parcomps[cpi_dryplayrate]->setVisible(*processor.getBoolParameter(cpi_bypass_stretch)); + //m_parcomps[cpi_stretchamount]->setVisible(!*processor.getBoolParameter(cpi_bypass_stretch)); + + //if (m_wavefilter_tab.getCurrentTabIndex() != processor.m_cur_tab_index) // m_wavefilter_tab.setCurrentTabIndex(processor.m_cur_tab_index, false); } } @@ -827,6 +876,24 @@ void PaulstretchpluginAudioProcessorEditor::filesDropped(const StringArray & fil } } +void PaulstretchpluginAudioProcessorEditor::urlOpened(const URL& url) +{ + DBG("Got URL: " << url.toString(false)); + std::unique_ptr wi (url.createInputStream (false)); + if (wi != nullptr) + { + File file = url.getLocalFile(); + DBG("Attempting to load after input stream create: " << file.getFullPathName()); + processor.setAudioFile(file); + } else { + File file = url.getLocalFile(); + DBG("Attempting to load after no input stream create: " << file.getFullPathName()); + processor.setAudioFile(file); + } + toFront(true); +} + + bool PaulstretchpluginAudioProcessorEditor::keyPressed(const KeyPress & press) { std::function action; @@ -901,7 +968,7 @@ void PaulstretchpluginAudioProcessorEditor::showAbout() "GPL licensed source code for this plugin at : https://bitbucket.org/xenakios/paulstretchplugin/overview\n"; if (host.type != juce::PluginHostType::UnknownHost) { - text += "Running in : "+host.getHostDescription()+"\n"; + text += String("Running in : ") + host.getHostDescription()+ String("\n"); } content->setJustificationType(Justification::centred); @@ -927,17 +994,54 @@ void PaulstretchpluginAudioProcessorEditor::showAbout() void PaulstretchpluginAudioProcessorEditor::toggleFileBrowser() { - if (m_filechooser == nullptr) +#if JUCE_IOS + + String curropendir = processor.m_propsfile->m_props_file->getValue("importfilefolder", + File::getSpecialLocation(File::userDocumentsDirectory).getFullPathName()); + + Component * parent = JUCEApplication::isStandaloneApp() ? nullptr : this; + + fileChooser.reset(new FileChooser("Choose an audio file to open...", + curropendir, + "*.wav;*.mp3;*.m4a;*.aif;*.aiff;*.caf;*.ogg;*.flac", + true, false, parent)); + + + fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, + [this] (const FileChooser& chooser) + { + auto results = chooser.getURLResults(); + if (results.size() > 0) + { + auto url = results.getReference (0); + + std::unique_ptr wi (url.createInputStream (false)); + if (wi != nullptr) + { + File file = url.getLocalFile(); + DBG("Attempting to load from: " << file.getFullPathName()); + + //curropendir = file.getParentDirectory(); + processor.setAudioFile(file); + processor.m_propsfile->m_props_file->setValue("importfilefolder", file.getParentDirectory().getFullPathName()); + } + } + }); + + +#else + if (m_filechooser == nullptr) { m_filechooser = std::make_unique(processor); addChildComponent(m_filechooser.get()); } - m_filechooser->setBounds(0, 26, getWidth()/2, getHeight() - 75); + m_filechooser->setBounds(0, m_import_button.getBottom(), getWidth()/2, getHeight() - 75); m_filechooser->setVisible(!m_filechooser->isVisible()); if (m_filechooser->isVisible()) m_import_button.setButtonText("Hide browser"); else m_import_button.setButtonText("Show browser"); +#endif } WaveformComponent::WaveformComponent(AudioFormatManager* afm, AudioThumbnail* thumb, StretchAudioSource* sas) @@ -2035,6 +2139,21 @@ void ParameterGroupComponent::addParameterComponent(ParameterComponent * pcomp) } } +void ParameterGroupComponent::replaceParameterComponent(ParameterComponent * oldcomp, ParameterComponent * newcomp) +{ + for (int i = 0; i < m_parcomps.size(); ++i) + { + if (m_parcomps[i] == oldcomp) { + removeChildComponent(oldcomp); + addAndMakeVisible(newcomp); + m_parcomps[i] = newcomp; + resized(); + break; + } + } +} + + int ParameterGroupComponent::doLayout(Rectangle bounds) { int titlew = m_namelabel ? 100 : m_enableButton ? 40 : 0; @@ -2142,11 +2261,11 @@ MyFileBrowserComponent::MyFileBrowserComponent(PaulstretchpluginAudioProcessor & String initiallocfn = m_proc.m_propsfile->m_props_file->getValue("importfilefolder", File::getSpecialLocation(File::userHomeDirectory).getFullPathName()); File initialloc(initiallocfn); - m_fbcomp = std::make_unique(1 | 4, + m_fbcomp = std::make_unique(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, initialloc, &m_filefilter, nullptr); m_fbcomp->addListener(this); addAndMakeVisible(m_fbcomp.get()); - setLookAndFeel(&m_filebwlookandfeel); + //setLookAndFeel(&m_filebwlookandfeel); } MyFileBrowserComponent::~MyFileBrowserComponent() @@ -2161,7 +2280,7 @@ void MyFileBrowserComponent::resized() void MyFileBrowserComponent::paint(Graphics & g) { - g.fillAll(Colours::black.withAlpha(0.8f)); + g.fillAll(Colours::black.withAlpha(0.9f)); } void MyFileBrowserComponent::selectionChanged() diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index bf471a2..7ab7bd1 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -115,6 +115,7 @@ public: //void addParameterComponent(std::unique_ptr pcomp); void addParameterComponent(ParameterComponent * pcomp); + void replaceParameterComponent(ParameterComponent * oldcomp, ParameterComponent * newcomp); void updateParameterComponents(); @@ -524,12 +525,15 @@ public: //SimpleFFTComponent m_sonogram; String m_last_err; + void urlOpened(const URL& url); + std::function getAudioDeviceManager; std::function showAudioSettingsDialog; private: bool isSpectrumProcGroupEnabled(int groupid); + void setSpectrumProcGroupEnabled(int groupid, bool enabled); CustomLookAndFeel m_lookandfeel; @@ -551,7 +555,9 @@ private: TextButton m_rewind_button; Label m_info_label; SpectralChainEditor m_spec_order_ed; - + double m_lastspec_select_time = 0.0; + int m_lastspec_select_group = -1; + void showSettingsMenu(); zoom_scrollbar m_zs; @@ -566,6 +572,7 @@ private: std::vector m_capturelens{ 2,5,10,30,60,120 }; std::unique_ptr m_filechooser; + std::unique_ptr fileChooser; WildcardFileFilter m_filefilter; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PaulstretchpluginAudioProcessorEditor) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 8a20f26..d36d242 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -290,6 +290,10 @@ ValueTree PaulstretchpluginAudioProcessor::getStateTree(bool ignoreoptions, bool storeToTreeProperties(paramtree, nullptr, "waveviewrange", m_wave_view_range); ValueTree freefilterstate = m_free_filter_envelope->saveState(Identifier("freefilter_envelope")); paramtree.addChild(freefilterstate, -1, nullptr); + + storeToTreeProperties(paramtree, nullptr, "pluginwidth", mPluginWindowWidth); + storeToTreeProperties(paramtree, nullptr, "pluginheight", mPluginWindowHeight); + return paramtree; } @@ -306,6 +310,9 @@ void PaulstretchpluginAudioProcessor::setStateFromTree(ValueTree tree) "capturewhenhostrunning", m_capture_when_host_plays,"mutewhilecapturing",m_mute_while_capturing, "savecapturedaudio",m_save_captured_audio, "muteprocwhilecapturing",m_mute_processed_while_capturing); getFromTreeProperties(tree, "tabaindex", m_cur_tab_index); + getFromTreeProperties(tree, "pluginwidth", mPluginWindowWidth); + getFromTreeProperties(tree, "pluginheight", mPluginWindowHeight); + if (tree.hasProperty("numspectralstagesb")) { std::vector old_order = m_stretch_source->getSpectrumProcessOrder(); diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 5d9cf94..60d3ef7 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -174,6 +174,7 @@ public: void parameterValueChanged(int parameterIndex, float newValue) override; void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override; + //============================================================================== void getStateInformation (MemoryBlock& destData) override; void setStateInformation (const void* data, int sizeInBytes) override; @@ -189,6 +190,10 @@ public: { return dynamic_cast(getParameters()[index]); } + void setLastPluginBounds(juce::Rectangle bounds) { mPluginWindowWidth = bounds.getWidth(); mPluginWindowHeight = bounds.getHeight();} + juce::Rectangle getLastPluginBounds() const { return juce::Rectangle(0,0,mPluginWindowWidth, mPluginWindowHeight); } + + void setDirty(); void setRecordingEnabled(bool b); bool isRecordingEnabled() { return m_is_recording; } @@ -269,7 +274,9 @@ private: double m_last_in_pos = 0.0; std::vector m_bufamounts{ 4096,8192,16384,32768,65536,262144 }; ProcessParameters m_ppar; - + int mPluginWindowWidth = 870; + int mPluginWindowHeight = 770; + void setFFTSize(double size); void startplay(Range playrange, int numoutchans, int maxBlockSize, String& err); SharedResourcePointer m_thumbcache; diff --git a/paulstretchplugin_ios.jucer b/paulstretchplugin_ios.jucer index d3385f9..767ae18 100644 --- a/paulstretchplugin_ios.jucer +++ b/paulstretchplugin_ios.jucer @@ -69,7 +69,8 @@ microphonePermissionNeeded="1" iosBackgroundAudio="1" buildNumber="100" iosScreenOrientation="UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight,UIInterfaceOrientationPortrait,UIInterfaceOrientationPortraitUpsideDown" iPadScreenOrientation="UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight,UIInterfaceOrientationPortrait,UIInterfaceOrientationPortraitUpsideDown" - UIStatusBarHidden="0" UIRequiresFullScreen="0" customPList="<plist version="1.0"> <dict> <key>ITSAppUsesNonExemptEncryption</key> <false/> <key>UIStatusBarHidden</key> <false/> <key>UIStatusBarStyle</key> <string>UIStatusBarStyleLightContent</string> <key>UIViewControllerBasedStatusBarAppearance</key> <false/> <key>NSLocalNetworkUsageDescription</key> <string>DrumJamPad uses networking to communicate with other local services</string> </dict> </plist>"> + UIStatusBarHidden="0" UIRequiresFullScreen="0" customPList="<plist version="1.0"> <dict> <key>ITSAppUsesNonExemptEncryption</key> <false/> <key>UIStatusBarHidden</key> <false/> <key>UIStatusBarStyle</key> <string>UIStatusBarStyleLightContent</string> <key>UIViewControllerBasedStatusBarAppearance</key> <false/> <key>NSLocalNetworkUsageDescription</key> <string>DrumJamPad uses networking to communicate with other local services</string> <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeIconFiles</key> <array/> <key>CFBundleTypeName</key> <string>Audio File</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>LSHandlerRank</key> <string>Owner</string> <key>LSItemContentTypes</key> <array> <string>com.microsoft.waveform-audio</string> <string>public.aiff-audio</string> <string>com.apple.coreaudio-format</string> <string>public.mpeg-4-audio</string> <string>public.mp3</string> </array> </dict> </array> </dict> </plist>" + UIFileSharingEnabled="1" UISupportsDocumentBrowser="1">