Skip to content

Commit 8d78795

Browse files
committed
Added template files for chorus
1 parent 93fa5aa commit 8d78795

File tree

7 files changed

+332
-0
lines changed

7 files changed

+332
-0
lines changed

plugins/Chorus/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Plugin-specific CMake for Chorus plugin
2+
add_plugin(Chorus
3+
VERSION 0.1.0
4+
PLUGIN_NAME "Chorus"
5+
PROD_NAME "Chorus"
6+
PROD_CODE CHOR
7+
SYNTH FALSE
8+
9+
SOURCES
10+
PluginProcessor.cpp
11+
PluginProcessor.h
12+
PluginEditor.cpp
13+
PluginEditor.h
14+
15+
INCLUDE_DIRS
16+
${CMAKE_CURRENT_SOURCE_DIR}/../common_includes # if any shared headers exist, otherwise omit
17+
RESOURCES
18+
logos/Jonssonic_logo.png
19+
knobs/JonssonicRotarySlider.png
20+
)

plugins/Chorus/Params.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//==============================================================================
2+
// Jonssonic Chorus Plugin Parameters
3+
//==============================================================================
4+
5+
#pragma once
6+
7+
#include <parameters/ParameterGroup.h>
8+
#include <parameters/ParameterSet.h>
9+
#include <parameters/ParameterTypes.h>
10+
11+
struct ChorusParams {
12+
13+
// Parameter IDs as enum
14+
enum class ID { Mix, Enable, Mode };
15+
16+
// Create parameter definitions
17+
inline jnsc::juce_interface::ParameterSet<ID> createParams() {
18+
using namespace jnsc::juce_interface;
19+
20+
ParameterSet<ID> params;
21+
// clang-format off
22+
// Float parameter ↓ id ↓ name ↓ min ↓ max ↓ def ↓ unit ↓ skew
23+
params.add(FloatParam<ID>{ID::Mix, "Mix", 0.0f, 100.0f, 50.0f, "%", 1.0f});
24+
25+
// Bool parameter ↓ id ↓ name ↓ def ↓ true label ↓ false label
26+
params.add(BoolParam<ID>{ID::Enable, "Enable", false, "On", "Off"});
27+
28+
// Choice parameter ↓ id ↓ name ↓ choices ↓ def idx
29+
params.add(ChoiceParam<ID>{ID::Mode, "Mode", {"Mode1", "Mode2", "Mode3"}, 0});
30+
// clang-format on
31+
return params;
32+
}
33+
};

plugins/Chorus/PluginEditor.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "PluginEditor.h"
2+
#include <gui/CustomLookAndFeel.h>
3+
4+
ChorusAudioProcessorEditor::ChorusAudioProcessorEditor(ChorusAudioProcessor& p)
5+
: AudioProcessorEditor(p), audioProcessor(p), controlPanelConfig([] {
6+
jnsc::juce_interface::ControlPanelConfig c;
7+
c.columns = 3; // Number of columns in the control panel
8+
c.showValueBoxes = true; // Show value boxes for sliders
9+
c.controlHeight = 80; // Height of each control in pixels
10+
c.labelHeight = 20; // Height of labels in pixels
11+
c.spacing = 10; // Spacing between controls in pixels
12+
c.title = "JONSSONIC"; // Plugin title
13+
c.subtitle = "CHORUS"; // Plugin subtitle
14+
// Optionally set other config fields here
15+
return c;
16+
}()),
17+
controlPanel(audioProcessor.getAPVTS(), controlPanelConfig) {
18+
customLookAndFeel = std::make_unique<TemplateLookAndFeel>(&controlPanelConfig);
19+
setLookAndFeel(customLookAndFeel.get());
20+
addAndMakeVisible(controlPanel); // Add and make the control panel visible in the editor
21+
setSize(400, 350); // Set the size of the editor window in pixels
22+
}
23+
24+
ChorusAudioProcessorEditor::~ChorusAudioProcessorEditor() {
25+
setLookAndFeel(nullptr); // Reset the look and feel to default
26+
customLookAndFeel.reset();
27+
}
28+
29+
//==============================================================================
30+
void ChorusAudioProcessorEditor::paint(juce::Graphics& g) {
31+
32+
if (auto* laf = dynamic_cast<TemplateLookAndFeel*>(&getLookAndFeel()))
33+
laf->drawCachedMainBackground(g);
34+
else
35+
g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
36+
}
37+
38+
void ChorusAudioProcessorEditor::resized() {
39+
controlPanel.setBounds(getLocalBounds()); // Make the control panel fill the entire editor area
40+
if (auto* laf = dynamic_cast<TemplateLookAndFeel*>(&getLookAndFeel()))
41+
laf->generateMainBackground(getWidth(), getHeight());
42+
}

