annotate flattendynamics-ladspa.cpp @ 17:ca4e31d86436

The complicated long-and-short-term approach is not working any better than a simple long-term window. Revert to that, with some decay.
author Chris Cannam
date Wed, 23 Jul 2014 15:17:47 +0100
parents c12cab43141d
children d25a2e91e9d8
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 #include "flattendynamics-ladspa.h"
Chris@0 4
Chris@0 5 #include <iostream>
Chris@0 6 #include <cmath>
Chris@0 7
Chris@2 8 using std::cerr;
Chris@2 9 using std::endl;
Chris@2 10
Chris@17 11 const float historySeconds = 4.f;
Chris@9 12 const float catchUpSeconds = 0.2f;
Chris@17 13 const float targetMaxRMS = 0.05f;
Chris@14 14 const float rmsMaxDecay = 0.999f; // per sample
Chris@3 15 const float maxGain = 20.f;
Chris@1 16
Chris@0 17 const char *const
Chris@0 18 FlattenDynamics::portNames[PortCount] =
Chris@0 19 {
Chris@0 20 "Input",
Chris@0 21 "Output",
Chris@1 22 "Gain",
Chris@0 23 };
Chris@0 24
Chris@0 25 const LADSPA_PortDescriptor
Chris@0 26 FlattenDynamics::ports[PortCount] =
Chris@0 27 {
Chris@0 28 LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
Chris@0 29 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
Chris@1 30 LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL,
Chris@0 31 };
Chris@0 32
Chris@0 33 const LADSPA_PortRangeHint
Chris@0 34 FlattenDynamics::hints[PortCount] =
Chris@0 35 {
Chris@0 36 { 0, 0, 0 },
Chris@0 37 { 0, 0, 0 },
Chris@1 38 { 0, 0, 0 },
Chris@0 39 };
Chris@0 40
Chris@0 41 const LADSPA_Properties
Chris@0 42 FlattenDynamics::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
Chris@0 43
Chris@0 44 const LADSPA_Descriptor
Chris@0 45 FlattenDynamics::ladspaDescriptor =
Chris@0 46 {
Chris@0 47 0xf0b375, // "Unique" ID
Chris@0 48 "flattendynamics", // Label
Chris@0 49 properties,
Chris@2 50 "Flatten Dynamics", // Name
Chris@0 51 "Queen Mary, University of London", // Maker
Chris@0 52 "BSD", // Copyright
Chris@0 53 PortCount,
Chris@0 54 ports,
Chris@0 55 portNames,
Chris@0 56 hints,
Chris@0 57 0, // Implementation data
Chris@15 58 ladspaInstantiate,
Chris@15 59 ladspaConnectPort,
Chris@15 60 ladspaActivate,
Chris@15 61 ladspaRun,
Chris@0 62 0, // Run adding
Chris@0 63 0, // Set run adding gain
Chris@15 64 ladspaDeactivate,
Chris@15 65 ladspaCleanup
Chris@0 66 };
Chris@0 67
Chris@0 68 const LADSPA_Descriptor *
Chris@0 69 FlattenDynamics::getDescriptor(unsigned long index)
Chris@0 70 {
Chris@0 71 if (index == 0) return &ladspaDescriptor;
Chris@0 72 return 0;
Chris@0 73 }
Chris@0 74
Chris@0 75 FlattenDynamics::FlattenDynamics(int sampleRate) :
Chris@0 76 m_sampleRate(sampleRate),
Chris@0 77 m_input(0),
Chris@1 78 m_output(0),
Chris@1 79 m_pgain(0),
Chris@1 80 m_history(0),
Chris@1 81 m_histlen(0),
Chris@1 82 m_histwrite(0),
Chris@1 83 m_histread(0),
Chris@17 84 m_sumOfSquares(0.f),
Chris@17 85 m_rms(0.f),
Chris@17 86 m_maxRms(0.f),
Chris@1 87 m_gain(1.f)
Chris@0 88 {
Chris@0 89 reset();
Chris@0 90 }
Chris@0 91
Chris@0 92 FlattenDynamics::~FlattenDynamics()
Chris@0 93 {
Chris@3 94 delete[] m_history;
Chris@0 95 }
Chris@0 96
Chris@0 97 LADSPA_Handle
Chris@15 98 FlattenDynamics::ladspaInstantiate(const LADSPA_Descriptor *, unsigned long rate)
Chris@0 99 {
Chris@0 100 FlattenDynamics *flatten = new FlattenDynamics(rate);
Chris@0 101 return flatten;
Chris@0 102 }
Chris@0 103
Chris@0 104 void
Chris@15 105 FlattenDynamics::ladspaConnectPort(LADSPA_Handle handle,
Chris@15 106 unsigned long port,
Chris@15 107 LADSPA_Data *location)
Chris@0 108 {
Chris@0 109 FlattenDynamics *flatten = (FlattenDynamics *)handle;
Chris@15 110 if (ports[port] & LADSPA_PORT_INPUT) {
Chris@15 111 flatten->connectInputPort(Port(port), location);
Chris@15 112 } else {
Chris@15 113 flatten->connectOutputPort(Port(port), location);
Chris@15 114 }
Chris@0 115 }
Chris@0 116
Chris@0 117 void
Chris@15 118 FlattenDynamics::ladspaActivate(LADSPA_Handle handle)
Chris@0 119 {
Chris@0 120 FlattenDynamics *flatten = (FlattenDynamics *)handle;
Chris@0 121 flatten->reset();
Chris@0 122 }
Chris@0 123
Chris@0 124 void
Chris@15 125 FlattenDynamics::ladspaRun(LADSPA_Handle handle, unsigned long samples)
Chris@0 126 {
Chris@0 127 FlattenDynamics *flatten = (FlattenDynamics *)handle;
Chris@15 128 flatten->process(samples);
Chris@0 129 }
Chris@0 130
Chris@0 131 void
Chris@15 132 FlattenDynamics::ladspaDeactivate(LADSPA_Handle handle)
Chris@0 133 {
Chris@15 134 ladspaActivate(handle); // both functions just reset the plugin
Chris@0 135 }
Chris@0 136
Chris@0 137 void
Chris@15 138 FlattenDynamics::ladspaCleanup(LADSPA_Handle handle)
Chris@0 139 {
Chris@0 140 delete (FlattenDynamics *)handle;
Chris@0 141 }
Chris@0 142
Chris@0 143 void
Chris@15 144 FlattenDynamics::connectInputPort(Port p, const float *location)
Chris@15 145 {
Chris@15 146 const float **ports[PortCount] = {
Chris@15 147 &m_input, 0, 0,
Chris@15 148 };
Chris@15 149
Chris@15 150 *ports[int(p)] = location;
Chris@15 151 }
Chris@15 152
Chris@15 153 void
Chris@15 154 FlattenDynamics::connectOutputPort(Port p, float *location)
Chris@15 155 {
Chris@15 156 float **ports[PortCount] = {
Chris@15 157 0, &m_output, &m_pgain,
Chris@15 158 };
Chris@15 159
Chris@15 160 *ports[int(p)] = location;
Chris@15 161 }
Chris@15 162
Chris@15 163 void
Chris@0 164 FlattenDynamics::reset()
Chris@0 165 {
Chris@3 166 delete[] m_history;
Chris@17 167 m_histlen = int(round(m_sampleRate * historySeconds));
Chris@2 168 if (m_histlen < 1) m_histlen = 1;
Chris@2 169 m_history = new float[m_histlen];
Chris@7 170 for (int i = 0; i < m_histlen; ++i) {
Chris@7 171 m_history[i] = 0.f;
Chris@7 172 }
Chris@1 173 m_histwrite = 0;
Chris@1 174 m_histread = 0;
Chris@1 175
Chris@17 176 m_sumOfSquares = 0.0;
Chris@17 177 m_rms = 0.f;
Chris@17 178 m_maxRms = 0.f;
Chris@1 179 m_gain = 1.f;
Chris@0 180 }
Chris@0 181
Chris@0 182 void
Chris@0 183 FlattenDynamics::updateParameters()
Chris@0 184 {
Chris@1 185 if (m_pgain) *m_pgain = m_gain;
Chris@0 186 }
Chris@0 187
Chris@0 188 void
Chris@15 189 FlattenDynamics::process(int sampleCount)
Chris@0 190 {
Chris@0 191 if (!m_input || !m_output) return;
Chris@16 192
Chris@0 193 updateParameters();
Chris@0 194
Chris@2 195 for (int i = 0; i < sampleCount; ++i) {
Chris@15 196 m_output[i] = processSingle(m_input[i]);
Chris@0 197 }
Chris@0 198 }
Chris@0 199
Chris@1 200 float
Chris@15 201 FlattenDynamics::processSingle(float f)
Chris@1 202 {
Chris@1 203 updateRMS(f);
Chris@2 204
Chris@17 205 if (m_rms == 0.f) {
Chris@2 206 return f;
Chris@2 207 }
Chris@2 208
Chris@17 209 if (m_rms >= m_maxRms) {
Chris@17 210 m_maxRms = m_rms;
Chris@17 211 } else {
Chris@17 212 m_maxRms = m_rms + (m_maxRms - m_rms) * rmsMaxDecay;
Chris@5 213 }
Chris@5 214
Chris@17 215 float targetGain = targetMaxRMS / m_maxRms;
Chris@8 216
Chris@3 217 if (targetGain > maxGain) {
Chris@3 218 targetGain = maxGain;
Chris@3 219 }
Chris@7 220
Chris@2 221 float catchUpSamples = catchUpSeconds * m_sampleRate;
Chris@2 222 // asymptotic, could improve?
Chris@2 223 m_gain = m_gain + (targetGain - m_gain) / catchUpSamples;
Chris@7 224
Chris@2 225 if (fabsf(f) * m_gain > 1.f) {
Chris@2 226 m_gain = 1.f / fabsf(f);
Chris@2 227 }
Chris@7 228
Chris@2 229 // cerr << "target gain = " << targetGain << ", gain = " << m_gain << endl;
Chris@2 230 return f * m_gain;
Chris@1 231 }
Chris@1 232
Chris@1 233 void
Chris@1 234 FlattenDynamics::updateRMS(float f)
Chris@1 235 {
Chris@11 236 // We update the RMS values by maintaining a sum-of-last-n-squares
Chris@11 237 // total (which the RMS is the square root of 1/n of) and
Chris@11 238 // recording the last n samples of history in a circular
Chris@11 239 // buffer. When a sample drops off the start of the history, we
Chris@11 240 // remove the square of it from the sum-of-squares total; then we
Chris@11 241 // add the square of the new sample.
Chris@11 242
Chris@1 243 int nextWrite = (m_histwrite + 1) % m_histlen;
Chris@1 244
Chris@17 245 float lose = 0.f;
Chris@1 246
Chris@1 247 if (nextWrite == m_histread) {
Chris@1 248 // full
Chris@17 249 lose = m_history[m_histread];
Chris@1 250 m_histread = (m_histread + 1) % m_histlen;
Chris@1 251 }
Chris@1 252
Chris@1 253 m_history[m_histwrite] = f;
Chris@1 254 m_histwrite = nextWrite;
Chris@1 255
Chris@2 256 int fill = (m_histwrite - m_histread + m_histlen) % m_histlen;
Chris@2 257
Chris@17 258 m_sumOfSquares -= lose * lose;
Chris@17 259 m_sumOfSquares += f * f;
Chris@1 260
Chris@17 261 m_rms = sqrt(m_sumOfSquares / fill);
Chris@2 262 // cerr << "rms = " << m_rms << " (from " << fill << " samples of " << m_histlen << ", latest " << f << ")" << endl;
Chris@1 263 }
Chris@1 264
Chris@0 265 const LADSPA_Descriptor *
Chris@0 266 ladspa_descriptor(unsigned long ix)
Chris@0 267 {
Chris@0 268 return FlattenDynamics::getDescriptor(ix);
Chris@0 269 }
Chris@0 270