Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Normalize sample" feature part 2: Normalization and UI #1089

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/engine/zone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include "sst/basic-blocks/mechanics/block-ops.h"
#include "group_and_zone_impl.h"
#include "dsp/sample_analytics.h"

namespace scxt::engine
{
Expand Down Expand Up @@ -154,6 +155,38 @@ void Zone::removeVoice(voice::Voice *v)
assert(false);
}

void Zone::setNormalizedSampleLevel(const bool usePeak, const int associatedSampleID)
{
const auto startSample = (associatedSampleID < 0) ? 0 : associatedSampleID;
const auto endSample = (associatedSampleID < 0) ? maxVariantsPerZone : associatedSampleID;

for (auto i = startSample; i < endSample; ++i)
{
if (variantData.variants[i].active && samplePointers[i])
{
auto normVal = usePeak ? dsp::sample_analytics::computePeak(samplePointers[i])
: dsp::sample_analytics::computeRMS(samplePointers[i]);
// convert linear measure into db
// To undo this, std::pow(amp / 10.f, 10.f)
variantData.variants[i].normalizationAmplitude = 10.f * std::log10(1.f / normVal);
}
}
}

void Zone::clearNormalizedSampleLevel(const int associatedSampleID)
{
const auto startSample = (associatedSampleID < 0) ? 0 : associatedSampleID;
const auto endSample = (associatedSampleID < 0) ? maxVariantsPerZone : associatedSampleID;

for (auto i = startSample; i < endSample; ++i)
{
if (variantData.variants[i].active && samplePointers[i])
{
variantData.variants[i].normalizationAmplitude = 0.f;
}
}
}

engine::Engine *Zone::getEngine()
{
if (parentGroup && parentGroup->parentPart && parentGroup->parentPart->parentPatch)
Expand Down
10 changes: 9 additions & 1 deletion src/engine/zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,18 @@ struct Zone : MoveableOnly<Zone>, HasGroupZoneProcessors<Zone>, SampleRateSuppor
int loopCountWhenCounted{0};

int64_t loopFade{0};
float normalizationAmplitude{0.f}; // db
// per-sample pitch and amplitude
float pitchOffset{0.f}; // semitones
float amplitude{0.f}; // db

bool operator==(const SingleVariant &other) const
{
return active == other.active && sampleID == other.sampleID &&
startSample == other.startSample && endSample == other.endSample &&
startLoop == other.startLoop && endLoop == other.endLoop;
startLoop == other.startLoop && endLoop == other.endLoop &&
normalizationAmplitude == other.normalizationAmplitude &&
pitchOffset == other.pitchOffset && amplitude == other.amplitude;
}
};

Expand Down Expand Up @@ -196,6 +202,8 @@ struct Zone : MoveableOnly<Zone>, HasGroupZoneProcessors<Zone>, SampleRateSuppor

return attachToSample(manager, variation);
}
void setNormalizedSampleLevel(bool usePeak = false, int associatedSampleID = -1);
void clearNormalizedSampleLevel(int associatedSampleID = -1);

