Match Length button toggles with red highlight, moved controls to right side

This commit is contained in:
Armin 2026-10-06 23:27:48 +02:00
commit 16322a2bb6
3 changed files with 73 additions and 31 deletions

View file

@ -156,8 +156,11 @@ MonoslicerEditor::MonoslicerEditor(MonoslicerProcessor& p)
addAndMakeVisible(stretchButton); addAndMakeVisible(stretchButton);
stretchButton.addListener(this); stretchButton.addListener(this);
stretchButton.setClickingTogglesState(true);
stretchButton.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a4a)); stretchButton.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a4a));
stretchButton.setColour(juce::TextButton::textColourOffId, juce::Colours::white); 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 // Key shift combo
addAndMakeVisible(keyShiftComboBox); addAndMakeVisible(keyShiftComboBox);
@ -328,35 +331,36 @@ void MonoslicerEditor::resized()
int comboGap = 4; int comboGap = 4;
int contentW = bw - 2 * sectionGap - comboGap; int contentW = bw - 2 * sectionGap - comboGap;
int leftW = static_cast<int>(contentW * 0.21); int sideW = static_cast<int>(contentW * 0.21);
int comboW = static_cast<int>(contentW * 0.12); int comboW = static_cast<int>(contentW * 0.12);
int kw = (contentW - leftW - comboW) / 7; int kw = (contentW - sideW - sideW - comboW - sectionGap * 2 - comboGap) / 7;
int adsrW = 4 * kw; int adsrW = 4 * kw;
int filterW = 3 * kw + comboW + comboGap;
// Bottom panel: buttons + stretch | amp ADSR | filter ADSR | filter knobs // 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); 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); clearSlicesButton.setBounds(btnArea.getX() + btnArea.getWidth() / 2 + 2, btnArea.getY(), btnArea.getWidth() / 2 - 2, 24);
leftArea.removeFromTop(4); rightArea.removeFromTop(4);
// Stretch controls // Stretch controls
int stretchW = leftArea.getWidth(); int stretchW = rightArea.getWidth();
int col1W = stretchW / 2 - 2; int col1W = stretchW / 2 - 2;
int col2W = stretchW / 2 - 2; int col2W = stretchW / 2 - 2;
bpmSlider.setBounds(leftArea.getX(), leftArea.getY(), col1W, 20); bpmSlider.setBounds(rightArea.getX(), rightArea.getY(), col1W, 20);
stretchBeatsComboBox.setBounds(leftArea.getX() + col1W + 4, leftArea.getY(), col2W, 20); stretchBeatsComboBox.setBounds(rightArea.getX() + col1W + 4, rightArea.getY(), col2W, 20);
leftArea.removeFromTop(24); rightArea.removeFromTop(24);
stretchButton.setBounds(leftArea.getX(), leftArea.getY(), stretchW, 22); stretchButton.setBounds(rightArea.getX(), rightArea.getY(), stretchW, 22);
leftArea.removeFromTop(26); rightArea.removeFromTop(26);
leftArea.removeFromTop(20); rightArea.removeFromTop(20);
keyShiftComboBox.setBounds(leftArea.getX(), leftArea.getY(), stretchW, 20); keyShiftComboBox.setBounds(rightArea.getX(), rightArea.getY(), stretchW, 20);
leftArea.removeFromTop(24); rightArea.removeFromTop(24);
vuMeter.setBounds(leftArea.getX(), leftArea.getY(), stretchW, 40); vuMeter.setBounds(rightArea.getX(), rightArea.getY(), stretchW, 40);
} }
bottomRow.removeFromLeft(sectionGap); bottomRow.removeFromRight(sectionGap);
auto adsrSection = bottomRow.removeFromLeft(adsrW); auto adsrSection = bottomRow.removeFromLeft(adsrW);
{ {
@ -430,6 +434,25 @@ void MonoslicerEditor::timerCallback()
waveformDisplay.setActiveSliceIndex(processorRef.currentActiveSlice.load()); waveformDisplay.setActiveSliceIndex(processorRef.currentActiveSlice.load());
waveformDisplay.setSelectedSlice(processorRef.selectedSlice.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<float>(bpmSlider.getValue());
bool matched = processorRef.isLengthMatchedToBeats(numBeats, bpm);
stretchButton.setToggleState(matched, juce::dontSendNotification);
}
// Release key click note when mouse is released // Release key click note when mouse is released
if (keyClickNote >= 0) if (keyClickNote >= 0)
{ {
@ -493,6 +516,8 @@ void MonoslicerEditor::buttonClicked(juce::Button* button)
processorRef.redo(); processorRef.redo();
} }
else if (button == &stretchButton) else if (button == &stretchButton)
{
if (stretchButton.getToggleState())
{ {
int beatsId = stretchBeatsComboBox.getSelectedId(); int beatsId = stretchBeatsComboBox.getSelectedId();
int numBeats = 4; int numBeats = 4;
@ -509,7 +534,12 @@ void MonoslicerEditor::buttonClicked(juce::Button* button)
float bpm = static_cast<float>(bpmSlider.getValue()); float bpm = static_cast<float>(bpmSlider.getValue());
processorRef.stretchToBeats(numBeats, bpm); processorRef.stretchToBeats(numBeats, bpm);
waveformDisplay.setSampleBuffer(&processorRef.getSampleBuffer(), processorRef.getSampleRateLoaded()); waveformDisplay.setSampleBuffer(&processorRef.getSampleBuffer(), processorRef.getSampleRateLoaded());
}
else
{
processorRef.undo();
waveformDisplay.setSampleBuffer(&processorRef.getSampleBuffer(), processorRef.getSampleRateLoaded());
}
} }
else if (button == &zoomInButton) else if (button == &zoomInButton)
{ {

View file

@ -768,6 +768,17 @@ juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
return new MonoslicerProcessor(); 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<double>(sampleBuffer.getNumSamples()) / sr;
double targetDuration = static_cast<double>(numBeats) * 60.0 / static_cast<double>(bpm);
return std::abs(currentDuration - targetDuration) < 0.005;
}
void MonoslicerProcessor::stretchToBeats(int numBeats, float bpm) void MonoslicerProcessor::stretchToBeats(int numBeats, float bpm)
{ {
if (sampleBuffer.getNumSamples() == 0 || bpm <= 0.0f || numBeats <= 0) return; if (sampleBuffer.getNumSamples() == 0 || bpm <= 0.0f || numBeats <= 0) return;

View file

@ -43,6 +43,7 @@ public:
void stretchToBeats(int numBeats, float bpm); void stretchToBeats(int numBeats, float bpm);
void psolaStretch(int targetLength); void psolaStretch(int targetLength);
float getHostBpm() const; float getHostBpm() const;
bool isLengthMatchedToBeats(int numBeats, float bpm) const;
void undo(); void undo();
void redo(); void redo();