mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 04:10:47 +02:00
Add ping-pong delay, fix preset save extension, align reverb/scale UI
This commit is contained in:
parent
d4db11b195
commit
af87193643
5 changed files with 129 additions and 1110 deletions
|
|
@ -13,12 +13,13 @@ public:
|
|||
writePos = 0;
|
||||
}
|
||||
|
||||
void setParameters(float timeMs, float fb, float mx) {
|
||||
void setParameters(float timeMs, float fb, float mx, bool pingpong = false) {
|
||||
timeSamplesF = timeMs * 0.001f * static_cast<float>(sampleRate);
|
||||
float bufMax = static_cast<float>(bufferL.size()) - 1.0f;
|
||||
timeSamplesF = std::clamp(timeSamplesF, 1.0f, bufMax);
|
||||
feedback = std::clamp(fb, 0.0f, 0.95f);
|
||||
mix = std::clamp(mx, 0.0f, 1.0f);
|
||||
pingPong = pingpong;
|
||||
}
|
||||
|
||||
void process(float& left, float& right) {
|
||||
|
|
@ -34,8 +35,15 @@ public:
|
|||
float outL = bufferL[i0] * (1.0f - frac) + bufferL[i1] * frac;
|
||||
float outR = bufferR[i0] * (1.0f - frac) + bufferR[i1] * frac;
|
||||
|
||||
if (pingPong) {
|
||||
// Classic ping-pong: each channel's echo bounces to the opposite
|
||||
// channel on the next repeat, so the taps alternate L/R.
|
||||
bufferL[writePos] = left + outR * feedback;
|
||||
bufferR[writePos] = right + outL * feedback;
|
||||
} else {
|
||||
bufferL[writePos] = left + outL * feedback;
|
||||
bufferR[writePos] = right + outR * feedback;
|
||||
}
|
||||
|
||||
writePos = (writePos + 1) % bufSize;
|
||||
|
||||
|
|
@ -50,4 +58,5 @@ private:
|
|||
float timeSamplesF = 8820.0f;
|
||||
float feedback = 0.3f;
|
||||
float mix = 0.25f;
|
||||
bool pingPong = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -521,6 +521,7 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
|
|||
setupSyncKnob(delayTimeKnob, delayTimeAttach, delayBeatAttach, "delayTime", "delayBeat", delaySyncBox);
|
||||
setupParam(delayFbKnob, delayFbAttach, "delayFeedback", "FBACK");
|
||||
setupParam(delayMixKnob, delayMixAttach, "delayMix", "MIX");
|
||||
setupParam(delayPingPongKnob, delayPingPongAttach, "delayPingPong", "PING");
|
||||
setupParam(reverbSizeKnob, reverbSizeAttach, "reverbSize", "SIZE");
|
||||
setupParam(reverbDampKnob, reverbDampAttach, "reverbDamping", "DAMP");
|
||||
setupParam(reverbMixKnob, reverbMixAttach, "reverbMix", "MIX");
|
||||
|
|
@ -737,13 +738,19 @@ void MainContentComponent::savePresetFile() {
|
|||
"*.cfl");
|
||||
|
||||
chooser->launchAsync(juce::FileBrowserComponent::saveMode
|
||||
| juce::FileBrowserComponent::canSelectFiles,
|
||||
| juce::FileBrowserComponent::canSelectFiles
|
||||
| juce::FileBrowserComponent::warnAboutOverwriting,
|
||||
[this, chooser](const juce::FileChooser& c) {
|
||||
std::unique_ptr<juce::FileChooser> deleter(chooser);
|
||||
auto file = c.getResult();
|
||||
if (file == juce::File())
|
||||
return;
|
||||
if (file.getFileExtension().toLowerCase() != ".cfl")
|
||||
|
||||
// Always force the .cfl extension (macOS native panel does not
|
||||
// reliably append it) and lowercase the filename.
|
||||
juce::String name = file.getFileNameWithoutExtension();
|
||||
if (name.toLowerCase() != name)
|
||||
file = file.getParentDirectory().getChildFile(name.toLowerCase());
|
||||
file = file.withFileExtension(".cfl");
|
||||
|
||||
auto state = processorRef.apvts.copyState();
|
||||
|
|
@ -942,9 +949,9 @@ void MainContentComponent::paint(juce::Graphics& g) {
|
|||
g.drawText(title, x + 6, y + titlePadY, w - 12, 14, juce::Justification::centred);
|
||||
};
|
||||
|
||||
drawSubSection(10, 508, 512, 140, "DISTORTION", 6);
|
||||
drawSubSection(528, 508, 470, 140, "COMPRESSOR", 6);
|
||||
drawSubSection(1004, 508, 270, 140, "AUTO-PAN", 6);
|
||||
drawSubSection(10, 508, 510, 140, "DISTORTION", 6);
|
||||
drawSubSection(528, 508, 468, 140, "COMPRESSOR", 6);
|
||||
drawSubSection(1004, 508, 268, 140, "AUTO-PAN", 6);
|
||||
drawSubSection(1280, 508, 370, 140, "LIMITER", 6);
|
||||
|
||||
// LFO sub-sections
|
||||
|
|
@ -952,8 +959,8 @@ void MainContentComponent::paint(juce::Graphics& g) {
|
|||
drawSubSection(560, 658, 530, 150, "LFO 2", 4);
|
||||
|
||||
// FX2 sub-sections
|
||||
drawSubSection(1100, 658, 265, 150, "DELAY", 4);
|
||||
drawSubSection(1375, 658, 275, 150, "REVERB", 4);
|
||||
drawSubSection(1015, 658, 375, 150, "DELAY", 4);
|
||||
drawSubSection(1395, 658, 255, 150, "REVERB", 4);
|
||||
}
|
||||
|
||||
void MainContentComponent::resized() {
|
||||
|
|
@ -1076,39 +1083,40 @@ void MainContentComponent::resized() {
|
|||
int lfoKnobY = 658 + 4 + 14 + 6; // subSectionY + titlePad + titleH + bottomPad
|
||||
|
||||
lfoLabel.setBounds(10, lfoY, 1080, fxLabelH);
|
||||
fx2Label.setBounds(1100, lfoY, 540, fxLabelH);
|
||||
fx2Label.setBounds(1015, lfoY, 635, fxLabelH);
|
||||
|
||||
// LFO 1 sub-section (X=15, Y=642, W=530, H=110)
|
||||
lfo1RateKnob.setBounds(22, lfoKnobY, lfoKnobSize, lfoKnobSize);
|
||||
lfo1DepthKnob.setBounds(132, lfoKnobY, lfoKnobSize, lfoKnobSize);
|
||||
lfo1ShapeBox.setBounds(242, lfoKnobY + (lfoKnobSize - 28) / 2, 130, 28);
|
||||
lfo1DestBox.setBounds(382, lfoKnobY + (lfoKnobSize - 28) / 2, 155, 28);
|
||||
lfo1ShapeBox.setBounds(242, lfoKnobY + (lfoKnobSize - 28) / 2, 105, 28);
|
||||
lfo1DestBox.setBounds(357, lfoKnobY + (lfoKnobSize - 28) / 2, 105, 28);
|
||||
lfo1SyncBox.setBounds(22, lfoKnobY + lfoKnobSize + 6, 200, 28);
|
||||
|
||||
// LFO 2 sub-section (X=560, Y=642, W=530, H=110)
|
||||
lfo2RateKnob.setBounds(568, lfoKnobY, lfoKnobSize, lfoKnobSize);
|
||||
lfo2DepthKnob.setBounds(678, lfoKnobY, lfoKnobSize, lfoKnobSize);
|
||||
lfo2ShapeBox.setBounds(788, lfoKnobY + (lfoKnobSize - 28) / 2, 130, 28);
|
||||
lfo2DestBox.setBounds(928, lfoKnobY + (lfoKnobSize - 28) / 2, 155, 28);
|
||||
lfo2ShapeBox.setBounds(788, lfoKnobY + (lfoKnobSize - 28) / 2, 105, 28);
|
||||
lfo2DestBox.setBounds(903, lfoKnobY + (lfoKnobSize - 28) / 2, 105, 28);
|
||||
lfo2SyncBox.setBounds(568, lfoKnobY + lfoKnobSize + 6, 200, 28);
|
||||
|
||||
// --- FX2 SECTION ---
|
||||
int fx2KnobY = lfoKnobY;
|
||||
|
||||
// Delay sub-section (X=1100, Y=690, W=265, H=130)
|
||||
// Row 1: TIME, FBACK, MIX
|
||||
delayTimeKnob.setBounds(1110, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayFbKnob.setBounds(1198, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayMixKnob.setBounds(1286, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delaySyncBox.setBounds(1110, fx2KnobY + lfoKnobSize + 6, 240, 28);
|
||||
// Delay sub-section: TIME, FBACK, MIX, PING + sync combo
|
||||
int dx = 1020;
|
||||
int dGap = lfoKnobSize + 8;
|
||||
delayTimeKnob.setBounds(dx, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayFbKnob.setBounds(dx + dGap, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayMixKnob.setBounds(dx + dGap * 2, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delayPingPongKnob.setBounds(dx + dGap * 3, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
delaySyncBox.setBounds(dx, fx2KnobY + lfoKnobSize + 6, 250, 28);
|
||||
|
||||
// Reverb sub-section (X=1375, Y=690, W=265, H=130)
|
||||
// Reverb sub-section: SIZE, DAMP, MIX (right edge aligned to 1650)
|
||||
{
|
||||
int rowW = 3 * lfoKnobSize + 2 * 8;
|
||||
int rx = 1650 - rowW;
|
||||
int rx = 1402;
|
||||
reverbSizeKnob.setBounds(rx, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
reverbDampKnob.setBounds(rx + (lfoKnobSize + 8), fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
reverbMixKnob.setBounds(rx + (lfoKnobSize + 8) * 2, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
reverbDampKnob.setBounds(rx + (lfoKnobSize + 4), fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
reverbMixKnob.setBounds(rx + (lfoKnobSize + 4) * 2, fx2KnobY, lfoKnobSize, lfoKnobSize);
|
||||
}
|
||||
|
||||
// --- PRESET + SCALE (vertically centered within top section, y: 0..128) ---
|
||||
|
|
@ -1134,8 +1142,8 @@ void MainContentComponent::resized() {
|
|||
semitoneTransposeLabel.setBounds(1214, labelY2, 32, labelH2);
|
||||
semitoneTransposeBox.setBounds(1246, comboY2, 74, comboH2);
|
||||
|
||||
scaleLabel.setBounds(1520, labelY2, 42, labelH2);
|
||||
uiScaleBox.setBounds(1566, comboY2, 80, comboH2);
|
||||
scaleLabel.setBounds(1514, labelY2, 42, labelH2);
|
||||
uiScaleBox.setBounds(1560, comboY2, 80, comboH2);
|
||||
|
||||
// Visualizer area
|
||||
int vizY = 818;
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ private:
|
|||
juce::ComboBox lfo2ShapeBox, lfo2DestBox, lfo2SyncBox;
|
||||
|
||||
// FX2 controls
|
||||
juce::Slider delayTimeKnob, delayFbKnob, delayMixKnob;
|
||||
juce::Slider delayTimeKnob, delayFbKnob, delayMixKnob, delayPingPongKnob;
|
||||
juce::ComboBox delaySyncBox;
|
||||
juce::Slider reverbSizeKnob, reverbDampKnob, reverbMixKnob;
|
||||
|
||||
|
|
@ -202,6 +202,7 @@ private:
|
|||
// FX2 attachments
|
||||
std::unique_ptr<SliderAttachment> delayTimeAttach, delayFbAttach, delayMixAttach, delayBeatAttach;
|
||||
std::unique_ptr<ComboBoxAttachment> delaySyncAttach;
|
||||
std::unique_ptr<SliderAttachment> delayPingPongAttach;
|
||||
std::unique_ptr<SliderAttachment> reverbSizeAttach, reverbDampAttach, reverbMixAttach;
|
||||
|
||||
void setupLabel(juce::Label& label, const juce::String& text);
|
||||
|
|
|
|||
|
|
@ -257,6 +257,9 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
|
|||
juce::NormalisableRange<float>(0.0f, static_cast<float>(numBeats - 1), 1.0f), 4.0f,
|
||||
juce::AudioParameterFloatAttributes()
|
||||
.withStringFromValueFunction(beatValueToText)));
|
||||
layout.add(std::make_unique<juce::AudioParameterChoice>(
|
||||
juce::ParameterID{"delayPingPong", 1}, "Delay Ping-Pong",
|
||||
juce::StringArray{"Off", "On"}, 0));
|
||||
|
||||
// REVERB
|
||||
layout.add(std::make_unique<juce::AudioParameterFloat>(
|
||||
|
|
@ -420,7 +423,8 @@ void ChromaFlockProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::
|
|||
int bi = juce::jlimit(0, numBeats - 1, juce::roundToInt(getParam("delayBeat")));
|
||||
delayTimeMs = beatValues[bi] * 60000.0f / static_cast<float>(currentBpm);
|
||||
}
|
||||
delay.setParameters(delayTimeMs, getParam("delayFeedback"), getParam("delayMix"));
|
||||
delay.setParameters(delayTimeMs, getParam("delayFeedback"), getParam("delayMix"),
|
||||
getParam("delayPingPong") > 0.5f);
|
||||
reverbFX.setParameters(getParam("reverbSize"), getParam("reverbDamping"), 0.8f, getParam("reverbMix"));
|
||||
limiter.setParameters(getParam("limiterThreshold"), getParam("limiterCeiling"),
|
||||
getParam("limiterRelease"));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue