Start CLAP front-end
This commit is contained in:
parent
db9d7e93e2
commit
417dbc1944
7
LICENSE.txt
Normal file
7
LICENSE.txt
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright 2022-2025 Signalsmith Audio / Geraint Luff
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
36
README.md
36
README.md
@ -1,32 +1,44 @@
|
||||
# Signalsmith Basics
|
||||
|
||||
A collection of basic effects, available as plugins and re-usable open-source C++ classes.
|
||||
A collection of basic effects, available as plugins and re-usable open-source (MIT) C++ classes.
|
||||
|
||||
* **Limiter**
|
||||
* **Crunch**
|
||||
* **Reverb**
|
||||
|
||||
## How to use
|
||||
|
||||
The [main project page](https://signalsmith-audio.co.uk/code/basics/) has details about the specific effects (and audio examples), but they are all quite similar to use:
|
||||
Each effect can be used just by including the corresponding header (e.g. `limiter.h`). This defines two classes `LimiterFloat` and `LimiterDouble`. Some of these have initialisation arguments (e.g. maximum lookahead) but these are always optional.
|
||||
|
||||
```cpp
|
||||
// Limiter with maximum attack/lookahead of 100ms
|
||||
signalsmith::basics::Limiter effect(100);
|
||||
|
||||
effect.configure(sampleRate, maxBlockSize, channels);
|
||||
effect.configure(sampleRate, maxBlockSize, inputChannels, outputChannels);
|
||||
```
|
||||
|
||||
Then when processing (all on the audio thread):
|
||||
Before use, these classes must be configured. The `.configure()` method returns `true` if the config was accepted. They can be configured multiple times, but (obviously) not while actively processing audio.
|
||||
|
||||
```cpp
|
||||
// clear buffers
|
||||
effect.reset()
|
||||
effect.configure(sampleRate, maxBlockSize, channels);
|
||||
```
|
||||
|
||||
// Change parameters with assignment
|
||||
effect.attackMs = 20;
|
||||
To process audio, pass in a classic array-of-buffers for input and output (which shouldn't be the same):
|
||||
|
||||
// process a block
|
||||
```cpp
|
||||
float **inputBuffers, **outputBuffers;
|
||||
int blockSize;
|
||||
effect.process(inputBuffers, outputBuffers, blockSize);
|
||||
```
|
||||
|
||||
You can also inspect latency (`effect.latencySamples()`) and tail length (`effect.tailSamples()`).
|
||||
When playback (re)starts, you can also call `.reset()` to clear any tails/state. You can also inspect latency (`effect.latencySamples()`) and tail length (`effect.tailSamples()`).
|
||||
|
||||
The actual implementations are templates (e.g. `LimiterSTFX`) which you shouldn't every need to use directly, although they should be fairly readable. These are wrapped up into the `...Float` and `...Double` classes by the code in `stfx/stfx-library.h`, which also provides helpers for parameter-smoothing/etc., and more typical `.configure()` and `.process()` functions (from `.configureSTFX()`/`.processSTFX()`).
|
||||
|
||||
### Parameters
|
||||
|
||||
Floating-point parameters are declared as `ParamRange`s in the `...STFX` template. This is an opaque type which can be assigned from / converted into `double` from any thread. Parameter smoothing is handled internally.
|
||||
|
||||
Integer parameters are declared as `ParamStepped`s. This is a similarly opaque type which converts to/from `int`.
|
||||
|
||||
### Implementation
|
||||
|
||||
The `.state()` method of these templates contain a lot of detail, almost all of which is ignored (and optimised away) when using the `...Float`/`...Double` classes.
|
||||
|
||||
3
SUPPORT.txt
Normal file
3
SUPPORT.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# https://geraintluff.github.io/SUPPORT.txt/
|
||||
|
||||
2026-01-01 Geraint Luff <geraint@signalsmith-audio.co.uk>
|
||||
89
clap/CMakeLists.txt
Normal file
89
clap/CMakeLists.txt
Normal file
@ -0,0 +1,89 @@
|
||||
cmake_minimum_required(VERSION 3.28)
|
||||
|
||||
project(plugins VERSION 1.0.0)
|
||||
set(NAME basics)
|
||||
|
||||
################ boilerplate config
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17) # for string_view
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
|
||||
enable_language(OBJCXX)
|
||||
endif ()
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
clap
|
||||
GIT_REPOSITORY https://github.com/geraintluff/clap.git # https://github.com/free-audio/clap
|
||||
GIT_TAG 2df92fe17911f15436176b9c37faec370166d14f # draft/web
|
||||
GIT_SHALLOW OFF
|
||||
)
|
||||
FetchContent_MakeAvailable(clap)
|
||||
|
||||
################ CLAP wrapper stuff
|
||||
|
||||
if(NOT DEFINED VST3_SDK_ROOT)
|
||||
if(EMSCRIPTEN)
|
||||
# don't download the VST3 SDK
|
||||
set(VST3_SDK_ROOT "./dummy/vst3sdk/path")
|
||||
else()
|
||||
set(CLAP_WRAPPER_DOWNLOAD_DEPENDENCIES TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CLAP_WRAPPER_OUTPUT_NAME clap-wrapper-target)
|
||||
set(CLAP_WRAPPER_DONT_ADD_TARGETS TRUE)
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
clap-wrapper
|
||||
GIT_REPOSITORY https://github.com/free-audio/clap-wrapper
|
||||
GIT_TAG 0.12.1 # first version with WCLAP stuff
|
||||
GIT_SHALLOW ON
|
||||
)
|
||||
FetchContent_MakeAvailable(clap-wrapper)
|
||||
|
||||
################ Helpers
|
||||
|
||||
FetchContent_Declare(
|
||||
clap-helpers
|
||||
GIT_REPOSITORY https://github.com/free-audio/clap-helpers
|
||||
GIT_TAG 58ab81b1dc8219e859529c1306f364bb3aedf7d5
|
||||
GIT_SHALLOW OFF
|
||||
)
|
||||
FetchContent_MakeAvailable(clap-helpers)
|
||||
|
||||
################ The actual plugin(s)
|
||||
|
||||
add_library(${NAME}_static STATIC)
|
||||
target_link_libraries(${NAME}_static PUBLIC
|
||||
clap
|
||||
clap-helpers
|
||||
)
|
||||
target_sources(${NAME}_static PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/source/${name}.cpp
|
||||
)
|
||||
|
||||
make_clapfirst_plugins(
|
||||
TARGET_NAME ${NAME}
|
||||
IMPL_TARGET ${NAME}_static
|
||||
|
||||
RESOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources"
|
||||
|
||||
OUTPUT_NAME "${NAME}"
|
||||
ENTRY_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/source/clap_entry.cpp
|
||||
|
||||
BUNDLE_IDENTIFIER "uk.co.signalsmith-audio.plugins.${NAME}"
|
||||
BUNDLE_VERSION "1.0.0"
|
||||
WINDOWS_FOLDER_VST3 TRUE
|
||||
|
||||
PLUGIN_FORMATS CLAP VST3 WCLAP # AUV2
|
||||
COPY_AFTER_BUILD FALSE
|
||||
|
||||
AUV2_MANUFACTURER_NAME "Signalsmith Audio"
|
||||
AUV2_MANUFACTURER_CODE "SigA"
|
||||
AUV2_SUBTYPE_CODE "BdDt"
|
||||
AUV2_INSTRUMENT_TYPE "aufx"
|
||||
)
|
||||
51
clap/Makefile
Normal file
51
clap/Makefile
Normal file
@ -0,0 +1,51 @@
|
||||
.PHONY: build build-emscripten emsdk
|
||||
PROJECT := plugins
|
||||
PLUGIN := example
|
||||
|
||||
CMAKE_PARAMS := -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=.. -G Xcode # -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
mac-installer: mac-installer-$(PLUGIN)
|
||||
|
||||
clean:
|
||||
rm -rf out
|
||||
|
||||
build:
|
||||
cmake . -B out/build $(CMAKE_PARAMS)
|
||||
|
||||
release-%: build
|
||||
cmake --build out/build --target $* --config Release
|
||||
|
||||
mac-installer-%: release-%_vst3 release-%_clap emscripten-%_wclap
|
||||
../stfx/front-end/clap/mac/make-pkg-installer.sh out/$*.pkg $* uk.co.signalsmith.installer.stfx.$* out/Release/$*
|
||||
|
||||
####### Emscripten #######
|
||||
# based on https://stunlock.gg/posts/emscripten_with_cmake/
|
||||
|
||||
CURRENT_DIR := $(shell pwd)
|
||||
EMSDK ?= $(CURRENT_DIR)/emsdk
|
||||
EMSDK_ENV = unset CMAKE_TOOLCHAIN_FILE; EMSDK_QUIET=1 . "$(EMSDK)/emsdk_env.sh";
|
||||
|
||||
emsdk:
|
||||
@ if ! test -d "$(EMSDK)" ;\
|
||||
then \
|
||||
echo "SDK not found - cloning from Github" ;\
|
||||
git clone https://github.com/emscripten-core/emsdk.git "$(EMSDK)" ;\
|
||||
cd "$(EMSDK)" && git pull && ./emsdk install latest && ./emsdk activate latest ;\
|
||||
$(EMSDK_ENV) emcc --check && python3 --version && cmake --version ;\
|
||||
fi
|
||||
|
||||
build-emscripten: emsdk
|
||||
$(EMSDK_ENV) emcmake cmake . -B out/build-emscripten -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=../Release -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
emscripten-%: build-emscripten
|
||||
$(EMSDK_ENV) cmake --build out/build-emscripten --target $* --config Release
|
||||
|
||||
####### Dev stuff #######
|
||||
|
||||
format:
|
||||
find source -iname \*.cpp -o -iname \*.h | xargs clang-format -i
|
||||
|
||||
dev: release-$(PLUGIN)_clap
|
||||
/Applications/REAPER.app/Contents/MacOS/REAPER REAPER/$(PLUGIN)/$(PLUGIN).RPP
|
||||
|
||||
wclap: emscripten-$(PLUGIN)_wclap
|
||||
360
clap/REAPER/basics/basics.RPP
Normal file
360
clap/REAPER/basics/basics.RPP
Normal file
@ -0,0 +1,360 @@
|
||||
<REAPER_PROJECT 0.1 "7.20/macOS-arm64" 1747749064
|
||||
<NOTES 0 2
|
||||
>
|
||||
RIPPLE 0
|
||||
GROUPOVERRIDE 0 0 0
|
||||
AUTOXFADE 129
|
||||
ENVATTACH 3
|
||||
POOLEDENVATTACH 0
|
||||
MIXERUIFLAGS 11 48
|
||||
ENVFADESZ10 40
|
||||
PEAKGAIN 1
|
||||
FEEDBACK 0
|
||||
PANLAW 1
|
||||
PROJOFFS 0 0 0
|
||||
MAXPROJLEN 0 0
|
||||
GRID 3199 8 1 8 1 0 0 0
|
||||
TIMEMODE 1 5 -1 30 0 0 -1
|
||||
VIDEO_CONFIG 0 0 256
|
||||
PANMODE 3
|
||||
PANLAWFLAGS 3
|
||||
CURSOR 0
|
||||
ZOOM 100 0 0
|
||||
VZOOMEX 6 0
|
||||
USE_REC_CFG 0
|
||||
RECMODE 1
|
||||
SMPTESYNC 0 30 100 40 1000 300 0 0 1 0 0
|
||||
LOOP 0
|
||||
LOOPGRAN 0 4
|
||||
RECORD_PATH "Media" ""
|
||||
<RECORD_CFG
|
||||
ZXZhdxgAAQ==
|
||||
>
|
||||
<APPLYFX_CFG
|
||||
>
|
||||
RENDER_FILE ""
|
||||
RENDER_PATTERN ""
|
||||
RENDER_FMT 0 2 0
|
||||
RENDER_1X 0
|
||||
RENDER_RANGE 1 0 0 18 1000
|
||||
RENDER_RESAMPLE 3 0 1
|
||||
RENDER_ADDTOPROJ 0
|
||||
RENDER_STEMS 0
|
||||
RENDER_DITHER 0
|
||||
TIMELOCKMODE 1
|
||||
TEMPOENVLOCKMODE 1
|
||||
ITEMMIX 1
|
||||
DEFPITCHMODE 589824 0
|
||||
TAKELANE 1
|
||||
SAMPLERATE 44100 0 0
|
||||
<RENDER_CFG
|
||||
ZXZhdxgAAQ==
|
||||
>
|
||||
LOCK 1
|
||||
<METRONOME 6 2
|
||||
VOL 0.25 0.125
|
||||
BEATLEN 4
|
||||
FREQ 1760 880 1
|
||||
SAMPLES "" ""
|
||||
SPLIGNORE 0 0
|
||||
SPLDEF 2 660 "" 0
|
||||
SPLDEF 3 440 "" 0
|
||||
PATTERN 0 169
|
||||
PATTERNSTR ABBB
|
||||
MULT 1
|
||||
>
|
||||
GLOBAL_AUTO -1
|
||||
TEMPO 120 4 4
|
||||
PLAYRATE 1 0 0.25 4
|
||||
SELECTION 0 0
|
||||
SELECTION2 0 0
|
||||
MASTERAUTOMODE 0
|
||||
MASTERTRACKHEIGHT 0 0
|
||||
MASTERPEAKCOL 16576
|
||||
MASTERMUTESOLO 0
|
||||
MASTERTRACKVIEW 1 0.6667 0.5 0.5 0 0 0 0 0 0 0 0 0 0
|
||||
MASTERHWOUT 0 0 1 0 0 0 0 -1
|
||||
MASTER_NCH 4 2
|
||||
MASTER_VOLUME 1 0 -1 -1 1
|
||||
MASTER_PANMODE 3
|
||||
MASTER_PANLAWFLAGS 3
|
||||
MASTER_FX 1
|
||||
MASTER_SEL 0
|
||||
<MASTERFXLIST
|
||||
WNDRECT 848 592 601 240
|
||||
SHOW 0
|
||||
LASTSEL 1
|
||||
DOCKED 0
|
||||
BYPASS 0 0 0
|
||||
<VST "VST3: [Basics] Limiter (Signalsmith Audio)" Signalsmith-Audio-signalsmith-basics.vst3 0 "" 767935365{85C8AB4F80EBAD2FB1B22206AC3BB024} ""
|
||||
hcPFLe5e7f4EAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAEAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAABqAAAAAQAAAP//EAA=
|
||||
QQAAAAEAAAAAAAAAAAAAEEAAAAAAAADwP5CkT2MBlO0/6sSBcQw8H0CDkljf3IUjQBaXn5dXrjlAAAAAAAAA8D8AAAAAAADgPxkAAAAAAAAAAAAAAAAAAAAAAAAAAACA
|
||||
gUAAAAAAAEBlQA==
|
||||
AAAQAAAA
|
||||
>
|
||||
FLOATPOS 0 0 0 0
|
||||
FXID {81F27681-ACFE-1D40-9AAE-F8C9A0035B1F}
|
||||
WAK 0 0
|
||||
BYPASS 0 0 0
|
||||
<VST "VST: ReaStream (Cockos) (8ch)" reastream.vst.dylib 0 "" 1920169074<5653547273747272656173747265616D> ""
|
||||
cnRzcu5e7f4IAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAgAAAABAAAAAAAAAAIAAAAAAAAA
|
||||
BAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAArAAAAAEAAAAAABAA
|
||||
AQAAAAIAAAABAAAAZGVmYXVsdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxMjcuMC4wLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
AAAQAAAA
|
||||
>
|
||||
FLOATPOS 0 0 0 0
|
||||
FXID {11A0D811-6420-CB40-95D6-F9D12E45C862}
|
||||
WAK 0 0
|
||||
>
|
||||
<MASTERPLAYSPEEDENV
|
||||
EGUID {590DCF42-C4B4-1D40-9C66-B99188BD30ED}
|
||||
ACT 0 -1
|
||||
VIS 0 1 1
|
||||
LANEHEIGHT 0 0
|
||||
ARM 0
|
||||
DEFSHAPE 0 -1 -1
|
||||
>
|
||||
<TEMPOENVEX
|
||||
EGUID {9E65AFD8-F464-FB46-AC60-C5553CE5E330}
|
||||
ACT 0 -1
|
||||
VIS 1 0 1
|
||||
LANEHEIGHT 0 0
|
||||
ARM 0
|
||||
DEFSHAPE 1 -1 -1
|
||||
>
|
||||
<PROJBAY
|
||||
>
|
||||
<TRACK {6D299D8B-E766-F540-80D3-DA6147A064BA}
|
||||
NAME "Sidechain Distortion"
|
||||
PEAKCOL 16576
|
||||
BEAT -1
|
||||
AUTOMODE 0
|
||||
PANLAWFLAGS 3
|
||||
VOLPAN 0.56085967561807 0 -1 -1 1
|
||||
MUTESOLO 0 0 0
|
||||
IPHASE 0
|
||||
PLAYOFFS 0 1
|
||||
ISBUS 1 1
|
||||
BUSCOMP 0 0 0 0 0
|
||||
SHOWINMIX 1 0.6667 0.5 1 0.5 0 0 0
|
||||
FIXEDLANES 9 0 0 0 0
|
||||
SEL 1
|
||||
REC 0 0 1 0 0 0 0 0
|
||||
VU 2
|
||||
TRACKHEIGHT 0 0 0 0 0 0 0
|
||||
INQ 0 0 0 0.5 100 0 0 100
|
||||
NCHAN 4
|
||||
FX 1
|
||||
TRACKID {6D299D8B-E766-F540-80D3-DA6147A064BA}
|
||||
PERF 0
|
||||
AUXRECV 2 3 1 0 0 0 0 0 2 -1:U 0 -1 ''
|
||||
AUXRECV 3 3 1 0 0 0 0 0 2 -1:U 0 -1 ''
|
||||
MIDIOUT -1
|
||||
MAINSEND 1 0
|
||||
<FXCHAIN
|
||||
WNDRECT 184 282 862 150
|
||||
SHOW 1
|
||||
LASTSEL 0
|
||||
DOCKED 0
|
||||
BYPASS 0 0 0
|
||||
<CLAP "CLAP: Sidechain Distortion (example) (Signalsmith Audio)" uk.co.signalsmith.dev.sidechain-distortion ""
|
||||
CFG 0 0 0 ""
|
||||
<OUT_PINS
|
||||
>
|
||||
<STATE
|
||||
v2d2ZXJzaW9uAGVzdGF0Zb9ndmVyc2lvbgBnbGltaXREYr9ldmFsdWX7wDrCUmkT/3z/Z3RvbmVrSHq/ZXZhbHVl+0AVARbxhJ0S////
|
||||
>
|
||||
>
|
||||
FLOATPOS 0 0 0 0
|
||||
FXID {1F02CE63-5B2C-DD40-B8DA-6796E8E4CD70}
|
||||
WAK 0 0
|
||||
>
|
||||
>
|
||||
<TRACK {D098F2BD-6569-F946-9F66-5C0548DFE5B1}
|
||||
NAME "Welcome Pad"
|
||||
PEAKCOL 16576
|
||||
BEAT -1
|
||||
AUTOMODE 0
|
||||
PANLAWFLAGS 3
|
||||
VOLPAN 1 0 -1 -1 1
|
||||
MUTESOLO 0 0 0
|
||||
IPHASE 0
|
||||
PLAYOFFS 0 1
|
||||
ISBUS 2 -1
|
||||
BUSCOMP 0 0 0 0 0
|
||||
SHOWINMIX 1 0.6667 0.5 1 0.5 0 0 0
|
||||
FIXEDLANES 9 0 0 0 0
|
||||
SEL 0
|
||||
REC 0 0 1 0 0 0 0 0
|
||||
VU 2
|
||||
TRACKHEIGHT 0 0 0 0 0 0 0
|
||||
INQ 0 0 0 0.5 100 0 0 100
|
||||
NCHAN 2
|
||||
FX 1
|
||||
TRACKID {D098F2BD-6569-F946-9F66-5C0548DFE5B1}
|
||||
PERF 0
|
||||
MIDIOUT -1
|
||||
MAINSEND 1 0
|
||||
<ITEM
|
||||
POSITION 0
|
||||
SNAPOFFS 0
|
||||
LENGTH 12.5
|
||||
LOOP 0
|
||||
ALLTAKES 0
|
||||
FADEIN 1 0 0 1 0 0 0
|
||||
FADEOUT 1 0 0 1 0 0 0
|
||||
MUTE 0 0
|
||||
SEL 0
|
||||
IGUID {96281C1B-0CD8-7A4C-BC7B-83E34DB38B24}
|
||||
IID 4
|
||||
NAME "Welcome Pad - stem"
|
||||
VOLPAN 1 0 1 -1
|
||||
SOFFS 0
|
||||
PLAYRATE 1 1 0 -1 0 0.0025
|
||||
CHANMODE 0
|
||||
GUID {D46D1639-7253-D040-86A3-47C6E3A0FBCB}
|
||||
<SOURCE WAVE
|
||||
FILE "Media/sidechain-distortion_stems_Welcome Pad.wav"
|
||||
>
|
||||
>
|
||||
>
|
||||
<TRACK {F4D9FA0E-571B-E24E-B158-4F619AA188D8}
|
||||
NAME Filtered
|
||||
PEAKCOL 16576
|
||||
BEAT -1
|
||||
AUTOMODE 0
|
||||
PANLAWFLAGS 3
|
||||
VOLPAN 0 0 -1 -1 1
|
||||
MUTESOLO 0 0 0
|
||||
IPHASE 0
|
||||
PLAYOFFS 0 1
|
||||
ISBUS 0 0
|
||||
BUSCOMP 0 0 0 0 0
|
||||
SHOWINMIX 1 0.6667 0.5 1 0.5 0 0 0
|
||||
FIXEDLANES 9 0 0 0 0
|
||||
SEL 0
|
||||
REC 0 0 1 0 0 0 0 0
|
||||
VU 2
|
||||
TRACKHEIGHT 0 0 0 0 0 0 0
|
||||
INQ 0 0 0 0.5 100 0 0 100
|
||||
NCHAN 2
|
||||
FX 1
|
||||
TRACKID {F4D9FA0E-571B-E24E-B158-4F619AA188D8}
|
||||
PERF 0
|
||||
MIDIOUT -1
|
||||
MAINSEND 1 0
|
||||
<FXCHAIN
|
||||
WNDRECT 847 421 815 444
|
||||
SHOW 2
|
||||
LASTSEL 1
|
||||
DOCKED 0
|
||||
BYPASS 0 0 0
|
||||
<JS "Geraint's JSFX/Utility/Panalysis/Panalysis.jsfx" ""
|
||||
1 0 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
>
|
||||
<JS_SER
|
||||
zczMPQAAAAAAAAAA
|
||||
>
|
||||
FLOATPOS 0 0 0 0
|
||||
FXID {6A5C4484-079E-F34D-8A18-CB456B094205}
|
||||
WAK 0 0
|
||||
BYPASS 0 0 0
|
||||
<VST "VST: ReaEQ (Cockos)" reaeq.vst.dylib 0 "" 1919247729<56535472656571726561657100000000> ""
|
||||
cWVlcu5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAAIAAAABAAAAAAAAAAIAAAAAAAAAagAAAAEAAAAAABAA
|
||||
IQAAAAIAAAAEAAAAAQAAAMS7LCro8zZAAAAAAAAA8D9GtvP91Hj1PwEDAAAAAQAAAHj7zCB2ucJAdouc1Fqk9D8QWDm0yHbyPwEBAAAAAQAAAAAAAAAAAPA/AAAAAEAC
|
||||
AABrAQAAAgAAAA==
|
||||
AAAQAAAA
|
||||
>
|
||||
FLOATPOS 0 0 0 0
|
||||
FXID {5262179C-F519-424F-8C34-4765E9AE04E7}
|
||||
WAK 0 0
|
||||
>
|
||||
<ITEM
|
||||
POSITION 0
|
||||
SNAPOFFS 0
|
||||
LENGTH 12.5
|
||||
LOOP 0
|
||||
ALLTAKES 0
|
||||
FADEIN 1 0 0 1 0 0 0
|
||||
FADEOUT 1 0 0 1 0 0 0
|
||||
MUTE 0 0
|
||||
SEL 1
|
||||
IGUID {102A7029-390F-704B-AE82-A4E1AA02CD03}
|
||||
IID 8
|
||||
NAME "Welcome Pad - stem"
|
||||
VOLPAN 1 0 1 -1
|
||||
SOFFS 0
|
||||
PLAYRATE 1 1 0 -1 0 0.0025
|
||||
CHANMODE 0
|
||||
GUID {6D40E4DA-D9A6-5149-915A-965CEB60DCAE}
|
||||
<SOURCE WAVE
|
||||
FILE "Media/sidechain-distortion_stems_Welcome Pad.wav"
|
||||
>
|
||||
>
|
||||
>
|
||||
<TRACK {F52BCBA5-0B54-6B40-8D30-4E09891F36EC}
|
||||
NAME Drums
|
||||
PEAKCOL 16576
|
||||
BEAT -1
|
||||
AUTOMODE 0
|
||||
PANLAWFLAGS 3
|
||||
VOLPAN 0 0 -1 -1 1
|
||||
MUTESOLO 1 0 0
|
||||
IPHASE 0
|
||||
PLAYOFFS 0 1
|
||||
ISBUS 0 0
|
||||
BUSCOMP 0 0 0 0 0
|
||||
SHOWINMIX 1 0.6667 0.5 1 0.5 0 0 0
|
||||
FIXEDLANES 9 0 0 0 0
|
||||
SEL 0
|
||||
REC 0 0 1 0 0 0 0 0
|
||||
VU 2
|
||||
TRACKHEIGHT 0 0 0 0 0 0 0
|
||||
INQ 0 0 0 0.5 100 0 0 100
|
||||
NCHAN 2
|
||||
FX 1
|
||||
TRACKID {F52BCBA5-0B54-6B40-8D30-4E09891F36EC}
|
||||
PERF 0
|
||||
MIDIOUT -1
|
||||
MAINSEND 1 0
|
||||
<FXCHAIN
|
||||
WNDRECT 900 170 815 444
|
||||
SHOW 0
|
||||
LASTSEL 0
|
||||
DOCKED 0
|
||||
BYPASS 0 0 0
|
||||
<VST "VST: ReaEQ (Cockos)" reaeq.vst.dylib 0 "" 1919247729<56535472656571726561657100000000> ""
|
||||
cWVlcu5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAAIAAAABAAAAAAAAAAIAAAAAAAAAagAAAAEAAAAAABAA
|
||||
IQAAAAIAAAADAAAAAQAAAJ0Q6aODFJFAAAAAAAAA8D+amZmZmZnpPwEEAAAAAQAAADNAfmGLZFZANFOMBg1H7z9/arx0kxjsPwEBAAAAAQAAAModDNkGk/o/AAAAAEAC
|
||||
AABrAQAAAgAAAA==
|
||||
AAAQAAAA
|
||||
>
|
||||
FLOATPOS 0 0 0 0
|
||||
FXID {4CCB9CFD-8185-AE45-8B5D-ADB8ED3C4344}
|
||||
WAK 0 0
|
||||
>
|
||||
<ITEM
|
||||
POSITION 0
|
||||
SNAPOFFS 0
|
||||
LENGTH 12.5
|
||||
LOOP 1
|
||||
ALLTAKES 0
|
||||
FADEIN 1 0 0 1 0 0 0
|
||||
FADEOUT 1 0 0 1 0 0 0
|
||||
MUTE 0 0
|
||||
SEL 0
|
||||
IGUID {AEF812EA-814E-294B-8BFA-79DB6C6C36A2}
|
||||
IID 1
|
||||
NAME Drums.wav
|
||||
VOLPAN 1 0 1 -1
|
||||
SOFFS 0
|
||||
PLAYRATE 1 1 0 -1 0 0.0025
|
||||
CHANMODE 0
|
||||
GUID {DE8AA9D2-A674-E747-9071-19071A8A8C7E}
|
||||
<SOURCE WAVE
|
||||
FILE "Media/Drums.wav"
|
||||
>
|
||||
>
|
||||
>
|
||||
>
|
||||
7
clap/env.sh.html
Normal file
7
clap/env.sh.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><title></title><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{display: flex;flex-direction:column;justify-content:stretch;align-items:center;min-height:100vh;justify-content:center;font:12pt sans-serif;color:#FFF;background:#222;margin:0;padding:0}img,video{max-width:100vw;max-height:100vh;flex-shrink:1}a{color:#8AF;text-decoration:none;margin-top:1em}iframe{background:#FFF;width:100%;flex-grow:0.8}</style>
|
||||
</head><body><form onsubmit="return false"><input id="p" type="password" autofocus><input type="submit" value="go" onclick="d(document.querySelector('#p').value).catch(e=>document.body.textContent='failed')"></form><script>
|
||||
let balloonOptions={"rounds":5,"buffers":32768},s64="Ho9CMRCSYNw84y+8VRdnwVrm5eZBlEWlC8LK0oP2AlM=",e64="xGiUxaRUjqura2lejNiFdvZs6SQB1P3wY9COVVzbHk6X5Z84KO_YaoeUq0hathjnPiVsUNHBvnsEmrP8q06pElSZeo8brf2bZWxSf6NxG3TL2Ghh_2Rw1Gl7qSpeJZ6vr3jnd-fL5hzC5nAbD2Hnx6mgH0sW3DMarGYnBItq3O8TE9LzvWSWceBGhD518fyjDKThWJEzvkTG5f04mcixVMdOQQj09zgO0Msd0CG-S65WFRqGzfPmnuPFCzyknhCdjHy8At1GtssIib3h-pBeWwyCnezZX5iprn8gnPhxgSHZgMVp082L18XqcaVAiiry8XGi3B-QGmRW5V2xuJGz3s-1GMJxl1-4Od7X-9yRwsmA2LplZtATa5zpF50pUspSXGGn1ZDWpTft34SW3Tf-cIEJ_PfitsV-w7k1OGMKgst6X5XxITQwhGYiO6GiO6T189ZDgswexN9jb-AZTFrLI_9U_53kJj-wjar2a8a1wSaBXmNivtPGDmALAFyS-zSj9Y_yllulg1h482G8MhTE4mOp3uIxOw";
|
||||
let cryptoUtils=(function r(){let n={factory:r};var e=[];for(let t=2;e.length<64;++t)e.some(r=>!(t%r))||e.push(t);let g=4294967296,p=e.slice(0,8).map(r=>Math.sqrt(r)*g|0),D=e.map(r=>Math.cbrt(r)*g|0),h=new Int32Array(64),d=new Int32Array(32),x=n.sha256=function(r,t){"string"==typeof r&&(r=(new TextEncoder).encode(r));var e="hex"==t,n=(t=(t=e?null:t)||new Uint8Array(32),r.length),a=16*Math.ceil((n+1+8)/64),i=32==a?d.fill(0):new Int32Array(a),o=new Uint8Array(i.buffer),y=(o.set(r),o[n]=128,new DataView(i.buffer));for(let r=0;r<a;++r)i[r]=y.getInt32(4*r);i[a-1]=8*n|0,i[a-2]=8*n/g|0;var f=new Int32Array(t.buffer);for(let r=0;r<8;++r)f[r]=p[r];var l=f.slice(0,8);for(let t=0;t<a;t+=16){for(let r=0;r<16;++r)h[r]=i[t+r];for(let r=16;r<64;++r){var w=h[r-15],c=h[r-2];h[r]=h[r-16]+h[r-7]+((w>>>7|w<<25)^(w>>>18|w<<14)^w>>>3)+((c>>>17|c<<15)^(c>>>19|c<<13)^c>>>10)|0}for(let r=0;r<64;r++){var A=l[0],u=l[4],v=l[7]+((u>>>6|u<<26)^(u>>>11|u<<21)^(u>>>25|u<<7))+(u&l[5]^~u&l[6])+D[r]+h[r],s=(A&l[1]^A&l[2]^l[1]&l[2])+((A>>>2|A<<30)^(A>>>13|A<<19)^(A>>>22|A<<10));l[7]=l[6],l[6]=l[5],l[5]=u,l[4]=l[3]+v|0,l[3]=l[2],l[2]=l[1],l[1]=A,l[0]=v+s|0}for(let r=0;r<8;r++)f[r]=l[r]=f[r]+l[r]|0}var U=new DataView(t.buffer);for(let r=0;r<8;r++)U.setInt32(4*r,f[r]);return e?Array.from(t,r=>(r>>>4).toString(16)+(15&r).toString(16)).join(""):t};return n.balloon=async function(n,r,t={}){var e=new TextEncoder,a=("string"==typeof n&&(n=x(e.encode(n))),"string"==typeof r&&(text=x(e.encode(r))),t.buffers=t.buffers||32768),i=t.rounds=t.rounds||4,o=t.delta=t.delta||3,y=[],f=t.workPeriod||100;let l=f*(1/(t.workRatio||1)-1);var w=t.progress||(r=>r);32!=n.length&&(n=x(n)),32!=r.length&&(r=x(r));let c=new Uint8Array(68),A=new DataView(c.buffer);var u=a*(1+i*(1+2*o));let v=0;function s(t,e){A.setUint32(0,v++,!0);for(let r=0;r<32;++r)c[4+r]=t[r];for(let r=0;r<32;++r)c[36+r]=e[r];return x(c)}y[0]=s(r,n);for(let r=1;r<a;++r)y[r]=s(y[r-1],n);let U=Date.now()+f;var g=new Uint8Array(32),p=new DataView(g.buffer);for(let e=0;e<i;++e)for(let t=0;t<a;++t){Date.now()>U&&(w(v,u),e=await new Promise((t,r)=>{setTimeout(r=>t(e),l)}),U=Date.now()+f);var D=0==t?y[a-1]:y[t-1];y[t]=s(D,y[t]);for(let r=0;r<o;++r){p.setUint32(0,e,!0),p.setUint32(4,t,!0),p.setUint32(8,r,!0);var h=s(n,g),h=new DataView(h.buffer).getUint32(0,!0);y[t]=s(y[t],y[h%a])}}return w(v,u),y[a-1]},n.aesPair=async function(r){return"string"==typeof r&&(r=Uint8Array.fromBase64(r)),crypto.subtle.importKey("raw",r,"AES-GCM",!1,["encrypt","decrypt"]).then(a=>({key:a,encrypt:(r,e)=>{"string"==typeof r&&(r=(new TextEncoder).encode(r));let n=new Uint8Array(12);return self.crypto.getRandomValues(n),crypto.subtle.encrypt({name:"AES-GCM",iv:n,tagLength:128},a,r).then(r=>{r=new Uint8Array(r);var t=new Uint8Array(12+r.length);return t.set(n),t.set(r,12),e?t.toBase64(!0):t})},decrypt:(r,t)=>{var e=(r="string"==typeof r?Uint8Array.fromBase64(r):r).subarray(0,12),r=r.subarray(12);return crypto.subtle.decrypt({name:"AES-GCM",iv:e,tagLength:128},a,r).then(r=>t?new TextDecoder("utf-8",{fatal:!0}).decode(r):r)}}))},n.balloonPair=async function(r,t,e={}){return n.balloon(r,t,e).then(n.aesPair)},Uint8Array.prototype.toBase64=function(r){let t="";return this.forEach(r=>t+=String.fromCharCode(r)),t=btoa(t),t=r?t.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+/,""):t},Uint8Array.fromBase64=r=>Uint8Array.from(atob(r.replace(/_/g,"/").replace(/-/g,"+")),r=>r.charCodeAt(0)),Uint8Array.prototype.toHex=function(){return Array.from(this,r=>(r>>>4).toString(16)+(15&r).toString(16)).join("")},Uint8Array.fromHex=t=>{var e=t.length/2,n=new Uint8Array(e);for(let r=0;r<e;++r)n[r]=parseInt(t.slice(2*r,2*r+2),16)||0;return n},n})();
|
||||
async function d(t){let e=document.body;balloonOptions.progress=((t,n)=>e.textContent=Math.round(100*t/n)+"%");let n=await cryptoUtils.balloonPair(Uint8Array.fromBase64(s64),t,balloonOptions),a=await n.decrypt(e64);e.textContent="";let o=new DataView(a),r=new Uint8Array(a),i=0;function l(t){let e=4294967296*o.getUint32(i)+o.getUint32(i+4);i+=8;let n=r.subarray(i,i+e);return i+=e,t?new TextDecoder("utf-8",{fatal:!0}).decode(n):n}let d={image:"img",audio:"audio",video:"video",text:"iframe"};for(;i<r.length;){let t=l(1),n=l(1),a=document.createElement("a");a.href=URL.createObjectURL(new File([l()],t,{type:n})),a.setAttribute("download",a.textContent=t),e.append(a);let o=n.split("/")[0];if(d[o]){let t=document.createElement(d[o]);t.src=t.data=a.href,t.controls=!0,e.append(t)}}}
|
||||
</script></body></html>
|
||||
62
clap/source/basics.cpp
Normal file
62
clap/source/basics.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef LOG_EXPR
|
||||
# include <iostream>
|
||||
# define LOG_EXPR(expr) std::cout << #expr " = " << (expr) << std::endl;
|
||||
#endif
|
||||
|
||||
#include "../crunch.h"
|
||||
#include "../limiter.h"
|
||||
#include "../reverb.h"
|
||||
|
||||
#include "./clap-stfx.h"
|
||||
|
||||
static stfx::clap::Plugins plugins;
|
||||
bool clap_init(const char *path) {
|
||||
plugins.add<signalsmith::basics::CrunchSTFX>({
|
||||
.clap_version = CLAP_VERSION,
|
||||
.id = "uk.co.signalsmith.basics.crunch",
|
||||
.name = "[Basics] Crunch",
|
||||
.vendor = "Signalsmith Audio",
|
||||
.url = "",
|
||||
.manual_url = "",
|
||||
.support_url = "",
|
||||
.version = "1.0.0"
|
||||
}, {
|
||||
CLAP_PLUGIN_FEATURE_AUDIO_EFFECT,
|
||||
CLAP_PLUGIN_FEATURE_DISTORTION,
|
||||
});
|
||||
|
||||
plugins.add<signalsmith::basics::LimiterSTFX>({
|
||||
.clap_version = CLAP_VERSION,
|
||||
.id = "uk.co.signalsmith.basics.limiter",
|
||||
.name = "[Basics] Limiter",
|
||||
.vendor = "Signalsmith Audio",
|
||||
.url = "",
|
||||
.manual_url = "",
|
||||
.support_url = "",
|
||||
.version = "1.0.0"
|
||||
}, {
|
||||
CLAP_PLUGIN_FEATURE_AUDIO_EFFECT,
|
||||
CLAP_PLUGIN_FEATURE_LIMITER,
|
||||
});
|
||||
|
||||
plugins.add<signalsmith::basics::ReverbSTFX>({
|
||||
.clap_version = CLAP_VERSION,
|
||||
.id = "uk.co.signalsmith.basics.reverb",
|
||||
.name = "[Basics] Reverb",
|
||||
.vendor = "Signalsmith Audio",
|
||||
.url = "",
|
||||
.manual_url = "",
|
||||
.support_url = "",
|
||||
.version = "1.0.0"
|
||||
}, {
|
||||
CLAP_PLUGIN_FEATURE_AUDIO_EFFECT,
|
||||
CLAP_PLUGIN_FEATURE_REVERB,
|
||||
});
|
||||
return plugins.clap_init(path);
|
||||
}
|
||||
void clap_deinit() {
|
||||
plugins.clap_deinit(path);
|
||||
}
|
||||
const void * clap_get_factory(const char *id) {
|
||||
return plugins.clap_get_factory(id);
|
||||
}
|
||||
41
clap/source/clap-stfx.h
Normal file
41
clap/source/clap-stfx.h
Normal file
@ -0,0 +1,41 @@
|
||||
#include "clap/clap.h"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace stfx { namespace clap {
|
||||
|
||||
template<template<class> class EffectSTFX>
|
||||
struct Plugin : public clap_plugin {
|
||||
template<class ...Args>
|
||||
Plugin(const clap_plugin_descriptor *desc, Args ...args) : effect(args...) {
|
||||
this->desc = desc;
|
||||
this->plugin_data = nullptr;
|
||||
}
|
||||
private:
|
||||
stfx::LibraryEffect<float, EffectSTFX> effect;
|
||||
};
|
||||
|
||||
struct Plugins {
|
||||
|
||||
template<template<class> class EffectSTFX, class ...Args>
|
||||
void add(clap_plugin_descriptor desc, std::initializer_list<const char *> features, Args ...args) {
|
||||
size_t index = featureLists.size();
|
||||
|
||||
featureLists.emplace_back(features);
|
||||
featureLists[index].push_back(nullptr);
|
||||
desc.features = featureLists[index].data();
|
||||
descriptors.push_back(desc);
|
||||
|
||||
creates.push_back([=](){
|
||||
return new Plugin<EffectSTFX>(&descriptors[index], args...);
|
||||
});
|
||||
}
|
||||
private:
|
||||
std::vector<std::vector<const char *>> featureLists;
|
||||
std::vector<clap_plugin_descriptor> descriptors;
|
||||
std::vector<std::function<const clap_plugin_t *()>> creates;
|
||||
};
|
||||
|
||||
}} // namespace
|
||||
14
clap/source/clap_entry.cpp
Normal file
14
clap/source/clap_entry.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "clap/entry.h"
|
||||
|
||||
extern bool clap_init(const char *);
|
||||
extern void clap_deinit();
|
||||
extern const void * clap_get_factory(const char *);
|
||||
|
||||
extern "C" {
|
||||
const CLAP_EXPORT clap_plugin_entry clap_entry{
|
||||
.clap_version = CLAP_VERSION,
|
||||
.init = clap_init,
|
||||
.deinit = clap_deinit,
|
||||
.get_factory = clap_get_factory
|
||||
};
|
||||
}
|
||||
286
crunch.h
286
crunch.h
@ -1,5 +1,5 @@
|
||||
/* Copyright 2022 Signalsmith Audio Ltd. / Geraint Luff
|
||||
Released under the Boost Software License (see LICENSE.txt) */
|
||||
See LICENSE.txt and SUPPORT.txt */
|
||||
#ifndef SIGNALSMITH_BASICS_CRUNCH_H
|
||||
#define SIGNALSMITH_BASICS_CRUNCH_H
|
||||
|
||||
@ -12,159 +12,163 @@ SIGNALSMITH_DSP_VERSION_CHECK(1, 4, 1)
|
||||
|
||||
namespace signalsmith { namespace basics {
|
||||
|
||||
template<class BaseEffect>
|
||||
class CrunchSTFX : public BaseEffect {
|
||||
using typename BaseEffect::Sample;
|
||||
using typename BaseEffect::ParamRange;
|
||||
using typename BaseEffect::ParamStepped;
|
||||
template<class BaseEffect>
|
||||
class CrunchSTFX;
|
||||
|
||||
int channels = 0;
|
||||
signalsmith::rates::Oversampler2xFIR<Sample> oversampler;
|
||||
struct GainshapeADAA {
|
||||
Sample prevX = 0, prevIntegral = 0;
|
||||
Sample fuzzPositive = 1, fuzzNegative = 1;
|
||||
using CrunchFloat = stfx::LibraryEffect<float, CrunchSTFX>;
|
||||
using CrunchDouble = stfx::LibraryEffect<double, CrunchSTFX>;
|
||||
|
||||
void setFuzzFactor(Sample k) {
|
||||
fuzzPositive = 1 + k - k*k;
|
||||
fuzzNegative = 1 - k - k*k;
|
||||
prevIntegral = integralGain(prevX);
|
||||
}
|
||||
template<class BaseEffect>
|
||||
class CrunchSTFX : public BaseEffect {
|
||||
static constexpr int oversampleHalfLatency = 16;
|
||||
static constexpr Sample autoGainLevel = 0.1;
|
||||
|
||||
Sample gain(Sample x) const {
|
||||
Sample fuzzGain = (x >= 0 ? fuzzPositive : fuzzNegative);
|
||||
return fuzzGain/std::sqrt(1 + x*x);
|
||||
}
|
||||
Sample integralGain(Sample x) const {
|
||||
if (x >= 0) {
|
||||
return fuzzPositive*std::log(std::sqrt(1 + x*x) + x);
|
||||
} else { // more accurate if we flip it
|
||||
return -fuzzNegative*std::log(std::sqrt(1 + x*x) - x);
|
||||
}
|
||||
}
|
||||
Sample averageGain(Sample range) const {
|
||||
// Average gain from 0-range, ignoring fuzz
|
||||
return std::log(std::sqrt(1 + range*range) + range)/range;
|
||||
}
|
||||
static constexpr Sample minDiffX = 1e-4;
|
||||
using typename BaseEffect::Sample;
|
||||
using typename BaseEffect::ParamRange;
|
||||
using typename BaseEffect::ParamStepped;
|
||||
|
||||
ParamRange drive{4};
|
||||
ParamRange fuzz{0};
|
||||
ParamRange toneHz{2000};
|
||||
ParamRange outGain{1};
|
||||
|
||||
const bool autoGain;
|
||||
CrunchSTFX(bool autoGain=true) : autoGain(autoGain) {}
|
||||
|
||||
template<class Storage>
|
||||
void state(Storage &storage) {
|
||||
storage.info("[Basics] Crunch", "A simple distortion/saturation");
|
||||
int version = storage.version(0);
|
||||
if (version != 0) return;
|
||||
|
||||
using namespace signalsmith::units;
|
||||
storage.range("drive", drive)
|
||||
.info("drive", "pre-distortion input gain")
|
||||
.range(dbToGain(-12), 4, dbToGain(40))
|
||||
.unit("dB", 1, dbToGain, gainToDb)
|
||||
.unit("");
|
||||
storage.range("fuzz", fuzz)
|
||||
.info("fuzz", "amplitude-independent distortion")
|
||||
.range(0, 0.5, 1)
|
||||
.unit("%", 0, pcToRatio, ratioToPc);
|
||||
storage.range("toneHz", toneHz)
|
||||
.info("tone", "limits the brightness of the distortion")
|
||||
.range(100, 4000, 20000)
|
||||
.unit("Hz", 0, 0, 1000)
|
||||
.unit("kHz", 1, kHzToHz, hzToKHz, 1000, 1e100);
|
||||
|
||||
storage.range("outGain", outGain)
|
||||
.info("out", "output gain")
|
||||
.range(dbToGain(-12), 1, dbToGain(24))
|
||||
.unit("dB", 1, dbToGain, gainToDb)
|
||||
.unit("");
|
||||
}
|
||||
|
||||
template<class Config>
|
||||
void configureSTFX(Config &config) {
|
||||
channels = config.outputChannels = config.inputChannels;
|
||||
config.auxInputs.resize(0);
|
||||
config.auxOutputs.resize(0);
|
||||
|
||||
void reset() {
|
||||
prevX = 0;
|
||||
prevIntegral = integralGain(prevX);
|
||||
}
|
||||
oversampler.resize(channels, config.maxBlockSize, oversampleHalfLatency, std::min(0.45, 21000/config.sampleRate));
|
||||
gainshapers.resize(channels);
|
||||
toneFilters.resize(channels);
|
||||
outputFilters.resize(channels);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
oversampler.reset();
|
||||
for (auto &g : gainshapers) g.reset();
|
||||
for (auto &f : toneFilters) f.reset();
|
||||
for (auto &f : outputFilters) f.reset();
|
||||
}
|
||||
|
||||
int latencySamples() const {
|
||||
return oversampleHalfLatency*2;
|
||||
}
|
||||
|
||||
template <class Io, class Config, class Block>
|
||||
void processSTFX(Io &io, Config &config, Block &block) {
|
||||
auto inputGain = block.smooth(drive);
|
||||
|
||||
Sample operator()(Sample x) {
|
||||
Sample diffX = x - prevX;
|
||||
Sample integral = integralGain(x);
|
||||
Sample diffIntegral = integral - prevIntegral;
|
||||
prevX = x;
|
||||
prevIntegral = integral;
|
||||
if (std::abs(diffX) < minDiffX) return gain(x);
|
||||
return diffIntegral/diffX;
|
||||
}
|
||||
};
|
||||
std::vector<GainshapeADAA> gainshapers;
|
||||
using Filter = signalsmith::filters::BiquadStatic<Sample>;
|
||||
std::vector<Filter> toneFilters, outputFilters;
|
||||
|
||||
static constexpr int oversampleHalfLatency = 16;
|
||||
static constexpr Sample autoGainLevel = 0.1;
|
||||
public:
|
||||
ParamRange drive{4};
|
||||
ParamRange fuzz{0};
|
||||
ParamRange toneHz{2000};
|
||||
ParamRange outGain{1};
|
||||
|
||||
bool autoGain;
|
||||
CrunchSTFX(bool autoGain=true) : autoGain(autoGain) {}
|
||||
|
||||
template<class Storage>
|
||||
void state(Storage &storage) {
|
||||
storage.info("[Basics] Crunch", "A simple distortion/saturation");
|
||||
int version = storage.version(0);
|
||||
if (version != 0) return;
|
||||
|
||||
using namespace signalsmith::units;
|
||||
storage.range("drive", drive)
|
||||
.info("drive", "pre-distortion input gain")
|
||||
.range(dbToGain(-12), 4, dbToGain(40))
|
||||
.unit("dB", 1, dbToGain, gainToDb)
|
||||
.unit("");
|
||||
storage.range("fuzz", fuzz)
|
||||
.info("fuzz", "amplitude-independent distortion")
|
||||
.range(0, 0.5, 1)
|
||||
.unit("%", 0, pcToRatio, ratioToPc);
|
||||
storage.range("toneHz", toneHz)
|
||||
.info("tone", "limits the brightness of the distortion")
|
||||
.range(100, 4000, 20000)
|
||||
.unit("Hz", 0, 0, 1000)
|
||||
.unit("kHz", 1, kHzToHz, hzToKHz, 1000, 1e100);
|
||||
|
||||
storage.range("outGain", outGain)
|
||||
.info("out", "output gain")
|
||||
.range(dbToGain(-12), 1, dbToGain(24))
|
||||
.unit("dB", 1, dbToGain, gainToDb)
|
||||
.unit("");
|
||||
double outputGainFrom = outGain.from();
|
||||
double outputGainTo = outGain.to();
|
||||
if (autoGain) {
|
||||
Sample averageGain = gainshapers[0].averageGain(autoGainLevel*drive.from());
|
||||
outputGainFrom /= drive.from()*averageGain;
|
||||
outputGainTo /= drive.to()*gainshapers[0].averageGain(autoGainLevel*drive.to());
|
||||
}
|
||||
auto outputGain = block.smooth(outputGainFrom, outputGainTo);
|
||||
|
||||
template<class Config>
|
||||
void configure(Config &config) {
|
||||
channels = config.outputChannels = config.inputChannels;
|
||||
config.auxInputs.resize(0);
|
||||
config.auxOutputs.resize(0);
|
||||
|
||||
oversampler.resize(channels, config.maxBlockSize, oversampleHalfLatency, std::min(0.45, 21000/config.sampleRate));
|
||||
gainshapers.resize(channels);
|
||||
toneFilters.resize(channels);
|
||||
outputFilters.resize(channels);
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
auto &gainshaper = gainshapers[c];
|
||||
gainshaper.setFuzzFactor(fuzz);
|
||||
auto &toneFilter = toneFilters[c];
|
||||
toneFilter.lowpass(toneHz/(config.sampleRate*2));
|
||||
auto &outputFilter = outputFilters[c];
|
||||
outputFilter.highpass((10 + 40*fuzz)/(config.sampleRate*2)); // more aggressive when fuzz is enabled, since it's very asymmetrical
|
||||
|
||||
oversampler.upChannel(c, io.input[c], block.length);
|
||||
Sample *samples = oversampler[c];
|
||||
for (int i = 0; i < block.length*2; ++i) {
|
||||
double hi = i*0.5;
|
||||
Sample x = samples[i]*inputGain.at(hi);
|
||||
Sample gain = gainshaper(x)*outputGain.at(hi);
|
||||
Sample y = x*toneFilter(gain);
|
||||
samples[i] = outputFilter(y);
|
||||
}
|
||||
oversampler.downChannel(c, io.output[c], block.length);
|
||||
}
|
||||
|
||||
}
|
||||
private:
|
||||
int channels = 0;
|
||||
signalsmith::rates::Oversampler2xFIR<Sample> oversampler;
|
||||
struct GainshapeADAA {
|
||||
Sample prevX = 0, prevIntegral = 0;
|
||||
Sample fuzzPositive = 1, fuzzNegative = 1;
|
||||
|
||||
void setFuzzFactor(Sample k) {
|
||||
fuzzPositive = 1 + k - k*k;
|
||||
fuzzNegative = 1 - k - k*k;
|
||||
prevIntegral = integralGain(prevX);
|
||||
}
|
||||
|
||||
Sample gain(Sample x) const {
|
||||
Sample fuzzGain = (x >= 0 ? fuzzPositive : fuzzNegative);
|
||||
return fuzzGain/std::sqrt(1 + x*x);
|
||||
}
|
||||
Sample integralGain(Sample x) const {
|
||||
if (x >= 0) {
|
||||
return fuzzPositive*std::log(std::sqrt(1 + x*x) + x);
|
||||
} else { // more accurate if we flip it
|
||||
return -fuzzNegative*std::log(std::sqrt(1 + x*x) - x);
|
||||
}
|
||||
}
|
||||
Sample averageGain(Sample range) const {
|
||||
// Average gain from 0-range, ignoring fuzz
|
||||
return std::log(std::sqrt(1 + range*range) + range)/range;
|
||||
}
|
||||
static constexpr Sample minDiffX = 1e-4;
|
||||
|
||||
void reset() {
|
||||
oversampler.reset();
|
||||
for (auto &g : gainshapers) g.reset();
|
||||
for (auto &f : toneFilters) f.reset();
|
||||
for (auto &f : outputFilters) f.reset();
|
||||
prevX = 0;
|
||||
prevIntegral = integralGain(prevX);
|
||||
}
|
||||
|
||||
int latencySamples() const {
|
||||
return oversampleHalfLatency*2;
|
||||
}
|
||||
|
||||
template <class Io, class Config, class Block>
|
||||
void processSTFX(Io &io, Config &config, Block &block) {
|
||||
auto inputGain = block.smooth(drive);
|
||||
|
||||
double outputGainFrom = outGain.from();
|
||||
double outputGainTo = outGain.to();
|
||||
if (autoGain) {
|
||||
Sample averageGain = gainshapers[0].averageGain(autoGainLevel*drive.from());
|
||||
outputGainFrom /= drive.from()*averageGain;
|
||||
outputGainTo /= drive.to()*gainshapers[0].averageGain(autoGainLevel*drive.to());
|
||||
}
|
||||
auto outputGain = block.smooth(outputGainFrom, outputGainTo);
|
||||
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
auto &gainshaper = gainshapers[c];
|
||||
gainshaper.setFuzzFactor(fuzz);
|
||||
auto &toneFilter = toneFilters[c];
|
||||
toneFilter.lowpass(toneHz/(config.sampleRate*2));
|
||||
auto &outputFilter = outputFilters[c];
|
||||
outputFilter.highpass((10 + 40*fuzz)/(config.sampleRate*2)); // more aggressive when fuzz is enabled, since it's very asymmetrical
|
||||
|
||||
oversampler.upChannel(c, io.input[c], block.length);
|
||||
Sample *samples = oversampler[c];
|
||||
for (int i = 0; i < block.length*2; ++i) {
|
||||
double hi = i*0.5;
|
||||
Sample x = samples[i]*inputGain.at(hi);
|
||||
Sample gain = gainshaper(x)*outputGain.at(hi);
|
||||
Sample y = x*toneFilter(gain);
|
||||
samples[i] = outputFilter(y);
|
||||
}
|
||||
oversampler.downChannel(c, io.output[c], block.length);
|
||||
}
|
||||
Sample operator()(Sample x) {
|
||||
Sample diffX = x - prevX;
|
||||
Sample integral = integralGain(x);
|
||||
Sample diffIntegral = integral - prevIntegral;
|
||||
prevX = x;
|
||||
prevIntegral = integral;
|
||||
if (std::abs(diffX) < minDiffX) return gain(x);
|
||||
return diffIntegral/diffX;
|
||||
}
|
||||
};
|
||||
|
||||
using Crunch = stfx::LibraryEffect<float, CrunchSTFX>;
|
||||
std::vector<GainshapeADAA> gainshapers;
|
||||
using Filter = signalsmith::filters::BiquadStatic<Sample>;
|
||||
std::vector<Filter> toneFilters, outputFilters;
|
||||
};
|
||||
|
||||
}} // namespace
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
/* Copyright 2021-2022 Geraint Luff / Signalsmith Audio Ltd
|
||||
/* Signalsmith's Templated FX
|
||||
|
||||
Copyright 2021-2022 Geraint Luff / Signalsmith Audio Ltd
|
||||
Released under the Boost Software License (see LICENSE.txt)
|
||||
|
||||
|
||||
The main thing you need is `stfx::LibraryEffect<Sample, EffectTemplate>`
|
||||
which produces a simple effect class from an STFX effect template.
|
||||
*/
|
||||
@ -46,6 +48,7 @@ namespace stfx {
|
||||
return ms*0.001;
|
||||
}
|
||||
|
||||
// Templates to add commonly-used units to range parameters
|
||||
template<class RangeParam>
|
||||
RangeParam & rangeGain(RangeParam ¶m) {
|
||||
return param
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user