diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index c302c4d..efac7ca 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -156,8 +156,11 @@ MonoslicerEditor::MonoslicerEditor(MonoslicerProcessor& p) addAndMakeVisible(stretchButton); stretchButton.addListener(this); + stretchButton.setClickingTogglesState(true); stretchButton.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a4a)); stretchButton.setColour(juce::TextButton::textColourOffId, juce::Colours::white); + stretchButton.setColour(juce::TextButton::buttonOnColourId, juce::Colour(0xffcc3333)); + stretchButton.setColour(juce::TextButton::textColourOnId, juce::Colours::white); // Key shift combo addAndMakeVisible(keyShiftComboBox); @@ -328,35 +331,36 @@ void MonoslicerEditor::resized() int comboGap = 4; int contentW = bw - 2 * sectionGap - comboGap; - int leftW = static_cast(contentW * 0.21); + int sideW = static_cast(contentW * 0.21); int comboW = static_cast(contentW * 0.12); - int kw = (contentW - leftW - comboW) / 7; + int kw = (contentW - sideW - sideW - comboW - sectionGap * 2 - comboGap) / 7; int adsrW = 4 * kw; - int filterW = 3 * kw + comboW + comboGap; // Bottom panel: buttons + stretch | amp ADSR | filter ADSR | filter knobs - auto leftArea = bottomRow.removeFromLeft(leftW); + // Place auto-slice controls and stretch controls on the right side + int rightW = sideW; + auto rightArea = bottomRow.removeFromRight(rightW); { - auto btnArea = leftArea.removeFromTop(28); + auto btnArea = rightArea.removeFromTop(28); autoSliceButton.setBounds(btnArea.getX(), btnArea.getY(), btnArea.getWidth() / 2 - 2, 24); clearSlicesButton.setBounds(btnArea.getX() + btnArea.getWidth() / 2 + 2, btnArea.getY(), btnArea.getWidth() / 2 - 2, 24); - leftArea.removeFromTop(4); + rightArea.removeFromTop(4); // Stretch controls - int stretchW = leftArea.getWidth(); + int stretchW = rightArea.getWidth(); int col1W = stretchW / 2 - 2; int col2W = stretchW / 2 - 2; - bpmSlider.setBounds(leftArea.getX(), leftArea.getY(), col1W, 20); - stretchBeatsComboBox.setBounds(leftArea.getX() + col1W + 4, leftArea.getY(), col2W, 20); - leftArea.removeFromTop(24); - stretchButton.setBounds(leftArea.getX(), leftArea.getY(), stretchW, 22); - leftArea.removeFromTop(26); - leftArea.removeFromTop(20); - keyShiftComboBox.setBounds(leftArea.getX(), leftArea.getY(), stretchW, 20); - leftArea.removeFromTop(24); - vuMeter.setBounds(leftArea.getX(), leftArea.getY(), stretchW, 40); + bpmSlider.setBounds(rightArea.getX(), rightArea.getY(), col1W, 20); + stretchBeatsComboBox.setBounds(rightArea.getX() + col1W + 4, rightArea.getY(), col2W, 20); + rightArea.removeFromTop(24); + stretchButton.setBounds(rightArea.getX(), rightArea.getY(), stretchW, 22); + rightArea.removeFromTop(26); + rightArea.removeFromTop(20); + keyShiftComboBox.setBounds(rightArea.getX(), rightArea.getY(), stretchW, 20); + rightArea.removeFromTop(24); + vuMeter.setBounds(rightArea.getX(), rightArea.getY(), stretchW, 40); } - bottomRow.removeFromLeft(sectionGap); + bottomRow.removeFromRight(sectionGap); auto adsrSection = bottomRow.removeFromLeft(adsrW); { @@ -430,6 +434,25 @@ void MonoslicerEditor::timerCallback() waveformDisplay.setActiveSliceIndex(processorRef.currentActiveSlice.load()); waveformDisplay.setSelectedSlice(processorRef.selectedSlice.load()); + // Update stretch button state + { + int beatsId = stretchBeatsComboBox.getSelectedId(); + int numBeats = 4; + switch (beatsId) + { + case 1: numBeats = 1; break; + case 2: numBeats = 2; break; + case 3: numBeats = 4; break; + case 4: numBeats = 8; break; + case 5: numBeats = 16; break; + case 6: numBeats = 32; break; + default: numBeats = 4; break; + } + float bpm = static_cast(bpmSlider.getValue()); + bool matched = processorRef.isLengthMatchedToBeats(numBeats, bpm); + stretchButton.setToggleState(matched, juce::dontSendNotification); + } + // Release key click note when mouse is released if (keyClickNote >= 0) { @@ -494,22 +517,29 @@ void MonoslicerEditor::buttonClicked(juce::Button* button) } else if (button == &stretchButton) { - int beatsId = stretchBeatsComboBox.getSelectedId(); - int numBeats = 4; - switch (beatsId) + if (stretchButton.getToggleState()) { - case 1: numBeats = 1; break; - case 2: numBeats = 2; break; - case 3: numBeats = 4; break; - case 4: numBeats = 8; break; - case 5: numBeats = 16; break; - case 6: numBeats = 32; break; - default: numBeats = 4; break; + int beatsId = stretchBeatsComboBox.getSelectedId(); + int numBeats = 4; + switch (beatsId) + { + case 1: numBeats = 1; break; + case 2: numBeats = 2; break; + case 3: numBeats = 4; break; + case 4: numBeats = 8; break; + case 5: numBeats = 16; break; + case 6: numBeats = 32; break; + default: numBeats = 4; break; + } + float bpm = static_cast(bpmSlider.getValue()); + processorRef.stretchToBeats(numBeats, bpm); + waveformDisplay.setSampleBuffer(&processorRef.getSampleBuffer(), processorRef.getSampleRateLoaded()); + } + else + { + processorRef.undo(); + waveformDisplay.setSampleBuffer(&processorRef.getSampleBuffer(), processorRef.getSampleRateLoaded()); } - float bpm = static_cast(bpmSlider.getValue()); - processorRef.stretchToBeats(numBeats, bpm); - waveformDisplay.setSampleBuffer(&processorRef.getSampleBuffer(), processorRef.getSampleRateLoaded()); - } else if (button == &zoomInButton) { diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 8e16fd0..ea5eab4 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -768,6 +768,17 @@ juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() return new MonoslicerProcessor(); } +bool MonoslicerProcessor::isLengthMatchedToBeats(int numBeats, float bpm) const +{ + if (sampleBuffer.getNumSamples() == 0 || bpm <= 0.0f || numBeats <= 0) + return false; + double sr = loadedSampleRate; + if (sr <= 0.0) return false; + double currentDuration = static_cast(sampleBuffer.getNumSamples()) / sr; + double targetDuration = static_cast(numBeats) * 60.0 / static_cast(bpm); + return std::abs(currentDuration - targetDuration) < 0.005; +} + void MonoslicerProcessor::stretchToBeats(int numBeats, float bpm) { if (sampleBuffer.getNumSamples() == 0 || bpm <= 0.0f || numBeats <= 0) return; diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 062d09e..c916c20 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -43,6 +43,7 @@ public: void stretchToBeats(int numBeats, float bpm); void psolaStretch(int targetLength); float getHostBpm() const; + bool isLengthMatchedToBeats(int numBeats, float bpm) const; void undo(); void redo();