Initial Commit

This commit is contained in:
Roland Rabien 2020-05-20 16:44:46 -07:00
commit 73110bbde2
20 changed files with 2600 additions and 0 deletions

25
.github/workflows/build_linux.yaml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Build Linux
on: [push]
jobs:
build:
name: Build Linux
runs-on: ubuntu-latest
steps:
- name: Fix up git URLs
run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig
shell: bash
- uses: actions/checkout@v1
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: "Run script"
run: |
export OS="linux"
./ci/build.sh
shell: bash
env:
APIKEY: ${{ secrets.APIKEY }}
- uses: actions/upload-artifact@v2
with:
name: VirtualAnalog_Linux
path: ci/bin/VirtualAnalog_Linux.zip

29
.github/workflows/build_macos.yaml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Build macOS
on: [push]
jobs:
build:
name: Build macOS
runs-on: macos-latest
steps:
- name: Fix up git URLs
run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig
shell: bash
- uses: actions/checkout@v1
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: "Run script"
run: |
export OS="mac"
./ci/build.sh
shell: bash
env:
APPLICATION: ${{ secrets.APPLICATION }}
INSTALLER: ${{ secrets.INSTALLER }}
APPLE_PASS: ${{ secrets.APPLE_PASS }}
APPLE_USER: ${{ secrets.APPLE_USER }}
APIKEY: ${{ secrets.APIKEY }}
- uses: actions/upload-artifact@v2
with:
name: VirtualAnalog_Mac
path: ci/bin/VirtualAnalog_Mac.zip

25
.github/workflows/build_windows.yaml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Build Windows
on: [push]
jobs:
build:
name: Build Windows
runs-on: windows-latest
steps:
- name: Fix up git URLs
run: echo -e '[url "https://github.com/"]\n insteadOf = "git@github.com:"' >> ~/.gitconfig
shell: bash
- uses: actions/checkout@v1
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: "Run script"
run: |
export OS="win"
./ci/build.sh
shell: bash
env:
APIKEY: ${{ secrets.APIKEY }}
- uses: actions/upload-artifact@v2
with:
name: VirtualAnalog_Win
path: ci/bin/VirtualAnalog_Win.zip

35
.gitignore vendored Normal file
View file

@ -0,0 +1,35 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
xcuserdata
Builds
JuceLibraryCode

9
.gitmodules vendored Normal file
View file

@ -0,0 +1,9 @@
[submodule "modules/gin"]
path = modules/gin
url = git@github.com:/FigBug/Gin.git
[submodule "modules/juce"]
path = modules/juce
url = git@github.com:juce-framework/JUCE.git
[submodule "modules/plugin_sdk"]
path = modules/plugin_sdk
url = git@github.com:TurnipHat/plugin_sdk.git

