Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 4 additions & 55 deletions Source/PluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,13 @@ ConekoAudioProcessorEditor::ConekoAudioProcessorEditor(ConekoAudioProcessor &p)
// set AudioFormatManager for reading IR file
formatManager.registerBasicFormats();

const auto sliderStyle = juce::Slider::RotaryHorizontalVerticalDrag;
const auto sliderLabelJustification = juce::Justification::centred;

addAndMakeVisible(openIRFileButton);
openIRFileButton.setButtonText("Open IR File...");
openIRFileButton.onClick = [this] { openButtonClicked(); };
addAndMakeVisible(irFileLabel);
irFileLabel.setText("", juce::dontSendNotification);
irFileLabel.setJustificationType(juce::Justification::centredLeft);

addAndMakeVisible(reverseButton);
reverseButton.setButtonText("Reverse IR");
reverseButton.setEnabled(enableIRParameters);
reverseButton.onClick = [this] {
audioProcessor.updateIRParameters();
shouldPaintWaveform = true;
repaint();
};
reverseButtonAttachment = std::make_unique<APVTS::ButtonAttachment>(
audioProcessor.apvts, "Reversed", reverseButton);

addAndMakeVisible(bypassButton);
bypassButton.setButtonText("Bypass");
bypassButtonAttachment = std::make_unique<APVTS::ButtonAttachment>(
Expand All @@ -61,32 +47,6 @@ ConekoAudioProcessorEditor::ConekoAudioProcessorEditor(ConekoAudioProcessor &p)
dryWetMixSliderAttachment = std::make_unique<APVTS::SliderAttachment>(
audioProcessor.apvts, "DryWetMix", dryWetMixSlider);

createSlider(decayTimeSlider, " s");
decayTimeSlider.setEnabled(enableIRParameters);
decayTimeSlider.onDragEnd = [this] {
audioProcessor.updateIRParameters();
shouldPaintWaveform = true;
repaint();
};
createLabel(decayTimeLabel, "Decay", &decayTimeSlider);
decayTimeSliderAttachment = std::make_unique<APVTS::SliderAttachment>(
audioProcessor.apvts, "DecayTime", decayTimeSlider);

createSlider(preDelayTimeSlider, " ms");
// preDelayTimeSlider.onDragEnd = [this] {
// audioProcessor.updateIRParameters();
// shouldPaintWaveform = true;
// repaint();
//};
createLabel(preDelayTimeLabel, "Pre-delay", &preDelayTimeSlider);
preDelayTimeSliderAttachment = std::make_unique<APVTS::SliderAttachment>(
audioProcessor.apvts, "PreDelayTime", preDelayTimeSlider);

createSlider(stereoWidthSlider, " %");
createLabel(stereoWidthLabel, "Width", &stereoWidthSlider);
stereoWidthSliderAttachment = std::make_unique<APVTS::SliderAttachment>(
audioProcessor.apvts, "StereoWidth", stereoWidthSlider);

createSlider(lowShelfFreqSlider, " Hz");
createLabel(lowShelfFreqLabel, "LowFreq", &lowShelfFreqSlider);
lowShelfFreqSliderAttachment = std::make_unique<APVTS::SliderAttachment>(
Expand Down Expand Up @@ -173,8 +133,6 @@ void ConekoAudioProcessorEditor::resized() {
40);
irFileLabel.setBounds(leftRightMargin, topBottomMargin + 45, dialWidth * 3,
20);
reverseButton.setBounds(leftRightMargin + dialWidth * 2, topBottomMargin + 40,
dialWidth, 30);
bypassButton.setBounds(getWidth() - leftRightMargin - dialWidth * 3,
topBottomMargin, dialWidth, 20);
inputGainSlider.setBounds(leftRightMargin,
Expand All @@ -186,15 +144,6 @@ void ConekoAudioProcessorEditor::resized() {
dryWetMixSlider.setBounds(leftRightMargin + dialWidth * 2,
getHeight() - topBottomMargin - dialHeight,
dialWidth, dialHeight);
decayTimeSlider.setBounds(leftRightMargin + dialWidth * 3,
getHeight() - topBottomMargin - dialHeight * 3 + 30,
dialWidth * 3, dialHeight * 3 - 30);
preDelayTimeSlider.setBounds(getWidth() - leftRightMargin - dialWidth * 3,
topBottomMargin + dialHeight / 3 * 2, dialWidth,
dialHeight);
stereoWidthSlider.setBounds(getWidth() - leftRightMargin - dialWidth * 3,
getHeight() - topBottomMargin - dialHeight,
dialWidth, dialHeight);
lowShelfFreqSlider.setBounds(getWidth() - leftRightMargin - dialWidth * 2,
topBottomMargin + dialHeight / 3 * 2, dialWidth,
dialHeight);
Expand All @@ -211,8 +160,8 @@ void ConekoAudioProcessorEditor::resized() {

void ConekoAudioProcessorEditor::openButtonClicked() {
fileChooser = std::make_unique<juce::FileChooser>(
"Choose a support IR File (WAV, AIFF, OGG)...", juce::File(),
"*.wav;*.aif;*.aiff;*.ogg", true, false);
"Choose a support IR File (WAV, AIFF, OGG, FLAC)...", juce::File(),
"*.wav;*.aif;*.aiff;*.ogg;*.flac", true, false);
auto chooserFlags = juce::FileBrowserComponent::openMode |
juce::FileBrowserComponent::canSelectFiles;
fileChooser->launchAsync(chooserFlags, [this](const juce::FileChooser &fc) {
Expand All @@ -233,8 +182,8 @@ void ConekoAudioProcessorEditor::openButtonClicked() {

shouldPaintWaveform = true;
enableIRParameters = true;
reverseButton.setEnabled(enableIRParameters);
decayTimeSlider.setEnabled(enableIRParameters);
// reverseButton.setEnabled(enableIRParameters);
// decayTimeSlider.setEnabled(enableIRParameters);
repaint();
}
}
Expand Down
11 changes: 0 additions & 11 deletions Source/PluginEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ class ConekoAudioProcessorEditor : public juce::AudioProcessorEditor {

juce::TextButton openIRFileButton;
juce::Label irFileLabel;
juce::ToggleButton reverseButton;
std::unique_ptr<APVTS::ButtonAttachment> reverseButtonAttachment;
juce::ToggleButton bypassButton;
std::unique_ptr<APVTS::ButtonAttachment> bypassButtonAttachment;
juce::Slider inputGainSlider;
Expand All @@ -55,15 +53,6 @@ class ConekoAudioProcessorEditor : public juce::AudioProcessorEditor {
juce::Slider dryWetMixSlider;
juce::Label dryWetMixLabel;
std::unique_ptr<APVTS::SliderAttachment> dryWetMixSliderAttachment;
juce::Slider decayTimeSlider;
juce::Label decayTimeLabel;
std::unique_ptr<APVTS::SliderAttachment> decayTimeSliderAttachment;
juce::Slider preDelayTimeSlider;
juce::Label preDelayTimeLabel;
std::unique_ptr<APVTS::SliderAttachment> preDelayTimeSliderAttachment;
juce::Slider stereoWidthSlider;
juce::Label stereoWidthLabel;
std::unique_ptr<APVTS::SliderAttachment> stereoWidthSliderAttachment;
juce::Slider lowShelfFreqSlider;
juce::Label lowShelfFreqLabel;
std::unique_ptr<APVTS::SliderAttachment> lowShelfFreqSliderAttachment;
Expand Down
146 changes: 16 additions & 130 deletions Source/PluginProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ int ConekoAudioProcessor::getNumPrograms() {

int ConekoAudioProcessor::getCurrentProgram() { return 0; }

void ConekoAudioProcessor::setCurrentProgram(int index) {}
void ConekoAudioProcessor::setCurrentProgram(int /*index*/) {}

const juce::String ConekoAudioProcessor::getProgramName(int index) {
const juce::String ConekoAudioProcessor::getProgramName(int /*oindex*/) {
return {};
}

void ConekoAudioProcessor::changeProgramName(int index,
const juce::String &newName) {}
void ConekoAudioProcessor::changeProgramName(int /*index*/,
const juce::String &/*newName*/) { }

//==============================================================================
void ConekoAudioProcessor::prepareToPlay(double sampleRate,
Expand All @@ -89,18 +89,12 @@ void ConekoAudioProcessor::prepareToPlay(double sampleRate,
spec.numChannels = getTotalNumOutputChannels();
spec.maximumBlockSize = samplesPerBlock;

soundtouch.setSampleRate(sampleRate);
soundtouch.setChannels(1);

inputGainer.prepare(spec);
inputGainer.reset();
outputGainer.prepare(spec);
outputGainer.reset();
dryWetMixer.prepare(spec);
dryWetMixer.reset();
delay.prepare(spec);
delay.setMaximumDelayInSamples(sampleRate);
delay.reset();
convolver.prepare(spec);
convolver.reset();

Expand Down Expand Up @@ -142,7 +136,7 @@ bool ConekoAudioProcessor::isBusesLayoutSupported(
#endif

void ConekoAudioProcessor::processBlock(juce::AudioBuffer<float> &buffer,
juce::MidiBuffer &midiMessages) {
juce::MidiBuffer &/*midiMessages*/) {
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
Expand All @@ -165,35 +159,19 @@ void ConekoAudioProcessor::processBlock(juce::AudioBuffer<float> &buffer,
auto isBypassed = apvts.getRawParameterValue("Bypassed");
updateFilterParameters();

if (isBypassed->load() == true) {
if (isBypassed->load() != 0.0) {
return;
}

inputGainer.setGainDecibels(inputGainValue->load());
outputGainer.setGainDecibels(outputGainValue->load());
dryWetMixer.setWetMixProportion(dryWetMixValue->load() / 100.0f);
delay.setDelay(preDelayTimeValue->load() / 1000.0 * this->getSampleRate());

auto block = juce::dsp::AudioBlock<float>(buffer);
auto context = juce::dsp::ProcessContextReplacing<float>(block);
inputGainer.process(context);
dryWetMixer.pushDrySamples(block);
convolver.process(context);
delay.process(context);

// set stereo width using mid/side technique
if (context.getInputBlock().getNumChannels() == 2) {
const float width = stereoWidthValue->load() / 100.0;
for (int sample = 0; sample < context.getInputBlock().getNumSamples();
++sample) {
float left = context.getInputBlock().getSample(0, sample);
float right = context.getInputBlock().getSample(1, sample);
context.getOutputBlock().setSample(
0, sample, left * (1 + width) / 2 + right * (1 - width) / 2);
context.getOutputBlock().setSample(
1, sample, left * (1 - width) / 2 + right * (1 + width) / 2);
}
}

lowShelfFilter.process(context);
highShelfFilter.process(context);
Expand All @@ -212,14 +190,14 @@ juce::AudioProcessorEditor *ConekoAudioProcessor::createEditor() {
}

//==============================================================================
void ConekoAudioProcessor::getStateInformation(juce::MemoryBlock &destData) {
void ConekoAudioProcessor::getStateInformation(juce::MemoryBlock &/*destData*/) {
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
}

void ConekoAudioProcessor::setStateInformation(const void *data,
int sizeInBytes) {
void ConekoAudioProcessor::setStateInformation(const void */*data*/,
int /*sizeInBytes*/) {
// You should use this method to restore your parameters from this memory
// block, whose contents will have been created by the getStateInformation()
// call.
Expand All @@ -246,123 +224,31 @@ void ConekoAudioProcessor::loadImpulseResponse() {
// normalized IR signal
float globalMaxMagnitude =
originalIRBuffer.getMagnitude(0, originalIRBuffer.getNumSamples());
originalIRBuffer.applyGain(1.0f / (globalMaxMagnitude + 0.01));

// trim IR signal
int numSamples = originalIRBuffer.getNumSamples();
int blockSize = static_cast<int>(std::floor(this->getSampleRate()) / 100);
int startBlockNum = 0;
int endBlockNum = numSamples / blockSize;
float localMaxMagnitude = 0.0f;
while ((startBlockNum + 1) * blockSize < numSamples) {
localMaxMagnitude =
originalIRBuffer.getMagnitude(startBlockNum * blockSize, blockSize);
// find the start position of IR
if (localMaxMagnitude > 0.001) {
break;
}
++startBlockNum;
}
localMaxMagnitude = 0.0f;
while ((endBlockNum - 1) * blockSize > 0) {
--endBlockNum;
localMaxMagnitude =
originalIRBuffer.getMagnitude(endBlockNum * blockSize, blockSize);
// find the time to decay by 60 dB (T60)
if (localMaxMagnitude > 0.001) {
break;
}
}

int trimmedNumSamples;
if (endBlockNum * blockSize < numSamples) {
trimmedNumSamples = (endBlockNum - startBlockNum) * blockSize - 1;
} else {
trimmedNumSamples = numSamples - startBlockNum * blockSize;
}
modifiedIRBuffer.setSize(originalIRBuffer.getNumChannels(), trimmedNumSamples,
false, true, false);
for (int channel = 0; channel < originalIRBuffer.getNumChannels();
++channel) {
for (int sample = 0; sample < trimmedNumSamples; ++sample) {
modifiedIRBuffer.setSample(
channel, sample,
originalIRBuffer.getSample(channel,
sample + startBlockNum * blockSize));
}
}
originalIRBuffer.applyGain(1.0f / (globalMaxMagnitude + 0.01f));

originalIRBuffer.makeCopyOf(modifiedIRBuffer);

auto decayTimeParam = apvts.getParameter("DecayTime");
double decayTime =
static_cast<double>(trimmedNumSamples) / this->getSampleRate();
decayTimeParam->beginChangeGesture();
decayTimeParam->setValueNotifyingHost(
decayTimeParam->convertTo0to1(decayTime));
decayTimeParam->endChangeGesture();

updateImpulseResponse(modifiedIRBuffer);
updateImpulseResponse(originalIRBuffer);
}

void ConekoAudioProcessor::updateImpulseResponse(
juce::AudioBuffer<float> irBuffer) {
convolver.loadImpulseResponse(std::move(irBuffer), this->getSampleRate(),
juce::dsp::Convolution::Stereo::yes,
juce::dsp::Convolution::Trim::no,
juce::dsp::Convolution::Trim::yes,
juce::dsp::Convolution::Normalise::yes);
}

// TODO: remove this since there aren't any more IR parameters?
void ConekoAudioProcessor::updateIRParameters() {
if (originalIRBuffer.getNumSamples() < 1) {
return;
}

// stretch IR according to decay time
auto decayTimeValue = apvts.getRawParameterValue("DecayTime");
int decaySample = static_cast<int>(
std::round(decayTimeValue->load() * this->getSampleRate()));
double stretchRatio =
originalIRBuffer.getNumSamples() / static_cast<double>(decaySample);

int numChannels = originalIRBuffer.getNumChannels();
soundtouch.setTempo(stretchRatio);
modifiedIRBuffer.setSize(numChannels, decaySample, false, true, false);
for (int channel = 0; channel < numChannels; ++channel) {
soundtouch.putSamples(originalIRBuffer.getReadPointer(channel),
originalIRBuffer.getNumSamples());
soundtouch.receiveSamples(modifiedIRBuffer.getWritePointer(channel),
decaySample);
soundtouch.clear();
}

// delay IR according to pre-delay time
// auto preDelayTimeValue = apvts.getRawParameterValue("PreDelayTime");
// int preDelaySample = static_cast<int>(std::round(preDelayTimeValue->load())
// /
// 1000 * this->getSampleRate());
// juce::AudioBuffer<float> tempBuffer(modifiedIRBuffer);
// modifiedIRBuffer.setSize(numChannels,
// preDelaySample + tempBuffer.getNumSamples(), false,
// false, false);
// modifiedIRBuffer.clear();
// for (int channel = 0; channel < numChannels; ++channel) {
// modifiedIRBuffer.copyFrom(channel, preDelaySample,
// tempBuffer.getReadPointer(channel),
// tempBuffer.getNumSamples());
//}

// reverse of the IR
auto isReversed = apvts.getRawParameterValue("Reversed");
if (isReversed->load() == true) {
modifiedIRBuffer.reverse(0, modifiedIRBuffer.getNumSamples());
}

updateImpulseResponse(modifiedIRBuffer);
updateImpulseResponse(originalIRBuffer);
}


void ConekoAudioProcessor::updateFilterParameters() {
const float sampleRate = this->getSampleRate();
const float sampleRate = static_cast<float>(this->getSampleRate());

auto lowShelfFreqValue = apvts.getRawParameterValue("LowShelfFreq");
auto lowShelfGainValue = apvts.getRawParameterValue("LowShelfGain");
Expand Down
5 changes: 2 additions & 3 deletions Source/PluginProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#pragma once

#include "../soundtouch/SoundTouch.h"
#include <JuceHeader.h>

//==============================================================================
Expand Down Expand Up @@ -73,14 +72,14 @@ class ConekoAudioProcessor : public juce::AudioProcessor {
juce::AudioBuffer<float> originalIRBuffer;
juce::AudioBuffer<float> modifiedIRBuffer;

soundtouch::SoundTouch soundtouch;
// soundtouch::SoundTouch soundtouch;

APVTS::ParameterLayout createParameters();

juce::dsp::Gain<float> inputGainer;
juce::dsp::Gain<float> outputGainer;
juce::dsp::DryWetMixer<float> dryWetMixer;
juce::dsp::DelayLine<float> delay;
// juce::dsp::DelayLine<float> delay;
juce::dsp::Convolution convolver;
juce::dsp::ProcessorDuplicator<juce::dsp::IIR::Filter<float>,
juce::dsp::IIR::Coefficients<float>>
Expand Down
Loading