diff --git a/CMakeLists.txt b/CMakeLists.txt index bb59761..cb9f366 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,4 +91,7 @@ target_link_libraries(Monoslicer ) # Suppress JUCE / VST3 SDK warnings globally for all targets. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics -Wno-deprecated-declarations -Wno-implicit-int-conversion-on-negation") +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") +endif() +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations -Wno-implicit-int-conversion-on-negation") diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 922a866..3f42c70 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -216,6 +216,10 @@ MonoslicerEditor::MonoslicerEditor(MonoslicerProcessor& p) processorRef.triggerSlice(sliceIdx, 0.8f); }); + addChildComponent(dropOverlay); + dropOverlay.setInterceptsMouseClicks(false, false); + dropOverlay.setVisible(false); + startTimerHz(30); } @@ -286,6 +290,7 @@ void MonoslicerEditor::resized() statusBarArea.getWidth() / 2 - 5, statusBarArea.getHeight()); buildInfoLabel.setBounds(statusBarArea.getCentreX(), statusBarArea.getY(), statusBarArea.getWidth() / 2 - 5, statusBarArea.getHeight()); + dropOverlay.setBounds(getLocalBounds()); int tx = 120; @@ -547,17 +552,58 @@ void MonoslicerEditor::comboBoxChanged(juce::ComboBox* comboBoxThatHasChanged) } } +// [EDITOR DIAG] temporary +static void ediag(const char* what, const juce::StringArray& files, int accept) +{ + FILE* f = fopen("/tmp/monoslicer_drag.log", "a"); + if (f == nullptr) return; + fprintf(f, "[editor] %s accept=%d files=%d", what, accept, (int)files.size()); + for (const auto& x : files) fprintf(f, " \"%s\"", x.toUTF8().getAddress()); + fprintf(f, "\n"); + fclose(f); +} + bool MonoslicerEditor::isInterestedInFileDrag(const juce::StringArray& files) { for (const auto& f : files) if (f.endsWithIgnoreCase(".wav") || f.endsWithIgnoreCase(".aiff") || f.endsWithIgnoreCase(".flac") || f.endsWithIgnoreCase(".ogg")) + { + ediag("isInterested", files, 1); return true; + } + ediag("isInterested", files, 0); return false; } +void MonoslicerEditor::fileDragEnter(const juce::StringArray& files, int, int) +{ + ediag("enter", files, isInterestedInFileDrag(files) ? 1 : 0); + if (isInterestedInFileDrag(files)) + { + dropOverlay.setVisible(true); + dropOverlay.toFront(false); + dropOverlay.repaint(); + } +} + +void MonoslicerEditor::fileDragMove(const juce::StringArray& files, int, int) +{ + if (isInterestedInFileDrag(files)) + dropOverlay.setVisible(true); +} + +void MonoslicerEditor::fileDragExit(const juce::StringArray&) +{ + ediag("exit", juce::StringArray(), 0); + dropOverlay.setVisible(false); +} + void MonoslicerEditor::filesDropped(const juce::StringArray& files, int, int) { + ediag("drop", files, 1); + dropOverlay.setVisible(false); + for (const auto& f : files) { juce::File file(f); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 3d42ff6..c53068a 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -65,8 +65,8 @@ public: auto drawBar = [&](float y, float level, float peak, const juce::String& legend) { - float frac = juce::jlimit(0.0f, 1.0f, level); - float peakFrac = juce::jlimit(0.0f, 1.0f, peak); + float frac = juce::jlimit(0.0f, 1.0f, level) * 0.95f; + float peakFrac = juce::jlimit(0.0f, 1.0f, peak) * 0.95f; // Legend text g.setColour(juce::Colour(0xff888888)); @@ -114,12 +114,29 @@ public: void resized() override; bool isInterestedInFileDrag(const juce::StringArray& files) override; + void fileDragEnter(const juce::StringArray& files, int x, int y) override; + void fileDragMove(const juce::StringArray& files, int x, int y) override; + void fileDragExit(const juce::StringArray& files) override; void filesDropped(const juce::StringArray& files, int x, int y) override; void buttonClicked(juce::Button* button) override; void comboBoxChanged(juce::ComboBox* comboBoxThatHasChanged) override; void timerCallback() override; private: + class DropOverlay : public juce::Component + { + public: + void paint(juce::Graphics& g) override + { + auto b = getLocalBounds().toFloat(); + g.setColour(juce::Colour(0x66003a8c)); + g.fillRoundedRectangle(b.reduced(3.0f), 8.0f); + g.setColour(juce::Colour(0xcc88ccff)); + g.drawRoundedRectangle(b.reduced(3.0f), 8.0f, 2.0f); + g.setFont(juce::Font(18.0f, juce::Font::bold)); + g.drawText(TRANS("Drop audio file to load"), b, juce::Justification::centred, true); + } + }; MonoslicerLookAndFeel lnf; MonoslicerProcessor& processorRef; @@ -199,6 +216,7 @@ private: const juce::String& paramId, std::unique_ptr& attachment); int keyClickNote = -1; + DropOverlay dropOverlay; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MonoslicerEditor) }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 7ddc743..1fed6b9 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -470,8 +470,12 @@ void MonoslicerProcessor::processBlock(juce::AudioBuffer& buffer, juce::M void MonoslicerProcessor::handleNoteOn(int noteNumber, float velocity) { - int offset = static_cast(apvts.getRawParameterValue("octaveOffset")->load()) * 12; - int sliceIdx = noteNumber - 60 + offset; + int offset = 0; + if (auto* p = dynamic_cast(apvts.getParameter("octaveOffset"))) + offset = p->get(); + + int note = noteNumber + offset * 12; + int sliceIdx = sliceManager.findSliceIndexForNote(note); sliceIdx = juce::jlimit(0, juce::jmax(0, sliceManager.getNumSlices() - 1), sliceIdx); if (apvts.getRawParameterValue("selectViaMidi")->load() > 0.5f) diff --git a/Source/SliceManager.cpp b/Source/SliceManager.cpp index b48ce5f..e0810e5 100644 --- a/Source/SliceManager.cpp +++ b/Source/SliceManager.cpp @@ -282,6 +282,24 @@ int SliceManager::findSliceIndexForSample(int samplePos) const return -1; } +int SliceManager::findSliceIndexForNote(int midiNote) const +{ + const juce::CriticalSection::ScopedLockType lock(mutex); + if (slices.empty()) + return -1; + + for (size_t i = 0; i < slices.size(); ++i) + if (slices[i].midiNote == midiNote) + return static_cast(i); + + int pitchClass = midiNote % 12; + for (size_t i = 0; i < slices.size(); ++i) + if (slices[i].midiNote % 12 == pitchClass) + return static_cast(i); + + return -1; +} + void SliceManager::sortSlices() { std::sort(slices.begin(), slices.end(), diff --git a/Source/SliceManager.h b/Source/SliceManager.h index b584efa..578505d 100644 --- a/Source/SliceManager.h +++ b/Source/SliceManager.h @@ -41,6 +41,7 @@ public: void setBeforeChangeCallback(std::function cb) { beforeChangeCallback = std::move(cb); } int findSliceIndexForSample(int samplePos) const; + int findSliceIndexForNote(int midiNote) const; juce::AudioBuffer* getMutableBuffer() { return sampleBuffer; } diff --git a/Source/WaveformDisplay.cpp b/Source/WaveformDisplay.cpp index 11ba1f9..dcb0f4b 100644 --- a/Source/WaveformDisplay.cpp +++ b/Source/WaveformDisplay.cpp @@ -403,8 +403,8 @@ void WaveformDisplay::drawWaveform(juce::Graphics& g, juce::Rectangle bou filledPath.closeSubPath(); - juce::ColourGradient gradient(juce::Colour(0xff4488ff), bounds.getX(), bounds.getY(), - juce::Colour(0xff2244aa), bounds.getX(), bounds.getBottom(), false); + juce::ColourGradient gradient(juce::Colour(0xff5599ff), bounds.getX(), bounds.getY(), + juce::Colour(0xff002255), bounds.getX(), bounds.getBottom(), false); g.setGradientFill(gradient); g.fillPath(filledPath);