plugins/Chorus/PluginEditor.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
#include "Version.h"
3+
4+
#include "PluginLookAndFeel.h"
5+
#include "PluginProcessor.h"
6+
#include <MinimalJuceHeader.h>
7+
#include <gui/ControlPanel.h>
8+
#include <gui/ControlPanelConfig.h>
9+
10+
class ChorusAudioProcessorEditor : public juce::AudioProcessorEditor {
11+
public:
12+
ChorusAudioProcessorEditor(ChorusAudioProcessor&);
13+
~ChorusAudioProcessorEditor() override;
14+
void paint(juce::Graphics&) override;
15+
void resized() override;
16+
17+
private:
18+
// We need a reference to the processor object in order to access its parameters
19+
ChorusAudioProcessor& audioProcessor;
20+
21+
// Configuration structure for the control panel (NOTE: has to be declared before controlPanel)
22+
jnsc::juce_interface::ControlPanelConfig controlPanelConfig;
23+
24+
// Automatic control panel for parameters
25+
jnsc::juce_interface::ControlPanel controlPanel;
26+
27+
std::unique_ptr<TemplateLookAndFeel> customLookAndFeel;
28+
29+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChorusAudioProcessorEditor)
30+
};

plugins/Chorus/PluginLookAndFeel.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include "utils/ResourceUtils.h"
4+
#include <gui/CustomLookAndFeel.h>
5+
6+
/**
7+
* @brief Look and Feel class for the Chorus plugin.
8+
* Inherits from CustomLookAndFeel and customizes the appearance
9+
* of the plugin's user interface elements.
10+
* @note Currently, it overrides the knob strip image with a plugin-specific one if available.
11+
*/
12+
13+
class TemplateLookAndFeel : public jnsc::juce_interface::CustomLookAndFeel {
14+
public:
15+
TemplateLookAndFeel(const jnsc::juce_interface::ControlPanelConfig* config) : CustomLookAndFeel(config) {
16+
// Override the knob strip with the plugin-specific one from the bundle's Resources folder at runtime
17+
juce::File knobFile = jnsc::juce_interface::getResourceFile("knobs/JonssonicRotarySlider_Template.png");
18+
if (knobFile.existsAsFile()) {
19+
setKnobStrip(juce::ImageFileFormat::loadFrom(knobFile));
20+
}
21+
}
22+
};

