Mercurial > hg > silvet
diff flattendynamics/flattendynamics-ladspa.cpp @ 366:5d0a2ebb4d17
Bring dependent libraries in to repo
author | Chris Cannam |
---|---|
date | Fri, 24 Jun 2016 14:47:45 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flattendynamics/flattendynamics-ladspa.cpp Fri Jun 24 14:47:45 2016 +0100 @@ -0,0 +1,270 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "flattendynamics-ladspa.h" + +#include <iostream> +#include <cmath> + +using std::cerr; +using std::endl; + +const float historySeconds = 4.f; +const float catchUpSeconds = 0.2f; +const float targetMaxRMS = 0.05f; +const float rmsMaxDecay = 0.999f; // per sample +const float maxGain = 10.f; + +const char *const +FlattenDynamics::portNames[PortCount] = +{ + "Input", + "Output", + "Gain", +}; + +const LADSPA_PortDescriptor +FlattenDynamics::ports[PortCount] = +{ + LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, +}; + +const LADSPA_PortRangeHint +FlattenDynamics::hints[PortCount] = +{ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, +}; + +const LADSPA_Properties +FlattenDynamics::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; + +const LADSPA_Descriptor +FlattenDynamics::ladspaDescriptor = +{ + 0xf0b375, // "Unique" ID + "flattendynamics", // Label + properties, + "Flatten Dynamics", // Name + "Queen Mary, University of London", // Maker + "BSD", // Copyright + PortCount, + ports, + portNames, + hints, + 0, // Implementation data + ladspaInstantiate, + ladspaConnectPort, + ladspaActivate, + ladspaRun, + 0, // Run adding + 0, // Set run adding gain + ladspaDeactivate, + ladspaCleanup +}; + +const LADSPA_Descriptor * +FlattenDynamics::getDescriptor(unsigned long index) +{ + if (index == 0) return &ladspaDescriptor; + return 0; +} + +FlattenDynamics::FlattenDynamics(int sampleRate) : + m_sampleRate(sampleRate), + m_input(0), + m_output(0), + m_pgain(0), + m_history(0), + m_histlen(0), + m_histwrite(0), + m_histread(0), + m_sumOfSquares(0.f), + m_rms(0.f), + m_maxRms(0.f), + m_gain(1.f) +{ + reset(); +} + +FlattenDynamics::~FlattenDynamics() +{ + delete[] m_history; +} + +LADSPA_Handle +FlattenDynamics::ladspaInstantiate(const LADSPA_Descriptor *, unsigned long rate) +{ + FlattenDynamics *flatten = new FlattenDynamics(rate); + return flatten; +} + +void +FlattenDynamics::ladspaConnectPort(LADSPA_Handle handle, + unsigned long port, + LADSPA_Data *location) +{ + FlattenDynamics *flatten = (FlattenDynamics *)handle; + if (ports[port] & LADSPA_PORT_INPUT) { + flatten->connectInputPort(Port(port), location); + } else { + flatten->connectOutputPort(Port(port), location); + } +} + +void +FlattenDynamics::ladspaActivate(LADSPA_Handle handle) +{ + FlattenDynamics *flatten = (FlattenDynamics *)handle; + flatten->reset(); +} + +void +FlattenDynamics::ladspaRun(LADSPA_Handle handle, unsigned long samples) +{ + FlattenDynamics *flatten = (FlattenDynamics *)handle; + flatten->process(samples); +} + +void +FlattenDynamics::ladspaDeactivate(LADSPA_Handle handle) +{ + ladspaActivate(handle); // both functions just reset the plugin +} + +void +FlattenDynamics::ladspaCleanup(LADSPA_Handle handle) +{ + delete (FlattenDynamics *)handle; +} + +void +FlattenDynamics::connectInputPort(Port p, const float *location) +{ + const float **ports[PortCount] = { + &m_input, 0, 0, + }; + + *ports[int(p)] = location; +} + +void +FlattenDynamics::connectOutputPort(Port p, float *location) +{ + float **ports[PortCount] = { + 0, &m_output, &m_pgain, + }; + + *ports[int(p)] = location; +} + +void +FlattenDynamics::reset() +{ + delete[] m_history; + m_histlen = int(round(m_sampleRate * historySeconds)); + if (m_histlen < 1) m_histlen = 1; + m_history = new float[m_histlen]; + for (int i = 0; i < m_histlen; ++i) { + m_history[i] = 0.f; + } + m_histwrite = 0; + m_histread = 0; + + m_sumOfSquares = 0.0; + m_rms = 0.f; + m_maxRms = 0.f; + m_gain = 1.f; +} + +void +FlattenDynamics::updateParameters() +{ + if (m_pgain) *m_pgain = m_gain; +} + +void +FlattenDynamics::process(int sampleCount) +{ + if (!m_input || !m_output) return; + + updateParameters(); + + for (int i = 0; i < sampleCount; ++i) { + m_output[i] = processSingle(m_input[i]); + } +} + +float +FlattenDynamics::processSingle(float f) +{ + updateRMS(f); + + if (m_rms == 0.f) { + return f; + } + + if (m_rms >= m_maxRms) { + m_maxRms = m_rms; + } else { + m_maxRms = m_rms + (m_maxRms - m_rms) * rmsMaxDecay; + } + + float targetGain = targetMaxRMS / m_maxRms; + + if (targetGain > maxGain) { + targetGain = maxGain; + } + + float catchUpSamples = catchUpSeconds * m_sampleRate; + // asymptotic, could improve? + m_gain = m_gain + (targetGain - m_gain) / catchUpSamples; + + if (fabsf(f) * m_gain > 1.f) { + m_gain = 1.f / fabsf(f); + } + +// cerr << "target gain = " << targetGain << ", gain = " << m_gain << endl; + return f * m_gain; +} + +void +FlattenDynamics::updateRMS(float f) +{ + // We update the RMS values by maintaining a sum-of-last-n-squares + // total (which the RMS is the square root of 1/n of) and + // recording the last n samples of history in a circular + // buffer. When a sample drops off the start of the history, we + // remove the square of it from the sum-of-squares total; then we + // add the square of the new sample. + + int nextWrite = (m_histwrite + 1) % m_histlen; + + float lose = 0.f; + + if (nextWrite == m_histread) { + // full + lose = m_history[m_histread]; + m_histread = (m_histread + 1) % m_histlen; + } + + m_history[m_histwrite] = f; + m_histwrite = nextWrite; + + int fill = (m_histwrite - m_histread + m_histlen) % m_histlen; + + m_sumOfSquares -= lose * lose; + m_sumOfSquares += f * f; + + m_rms = sqrt(m_sumOfSquares / fill); +// cerr << "rms = " << m_rms << " (from " << fill << " samples of " << m_histlen << ", latest " << f << ")" << endl; +} + +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long ix) +{ + return FlattenDynamics::getDescriptor(ix); +} +