Awesome
Melatonin Parameters
A very basic C++ JUCE module that contains
- A logarithmic
NormalizableRange
"factory" that accepts an exponent value to toy with the amount of skew. stringFromTimeValue
andtimeValueFromString
helpers for parameters that behave in a user-friendly way.- Tests for the above written in Catch2.
These are the parameter behaviors I want by default. Having something like this in JUCE proper probably makes sense.
How to use
If you are a solo coder using Projucer, you could git clone and then manually add this module your project via the UI.
I'm not a fan of copying and pasting dependencies (IMO that's one of reasons we can't have nice things in C++). Assuming you use git and want/have modules in a modules
folder in your project, this will set you up with a git submodule tracking the main
branch:
git submodule add -b main https://github.com/sudara/melatonin_parameters modules/melatonin_parameters
git commit -m "Added melatonin_parameters submodule."
When you want to update melatonin_parameters, you can now run
git submodule update --remote --merge modules/melatonin_parameters
If you use CMake, you can inform JUCE about the module in your CMakeLists.txt
:
juce_add_module("modules/melatonin_parameters")
zzzzzz.... Wake me up when C++ has widely supported package management plzthxbai.
Running tests
Catch2 tests are in melatonin_parameters.cpp
surrounded by if RUN_MELATONIN_TESTS
Assuming you use CMake:
-
Have Catch2 setup in your project. See pamplejuce.
-
Add the module as above
-
Add the preprocessor flag to the test binary with:
target_compile_definitions(Tests PRIVATE RUN_MELATONIN_TESTS=1)
where Tests
is the name of your test binary.
Logarithmic Range
It has a default exponent setting of 6:
juce::AudioParameterFloat ("release", "Release", logarithmicRange (0, 15.0f), 0.1f),
A custom exponent of 10 might be a sensible value for frequency:
juce::AudioParameterFloat ("frequency", "Frequency", logarithmicRange (20.0f, 20000.0f, 10.0f), 0.1f),
Reversed Logarithmic Range
When you want the slider to be reversed in direction. The arguments are still in the same order, lowest possibility first.
juce::AudioParameterFloat ("release", "Release", reversedLogarithmicRange (0, 15.0f), 0.1f),
Why
Auditory perception of time and frequency is logarithmic, so it's a good default for many knobs in audio.
Imagine a release knob. The first little bit of the knob, you'd probably want some fine control over 0-100ms to craft short release times. Maybe by the time you get to the middle of the knob it's at 1s. And then the rest of the knob can handle 1-10s, where granularity is much less important.
Implementation
The math can be viewed at this desmos link.
<details>Github can't render latex, but here's the latex formulas in case the desmos link goes away.
From a normalized 0-1 to an unnormalized y0 to y1:
y_{0}\ +\frac{2^{kx}-1}{2^{k}-1}\left(y_{1}-y_{0}\right)
To a normalized 0-1 from an unnormalized y0 to y1
\frac{\log_{2}\left(\frac{x-y_{0}}{y_{1}-y_{0}}\left(2^{k}-1\right)+1\right)}{k}
</details>
Understanding the math is a bit of a pain, but the core idea is to be able to translate to and from any logarithmic range to JUCE's normalized 0-1 range.
See the references for more detail. Other solutions I ran into had oddities such as not allowing the logarithmic minimum to be 0 (as it would result in division by 0).
I went back to the math to cook up something that I could understand, where the range could start at 0, and where the exponential-ness (skew) and starting positions could be adjusted.
stringFromTimeValue and timeValueFromString
How to use
juce::AudioParameterFloat> ("delay", "Delay", logarithmicRange(0.0f, 1.0f), 0.0f, juce::String(), juce::AudioProcessorParameter::genericParameter, stringFromTimeValue, timeValueFromString)
Why?
In JUCE, parameters don't "know" what units they are nor how to display them. Even though most plugins will have similar values such as frequency and time and "note value", the formatting display of those values is up to the user; there are no out of the box defaults.
stringFromTimeValue Implementation
This is is optimized for normal person usability, not for accuracy.
- Under 0.5s, values are displayed as ms with NO decimal places. It will look like
1ms
,5ms
, etc. - Excess 0s are removed.
1.00
is displayed as1s
- A max of two digits after the decimal place.
1.111
will display as1.11
.
timeValueFromString Implementation
-
Values can be with or without decimal
-
ms
ands
are accepted as units:0ms
,11.1ms
,100ms
,1.0s
,15.98s
are all valid. -
Without a unit, single digits or a decimal place will trigger seconds conversion:
1
or1.
or1.0
-
Without a unit, double and triple digits convert to ms.
See the tests for more detail.
References
- https://github.com/ffAudio/foleys_gui_magic/blob/master/Helpers/foleys_Conversions.h#L41
- https://forum.juce.com/t/logarithmic-slider-for-frequencies-iir-hpf/37569/21
- https://forum.cockos.com/showpost.php?p=2017848&postcount=11
TODO
- Implement and test snapToLegalValue
- Implement versioning to make it safer to use via CMake