struct ZoneMappingData
{
Expand Down
8 changes: 7 additions & 1 deletion src/json/engine_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,10 @@ SC_STREAMDEF(scxt::engine::Zone::SingleVariant, SC_FROM({
{"loopMode", s.loopMode},
{"loopDirection", s.loopDirection},
{"loopCountWhenCounted", s.loopCountWhenCounted},
{"loopFade", s.loopFade}};
{"loopFade", s.loopFade},
{"normalizationAmplitude", s.normalizationAmplitude},
{"pitchOffset", s.pitchOffset},
{"amplitude", s.amplitude}};
}
else
{
Expand All @@ -345,6 +348,9 @@ SC_STREAMDEF(scxt::engine::Zone::SingleVariant, SC_FROM({
s.loopDirection);
findOrSet(v, "loopFade", 0, s.loopFade);
findOrSet(v, "loopCountWhenCounted", 0, s.loopCountWhenCounted);
findOrSet(v, "normalizationAmplitude", 0.f, s.normalizationAmplitude);
findOrSet(v, "pitchOffset", 0.f, s.pitchOffset);
findOrSet(v, "amplitude", 0.f, s.amplitude);
}
else
{
Expand Down
2 changes: 2 additions & 0 deletions src/messaging/client/client_serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ enum ClientToSerializationMessagesIds
c2s_update_zone_mapping_int16_t,
c2s_update_lead_zone_single_variant,
c2s_update_zone_variants_int16_t,
c2s_normalize_zone_samples,
c2s_clear_normalize_zone_samples,

c2s_update_zone_output_float_value,
c2s_update_zone_output_int16_t_value,
Expand Down
40 changes: 40 additions & 0 deletions src/messaging/client/zone_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,46 @@ CLIENT_TO_SERIAL(UpdateLeadZoneSingleVariant, c2s_update_lead_zone_single_varian
updateLeadZoneSingleVariantPayload_t,
doUpdateLeadZoneSingleVariant(payload, engine, cont));

using associatedSampleZoneNormalizePayload_t = std::tuple<size_t, bool>;
inline void doAssociatedSampleZoneNormalize(const associatedSampleZoneNormalizePayload_t &payload,
const engine::Engine &engine, MessageController &cont)
{
const auto &samples = payload;
auto sz = engine.getSelectionManager()->currentLeadZone(engine);
if (sz.has_value())
{
auto [ps, gs, zs] = *sz;
cont.scheduleAudioThreadCallback([p = ps, g = gs, z = zs, sampv = samples](auto &eng) {
auto &[idx, use_peak] = sampv;
eng.getPatch()->getPart(p)->getGroup(g)->getZone(z)->setNormalizedSampleLevel(use_peak,
idx);
});
}
}
CLIENT_TO_SERIAL(AssociatedSampleZoneNomalizeRequest, c2s_normalize_zone_samples,
associatedSampleZoneNormalizePayload_t,
doAssociatedSampleZoneNormalize(payload, engine, cont));

using associatedSampleZoneNormalizeClearPayload_t = size_t;
inline void
doAssociatedSampleZoneNormalizeClear(const associatedSampleZoneNormalizeClearPayload_t &payload,
const engine::Engine &engine, MessageController &cont)
{
const auto &samples = payload;
auto sz = engine.getSelectionManager()->currentLeadZone(engine);
if (sz.has_value())
{
auto [ps, gs, zs] = *sz;
cont.scheduleAudioThreadCallback([p = ps, g = gs, z = zs, sampv = samples](auto &eng) {
auto &idx = sampv;
eng.getPatch()->getPart(p)->getGroup(g)->getZone(z)->clearNormalizedSampleLevel(idx);
});
}
}
CLIENT_TO_SERIAL(AssociatedSampleZoneNomalizeClearRequest, c2s_clear_normalize_zone_samples,
associatedSampleZoneNormalizeClearPayload_t,
doAssociatedSampleZoneNormalizeClear(payload, engine, cont));

CLIENT_TO_SERIAL_CONSTRAINED(UpdateZoneVariantsInt16TValue, c2s_update_zone_variants_int16_t,
detail::diffMsg_t<int16_t>, engine::Zone::Variants,
detail::updateZoneMemberValue(&engine::Zone::variantData, payload,
Expand Down
8 changes: 8 additions & 0 deletions src/voice/voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,14 @@ template <bool OS> bool Voice::processWithOS()
{
halfRate.process_block_D2(output[0], output[1], blockSize << 1);
}

auto scale_db = zone->variantData.variants[sampleIndex].normalizationAmplitude;
// we are in dB so 0 change is 0 dB
if (scale_db != 0.f)
{
const float linear_scale = dsp::dbTable.dbToLinear(scale_db);
sst::basic_blocks::mechanics::scale_by<blockSize>(linear_scale, output[0], output[1]);
}
}
else
memset(output, 0, sizeof(output));
Expand Down
Loading