29
LICENSE Normal file
View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, Roland Rabien
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# Wavetable
Wavetable VST / AU plugin
![Build macOS](https://github.com/FigBug/Wavetable/workflows/Build%20macOS/badge.svg)
![Build Windows](https://github.com/FigBug/Wavetable/workflows/Build%20Windows/badge.svg)
![Build Linux](https://github.com/FigBug/Wavetable/workflows/Build%20Linux/badge.svg)
A 4 oscillator subtractive synth with 2 filters and flexible modulation options.
https://socalabs.com/synths/Wavetable/

149
ci/build.sh Executable file
View file

@ -0,0 +1,149 @@
#!/bin/bash -e
PLUGIN="Wavetable"
# linux specific stiff
if [ $OS = "linux" ]; then
export GDK_BACKEND=x11
sudo apt-get update
sudo apt-get install clang git ladspa-sdk freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 juce-tools xvfb
Xvfb :99 &
export DISPLAY=:99
sleep 5
fi
# mac specific stuff
if [ $OS = "mac" ]; then
# Create a temp keychain
if [ -n "$GITHUB_ACTIONS" ]; then
echo "Create a keychain"
security create-keychain -p nr4aGPyz Keys.keychain
echo $APPLICATION | base64 -D -o /tmp/Application.p12
echo $INSTALLER | base64 -D -o /tmp/Installer.p12
security import /tmp/Application.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign
security import /tmp/Installer.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign
security list-keychains -s Keys.keychain
security default-keychain -s Keys.keychain
security unlock-keychain -p nr4aGPyz Keys.keychain
security set-keychain-settings -l -u -t 13600 Keys.keychain
security set-key-partition-list -S apple-tool:,apple: -s -k nr4aGPyz Keys.keychain
fi
DEV_APP_ID="Developer ID Application: Roland Rabien (3FS7DJDG38)"
DEV_INST_ID="Developer ID Installer: Roland Rabien (3FS7DJDG38)"
fi
ROOT=$(cd "$(dirname "$0")/.."; pwd)
cd "$ROOT"
echo "$ROOT"
cd "$ROOT/ci"
rm -Rf bin
mkdir bin
# Get the hash
cd "$ROOT/modules/juce"
HASH=`git rev-parse HEAD`
echo "Hash: $HASH"
# Get the Projucer
cd "$ROOT/ci/bin"
while true
do
PROJUCER_URL=$(curl -s -S "https://projucer.rabien.com/get_projucer.php?hash=$HASH&os=$OS&key=$APIKEY")
echo "Response: $PROJUCER_URL"
if [[ $PROJUCER_URL == http* ]]; then
curl -s -S $PROJUCER_URL -o "$ROOT/ci/bin/Projucer.zip"
unzip Projucer.zip
break
fi
sleep 15
done
# Resave jucer file
if [ "$OS" = "mac" ]; then
"$ROOT/ci/bin/Projucer.app/Contents/MacOS/Projucer" --resave "$ROOT/plugin/$PLUGIN.jucer"
elif [ "$OS" = "linux" ]; then
"$ROOT/ci/bin/Projucer" --resave "$ROOT/plugin/$PLUGIN.jucer"
else
"$ROOT/ci/bin/Projucer.exe" --resave "$ROOT/plugin/$PLUGIN.jucer"
fi
# Build mac version
if [ "$OS" = "mac" ]; then
cd "$ROOT/plugin/Builds/MacOSX"
xcodebuild -configuration Release || exit 1
cp -R ~/Library/Audio/Plug-Ins/VST/$PLUGIN.vst "$ROOT/ci/bin"
cp -R ~/Library/Audio/Plug-Ins/Components/$PLUGIN.component "$ROOT/ci/bin"
cd "$ROOT/ci/bin"
for filename in ./*.vst; do
codesign -s "$DEV_APP_ID" -v "$filename" --options=runtime --timestamp
done
for filename in ./*.component; do
codesign -s "$DEV_APP_ID" -v "$filename" --options=runtime --timestamp
done
# Build notarize tool
cd "$ROOT/modules/gin/tools/notarize"
"$ROOT/ci/bin/Projucer.app/Contents/MacOS/Projucer" --set-global-search-path osx defaultJuceModulePath "$ROOT/modules/juce/modules"
"$ROOT/ci/bin/Projucer.app/Contents/MacOS/Projucer" --resave "notarize.jucer"
cd Builds/MacOSX
xcodebuild -configuration Release || exit 1
cd build/Release
cp notarize "$ROOT/ci/bin"
# Notarize
cd "$ROOT/ci/bin"
zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.component
"$ROOT/ci/bin/notarize" -ns ${PLUGIN}_Mac.zip $APPLE_USER $APPLE_PASS com.figbug.$PLUGIN.vst
rm ${PLUGIN}_Mac.zip
xcrun stapler staple $PLUGIN.vst
xcrun stapler staple $PLUGIN.component
zip -r ${PLUGIN}_Mac.zip $PLUGIN.vst $PLUGIN.component
curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
# Build linux version
if [ "$OS" = "linux" ]; then
cd "$ROOT/plugin/Builds/LinuxMakefile"
make CONFIG=Release
cd "$ROOT/plugin/Builds/LinuxMakefile"
cp ./build/$PLUGIN.so "$ROOT/ci/bin"
cd "$ROOT/ci/bin"
rm -Rf ${PLUGIN}_Linux.zip
zip -r ${PLUGIN}_Linux.zip $PLUGIN.so
curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
# Build Win version
if [ "$OS" = "win" ]; then
VS_WHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe"
MSBUILD_EXE=$("$VS_WHERE" -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe")
echo $MSBUILD_EXE
cd "$ROOT/plugin/Builds/VisualStudio2019"
"$MSBUILD_EXE" "$PLUGIN.sln" "//p:VisualStudioVersion=16.0" "//m" "//t:Build" "//p:Configuration=Release64" "//p:Platform=x64" "//p:PreferredToolArchitecture=x64"
"$MSBUILD_EXE" "$PLUGIN.sln" "//p:VisualStudioVersion=16.0" "//m" "//t:Build" "//p:Configuration=Release" "//p:PlatformTarget=x86" "//p:PreferredToolArchitecture=x64"
cd "$ROOT/ci/bin"
cp "$ROOT/plugin/Builds/VisualStudio2019/x64/Release64/VST/${PLUGIN}.dll" .
cp "$ROOT/plugin/Builds/VisualStudio2019/Win32/Release/VST/${PLUGIN}_32b.dll" .
7z a ${PLUGIN}_Win.zip ${PLUGIN}.dll ${PLUGIN}_32b.dll
curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi

1
modules/gin Submodule

@ -0,0 +1 @@
Subproject commit 2aa9e9fb9208a7e49eef07fe9a65365351b8481a

1
modules/juce Submodule

@ -0,0 +1 @@
Subproject commit 83b1436c6a21f82ffdc4125592836f21dbd7b1e7

1
modules/plugin_sdk Submodule

@ -0,0 +1 @@
Subproject commit bcbef199f4dd78b883aebd6477cc104dad850e1b

420
plugin/Source/Boxes.h Normal file
View file

@ -0,0 +1,420 @@
#pragma once
#include <JuceHeader.h>
#include "PluginProcessor.h"
#include "Cfg.h"
//==============================================================================
class CommonBox : public gin::PagedControlBox
{
public:
CommonBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_)
: gin::PagedControlBox (e), proc (proc_)
{
auto& g = proc.globalParams;
addPage ("Main", 3, 2);
addControl (0, new gin::Knob (g.level), 0, 0);
addControl (0, new gin::Switch (g.mono), 1, 0);
addControl (0, new gin::Select (g.glideMode), 2, 0);
addControl (0, v = new gin::Knob (g.voices), 0, 1);
addControl (0, l = new gin::Switch (g.legato), 1, 1);
addControl (0, s = new gin::Knob (g.glideRate), 2, 1);
watchParam (g.mono);
watchParam (g.glideMode);
addPage ("Control", 1, 2);
addControl (1, new gin::Switch (g.mpe), 0, 0);
}
void paramChanged() override
{
gin::PagedControlBox::paramChanged();
auto& g = proc.globalParams;
v->setEnabled (! g.mono->isOn());
l->setEnabled (g.glideMode->getProcValue() != 0.0f);
s->setEnabled (g.glideMode->getProcValue() != 0.0f);
}
WavetableAudioProcessor& proc;
ParamComponentPtr v, l, s;
};
//==============================================================================
class OscillatorBox : public gin::PagedControlBox
{
public:
OscillatorBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_)
: gin::PagedControlBox (e), proc (proc_)
{
for ( int i = 0; i < numElementsInArray (proc.wtParams); i++)
{
auto& wt = proc.wtParams[i];
addPage ("WT " + String (i + 1), 3, 4);
addPageEnable (i * 2, wt.enable);
addControl (i * 2, new gin::Knob (wt.tune, true), 1, 0);
addControl (i * 2, new gin::Knob (wt.finetune, true), 2, 0);
addControl (i * 2, new gin::Knob (wt.pan, true), 0, 1);
addControl (i * 2, new gin::Knob (wt.level), 1, 1);
addPage ("Unison " + String (i + 1), 2, 4);
addControl (i * 2 + 1, new gin::Knob (wt.voices), 0, 0);
addControl (i * 2 + 1, trans[i] = new gin::Knob (wt.voicesTrns, true), 1, 0);
addControl (i * 2 + 1, detune[i] = new gin::Knob (wt.detune), 0, 1);
addControl (i * 2 + 1, spread[i] = new gin::Knob (wt.spread), 1, 1);
watchParam (wt.voices);
}
for ( int i = 0; i < numElementsInArray (proc.oscParams); i++)
{
auto& osc = proc.oscParams[i];
addPage ("OSC " + String (i + 1), 3, 4);
addPageEnable (i * 2, osc.enable);
addControl (Cfg::numWTs * 2 + i * 2, new gin::Select (osc.wave), 0, 0);
addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.tune, true), 1, 0);
addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.finetune, true), 2, 0);
addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.pan, true), 0, 1);
addControl (Cfg::numWTs * 2 + i * 2, new gin::Knob (osc.level), 1, 1);
addControl (Cfg::numWTs * 2 + i * 2, pw[Cfg::numWTs + i] = new gin::Knob (osc.pulsewidth), 2, 1);
watchParam (osc.wave);
addPage ("Unison " + String (i + 1), 2, 4);
addControl (Cfg::numWTs * 2 + i * 2 + 1, new gin::Knob (osc.voices), 0, 0);
addControl (Cfg::numWTs * 2 + i * 2 + 1, trans[Cfg::numWTs + i] = new gin::Knob (osc.voicesTrns, true), 1, 0);
addControl (Cfg::numWTs * 2 + i * 2 + 1, detune[Cfg::numWTs + i] = new gin::Knob (osc.detune), 0, 1);
addControl (Cfg::numWTs * 2 + i * 2 + 1, spread[Cfg::numWTs + i] = new gin::Knob (osc.spread), 1, 1);
watchParam (osc.voices);
}
setPageOpen (0, true);
setPageOpen (2, true);
setPageOpen (4, true);
}
void paramChanged() override
{
gin::PagedControlBox::paramChanged();
for ( int i = 0; i < numElementsInArray (proc.wtParams); i++)
{
auto& wt = proc.oscParams[i];
trans[i]->setEnabled (wt.voices->getProcValue() > 1);
detune[i]->setEnabled (wt.voices->getProcValue() > 1);
spread[i]->setEnabled (wt.voices->getProcValue() > 1);
}
for ( int i = 0; i < numElementsInArray (proc.oscParams); i++)
{
auto& osc = proc.oscParams[i];
pw[Cfg::numWTs + i]->setEnabled ((gin::Wave) int (osc.wave->getProcValue()) == gin::Wave::pulse);
trans[Cfg::numWTs + i]->setEnabled (osc.voices->getProcValue() > 1);
detune[Cfg::numWTs + i]->setEnabled (osc.voices->getProcValue() > 1);
spread[Cfg::numWTs + i]->setEnabled (osc.voices->getProcValue() > 1);
}
}
WavetableAudioProcessor& proc;
ParamComponentPtr pw[Cfg::numOSCs + Cfg::numWTs], trans[Cfg::numOSCs + Cfg::numWTs],
detune[Cfg::numOSCs + Cfg::numWTs], spread[Cfg::numOSCs + Cfg::numWTs];
};
//==============================================================================
class FilterAmpBox : public gin::PagedControlBox
{
public:
FilterAmpBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_)
: gin::PagedControlBox (e), proc (proc_)
{
for (int i = 0; i < numElementsInArray (proc.filterParams); i++)
{
auto& flt = proc.filterParams[i];
addPage ("Filter " + String (i + 1), 8, 2);
addBottomButton (i, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcFilter[i], true));
addPageEnable (i, flt.enable);
addControl (i, new gin::Select (flt.type), 0, 0);
auto freq = new gin::Knob (flt.frequency);
addControl (i, freq, 1, 0);
addControl (i, new gin::Knob (flt.resonance), 2, 0);
addControl (i, new gin::Knob (flt.keyTracking), 0, 1);
addControl (i, new gin::Knob (flt.amount, true), 1, 1);
addControl (i, v[i] = new gin::Knob (flt.velocityTracking), 2, 1);
adsr[i] = new gin::ADSRComponent ();
adsr[i]->setParams (flt.attack, flt.decay, flt.sustain, flt.release);
addControl (i, adsr[i], 3, 0, 3, 2);
addControl (i, a[i] = new gin::Knob (flt.attack), 6, 0);
addControl (i, d[i] = new gin::Knob (flt.decay), 7, 0);
addControl (i, s[i] = new gin::Knob (flt.sustain), 6, 1);
addControl (i, r[i] = new gin::Knob (flt.release), 7, 1);
watchParam (flt.amount);
freq->setLiveValuesCallback ([this, i] ()
{
if (proc.filterParams[i].amount->getUserValue() != 0.0f ||
proc.filterParams[i].keyTracking->getUserValue() != 0.0f ||
proc.modMatrix.isModulated (gin::ModDstId (proc.filterParams[i].frequency->getModIndex())))
return proc.getLiveFilterCutoff (i);
return Array<float>();
});
int n = numElementsInArray (proc.filterParams);
auto& env = proc.adsrParams;
addPage ("ADSR", 8, 2);
addControl (n, new gin::Knob (env.attack), 0, 0);
addControl (n, new gin::Knob (env.decay), 1, 0);
addControl (n, new gin::Knob (env.sustain), 0, 1);
addControl (n, new gin::Knob (env.release), 1, 1);
addControl (n, new gin::Knob (env.velocityTracking), 2, 0);
auto g = new gin::ADSRComponent ();
g->setParams (env.attack, env.decay, env.sustain, env.release);
addControl (n, g, 3, 0, 3, 2);
}
}
void paramChanged () override
{
gin::PagedControlBox::paramChanged ();
for ( int i = 0; i < numElementsInArray (proc.filterParams); i++)
{
auto& flt = proc.filterParams[i];
v[i]->setEnabled (flt.amount->getUserValue() != 0.0f);
a[i]->setEnabled (flt.amount->getUserValue() != 0.0f);
d[i]->setEnabled (flt.amount->getUserValue() != 0.0f);
s[i]->setEnabled (flt.amount->getUserValue() != 0.0f);
r[i]->setEnabled (flt.amount->getUserValue() != 0.0f);
adsr[i]->setEnabled (flt.amount->getUserValue() != 0.0f);
}
}
WavetableAudioProcessor& proc;
ParamComponentPtr v[Cfg::numFilters], a[Cfg::numFilters], d[Cfg::numFilters], s[Cfg::numFilters], r[Cfg::numFilters];
gin::ADSRComponent* adsr[Cfg::numFilters];
};
//==============================================================================
class ModulationBox : public gin::PagedControlBox
{
public:
ModulationBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_)
: gin::PagedControlBox (e), proc (proc_)
{
int cnt = 0;
for (int i = 0; i < numElementsInArray (proc.lfoParams); i++, cnt++)
{
auto& lfo = proc.lfoParams[i];
addPage ("LFO" + String (i + 1), 5, 2);
addPageEnable (cnt, lfo.enable);
addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcLFO[i], true));
addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcMonoLFO[i], false));
addControl (cnt, new gin::Select (lfo.wave), 0, 0);
addControl (cnt, new gin::Switch (lfo.sync), 1, 0);
addControl (cnt, r[i] = new gin::Knob (lfo.rate), 2, 0);
addControl (cnt, b[i] = new gin::Select (lfo.beat), 2, 0);
addControl (cnt, new gin::Knob (lfo.depth, true), 0, 1);
addControl (cnt, new gin::Knob (lfo.phase, true), 1, 1);
addControl (cnt, new gin::Knob (lfo.offset, true), 2, 1);
addControl (cnt, new gin::Knob (lfo.fade, true), 3, 1);
addControl (cnt, new gin::Knob (lfo.delay), 4, 1);
auto l = new gin::LFOComponent();
l->setParams (lfo.wave, lfo.sync, lfo.rate, lfo.beat, lfo.depth, lfo.offset, lfo.phase, lfo.enable);
addControl (cnt, l, 3, 0, 2, 1);
watchParam (lfo.sync);
}
for (int i = 0; i < numElementsInArray (proc.envParams); i++, cnt++)
{
auto& env = proc.envParams[i];
addPage ("ENV" + String (i + 1), 5, 2);
addPageEnable (cnt, env.enable);
addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcEnv[i], true));
addControl (cnt, new gin::Knob (env.attack), 0, 0);
addControl (cnt, new gin::Knob (env.decay), 1, 0);
addControl (cnt, new gin::Knob (env.sustain), 0, 1);
addControl (cnt, new gin::Knob (env.release), 1, 1);
auto adsr = new gin::ADSRComponent ();
adsr->setParams (env.attack, env.decay, env.sustain, env.release);
addControl (cnt, adsr, 2, 0, 3, 2);
}
auto& stp = proc.stepLfoParams;
addPage ("Step", 6, 2);
addPageEnable (cnt, stp.enable);
addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcStep, true));
addBottomButton (cnt, new gin::ModulationSourceButton (proc.modMatrix, proc.modSrcMonoStep, false));
addControl (cnt, new gin::Select (stp.beat), 0, 0);
addControl (cnt, new gin::Knob (stp.length), 0, 1);
auto s = new gin::StepLFOComponent();
s->setParams (stp.beat, stp.length, stp.level, stp.enable);
addControl (cnt, s, 1, 0, 5, 2);
cnt++;
addPage ("All", 5, 2);
addControl (cnt, new gin::ModSrcListBox (proc.modMatrix), 0, 0, 5, 2);
cnt++;
addPage ("Mod Matrix", 5, 2);
addControl (cnt, new gin::ModMatrixBox (proc, proc.modMatrix), 0, 0, 5, 2);
cnt++;
}
void paramChanged () override
{
gin::PagedControlBox::paramChanged ();
for (int i = 0; i < numElementsInArray (proc.lfoParams); i++)
{
auto& lfo = proc.lfoParams[i];
r[i]->setVisible (! lfo.sync->isOn());
b[i]->setVisible (lfo.sync->isOn());
}
}
WavetableAudioProcessor& proc;
ParamComponentPtr r[Cfg::numLFOs], b[Cfg::numLFOs];
};
//==============================================================================
class EffectsBox : public gin::PagedControlBox
{
public:
EffectsBox (gin::ProcessorEditor& e, WavetableAudioProcessor& proc_)
: gin::PagedControlBox (e), proc (proc_)
{
int idx = 0;
addPage ("Gate", 8, 2);
addPageEnable (idx, proc.gateParams.enable);
addControl (idx, new gin::Select (proc.gateParams.beat), 0, 0);
addControl (idx, new gin::Knob (proc.gateParams.length), 1, 0);
addControl (idx, new gin::Knob (proc.gateParams.attack), 0, 1);
addControl (idx, new gin::Knob (proc.gateParams.release), 1, 1);
auto g = new gin::GateEffectComponent ();
g->setParams (proc.gateParams.length, proc.gateParams.l, proc.gateParams.r, proc.gateParams.enable);
addControl (idx, g, 2, 0, 6, 2);
idx++;
addPage ("Chorus", 3, 2);
addPageEnable (idx, proc.chorusParams.enable);
addControl (idx, new gin::Knob (proc.chorusParams.delay), 0, 0);
addControl (idx, new gin::Knob (proc.chorusParams.rate), 1, 0);
addControl (idx, new gin::Knob (proc.chorusParams.depth), 2, 0);
addControl (idx, new gin::Knob (proc.chorusParams.width), 0, 1);
addControl (idx, new gin::Knob (proc.chorusParams.mix), 1, 1);
idx++;
addPage ("Distortion", 2, 2);
addPageEnable (idx, proc.distortionParams.enable);
addControl (idx, new gin::Knob (proc.distortionParams.amount), 0, 0);
addControl (idx, new gin::Knob (proc.distortionParams.highpass), 1, 0);
addControl (idx, new gin::Knob (proc.distortionParams.output), 0, 1);
addControl (idx, new gin::Knob (proc.distortionParams.mix), 1, 1);
idx++;
addPage ("EQ", 6, 2);
addPageEnable (idx, proc.eqParams.enable);
addControl (idx, new gin::Knob (proc.eqParams.loFreq), 0, 0);
addControl (idx, new gin::Knob (proc.eqParams.loGain), 1, 0);
addControl (idx, new gin::Knob (proc.eqParams.loQ), 2, 0);
addControl (idx, new gin::Knob (proc.eqParams.mid1Freq), 3, 0);
addControl (idx, new gin::Knob (proc.eqParams.mid1Gain), 4, 0);
addControl (idx, new gin::Knob (proc.eqParams.mid1Q), 5, 0);
addControl (idx, new gin::Knob (proc.eqParams.mid2Freq), 0, 1);
addControl (idx, new gin::Knob (proc.eqParams.mid2Gain), 1, 1);
addControl (idx, new gin::Knob (proc.eqParams.mid2Q), 2, 1);
addControl (idx, new gin::Knob (proc.eqParams.hiFreq), 3, 1);
addControl (idx, new gin::Knob (proc.eqParams.hiGain), 4, 1);
addControl (idx, new gin::Knob (proc.eqParams.hiQ), 5, 1);
idx++;
addPage ("Comp", 3, 2);
addPageEnable (idx, proc.compressorParams.enable);
addControl (idx, new gin::Knob (proc.compressorParams.attack), 0, 0);
addControl (idx, new gin::Knob (proc.compressorParams.release), 1, 0);
addControl (idx, new gin::Knob (proc.compressorParams.ratio), 2, 0);
addControl (idx, new gin::Knob (proc.compressorParams.threshold), 0, 1);
addControl (idx, new gin::Knob (proc.compressorParams.gain), 1, 1);
idx++;
addPage ("Delay", 3, 2);
addPageEnable (idx, proc.delayParams.enable);
addControl (idx, new gin::Switch (proc.delayParams.sync), 0, 0);
addControl (idx, t = new gin::Knob (proc.delayParams.time), 1, 0);
addControl (idx, b = new gin::Select (proc.delayParams.beat), 1, 0);
addControl (idx, new gin::Knob (proc.delayParams.fb), 2, 0);
addControl (idx, new gin::Knob (proc.delayParams.cf), 1, 1);
addControl (idx, new gin::Knob (proc.delayParams.mix), 2, 1);
idx++;
watchParam (proc.delayParams.sync);
addPage ("Reverb", 3, 2);
addPageEnable (idx, proc.reverbParams.enable);
addControl (idx, new gin::Knob (proc.reverbParams.damping), 0, 0);
addControl (idx, new gin::Knob (proc.reverbParams.freezeMode), 1, 0);
addControl (idx, new gin::Knob (proc.reverbParams.roomSize), 2, 0);
addControl (idx, new gin::Knob (proc.reverbParams.width), 0, 1);
addControl (idx, new gin::Knob (proc.reverbParams.mix), 1, 1);
idx++;
addPage ("Limiter", 2, 2);
addPageEnable (idx, proc.limiterParams.enable);
addControl (idx, new gin::Knob (proc.limiterParams.attack), 0, 0);
addControl (idx, new gin::Knob (proc.limiterParams.release), 1, 0);
addControl (idx, new gin::Knob (proc.limiterParams.threshold), 0, 1);
addControl (idx, new gin::Knob (proc.limiterParams.gain), 1, 1);
idx++;
addPage ("Scope", 8, 2);
auto scope = new gin::TriggeredScope (proc.fifo);
scope->setNumChannels (2);
scope->setTriggerMode (gin::TriggeredScope::TriggerMode::Up);
scope->setColour (gin::TriggeredScope::lineColourId, Colours::transparentBlack);
addControl (idx, scope, 0, 0, 8, 2);
idx++;
setPageOpen (0, false);
}
void paramChanged () override
{
gin::PagedControlBox::paramChanged ();
t->setVisible (! proc.delayParams.sync->isOn());
b->setVisible (proc.delayParams.sync->isOn());
}
WavetableAudioProcessor& proc;
ParamComponentPtr t, b;
};

10
plugin/Source/Cfg.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
namespace Cfg
{
constexpr static int numWTs = 2;
constexpr static int numOSCs = 1;
constexpr static int numFilters = 1;
constexpr static int numENVs = 3;
constexpr static int numLFOs = 3;
}

View file

@ -0,0 +1,88 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
using namespace gin;
//==============================================================================
WavetableAudioProcessorEditor::WavetableAudioProcessorEditor (WavetableAudioProcessor& p)
: ProcessorEditor (p, 50, 50 + 15), proc (p)
{
oscHeaders.addChildComponent (modOverview);
gin::addAndMakeVisible (*this, { &commonHeader, &common, &unisonHeader });
gin::addAndMakeVisible (*this, { &oscHeaders });
gin::addAndMakeVisible (*this, { &oscillators });
gin::addAndMakeVisible (*this, { &ampFiltersHeader });
gin::addAndMakeVisible (*this, { &ampFilters });
gin::addAndMakeVisible (*this, { &modulationHeader, &modulation });
gin::addAndMakeVisible (*this, { &effectsHeader, &effects });
oscHeaders.addAndMakeVisible ( usage );
setGridSize (14, 8, 0, 3 * 25);
}
WavetableAudioProcessorEditor::~WavetableAudioProcessorEditor()
{
}
//==============================================================================
void WavetableAudioProcessorEditor::paint (Graphics& g)
{
ProcessorEditor::paint (g);
g.setColour (Colours::white.withAlpha (0.2f));
auto rc = getFullGridArea();
g.drawRect (rc.expanded (1));
}
void WavetableAudioProcessorEditor::resized()
{
ProcessorEditor::resized ();
auto rc = getFullGridArea();
int hh = 25;
int gx = getGridWidth();
int gy = getGridHeight();
// Oscillators
{
auto rHeaders = rc.removeFromTop (hh);
oscHeaders.setBounds (rHeaders.removeFromLeft (gx * 14));
auto rOscs = rc.removeFromTop (gy * 4);
oscillators.setBounds (rOscs.removeFromLeft (gx * 14));
modOverview.setBounds (4, 4, 200, hh - 8);
usage.setBounds ( oscHeaders.getLocalBounds().removeFromRight (14 * 5 + 2).withSizeKeepingCentre (14 * 5 + 2, 16));
}
// ADSR and mod
{
auto rHeaders = rc.removeFromTop (hh);
ampFiltersHeader.setBounds (rHeaders.removeFromLeft (gx * 6));
modulationHeader.setBounds (rHeaders.removeFromLeft (gx * 8));
auto rFilters = rc.removeFromTop (gy * 2);
ampFilters.setBounds (rFilters.removeFromLeft (gx * 6));
modulation.setBounds (rFilters.removeFromLeft (gx * 8));
}
// Effects & Common
{
auto rHeaders = rc.removeFromTop (hh);
effectsHeader.setBounds (rHeaders.removeFromLeft (gx * 10));
commonHeader.setBounds (rHeaders.removeFromLeft (gx * 4));
auto rControls = rc.removeFromTop (gy * 2);
effects.setBounds (rControls.removeFromLeft (gx * 10));
common.setBounds (rControls.removeFromLeft (gx * 4));
}
}

View file

@ -0,0 +1,43 @@
#pragma once
#include <JuceHeader.h>
#include "PluginProcessor.h"
#include "Boxes.h"
//==============================================================================
class WavetableAudioProcessorEditor : public gin::ProcessorEditor
{
public:
WavetableAudioProcessorEditor (WavetableAudioProcessor&);
~WavetableAudioProcessorEditor() override;
//==============================================================================
void paint (Graphics&) override;
void resized() override;
private:
WavetableAudioProcessor& proc;
gin::ModulationOverview modOverview { proc.modMatrix };
gin::ControlHeader commonHeader { "Common" };
gin::ControlHeader unisonHeader { "Unison" };
CommonBox common { *this, proc };
gin::ControlHeader oscHeaders = { "Oscillators" };
OscillatorBox oscillators = { *this, proc };
gin::ControlHeader ampFiltersHeader { "Filter / ADSR" };
FilterAmpBox ampFilters { *this, proc };
gin::ControlHeader modulationHeader { "Modulation" };
ModulationBox modulation { *this, proc };
gin::ControlHeader effectsHeader { "Effects" };
EffectsBox effects { *this, proc };
gin::SynthesiserUsage usage { proc };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavetableAudioProcessorEditor)
};

View file

@ -0,0 +1,869 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include "WavetableVoice.h"
using namespace gin;
static String waveTextFunction (const Parameter&, float v)
{
switch ((Wave)int (v))
{
case Wave::sine: return "Sine";
case Wave::triangle: return "Triangle";
case Wave::sawUp: return "Saw (Up)";
case Wave::sawDown: return "Saw (Down)";
case Wave::pulse: return "Pulse";
case Wave::square: return "Square";
case Wave::noise: return "Noise";
default:
jassertfalse;
return {};
}
}
static String lfoTextFunction (const Parameter&, float v)
{
switch ((LFO::WaveShape)int (v))
{
case LFO::WaveShape::none: return "None";
case LFO::WaveShape::sine: return "Sine";
case LFO::WaveShape::triangle: return "Triangle";
case LFO::WaveShape::sawUp: return "Saw Up";
case LFO::WaveShape::sawDown: return "Saw Down";
case LFO::WaveShape::square: return "Square";
case LFO::WaveShape::squarePos: return "Square+";
case LFO::WaveShape::sampleAndHold: return "S&H";
case LFO::WaveShape::noise: return "Noise";
case LFO::WaveShape::stepUp3: return "Step Up 3";
case LFO::WaveShape::stepUp4: return "Step Up 4";
case LFO::WaveShape::stepup8: return "Step Up 8";
case LFO::WaveShape::stepDown3: return "Step Down 3";
case LFO::WaveShape::stepDown4: return "Step Down 4";
case LFO::WaveShape::stepDown8: return "Step Down 8";
case LFO::WaveShape::pyramid3: return "Pyramid 3";
case LFO::WaveShape::pyramid5: return "Pyramid 5";
case LFO::WaveShape::pyramid9: return "Pyramid 9";
default:
jassertfalse;
return {};
}
}
static String enableTextFunction (const Parameter&, float v)
{
return v > 0.0f ? "On" : "Off";
}
static String durationTextFunction (const Parameter&, float v)
{
return NoteDuration::getNoteDurations()[size_t (v)].getName();
}
static String distortionAmountTextFunction (const Parameter&, float v)
{
return String (v * 5.0f - 1.0f, 1);
}
static String filterTextFunction (const Parameter&, float v)
{
switch (int (v))
{
case 0: return "LP 12";
case 1: return "LP 24";
case 2: return "HP 12";
case 3: return "HP 24";
case 4: return "BP 12";
case 5: return "BP 24";
case 6: return "NT 12";
case 7: return "NT 24";
default:
jassertfalse;
return {};
}
}
static String freqTextFunction (const Parameter&, float v)
{
return String (int (getMidiNoteInHertz (v)));
}
static String glideModeTextFunction (const Parameter&, float v)
{
switch (int (v))
{
case 0: return "Off";
case 1: return "Glissando";
case 2: return "Portamento";
default:
jassertfalse;
return {};
}
}
//==============================================================================
void WavetableAudioProcessor::WTParams::setup (WavetableAudioProcessor& p, int idx)
{
String id = "wt" + String (idx + 1);
String nm = "WT" + String (idx + 1) + " ";
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f);
voices = p.addIntParam (id + "unison", nm + "Unison", "Unison", "", { 1.0, 8.0, 1.0, 1.0 }, 1.0, 0.0f);
voicesTrns = p.addExtParam (id + "unisontrns", nm + "Unison Trns", "LTrans", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f);
tune = p.addExtParam (id + "tune", nm + "Tune", "Tune", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f);
finetune = p.addExtParam (id + "finetune", nm + "Fine Tune", "Fine", "ct", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
level = p.addExtParam (id + "level", nm + "Level", "Level", "db", { -100.0, 0.0, 1.0, 4.0 }, 0.0, 0.0f);
detune = p.addExtParam (id + "detune", nm + "Detune", "Detune", "", { 0.0, 0.5, 0.0, 1.0 }, 0.0, 0.0f);
spread = p.addExtParam (id + "spread", nm + "Spread", "Spread", "%", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
level->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
}
//==============================================================================
void WavetableAudioProcessor::OSCParams::setup (WavetableAudioProcessor& p, int idx)
{
String id = "osc" + String (idx + 1);
String nm = "OSC" + String (idx + 1) + " ";
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f);
wave = p.addIntParam (id + "wave", nm + "Wave", "Wave", "", { 1.0, 7.0, 1.0, 1.0 }, 1.0, 0.0f, waveTextFunction);
voices = p.addIntParam (id + "unison", nm + "Unison", "Unison", "", { 1.0, 8.0, 1.0, 1.0 }, 1.0, 0.0f);
voicesTrns = p.addExtParam (id + "unisontrns", nm + "Unison Trns", "LTrans", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f);
tune = p.addExtParam (id + "tune", nm + "Tune", "Tune", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f);
finetune = p.addExtParam (id + "finetune", nm + "Fine Tune", "Fine", "ct", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
level = p.addExtParam (id + "level", nm + "Level", "Level", "db", { -100.0, 0.0, 1.0, 4.0 }, 0.0, 0.0f);
pulsewidth = p.addExtParam (id + "pulsewidth", nm + "Pulse Width", "PW", "%", { 1.0, 99.0, 0.0, 1.0 }, 50.0, 0.0f);
detune = p.addExtParam (id + "detune", nm + "Detune", "Detune", "", { 0.0, 0.5, 0.0, 1.0 }, 0.0, 0.0f);
spread = p.addExtParam (id + "spread", nm + "Spread", "Spread", "%", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
level->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
}
//==============================================================================
void WavetableAudioProcessor::FilterParams::setup (WavetableAudioProcessor& p, int idx)
{
String id = "flt" + String (idx + 1);
String nm = "FLT" + String (idx + 1) + " ";
float maxFreq = float (getMidiNoteFromHertz (20000.0));
enable = p.addIntParam (id + "enable", nm + "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f);
type = p.addIntParam (id + "type", nm + "Type", "Type", "", { 0.0, 7.0, 1.0, 1.0 }, 0.0, 0.0f, filterTextFunction);
keyTracking = p.addExtParam (id + "key", nm + "Key", "Key", "%", { 0.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
velocityTracking = p.addExtParam (id + "vel", nm + "Vel", "Vel", "%", { 0.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
frequency = p.addExtParam (id + "freq", nm + "Freq", "Freq", "Hz", { 0.0, maxFreq, 0.0, 1.0 }, 64.0, 0.0f, freqTextFunction);
resonance = p.addExtParam (id + "res", nm + "Res", "Res", "", { 0.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
amount = p.addExtParam (id + "amount", nm + "Amount", "Amnt", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
decay = p.addExtParam (id + "decay", nm + "Decay", "D", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain = p.addExtParam (id + "sustain", nm + "Sustain", "S", "%", { 0.0, 100.0, 0.0, 1.0 }, 80.0f, 0.0f);
release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain->conversionFunction = [] (float in) { return in / 100.0f; };
velocityTracking->conversionFunction = [] (float in) { return in / 100.0f; };
keyTracking->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::EnvParams::setup (WavetableAudioProcessor& p, int idx)
{
String id = "env" + String (idx + 1);
String nm = "ENV" + String (idx + 1) + " ";
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
decay = p.addExtParam (id + "decay", nm + "Decay", "D", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain = p.addExtParam (id + "sustain", nm + "Sustain", "S", "%", { 0.0, 100.0, 0.0, 1.0 }, 80.0f, 0.0f);
release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::LFOParams::setup (WavetableAudioProcessor& p, int idx)
{
String id = "lfo" + String (idx + 1);
String nm = "LFO" + String (idx + 1) + " ";
auto notes = NoteDuration::getNoteDurations();
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
sync = p.addIntParam (id + "sync", nm + "Sync", "Sync", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
wave = p.addIntParam (id + "wave", nm + "Wave", "Wave", "", { 1.0, 17.0, 1.0, 1.0 }, 1.0, 0.0f, lfoTextFunction);
rate = p.addExtParam (id + "rate", nm + "Rate", "Rate", "Hz", { 0.0, 50.0, 0.0, 0.3f }, 10.0, 0.0f);
beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 13.0, 0.0f, durationTextFunction);
depth = p.addExtParam (id + "depth", nm + "Depth", "Depth", "", { -1.0, 1.0, 0.0, 1.0 }, 1.0, 0.0f);
phase = p.addExtParam (id + "phase", nm + "Phase", "Phase", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
offset = p.addExtParam (id + "offset", nm + "Offset", "Offset", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
fade = p.addExtParam (id + "fade", nm + "Fade", "Fade", "s", { -60.0, 60.0, 0.0, 0.2f, true }, 0.1f, 0.0f);
delay = p.addExtParam (id + "delay", nm + "Delay", "Delay", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
}
//==============================================================================
void WavetableAudioProcessor::StepLFOParams::setup (WavetableAudioProcessor& p)
{
String id = "slfo";
String nm = "Step LFO";
auto notes = NoteDuration::getNoteDurations();
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 13.0, 0.0f, durationTextFunction);
length = p.addIntParam (id + "length", nm + "Length", "Length", "", { 2.0, 32.0, 1.0, 1.0f }, 8.0f, 0.0f);
for (int i = 0; i < 32; i++)
{
auto num = String (i + 1);
level[i] = p.addIntParam (id + "step" + num, nm + "Step " + num, "", "", { -1.0, 1.0, 0.0, 1.0f }, 0.0f, 0.0f);
}
}
//==============================================================================
void WavetableAudioProcessor::GateParams::setup (WavetableAudioProcessor& p)
{
String id = "gate";
String nm = "Gate";
auto notes = NoteDuration::getNoteDurations();
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 7.0, 0.0f, durationTextFunction);
length = p.addIntParam (id + "length", nm + "Length", "Length", "", { 2.0, 32.0, 1.0, 1.0f }, 8.0f, 0.0f);
attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
for (int i = 0; i < 32; i++)
{
auto num = String (i + 1);
l[i] = p.addIntParam (id + "l" + num, nm + "L " + num, "", "", { 0.0, 1.0, 1.0, 1.0f }, (i % 2 == 0 || i % 5 == 0) ? 1.0f : 0.0f, 0.0f);
r[i] = p.addIntParam (id + "r" + num, nm + "R " + num, "", "", { 0.0, 1.0, 1.0, 1.0f }, (i % 2 == 0 || i % 5 == 0) ? 1.0f : 0.0f, 0.0f);
}
}
//==============================================================================
void WavetableAudioProcessor::ADSRParams::setup (WavetableAudioProcessor& p)
{
velocityTracking = p.addExtParam ("vel", "Vel", "Vel", "", { 0.0, 100.0, 0.0, 1.0 }, 100.0, 0.0f);
attack = p.addExtParam ("attack", "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
decay = p.addExtParam ("decay", "Decay", "D", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain = p.addExtParam ("sustain", "Sustain", "S", "%", { 0.0, 100.0, 0.0, 1.0 }, 80.0f, 0.0f);
release = p.addExtParam ("release", "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain->conversionFunction = [] (float in) { return in / 100.0f; };
velocityTracking->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::GlobalParams::setup (WavetableAudioProcessor& p)
{
mono = p.addIntParam ("mono", "Mono", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
glideMode = p.addIntParam ("gMode", "Glide Mode", "Glide", "", { 0.0, 2.0, 0.0, 1.0 }, 0.0f, 0.0f, glideModeTextFunction);
glideRate = p.addExtParam ("gRate", "Glide Rate", "Rate", "s", { 0.001f, 20.0, 0.0, 0.2f }, 0.3f, 0.0f);
legato = p.addIntParam ("legato", "Legato", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
level = p.addExtParam ("level", "Level", "", "db", { -100.0, 0.0, 1.0, 4.0f }, 0.0, 0.0f);
voices = p.addIntParam ("voices", "Voices", "", "", { 2.0, 40.0, 1.0, 1.0 }, 40.0f, 0.0f);
mpe = p.addIntParam ("mpe", "MPE", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
level->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
}
//==============================================================================
void WavetableAudioProcessor::ChorusParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("chEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
delay = p.addExtParam ("chDelay", "Delay", "", "ms", {0.1f, 30.0f, 0.0f, 1.0f}, 1.0f, 0.0f);
depth = p.addExtParam ("chDepth", "Depth", "", "ms", {0.1f, 20.0f, 0.0f, 1.0f}, 1.0f, 0.0f);
rate = p.addExtParam ("chSpeed", "Speed", "", "Hz", {0.1f, 10.0f, 0.0f, 1.0f}, 3.0f, 0.0f);
width = p.addExtParam ("chWidth", "Width", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.5f, 0.0f);
mix = p.addExtParam ("chMix", "Mix", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.5f, 0.0f);
delay->conversionFunction = [] (float in) { return in / 1000.0f; };
depth->conversionFunction = [] (float in) { return in / 1000.0f; };
}
//==============================================================================
void WavetableAudioProcessor::DistortionParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("dsEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
amount = p.addExtParam ("dsAmount", "Amount", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.2f, 0.0f, distortionAmountTextFunction);
highpass = p.addExtParam ("dsHighpass", "Highpass", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
output = p.addExtParam ("dsOutput", "Output", "", "", { 0.0, 1.0, 0.0, 1.0 }, 1.0, 0.0f);
mix = p.addExtParam ("dsMix", "Mix", "", "", { 0.0, 1.0, 0.0, 1.0 }, 1.0, 0.0f);
}
//==============================================================================
void WavetableAudioProcessor::EQParams::setup (WavetableAudioProcessor& p)
{
float maxFreq = float (getMidiNoteFromHertz (20000.0));
float d1 = float (getMidiNoteFromHertz (80.0));
float d2 = float (getMidiNoteFromHertz (3000.0));
float d3 = float (getMidiNoteFromHertz (5000.0));
float d4 = float (getMidiNoteFromHertz (17000.0));
enable = p.addIntParam ("eqEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
loFreq = p.addExtParam ("eqLoFreq", "Lo Freq", "Freq", "Hz", { 0.0, maxFreq, 0.0, 1.0 }, d1, 0.0f, freqTextFunction);
loQ = p.addExtParam ("eqLoQ", "Lo Q", "Q", "", { 0.025f, 40.0f, 0.0, 0.2f }, 1.0f, 0.0f);
loGain = p.addExtParam ("eqLoGain", "Lo Gain", "Gain", "dB", { -20.0f, 20.0f, 0.0, 1.0 }, 0.0f, 0.0f);
mid1Freq = p.addExtParam ("eqM1Freq", "Min 1 Freq", "Freq", "Hz", { 0.0, maxFreq, 0.0, 1.0 }, d2, 0.0f, freqTextFunction);
mid1Q = p.addExtParam ("eqM1Q", "Min 1 Q", "Q", "", { 0.025f, 40.0f, 0.0, 0.2f }, 1.0f, 0.0f);
mid1Gain = p.addExtParam ("eqM1Gain", "Min 1 Gain", "Gain", "dB", { -20.0f, 20.0f, 0.0, 1.0 }, 0.0f, 0.0f);
mid2Freq = p.addExtParam ("eqM2Freq", "Mid 2 Freq", "Freq", "Hz", { 0.0, maxFreq, 0.0, 1.0 }, d3, 0.0f, freqTextFunction);
mid2Q = p.addExtParam ("eqM2Q", "Mid 2 Q", "Q", "", { 0.025f, 40.0f, 0.0, 0.2f }, 1.0f, 0.0f);
mid2Gain = p.addExtParam ("eqM2Gain", "Mid 2 Gain", "Gain", "dB", { -20.0f, 20.0f, 0.0, 1.0 }, 0.0f, 0.0f);
hiFreq = p.addExtParam ("eqHiFreq", "Hi Freq", "Freq", "Hz", { 0.0, maxFreq, 0.0, 1.0 }, d4, 0.0f, freqTextFunction);
hiQ = p.addExtParam ("eqHiQ", "Hi Q", "Q", "", { 0.025f, 40.0f, 0.0, 0.2f }, 1.0f, 0.0f);
hiGain = p.addExtParam ("eqHiGain", "Hi Gain", "Gain", "dB", { -20.0f, 20.0f, 0.0, 1.0 }, 0.0f, 0.0f);
loFreq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); };
mid1Freq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); };
mid2Freq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); };
hiFreq->conversionFunction = [] (float in) { return getMidiNoteInHertz (in); };
loGain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
mid1Gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
mid2Gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
hiGain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
}
//==============================================================================
void WavetableAudioProcessor::CompressorParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("cpEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
attack = p.addExtParam ("cpAttack", "Attack", "", "ms", { 1.0f, 200.0f, 0.0f, 0.4f}, 1.0f, 0.1f);
release = p.addExtParam ("cpRelease", "Release", "", "ms", { 1.0f, 2000.0f, 0.0f, 0.4f}, 5.0f, 0.1f);
ratio = p.addExtParam ("cpRatio", "Ratio", "", "", { 1.0f, 30.0f, 0.0f, 0.4f}, 5.0f, 0.1f);
threshold = p.addExtParam ("cpThreshold", "Thresh", "", "dB", { -60.0f, 0.0f, 0.0f, 1.0f}, -30.0f, 0.1f);
gain = p.addExtParam ("cpGain", "Gain", "", "dB", { -30.0f, 30.0f, 0.0f, 1.0f}, 0.0f, 0.1f);
attack->conversionFunction = [] (float in) { return in / 1000.0f; };
release->conversionFunction = [] (float in) { return in / 1000.0f; };
gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
}
//==============================================================================
void WavetableAudioProcessor::DelayParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("dlEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
float mxd = float (NoteDuration::getNoteDurations().size()) - 1.0f;
sync = p.addExtParam ("dlSync", "Sync", "", "", { 0.0f, 1.0f, 1.0f, 1.0f}, 0.0f, 0.0f, enableTextFunction);
time = p.addExtParam ("dlTime", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 0.3f}, 1.0f, 0.0f);
beat = p.addExtParam ("dlBeat", "Delay", "", "", { 0.0f, mxd, 1.0f, 1.0f}, 13.0f, 0.0f, durationTextFunction);
fb = p.addExtParam ("dlFb", "FB", "", "dB", {-100.0f, 0.0f, 0.0f, 5.0f}, -10.0f, 0.1f);
cf = p.addExtParam ("dlCf", "CF", "", "dB", {-100.0f, 0.0f, 0.0f, 5.0f}, -100.0f, 0.1f);
mix = p.addExtParam ("dlMix", "Mix", "", "%", { 0.0f, 100.0f, 0.0f, 1.0f}, 0.5f, 0.1f);
delay = p.addIntParam ("dlDelay", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 1.0f}, 1.0f, {0.2f, SmoothingType::eased});
fb->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
cf->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
mix->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::ReverbParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("rvEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f);
damping = p.addExtParam ("rvbDamping", "Damping", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
freezeMode = p.addExtParam ("rvbFreeze", "Freeze", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
roomSize = p.addExtParam ("rvbSize", "Size", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
width = p.addExtParam ("rvbWidth", "Width", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
mix = p.addExtParam ("rvbMix", "Mix", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
}
//==============================================================================
void WavetableAudioProcessor::LimiterParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("lmEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f);
attack = p.addExtParam ("lmAttack", "Attack", "", "ms", { 1.0f, 5.0f, 0.0f, 0.4f}, 1.0f, 0.1f);
release = p.addExtParam ("lmRelease", "Release", "", "ms", { 1.0f, 100.0f, 0.0f, 0.4f}, 5.0f, 0.1f);
threshold = p.addExtParam ("lmThreshold", "Ceil", "", "dB", { -60.0f, 0.0f, 0.0f, 1.0f}, -30.0f, 0.1f);
gain = p.addExtParam ("lmGain", "Gain", "", "dB", { -30.0f, 30.0f, 0.0f, 1.0f}, 0.0f, 0.1f);
attack->conversionFunction = [] (float in) { return in / 1000.0f; };
release->conversionFunction = [] (float in) { return in / 1000.0f; };
gain->conversionFunction = [] (float in) { return Decibels::decibelsToGain (in); };
}
//==============================================================================
WavetableAudioProcessor::WavetableAudioProcessor()
{
enableLegacyMode();
setVoiceStealingEnabled (true);
for (int i = 0; i < numElementsInArray (wtParams); i++)
wtParams[i].setup (*this, i);
for (int i = 0; i < numElementsInArray (oscParams); i++)
oscParams[i].setup (*this, i);
for (int i = 0; i < numElementsInArray (filterParams); i++)
filterParams[i].setup (*this, i);
for (int i = 0; i < numElementsInArray (envParams); i++)
envParams[i].setup (*this, i);
for (int i = 0; i < numElementsInArray (lfoParams); i++)
lfoParams[i].setup (*this, i);
stepLfoParams.setup (*this);
adsrParams.setup (*this);
globalParams.setup (*this);
gateParams.setup (*this);
chorusParams.setup (*this);
distortionParams.setup (*this);
eqParams.setup (*this);
compressorParams.setup (*this);
delayParams.setup (*this);
reverbParams.setup (*this);
limiterParams.setup (*this);
eq.setNumChannels (2);
compressor.setNumChannels (2);
limiter.setNumChannels (2);
for (int i = 0; i < 50; i++)
{
auto voice = new WavetableVoice (*this, bandLimitedLookupTables);
modMatrix.addVoice (voice);
addVoice (voice);
}
setupModMatrix();
}
WavetableAudioProcessor::~WavetableAudioProcessor()
{
}
//==============================================================================
void WavetableAudioProcessor::stateUpdated()
{
modMatrix.stateUpdated (state);
}
void WavetableAudioProcessor::updateState()
{
modMatrix.updateState (state);
}
//==============================================================================
void WavetableAudioProcessor::setupModMatrix()
{
modSrcPressure = modMatrix.addPolyModSource ("mpep", "MPE Pressure", false);
modSrcTimbre = modMatrix.addPolyModSource ("mpet", "MPE Timbre", false);
modScrPitchBend = modMatrix.addMonoModSource ("pb", "Pitch Bend", true);
modSrcNote = modMatrix.addPolyModSource ("note", "MIDI Note Number", false);
modSrcVelocity = modMatrix.addPolyModSource ("vel", "MIDI Velocity", false);
for (int i = 0; i <= 119; i++)
{
String name = MidiMessage::getControllerName (i);
if (name.isEmpty())
modSrcCC.add (modMatrix.addMonoModSource (String::formatted ("cc%d", i), String::formatted ("CC %d", i), false));
else
modSrcCC.add (modMatrix.addMonoModSource (String::formatted ("cc%d", i), String::formatted ("CC %d ", i) + name, false));
}
for (int i = 0; i < Cfg::numLFOs; i++)
modSrcMonoLFO.add (modMatrix.addMonoModSource (String::formatted ("mlfo%d", i + 1), String::formatted ("LFO %d (Mono)", i + 1), true));
for (int i = 0; i < Cfg::numLFOs; i++)
modSrcLFO.add (modMatrix.addPolyModSource (String::formatted ("lfo%d", i + 1), String::formatted ("LFO %d", i + 1), true));
modSrcMonoStep = modMatrix.addMonoModSource ("mstep", "Step LFO (Mono)", true);
modSrcStep = modMatrix.addPolyModSource ("step", "Step LFO", true);
for (int i = 0; i < Cfg::numFilters; i++)
modSrcFilter.add (modMatrix.addPolyModSource (String::formatted ("fenv%d", i + 1), String::formatted ("Filter Envelope %d", i + 1), false));
for (int i = 0; i < Cfg::numENVs; i++)
modSrcEnv.add (modMatrix.addPolyModSource (String::formatted ("env%d", i + 1), String::formatted ("Envelope %d", i + 1), false));
auto firstMonoParam = globalParams.mono;
bool polyParam = true;
for (auto pp : getPluginParameters())
{
if (pp == firstMonoParam)
polyParam = false;
if (! pp->isInternal())
{
modMatrix.addParameter (pp, polyParam);
}
}
modMatrix.build();
}
void WavetableAudioProcessor::reset()
{
Processor::reset();
gate.reset();
chorus.reset();
distortion.reset();
stereoDelay.reset();
compressor.reset();
limiter.reset();
eq.reset();
reverb.reset();
for (auto& l : modLFOs)
l.reset();
modStepLFO.reset();
}
void WavetableAudioProcessor::prepareToPlay (double newSampleRate, int newSamplesPerBlock)
{
Processor::prepareToPlay (newSampleRate, newSamplesPerBlock);
bandLimitedLookupTables.setSampleRate (newSampleRate);
setCurrentPlaybackSampleRate (newSampleRate);
modMatrix.setSampleRate (newSampleRate);
gate.setSampleRate (newSampleRate);
chorus.setSampleRate (newSampleRate);
distortion.setSampleRate (newSampleRate);
stereoDelay.setSampleRate (newSampleRate);
compressor.setSampleRate (newSampleRate);
limiter.setSampleRate (newSampleRate);
eq.setSampleRate (newSampleRate);
reverb.setSampleRate (newSampleRate);
for (auto& l : modLFOs)
l.setSampleRate (newSampleRate);
modStepLFO.setSampleRate (newSampleRate);
}
void WavetableAudioProcessor::releaseResources()
{
}
void WavetableAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi)
{
ScopedNoDenormals noDenormals;
startBlock();
setMPE (globalParams.mpe->isOn());
playhead = getPlayHead();
int pos = 0;
int todo = buffer.getNumSamples();
buffer.clear();
setMono (globalParams.mono->isOn());
setLegato (globalParams.legato->isOn());
setGlissando (globalParams.glideMode->getProcValue() == 1.0f);
setPortamento (globalParams.glideMode->getProcValue() == 2.0f);
setGlideRate (globalParams.glideRate->getProcValue());
setNumVoices (int (globalParams.voices->getProcValue()));
while (todo > 0)
{
int thisBlock = std::min (todo, 32);
updateParams (thisBlock);
renderNextBlock (buffer, midi, pos, thisBlock);
auto slice = sliceBuffer (buffer, pos, thisBlock);
applyEffects (slice);
modMatrix.finishBlock (thisBlock);
pos += thisBlock;
todo -= thisBlock;
}
playHead = nullptr;
fifo.write (buffer);
endBlock (buffer.getNumSamples());
}
Array<float> WavetableAudioProcessor::getLiveFilterCutoff (int i)
{
Array<float> values;
for (auto v : voices)
{
if (v->isActive())
{
auto vav = dynamic_cast<WavetableVoice*>(v);
values.add (vav->getFilterCutoffNormalized (i));
}
}
return values;
}
void WavetableAudioProcessor::applyEffects (AudioSampleBuffer& buffer)
{
// Apply gate
if (gateParams.enable->isOn())
gate.process (buffer, noteOnIndex, noteOffIndex);
// Apply Chorus
if (chorusParams.enable->isOn())
chorus.process (buffer);
// Apply Distortion
if (distortionParams.enable->isOn())
distortion.process (buffer);
// Apply EQ
if (eqParams.enable->isOn())
eq.process (buffer);
// Apply Compressor
if (compressorParams.enable->isOn())
compressor.process (buffer);
// Apply Delay
if (delayParams.enable->isOn())
stereoDelay.process (buffer);
// Apply Reverb
if (reverbParams.enable->isOn())
reverb.processStereo (buffer.getWritePointer (0), buffer.getWritePointer (1), buffer.getNumSamples ());
// Apply Limiter
if (limiterParams.enable->isOn())
limiter.process (buffer);
// Output gain
outputGain.process (buffer);
}
void WavetableAudioProcessor::updateParams (int newBlockSize)
{
// Update Mono LFOs
for (int i = 0; i < Cfg::numLFOs; i++)
{
if (lfoParams[i].enable->isOn())
{
LFO::Parameters params;
float freq = 0;
if (lfoParams[i].sync->getProcValue() > 0.0f)
freq = 1.0f / NoteDuration::getNoteDurations()[size_t (lfoParams[i].beat->getProcValue())].toSeconds (playhead);
else
freq = modMatrix.getValue (lfoParams[i].rate);
params.waveShape = (LFO::WaveShape) int (lfoParams[i].wave->getProcValue());
params.frequency = freq;
params.phase = modMatrix.getValue (lfoParams[i].phase);
params.offset = modMatrix.getValue (lfoParams[i].offset);
params.depth = modMatrix.getValue (lfoParams[i].depth);
params.delay = 0;
params.fade = 0;
modLFOs[i].setParameters (params);
modLFOs[i].process (newBlockSize);
modMatrix.setMonoValue (modSrcMonoLFO[i], modLFOs[i].getOutput());
}
else
{
modMatrix.setMonoValue (modSrcMonoLFO[i], 0);
}
}
// Update Mono Step LFO
if (stepLfoParams.enable->isOn())
{
float freq = 1.0f / NoteDuration::getNoteDurations()[size_t (stepLfoParams.beat->getProcValue())].toSeconds (playhead);
modStepLFO.setFreq (freq);
int n = int (stepLfoParams.length->getProcValue());
modStepLFO.setNumPoints (n);
for (int i = n; --i >= 0;)
modStepLFO.setPoint (i, stepLfoParams.level[i]->getProcValue());
modStepLFO.process (newBlockSize);
modMatrix.setMonoValue (modSrcMonoStep, modStepLFO.getOutput());
}
else
{
modMatrix.setMonoValue (modSrcMonoStep, 0);
}
// Update Gate
if (gateParams.enable->isOn())
{
float freq = 1.0f / NoteDuration::getNoteDurations()[size_t (gateParams.beat->getProcValue())].toSeconds (playhead);
int n = int (gateParams.length->getProcValue());
gate.setLength (n);
for (int i = 0; i < n; i++)
gate.setStep (i, gateParams.l[i]->isOn(), gateParams.r[i]->isOn());
gate.setFrequency (freq);
gate.setAttack (modMatrix.getValue (gateParams.attack));
gate.setRelease (modMatrix.getValue (gateParams.release));
}
// Update Chorus
if (chorusParams.enable->isOn())
{
chorus.setParams (modMatrix.getValue (chorusParams.delay),
modMatrix.getValue (chorusParams.rate),
modMatrix.getValue (chorusParams.depth),
modMatrix.getValue (chorusParams.width),
modMatrix.getValue (chorusParams.mix));
}
// Update Distortion
if (distortionParams.enable->isOn())
{
distortion.setParams (modMatrix.getValue (distortionParams.amount),
modMatrix.getValue (distortionParams.highpass),
modMatrix.getValue (distortionParams.output),
modMatrix.getValue (distortionParams.mix));
}
// Update EQ
if (eqParams.enable->isOn())
{
eq.setParams (0, EQ::lowshelf,
modMatrix.getValue (eqParams.loFreq),
modMatrix.getValue (eqParams.loQ),
modMatrix.getValue (eqParams.loGain));
eq.setParams (1, EQ::peak,
modMatrix.getValue (eqParams.mid1Freq),
modMatrix.getValue (eqParams.mid1Q),
modMatrix.getValue (eqParams.mid1Gain));
eq.setParams (2, EQ::peak,
modMatrix.getValue (eqParams.mid2Freq),
modMatrix.getValue (eqParams.mid2Q),
modMatrix.getValue (eqParams.mid2Gain));
eq.setParams (3, EQ::highshelf,
modMatrix.getValue (eqParams.hiFreq),
modMatrix.getValue (eqParams.hiQ),
modMatrix.getValue (eqParams.hiGain));
}
// Update Compressor
if (compressorParams.enable->isOn())
{
compressor.setInputGain (1.0f);
compressor.setOutputGain (modMatrix.getValue (compressorParams.gain));
compressor.setParams (modMatrix.getValue (compressorParams.attack),
modMatrix.getValue (compressorParams.release),
modMatrix.getValue (compressorParams.threshold),
modMatrix.getValue (compressorParams.ratio),
6);
}
// Update Delay
if (delayParams.enable->isOn())
{
if (delayParams.sync->isOn())
{
auto& duration = NoteDuration::getNoteDurations()[(size_t)delayParams.beat->getUserValueInt()];
delayParams.delay->setUserValue (duration.toSeconds (getPlayHead()));
}
else
{
delayParams.delay->setUserValue (delayParams.time->getUserValue());
}
stereoDelay.setParams (delayParams.delay->getUserValue(),
modMatrix.getValue (delayParams.mix),
modMatrix.getValue (delayParams.fb),
modMatrix.getValue (delayParams.cf));
}
// Update Reverb
if (reverbParams.enable->isOn())
{
Reverb::Parameters p;
auto mix = modMatrix.getValue (reverbParams.mix);
WetDryMix wetDry (mix);
p.damping = modMatrix.getValue (reverbParams.damping);
p.freezeMode = modMatrix.getValue (reverbParams.freezeMode);
p.roomSize = modMatrix.getValue (reverbParams.roomSize);
p.width = modMatrix.getValue (reverbParams.width);
p.dryLevel = wetDry.dryGain;
p.wetLevel = wetDry.wetGain;
reverb.setParameters (p);
}
// Update Limiter
if (limiterParams.enable->isOn())
{
limiter.setInputGain (1.0f);
limiter.setOutputGain (modMatrix.getValue (limiterParams.gain));
limiter.setParams (modMatrix.getValue (limiterParams.attack),
modMatrix.getValue (limiterParams.release),
modMatrix.getValue (limiterParams.threshold),
100, 6);
}
// Output gain
outputGain.setGain (modMatrix.getValue (globalParams.level));
}
void WavetableAudioProcessor::handleMidiEvent (const MidiMessage& m)
{
MPESynthesiser::handleMidiEvent (m);
if (m.isPitchWheel())
modMatrix.setMonoValue (modScrPitchBend, float (m.getPitchWheelValue()) / 0x2000 - 1.0f);
}
void WavetableAudioProcessor::handleController ([[maybe_unused]] int ch, int num, int val)
{
modMatrix.setMonoValue (modSrcCC[num], val / 127.0f);
}
//==============================================================================
bool WavetableAudioProcessor::hasEditor() const
{
return true;
}
AudioProcessorEditor* WavetableAudioProcessor::createEditor()
{
return new WavetableAudioProcessorEditor (*this);
}
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new WavetableAudioProcessor();
}

View file

@ -0,0 +1,282 @@
#pragma once
#include <JuceHeader.h>
#include "WavetableVoice.h"
//==============================================================================
class WavetableAudioProcessor : public gin::Processor,
public gin::Synthesiser
{
public:
//==============================================================================
WavetableAudioProcessor();
~WavetableAudioProcessor() override;
void stateUpdated() override;
void updateState() override;
//==============================================================================
void reset() override;
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
void processBlock (AudioBuffer<float>&, MidiBuffer&) override;
//==============================================================================
AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
void updateParams (int blockSize);
void setupModMatrix();
gin::BandLimitedLookupTables bandLimitedLookupTables;
//==============================================================================
void handleMidiEvent (const MidiMessage& m) override;
void handleController (int ch, int num, int val) override;
//==============================================================================
Array<float> getLiveFilterCutoff (int idx);
void applyEffects (AudioSampleBuffer& buffer);
// WT Params
struct WTParams
{
WTParams() = default;
gin::Parameter::Ptr enable, voices, voicesTrns, tune, finetune,
level, detune, spread, pan;
void setup (WavetableAudioProcessor& p, int idx);
JUCE_DECLARE_NON_COPYABLE (WTParams)
};
// Voice Params
struct OSCParams
{
OSCParams() = default;
gin::Parameter::Ptr enable , wave, voices, voicesTrns, tune, finetune,
level, pulsewidth, detune, spread, pan;
void setup (WavetableAudioProcessor& p, int idx);
JUCE_DECLARE_NON_COPYABLE (OSCParams)
};
struct FilterParams
{
FilterParams() = default;
gin::Parameter::Ptr enable, type, keyTracking, velocityTracking,
frequency, resonance, amount,
attack, decay, sustain, release;
void setup (WavetableAudioProcessor& p, int idx);
JUCE_DECLARE_NON_COPYABLE (FilterParams)
};
struct EnvParams
{
EnvParams() = default;
gin::Parameter::Ptr enable, attack, decay, sustain, release;
void setup (WavetableAudioProcessor& p, int idx);
JUCE_DECLARE_NON_COPYABLE (EnvParams)
};
struct LFOParams
{
LFOParams() = default;
gin::Parameter::Ptr enable, sync, wave, rate, beat, depth, phase, offset, fade, delay;
void setup (WavetableAudioProcessor& p, int idx);
JUCE_DECLARE_NON_COPYABLE (LFOParams)
};
struct StepLFOParams
{
StepLFOParams() = default;
gin::Parameter::Ptr enable, beat, length;
gin::Parameter::Ptr level[32] = { nullptr };
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (StepLFOParams)
};
struct ADSRParams
{
ADSRParams() = default;
gin::Parameter::Ptr attack, decay, sustain, release, velocityTracking;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (ADSRParams)
};
// Global Params
struct GlobalParams
{
GlobalParams() = default;
gin::Parameter::Ptr mono, glideMode, glideRate, legato, level, voices, mpe;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (GlobalParams)
};
struct GateParams
{
GateParams() = default;
gin::Parameter::Ptr enable, beat, length, attack, release;
gin::Parameter::Ptr l[32] = { nullptr };
gin::Parameter::Ptr r[32] = { nullptr };
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (GateParams)
};
struct ChorusParams
{
ChorusParams() = default;
gin::Parameter::Ptr enable, delay, rate, depth, width, mix;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (ChorusParams)
};
struct DistortionParams
{
DistortionParams() = default;
gin::Parameter::Ptr enable, amount, highpass, output, mix;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (DistortionParams)
};
struct EQParams
{
EQParams() = default;
gin::Parameter::Ptr enable,
loFreq, loGain, loQ,
mid1Freq, mid1Gain, mid1Q,
mid2Freq, mid2Gain, mid2Q,
hiFreq, hiGain, hiQ;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (EQParams)
};
struct CompressorParams
{
CompressorParams() = default;
gin::Parameter::Ptr enable, attack, release, ratio, threshold, gain;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (CompressorParams)
};
struct DelayParams
{
DelayParams() = default;
gin::Parameter::Ptr enable, sync, time, beat, fb, cf, mix, delay;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (DelayParams)
};
struct ReverbParams
{
ReverbParams() = default;
gin::Parameter::Ptr enable, damping, freezeMode, roomSize, width, mix;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (ReverbParams)
};
struct LimiterParams
{
LimiterParams() = default;
gin::Parameter::Ptr enable, attack, release, threshold, gain;
void setup (WavetableAudioProcessor& p);
JUCE_DECLARE_NON_COPYABLE (LimiterParams)
};
//==============================================================================
gin::ModSrcId modSrcPressure, modSrcTimbre, modScrPitchBend,
modSrcNote, modSrcVelocity, modSrcStep, modSrcMonoStep;
Array<gin::ModSrcId> modSrcCC, modSrcMonoLFO, modSrcLFO, modSrcFilter, modSrcEnv;
//==============================================================================
WTParams wtParams[Cfg::numWTs];
OSCParams oscParams[Cfg::numOSCs];
FilterParams filterParams[Cfg::numFilters];
EnvParams envParams[Cfg::numENVs];
LFOParams lfoParams[Cfg::numLFOs];
StepLFOParams stepLfoParams;
ADSRParams adsrParams;
GlobalParams globalParams;
GateParams gateParams;
ChorusParams chorusParams;
DistortionParams distortionParams;
EQParams eqParams;
CompressorParams compressorParams;
DelayParams delayParams;
ReverbParams reverbParams;
LimiterParams limiterParams;
//==============================================================================
gin::GateEffect gate;
gin::Modulation chorus { 0.5f };
gin::Distortion distortion;
gin::StereoDelay stereoDelay { 120.1 };
gin::Dynamics compressor;
gin::Dynamics limiter;
gin::EQ eq {4};
Reverb reverb;
gin::GainProcessor outputGain;
gin::AudioFifo fifo { 2, 44100 };
//==============================================================================
gin::ModMatrix modMatrix;
gin::LFO modLFOs[Cfg::numLFOs];
gin::StepLFO modStepLFO;
AudioPlayHead* playhead = nullptr;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavetableAudioProcessor)
};

View file

@ -0,0 +1,379 @@
#include "WavetableVoice.h"
#include "PluginProcessor.h"
using namespace gin;
//==============================================================================
WavetableVoice::WavetableVoice (WavetableAudioProcessor& p, BandLimitedLookupTables& bllt)
: proc (p)
, bandLimitedLookupTables (bllt)
{
for (auto& f : filters)
f.setNumChannels (2);
}
void WavetableVoice::noteStarted()
{
fastKill = false;
startVoice();
auto note = getCurrentlyPlayingNote();
if (glideInfo.fromNote != -1 && (glideInfo.glissando || glideInfo.portamento))
{
noteSmoother.setTime (glideInfo.rate);
noteSmoother.setValueUnsmoothed (glideInfo.fromNote / 127.0f);
noteSmoother.setValue (note.initialNote / 127.0f);
}
else
{
noteSmoother.setValueUnsmoothed (note.initialNote / 127.0f);
}
proc.modMatrix.setPolyValue (*this, proc.modSrcVelocity, note.noteOnVelocity.asUnsignedFloat());
proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.initialTimbre.asUnsignedFloat());
proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat());
ScopedValueSetter<bool> svs (disableSmoothing, true);
for (auto& f : filters)
f.reset();
for (auto& a : filterADSRs)
a.reset();
for (auto& a : modADSRs)
a.noteOn();
for (auto& l : modLFOs)
l.reset();
updateParams (0);
snapParams();
updateParams (0);
snapParams();
for (auto& osc : oscillators)
osc.noteOn();
for (auto& a : filterADSRs)
a.noteOn();
for (auto& a : modADSRs)
a.noteOn();
for (auto& l : modLFOs)
l.noteOn();
modStepLFO.reset();
modStepLFO.noteOn();
adsr.reset();
adsr.noteOn();
}
void WavetableVoice::noteRetriggered()
{
auto note = getCurrentlyPlayingNote();
if (glideInfo.fromNote != -1 && (glideInfo.glissando || glideInfo.portamento))
{
noteSmoother.setTime (glideInfo.rate);
noteSmoother.setValue (note.initialNote / 127.0f);
}
else
{
noteSmoother.setValueUnsmoothed (note.initialNote / 127.0f);
}
proc.modMatrix.setPolyValue (*this, proc.modSrcVelocity, note.noteOnVelocity.asUnsignedFloat());
proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.initialTimbre.asUnsignedFloat());
proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat());
updateParams (0);
for (auto& osc : oscillators)
osc.noteOn();
for (auto& a : filterADSRs)
a.noteOn();
for (auto& a : modADSRs)
a.noteOn();
modStepLFO.noteOn();
adsr.noteOn();
}
void WavetableVoice::noteStopped (bool allowTailOff)
{
adsr.noteOff();
for (auto& a : filterADSRs)
a.noteOff();
for (auto& a : modADSRs)
a.noteOff();
if (! allowTailOff)
{
clearCurrentNote();
stopVoice();
}
}
void WavetableVoice::notePressureChanged()
{
auto note = getCurrentlyPlayingNote();
proc.modMatrix.setPolyValue (*this, proc.modSrcPressure, note.pressure.asUnsignedFloat());
}
void WavetableVoice::noteTimbreChanged()
{
auto note = getCurrentlyPlayingNote();
proc.modMatrix.setPolyValue (*this, proc.modSrcTimbre, note.initialTimbre.asUnsignedFloat());
}
void WavetableVoice::setCurrentSampleRate (double newRate)
{
MPESynthesiserVoice::setCurrentSampleRate (newRate);
for (auto& osc : oscillators)
osc.setSampleRate (newRate);
for (auto& f : filters)
f.setSampleRate (newRate);
for (auto& a : filterADSRs)
a.setSampleRate (newRate);
for (auto& l : modLFOs)
l.setSampleRate (newRate);
modStepLFO.setSampleRate (newRate);
noteSmoother.setSampleRate (newRate);
adsr.setSampleRate (newRate);
}
void WavetableVoice::renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
{
updateParams (numSamples);
// Run OSC
ScratchBuffer buffer (2, numSamples);
for (int i = 0; i < Cfg::numOSCs; i++)
if (proc.oscParams[i].enable->isOn())
oscillators[i].processAdding (currentMidiNotes[i], oscParams[i], buffer);
// Apply velocity
float velocity = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat();
buffer.applyGain (velocityToGain (velocity, ampKeyTrack));
// Apply filters
for (int i = 0; i < numElementsInArray (filters); i++)
if (proc.filterParams[i].enable->isOn())
filters[i].process (buffer);
// Run ADSR
adsr.processMultiplying (buffer);
if (adsr.getState() == AnalogADSR::State::idle)
{
clearCurrentNote();
stopVoice();
}
// Copy output to synth
outputBuffer.addFrom (0, startSample, buffer, 0, 0, numSamples);
outputBuffer.addFrom (1, startSample, buffer, 1, 0, numSamples);
finishBlock (numSamples);
}
void WavetableVoice::updateParams (int blockSize)
{
auto note = getCurrentlyPlayingNote();
proc.modMatrix.setPolyValue (*this, proc.modSrcNote, note.initialNote / 127.0f);
for (int i = 0; i < Cfg::numOSCs; i++)
{
if (! proc.oscParams[i].enable->isOn()) continue;
currentMidiNotes[i] = noteSmoother.getCurrentValue() * 127.0f;
if (glideInfo.glissando) currentMidiNotes[i] = roundToInt (currentMidiNotes[i]);
currentMidiNotes[i] += float (note.totalPitchbendInSemitones);
currentMidiNotes[i] += getValue (proc.oscParams[i].tune) + getValue (proc.oscParams[i].finetune) / 100.0f;
oscParams[i].wave = (Wave) int (proc.oscParams[i].wave->getProcValue());
oscParams[i].voices = int (proc.oscParams[i].voices->getProcValue());
oscParams[i].vcTrns = int (proc.oscParams[i].voicesTrns->getProcValue());
oscParams[i].pw = getValue (proc.oscParams[i].pulsewidth) / 100.0f;
oscParams[i].pan = getValue (proc.oscParams[i].pan);
oscParams[i].spread = getValue (proc.oscParams[i].spread) / 100.0f;
oscParams[i].detune = getValue (proc.oscParams[i].detune);
oscParams[i].gain = getValue (proc.oscParams[i].level);
}
ampKeyTrack = getValue (proc.adsrParams.velocityTracking);
for (int i = 0; i < Cfg::numFilters; i++)
{
if (! proc.filterParams[i].enable->isOn())
{
proc.modMatrix.setPolyValue (*this, proc.modSrcFilter[i], 0);
continue;
}
filterADSRs[i].setAttack (getValue (proc.filterParams[i].attack));
filterADSRs[i].setSustainLevel (getValue (proc.filterParams[i].sustain));
filterADSRs[i].setDecay (getValue (proc.filterParams[i].decay));
filterADSRs[i].setRelease (getValue (proc.filterParams[i].release));
filterADSRs[i].process (blockSize);
float filterWidth = float (getMidiNoteFromHertz (20000.0));
float filterEnv = filterADSRs[i].getOutput();
float filterSens = getValue (proc.filterParams[i].velocityTracking);
filterSens = currentlyPlayingNote.noteOnVelocity.asUnsignedFloat() * filterSens + 1.0f - filterSens;
float n = getValue (proc.filterParams[i].frequency);
n += (currentlyPlayingNote.initialNote - 60) * getValue (proc.filterParams[i].keyTracking);
n += filterEnv * filterSens * getValue (proc.filterParams[i].amount) * filterWidth;
float f = getMidiNoteInHertz (n);
float maxFreq = std::min (20000.0f, float (getSampleRate() / 2));
f = jlimit (4.0f, maxFreq, f);
float q = Q / (1.0f - (getValue (proc.filterParams[i].resonance) / 100.0f) * 0.99f);
switch (int (proc.filterParams[i].type->getProcValue()))
{
case 0:
filters[i].setType (Filter::lowpass);
filters[i].setSlope (Filter::db12);
break;
case 1:
filters[i].setType (Filter::lowpass);
filters[i].setSlope (Filter::db24);
break;
case 2:
filters[i].setType (Filter::highpass);
filters[i].setSlope (Filter::db12);
break;
case 3:
filters[i].setType (Filter::highpass);
filters[i].setSlope (Filter::db24);
break;
case 4:
filters[i].setType (Filter::bandpass);
filters[i].setSlope (Filter::db12);
break;
case 5:
filters[i].setType (Filter::bandpass);
filters[i].setSlope (Filter::db24);
break;
case 6:
filters[i].setType (Filter::notch);
filters[i].setSlope (Filter::db12);
break;
case 7:
filters[i].setType (Filter::notch);
filters[i].setSlope (Filter::db24);
break;
}
filters[i].setParams (f, q);
proc.modMatrix.setPolyValue (*this, proc.modSrcFilter[i], filterADSRs[i].getOutput());
}
for (int i = 0; i < Cfg::numENVs; i++)
{
if (proc.envParams[i].enable->isOn())
{
modADSRs[i].setAttack (getValue (proc.envParams[i].attack));
modADSRs[i].setSustainLevel (getValue (proc.envParams[i].sustain));
modADSRs[i].setDecay (getValue (proc.envParams[i].decay));
modADSRs[i].setRelease (getValue (proc.envParams[i].release));
proc.modMatrix.setPolyValue (*this, proc.modSrcEnv[i], modADSRs[i].getOutput());
modADSRs[i].process (blockSize);
}
else
{
proc.modMatrix.setPolyValue (*this, proc.modSrcEnv[i], 0.0f);
}
}
for (int i = 0; i < Cfg::numLFOs; i++)
{
if (proc.lfoParams[i].enable->isOn())
{
LFO::Parameters params;
float freq = 0;
if (proc.lfoParams[i].sync->getProcValue() > 0.0f)
freq = 1.0f / NoteDuration::getNoteDurations()[size_t (proc.lfoParams[i].beat->getProcValue())].toSeconds (proc.playhead);
else
freq = getValue (proc.lfoParams[i].rate);
params.waveShape = (LFO::WaveShape) int (proc.lfoParams[i].wave->getProcValue());
params.frequency = freq;
params.phase = getValue (proc.lfoParams[i].phase);
params.offset = getValue (proc.lfoParams[i].offset);
params.depth = getValue (proc.lfoParams[i].depth);
params.delay = getValue (proc.lfoParams[i].delay);
params.fade = getValue (proc.lfoParams[i].fade);
modLFOs[i].setParameters (params);
modLFOs[i].process (blockSize);
proc.modMatrix.setPolyValue (*this, proc.modSrcLFO[i], modLFOs[i].getOutput());
}
else
{
proc.modMatrix.setPolyValue (*this, proc.modSrcLFO[i], 0);
}
}
// Update Step LFO
if (proc.stepLfoParams.enable->isOn())
{
float freq = 1.0f / NoteDuration::getNoteDurations()[size_t (proc.stepLfoParams.beat->getProcValue())].toSeconds (proc.playhead);
modStepLFO.setFreq (freq);
int n = int (proc.stepLfoParams.length->getProcValue());
modStepLFO.setNumPoints (n);
for (int i = n; --i >= 0;)
modStepLFO.setPoint (i, proc.stepLfoParams.level[i]->getProcValue());
modStepLFO.process (blockSize);
proc.modMatrix.setPolyValue (*this, proc.modSrcStep, modStepLFO.getOutput());
}
else
{
proc.modMatrix.setPolyValue (*this, proc.modSrcStep, 0);
}
adsr.setAttack (getValue (proc.adsrParams.attack));
adsr.setDecay (getValue (proc.adsrParams.decay));
adsr.setSustainLevel (getValue (proc.adsrParams.sustain));
adsr.setRelease (fastKill ? 0.01f : getValue (proc.adsrParams.release));
noteSmoother.process (blockSize);
}
bool WavetableVoice::isVoiceActive()
{
return isActive();
}
float WavetableVoice::getFilterCutoffNormalized (int idx)
{
float freq = filters[idx].getFrequency();
return proc.filterParams[idx].frequency->getUserRange().convertTo0to1 (getMidiNoteFromHertz (freq));
}

View file

@ -0,0 +1,58 @@
#pragma once
#include <JuceHeader.h>
#include "Cfg.h"
class WavetableAudioProcessor;
//==============================================================================
class WavetableVoice : public gin::SynthesiserVoice,
public gin::ModVoice
{
public:
WavetableVoice (WavetableAudioProcessor& p, gin::BandLimitedLookupTables& bandLimitedLookupTables);
void noteStarted() override;
void noteRetriggered() override;
void noteStopped (bool allowTailOff) override;
void notePressureChanged() override;
void noteTimbreChanged() override;
void notePitchbendChanged() override {}
void noteKeyStateChanged() override {}
void setCurrentSampleRate (double newRate) override;
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override;
bool isVoiceActive() override;
float getFilterCutoffNormalized (int idx);
private:
void updateParams (int blockSize);
WavetableAudioProcessor& proc;
gin::BandLimitedLookupTables& bandLimitedLookupTables;
gin::VoicedStereoOscillator oscillators[Cfg::numOSCs] =
{
bandLimitedLookupTables,
};
gin::Filter filters[Cfg::numFilters];
gin::ADSR filterADSRs[Cfg::numFilters];
gin::ADSR modADSRs[Cfg::numENVs];
gin::LFO modLFOs[Cfg::numLFOs];
gin::StepLFO modStepLFO;
gin::AnalogADSR adsr;
float currentMidiNotes[Cfg::numOSCs];
gin::VoicedStereoOscillator::Params oscParams[Cfg::numOSCs];
gin::EasedValueSmoother<float> noteSmoother;
float ampKeyTrack = 1.0f;
};

137
plugin/Wavetable.jucer Normal file
View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT id="C56KyA" name="Wavetable" projectType="audioplug" jucerVersion="5.4.7"
companyName="SocaLabs" reportAppUsage="0" displaySplashScreen="0"
pluginFormats="buildAU,buildStandalone,buildVST" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn"
pluginManufacturerCode="Soca" pluginCode="SLwt" pluginAUMainType="'aumu'"
cppLanguageStandard="latest" pluginChannelConfigs="{0,2}" version="0.0.2">
<MAINGROUP id="Hs1HPA" name="Wavetable">
<GROUP id="{0CE63D98-F319-7656-21CD-D7A21B4A4B6C}" name="Source">
<FILE id="Ma3e0n" name="Cfg.h" compile="0" resource="0" file="Source/Cfg.h"/>
<FILE id="f12Jxy" name="Boxes.h" compile="0" resource="0" file="Source/Boxes.h"/>
<FILE id="hVjqti" name="WavetableVoice.cpp" compile="1" resource="0"
file="Source/WavetableVoice.cpp"/>
<FILE id="BmWCuH" name="WavetableVoice.h" compile="0" resource="0"
file="Source/WavetableVoice.h"/>
<FILE id="p0fko7" name="PluginProcessor.cpp" compile="1" resource="0"
file="Source/PluginProcessor.cpp"/>
<FILE id="IM4H1y" name="PluginProcessor.h" compile="0" resource="0"
file="Source/PluginProcessor.h"/>
<FILE id="EG0VAx" name="PluginEditor.cpp" compile="1" resource="0"
file="Source/PluginEditor.cpp"/>
<FILE id="rezjyh" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/>
</GROUP>
</MAINGROUP>
<EXPORTFORMATS>
<XCODE_MAC targetFolder="Builds/MacOSX" vstLegacyFolder="../modules/plugin_sdk/vstsdk2.4"
extraCompilerFlags="-Wunused-value">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" recommendedWarnings="LLVM" osxCompatibility="10.9 SDK"/>
<CONFIGURATION isDebug="0" name="Release" recommendedWarnings="LLVM" osxCompatibility="10.9 SDK"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_devices" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_formats" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_plugin_client" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_processors" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_utils" path="../modules/juce/modules"/>
<MODULEPATH id="juce_core" path="../modules/juce/modules"/>
<MODULEPATH id="juce_cryptography" path="../modules/juce/modules"/>
<MODULEPATH id="juce_data_structures" path="../modules/juce/modules"/>
<MODULEPATH id="juce_events" path="../modules/juce/modules"/>
<MODULEPATH id="juce_graphics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_gui_basics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_gui_extra" path="../modules/juce/modules"/>
<MODULEPATH id="juce_opengl" path="../modules/juce/modules"/>
<MODULEPATH id="gin_plugin" path="../modules/gin/modules"/>
<MODULEPATH id="gin" path="../modules/gin/modules"/>
<MODULEPATH id="gin_dsp" path="../modules/gin/modules"/>
<MODULEPATH id="juce_dsp" path="../modules/juce/modules"/>
</MODULEPATHS>
</XCODE_MAC>
<VS2019 targetFolder="Builds/VisualStudio2019" vstLegacyFolder="../modules/plugin_sdk/vstsdk2.4">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="Wavetable_32b" winArchitecture="Win32"
useRuntimeLibDLL="0"/>
<CONFIGURATION isDebug="0" name="Release" useRuntimeLibDLL="0" winArchitecture="Win32"
targetName="Wavetable_32b"/>
<CONFIGURATION isDebug="1" name="Debug64" useRuntimeLibDLL="0"/>
<CONFIGURATION isDebug="0" name="Release64" linkTimeOptimisation="0" useRuntimeLibDLL="0"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_devices" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_formats" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_plugin_client" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_processors" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_utils" path="../modules/juce/modules"/>
<MODULEPATH id="juce_core" path="../modules/juce/modules"/>
<MODULEPATH id="juce_cryptography" path="../modules/juce/modules"/>
<MODULEPATH id="juce_data_structures" path="../modules/juce/modules"/>
<MODULEPATH id="juce_events" path="../modules/juce/modules"/>
<MODULEPATH id="juce_graphics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_gui_basics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_gui_extra" path="../modules/juce/modules"/>
<MODULEPATH id="juce_opengl" path="../modules/juce/modules"/>
<MODULEPATH id="gin_plugin" path="../modules/gin/modules"/>
<MODULEPATH id="gin" path="../modules/gin/modules"/>
<MODULEPATH id="gin_dsp" path="../modules/gin/modules"/>
<MODULEPATH id="juce_dsp" path="../modules/juce/modules"/>
</MODULEPATHS>
</VS2019>
<LINUX_MAKE targetFolder="Builds/LinuxMakefile" vstLegacyFolder="../modules/plugin_sdk/vstsdk2.4"
extraCompilerFlags="-fvisibility=hidden" extraLinkerFlags="-fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed -Wl,--strip-all">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" libraryPath="/usr/X11R6/lib/" linuxArchitecture="-m64"/>
<CONFIGURATION isDebug="0" name="Release" libraryPath="/usr/X11R6/lib/" linuxArchitecture="-m64"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_devices" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_formats" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_plugin_client" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_processors" path="../modules/juce/modules"/>
<MODULEPATH id="juce_audio_utils" path="../modules/juce/modules"/>
<MODULEPATH id="juce_core" path="../modules/juce/modules"/>
<MODULEPATH id="juce_cryptography" path="../modules/juce/modules"/>
<MODULEPATH id="juce_data_structures" path="../modules/juce/modules"/>
<MODULEPATH id="juce_events" path="../modules/juce/modules"/>
<MODULEPATH id="juce_graphics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_gui_basics" path="../modules/juce/modules"/>
<MODULEPATH id="juce_gui_extra" path="../modules/juce/modules"/>
<MODULEPATH id="juce_opengl" path="../modules/juce/modules"/>
<MODULEPATH id="gin_plugin" path="../modules/gin/modules"/>
<MODULEPATH id="gin" path="../modules/gin/modules"/>
<MODULEPATH id="gin_dsp" path="../modules/gin/modules"/>
<MODULEPATH id="juce_dsp" path="../modules/juce/modules"/>
</MODULEPATHS>
</LINUX_MAKE>
</EXPORTFORMATS>
<MODULES>
<MODULE id="gin" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="gin_dsp" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="gin_plugin" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_audio_plugin_client" showAllCode="1" useLocalCopy="0"
useGlobalPath="0"/>
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_dsp" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
</MODULES>
<LIVE_SETTINGS>
<OSX/>
<WINDOWS/>
</LIVE_SETTINGS>
<JUCEOPTIONS JUCE_VST3_CAN_REPLACE_VST2="0" JUCE_STRICT_REFCOUNTEDPOINTER="1"/>
</JUCERPROJECT>