diff --git a/README.md b/README.md index 31a419f..b96d6db 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![SOUL-VA_logo](https://user-images.githubusercontent.com/42720670/143501884-f9a4daac-9460-4312-bacf-4984ef002dc4.png) # The SOUL Virtual Analog Library -[SOUL-VA](https://github.com/thezhe/SOUL-VA) is a collection of analog-inspired audio effects. Unlike other libraries, this project achieves [analytical](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems) solutions and [a strict -60dB peak amplitude limit for aliasing artifacts](https://github.com/thezhe/SOUL-VA#example-3-onepolec_lan-nonlinearity--500). Each effect can run at full quality at 44.1kHz without any additional antialiasing measures. In addition, most parameters are stable and artifact-free under modulation up through 20Hz. +[SOUL-VA](https://github.com/thezhe/SOUL-VA) is a collection of analog-inspired audio effects. Unlike other libraries, this project achieves [analytical](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems) solutions and [a strict -60dB peak amplitude limit for aliasing artifacts](https://github.com/thezhe/SOUL-VA#example-3-onepolec_lan-nonlinearity--500). Each effect can run at full quality at 44.1kHz without any additional antialiasing measures. In addition, unless marked otherwise, parameters are artifact-free under modulation up through 20Hz. ## Background Knowledge This library considers background knowledge trivial; *SOUL-VA does not re-explain any of the following concepts*: @@ -15,11 +15,7 @@ This library considers background knowledge trivial; *SOUL-VA does not re-explai The current effect endpoints in `VA::HighLevel` will not be removed (excluding major bugs and design revisions), but new endpoints may appear in updates. In other words, effects are backward compatible, but internal code is subject to changes. ## Contents -- `include/VA.soul` - single-file library -- `examples/main.soul` - example `[[main]]` instance for instantiating `Processors` from `VA::HighLevel` -- `examples/main.soulpatch` - 'includes' and 'links' VA.soul with main.soul -- `tests/testMain.m` - script that runs test cases on main.soulpatch -- `tools/soul.json` - [VSCode snippets](https://code.visualstudio.com/docs/editor/userdefinedsnippets) +Files within each top-level directory start with a short summary of their function. Users can utilize VA::HighLevel using only the `include/` and `examples/` directories. ## Official Ways to Use SOUL-VA 1. [SOUL Playground](https://soul.dev/lab/) @@ -32,17 +28,17 @@ Include soul.exe in the 'Path' environment variable. Set `SOUL-VA/tests/` as the ## Contributing Please post bugs in issues and feature requests in discussions. Bug fixes take priority. Pull requests are not accepted at the moment. -## Octave Examples -The following sections explain the output after running `testMain (44100)` on different `VA::HighLevel` effects (instantiated in `Processor [[main]]`). The script is useful for catching common errors in development without needing to listen to processed samples. -### Example 1: `Dummy` +## Octave Examples (`tests/testEffect.m`) +The following sections explain the output after running `testEffect (44100)` on different `VA::HighLevel` effects (instantiated in `effects.soul`). The script is useful for catching common errors in development without needing to listen to processed samples. +### Example Effect 1: `Dummy` The system is trivial (and linear) and simply passes signals through unmodified. Notice how the step response input is actually a pulse signal with values 0.5 and 0.25 so that the test can measure overshoot (up towards 1) and undershoot (down towards -1). The DC IO plot is the same as the decibel mapping of a dynamic range compressor with a ratio of 1 and SinRamp IO plot shows what the system would look like as a waveshaper (may not always be a function). ![Dummy2](https://user-images.githubusercontent.com/42720670/143499549-a8484fe7-bb55-4c24-8242-aa6dd5be6b1c.png) ![Dummy1](https://user-images.githubusercontent.com/42720670/143499553-e699e725-ad35-413c-9378-3121313d5d49.png) -### Example 2: `OnepoleC_Lan` (nonlinearity = 200) +### Example Effect 2: `OnepoleC_Lan` (nonlinearity = 200) The system is significantly nonlinear and all outputs show some sort of nontrivial filtering. While the magnitude response applies only to linear systems, its plot accurately predicts that `OnepoleC_Lan` tends to boost bass frequencies. ![OnepoleC_Lan(200)2](https://user-images.githubusercontent.com/42720670/143499888-6d6bb662-d376-4e94-90f3-c417c346b851.png) ![OnepoleC_Lan(200)1](https://user-images.githubusercontent.com/42720670/143499897-f637bf2f-9c7f-469a-954f-06ace715cf5c.png) -### Example 3: `OnepoleC_Lan` (nonlinearity = 500) +### Example Effect 3: `OnepoleC_Lan` (nonlinearity = 500) The system is nontrivial, but does not meet the standards of this library. Not all aliasing components are less than -60dB as shown by the partials (black dots) in the bottom right-hand corner of 'SinSweep (BW)'. These partials are not desired since they are not parallel to any harmonics/inharmonics, nor are their frequencies low enough to be residual DC noise. ![OnepoleC_Lan(500)2](https://user-images.githubusercontent.com/42720670/143499912-0e513b21-b668-488e-ae87-a767db9aadab.png) ![OnepoleC_Lan(500)1](https://user-images.githubusercontent.com/42720670/143499917-0621c055-8e9d-4c08-891e-cf0de483885d.png) diff --git a/include/VA.soul b/include/VA.soul index d48825e..a603092 100644 --- a/include/VA.soul +++ b/include/VA.soul @@ -35,10 +35,9 @@ /** Task List Test: - min buffer size - - fix approximation for ADAA2 and snippet - - smooth nl, increase slew rate Future Tasks: + - add updateSample to snippets - all ADAA in one processor - Add more static_assert checks - convert Omega endpoints to cutoff endpoints for consistency @@ -147,8 +146,8 @@ VA::HighLevel Processors 12/22/21 let { dcBlockerIn = filt::dc_blocker::Processor (5.f); - dcBlockerOut = filt::dc_blocker::Processor (5.f); - + dcBlockerOutN = DC_BlockerN::Processor (5.f, 8); + stereoLink = StereoLink::Processor (initialStereoLinkPercent); internal = Internal::Processor (initialNonlinearity) * 2; @@ -170,7 +169,7 @@ VA::HighLevel Processors 12/22/21 internal.lowpassOut -> onepoleMixer.lowpassIn; onepoleMixer.out -> autoSmoothedVolume.in; - autoSmoothedVolume.out -> dcBlockerOut -> out; + autoSmoothedVolume.out -> dcBlockerOutN -> out; } } @@ -602,6 +601,62 @@ Filter Processors 12/21/21 } } + /** Variable order DC blocker using soul::filters::dc_blocker */ + namespace DC_BlockerN + { + namespace M (int order) + { + struct Coeffs + { + filt::dc_blocker::Coeffs[order] coeffs; + } + + void update (Coeffs& c, float64 SampleRate, float64 freqHz) + { + for (int i = 0; i < order; ++i) + filt::dc_blocker::update (c.coeffs.at(i), SampleRate, freqHz); + } + + struct State + { + filt::dc_blocker::State[order] state; + } + + SampleType process (State& s, SampleType x, Coeffs& c) + { + var y = filt::dc_blocker::process (s.state.at(0), x, c.coeffs.at(0)); + + for (int i = 1; i < order; ++i) + y = filt::dc_blocker::process (s.state.at(i), y, c.coeffs.at(i)); + + return y; + } + } + + processor Processor (float64 frequency = 5, int order = 1) + { + input stream SampleType in; + output stream SampleType out; + + void run() + { + M(order)::State s; + M(order)::Coeffs c; + + M(order)::update (c, processor.frequency, frequency); + + loop + { + out << M(order)::process (s, in, c); + + advance(); + } + } + } + } + + + /** Modified version of https://soul.dev/lab/?id=Delay For feedback make a loop with a unit delay (i.e. DelayLine.out -> [1] -> add your effects here -> DelayLine.in) diff --git a/tests/effect.soul b/tests/effect.soul index a2f5464..eb9fa3d 100644 --- a/tests/effect.soul +++ b/tests/effect.soul @@ -1,4 +1,4 @@ -/** Duplicate of '../examples/main.soul' for testEffect.m */ +/** Duplicate of '../examples/main.soul' boilerplate for testEffect.m */ namespace main { @@ -66,7 +66,7 @@ namespace main let { //define effect as a 'Processor' from 'VA::HighLevel' - effect = VA::HighLevel::OnepoleC_Lan::Processor; + effect = VA::HighLevel::OnepoleC_Lan::Processor (200); //comment out for stereo up = FloatToSampleType::Processor; diff --git a/tests/effect.soulpatch b/tests/effect.soulpatch index d5ff1dd..f043a56 100644 --- a/tests/effect.soulpatch +++ b/tests/effect.soulpatch @@ -1,15 +1,15 @@ { "soulPatchV1": { - "ID": "com.TheZhe.SOULVA.main", + "ID": "com.TheZhe.SOULVA.effect", "version": "1.0", - "name": "main", - "description": "link VA.soul with main.soul", + "name": "effect", + "description": "link VA.soul with effect.soul", "category": "effect", "manufacturer": "TheZhe", "website": "https://github.com/thezhe/SOUL-VA", "isInstrument": false, "source": [ - "main.soul", + "effect.soul", "../include/VA.soul" ] } diff --git a/tools/soul.json b/tools/soul.jsonc similarity index 99% rename from tools/soul.json rename to tools/soul.jsonc index e1529b8..1ff0e8a 100644 --- a/tools/soul.json +++ b/tools/soul.jsonc @@ -1,3 +1,4 @@ +/** VSCode snippets (https://code.visualstudio.com/docs/editor/userdefinedsnippets) */ { "VA::graph": { "prefix": "VA::graph",