plugins/Chorus/PluginProcessor.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#include "PluginProcessor.h"
2+
#include "PluginEditor.h"
3+
#include <iostream>
4+
#include <jonssonic/utils/buffer_utils.h>
5+
6+
ChorusAudioProcessor::ChorusAudioProcessor() : parameterManager(ChorusParams().createParams(), *this) {
7+
// ============================================================================
8+
// [DEBUG]: Prints all APVTS parameter IDs at startup
9+
// This helps verify that your parameters are registered correctly.
10+
// You can remove or comment out this block when you no longer need it.
11+
auto& apvts = parameterManager.getAPVTS();
12+
DBG("[DEBUG] APVTS parameter IDs at startup:");
13+
for (auto* param : apvts.processor.getParameters()) {
14+
if (auto* paramWithID = dynamic_cast<juce::AudioProcessorParameterWithID*>(param)) {
15+
DBG(" " + paramWithID->paramID);
16+
}
17+
}
18+
// ============================================================================
19+
20+
// Register callbacks for parameter changes
21+
using ID = ChorusParams::ID;
22+
23+
parameterManager.on(ID::Mix, [this](float value, bool skipSmoothing) {
24+
DBG("[DEBUG] Mix changed: " + juce::String(value) + ", skipSmoothing: " + (skipSmoothing ? "true" : "false"));
25+
// Call your DSP mix setter here
26+
dryWetMixer.setMix(value * 0.01f); // We are converting from [0,100] to [0,1]
27+
});
28+
parameterManager.on(ID::Enable, [this](bool value, bool skipSmoothing) {
29+
DBG("[DEBUG] Enable changed: " + juce::String(value ? "true" : "false") +
30+
", skipSmoothing: " + (skipSmoothing ? "true" : "false"));
31+
// Call your DSP enable setter here
32+
});
33+
parameterManager.on(ID::Mode, [this](int value, bool skipSmoothing) {
34+
DBG("[DEBUG] Mode changed: " + juce::String(value) + ", skipSmoothing: " + (skipSmoothing ? "true" : "false"));
35+
// Call your DSP mode setter here
36+
});
37+
}
38+
39+
ChorusAudioProcessor::~ChorusAudioProcessor() {}
40+
41+
void ChorusAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
42+
auto numChannels = static_cast<size_t>(getTotalNumOutputChannels());
43+
// Prepare all DSP objects and buffers here
44+
dryWetMixer.prepare(numChannels, static_cast<float>(sampleRate));
45+
fxBuffer.resize(numChannels, static_cast<size_t>(samplesPerBlock));
46+
47+
// Initialize DSP with parameter defaults (defined in Params.h) (skip smoothing for instant setup)
48+
parameterManager.syncAll(true);
49+
}
50+
51+
void ChorusAudioProcessor::releaseResources() {
52+
// Release DSP resources here
53+
dryWetMixer.reset();
54+
fxBuffer.resize(0, 0);
55+
}
56+
57+
void ChorusAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) {
58+
// Get audio buffer info
59+
const int numInputChannels = getTotalNumInputChannels();
60+
const int numOutputChannels = getTotalNumOutputChannels();
61+
const int numSamples = buffer.getNumSamples();
62+
63+
// Early return if no audio to process
64+
if (numInputChannels == 0 || numOutputChannels == 0 || numSamples == 0)
65+
return;
66+
// Update all parameters from FIFO (GUI thread → Audio thread)
67+
parameterManager.update();
68+
69+
// Handle denormals
70+
juce::ScopedNoDenormals noDenormals;
71+
72+
// Note: Jonssonic DSP expects numInputChannels == numOutputChannels
73+
// So we map the input channels to output channels accordingly
74+
jnsc::utils::mapChannels<float>(
75+
buffer.getArrayOfReadPointers(), // juce::AudioBuffer<float> uses getArrayOfReadPointers() for const access
76+
fxBuffer.writePtrs(), // Jonssonic::AudioBuffer<float> uses writePtrs() for non-const access
77+
numInputChannels,
78+
numOutputChannels,
79+
numSamples);
80+
81+
// DSP processing here (this template includes only a dry/wet mixer as an example)
82+
dryWetMixer.processBlock(buffer.getArrayOfReadPointers(), // dry buffer
83+
fxBuffer.readPtrs(), // wet buffer
84+
buffer.getArrayOfWritePointers(), // final output
85+
static_cast<size_t>(numSamples)); // number of samples
86+
}
87+
88+
void ChorusAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
89+
// This is taken care of by the parameter manager automatically
90+
parameterManager.saveState(destData);
91+
}
92+
93+
void ChorusAudioProcessor::setStateInformation(const void* data, int sizeInBytes) {
94+
// This is taken care of by the parameter manager automatically
95+
parameterManager.loadState(data, sizeInBytes);
96+
}
97+
98+
bool ChorusAudioProcessor::acceptsMidi() const {
99+
return true;
100+
}
101+
102+
//==============================================================================
103+
const juce::String ChorusAudioProcessor::getName() const {
104+
return JucePlugin_Name;
105+
}
106+
bool ChorusAudioProcessor::producesMidi() const {
107+
return false;
108+
}
109+
bool ChorusAudioProcessor::isMidiEffect() const {
110+
return false;
111+
}
112+
double ChorusAudioProcessor::getTailLengthSeconds() const {
113+
return 0.0;
114+
}
115+
int ChorusAudioProcessor::getNumPrograms() {
116+
return 1;
117+
}
118+
int ChorusAudioProcessor::getCurrentProgram() {
119+
return 0;
120+
}
121+
void ChorusAudioProcessor::setCurrentProgram(int) {}
122+
const juce::String ChorusAudioProcessor::getProgramName(int) {
123+
return {};
124+
}
125+
void ChorusAudioProcessor::changeProgramName(int, const juce::String&) {}
126+
bool ChorusAudioProcessor::hasEditor() const {
127+
return true;
128+
}
129+
juce::AudioProcessorEditor* ChorusAudioProcessor::createEditor() {
130+
return new ChorusAudioProcessorEditor(*this);
131+
}
132+
//==============================================================================
133+
134+
//==============================================================================
135+
// This creates new instances of the plugin..
136+
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
137+
return new ChorusAudioProcessor();
138+
}

plugins/Chorus/PluginProcessor.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#pragma once
2+
3+
#include "Params.h"
4+
#include <MinimalJuceHeader.h>
5+
#include <jonssonic/core/common/audio_buffer.h>
6+
#include <jonssonic/core/mixing/dry_wet_mixer.h>
7+
#include <parameters/ParameterManager.h>
8+
9+
class ChorusAudioProcessor : public juce::AudioProcessor {
10+
public:
11+
ChorusAudioProcessor();
12+
~ChorusAudioProcessor() override;
13+
14+
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
15+
void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
16+
void releaseResources() override;
17+
18+
void getStateInformation(juce::MemoryBlock& destData) override;
19+
void setStateInformation(const void* data, int sizeInBytes) override;
20+
21+
//==============================================================================
22+
juce::AudioProcessorEditor* createEditor() override;
23+
bool hasEditor() const override;
24+
const juce::String getName() const override;
25+
bool acceptsMidi() const override;
26+
bool producesMidi() const override;
27+
bool isMidiEffect() const override;
28+
double getTailLengthSeconds() const override;
29+
int getNumPrograms() override;
30+
int getCurrentProgram() override;
31+
void setCurrentProgram(int) override;
32+
const juce::String getProgramName(int) override;
33+
void changeProgramName(int, const juce::String&) override;
34+
//==============================================================================
35+
36+
// Parameter access for editor
37+
juce::AudioProcessorValueTreeState& getAPVTS() { return parameterManager.getAPVTS(); }
38+
39+
private:
40+
// DSP objects and buffers
41+
jnsc::AudioBuffer<float> fxBuffer; // Buffer for effect processing
42+
jnsc::DryWetMixer<float> dryWetMixer; // Dry/wet mixer
43+
44+
// Parameter manager
45+
jnsc::juce_interface::ParameterManager<ChorusParams::ID> parameterManager;
46+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChorusAudioProcessor)
47+
};

0 commit comments

Comments
 (0)