Open Andreya-Autumn opened 2 months ago
The key insight here is to not have normalize on load and store it and so on, but rather have a user action which lets the mapping or round robin level to the decibel change required to normalize to (whatever measure we want)
OK so here's the approach I would take laid out in quite a bit of detail. And I would do this in three steps.
Step one is add "sample analytics" to the codebase with a couple of very simple analytics (get peak and get rms) with a test perhaps
Step two is make it so the zone can use those analytics to normalize either one or all samples in its sample set
Step three is to make a UI gesture to hook that up.
peak and rms could go on sample.h but the project will eventually have lots of other sample analytics for things like transient detection and slicing and stuff, and those should not go on sample, since sample is really intended to be the store of raw data. So I propose
src/dsp/sample_analytics.h
and .cpp
namespace scxt::dsp::sample_analytics
{
float computePeak(const std::shared_ptr<sample::Sample> &s);
float computeRMS(const std::shared_ptr<sample::Sample> &s);
}
This approach means we could add things like 'find transients for slicing' here in the future.
Once those are implemented you are at merge point one!
You want a function in zone.h
to set the level. It probably has a signature like this
void setNormalizedSampleLevel(bool usePeak=false, int associatedSampleID=-1);
And then an implementation roughly like
void Zone::setNormalizedSampleLevel(bool usePeak, int associatedSampleID)
{
auto startSample = (associatedSampleID < 0) ? 0 : associatedSampleID;
auto endSample = (associatedSampleID < 0) ? maxSamplesPerZone : associatedSampleID;
for (auto i=startSample; i < endSample; ++i)
{
if (sampleData.samples[i].active && samplePointers[i])
{
auto normVal = isPeak ?
dsp::sample_analytics::computePeak(samplePointers[i]) :
dsp::sample_analytics::computerRMS(samplePointers[i]);
sampleData.samples[i].volume = (whatever calculation of norm val sets volume properly)
}
}
}
cool
Go read the how the ui hangs together documentation some or ask on discord but the short version is
if you get to this point I'll add some more comments
Sample Analytics section is done
The proposal to implement is as follows (from my understanding):
The normalization factor (in dB) will be displayed once a sample has been normalized, something like "normalized: +11.58dB" (the method of normalization is not recorded, only its magnitude).
This normalization is kept separate from the mapping and is done per AssociatedSample
, and is not user-editable (except by the means of selecting a different measure to normalize by, or resetting normalization).
This factor is implicitly shown in the waveform display, and zone amplitude would be an offset from this value.
Yup; and to do that you will add a new variable to associated sample called 'normalizationAmplitude' which streams etc... and the UI can trigger based on it not being exactly 0.
Then the paint method of MappingPane.cpp SampleDisplay can show the text. It is a 'HasEditor' so you have methods like
auto font = editor->themeApplier.interRegularFor(13);
auto col = editor->themeColor(theme::ColorMap::generic_content_high);
g.drawText("Normalize", (rectangle), juce::Justification::centered);
While in there you might want to add the #973 fields also and set them up in streaming and stuff for them even if we don't add editors now.
Would be really useful for at least two reasons: We have a nice velocity feature which depends on samples being even level, and #947.
I imagine we'd want to use RMS, but some exploration needed there probably.