Mercurial > hg > devuvuzelator
changeset 4:d90abfa9585a
* Adaptive method
author | Chris Cannam |
---|---|
date | Fri, 11 Jun 2010 15:38:44 +0100 |
parents | 8b79175c9f02 |
children | 45bcfa3d5da7 |
files | devuvuzelator-ladspa.cpp devuvuzelator-vst.cpp devuvuzelator.cpp median.h |
diffstat | 4 files changed, 261 insertions(+), 128 deletions(-) [+] |
line wrap: on
line diff
--- a/devuvuzelator-ladspa.cpp Fri Jun 11 11:45:41 2010 +0100 +++ b/devuvuzelator-ladspa.cpp Fri Jun 11 15:38:44 2010 +0100 @@ -5,7 +5,10 @@ #include <iostream> #include <cmath> -#define FFTSIZE 1024 +#include "median.h" + +#define FFTSIZE 4096 +#define WINSIZE 1024 class Devuvuzelator { @@ -19,13 +22,12 @@ enum { InputPort = 0, OutputPort = 1, - LowPort = 2, - HighPort = 3, - FundamentalPort = 4, - BandwidthPort = 5, - HarmonicsPort = 6, - ReductionPort = 7, - PortCount = 8, + LatencyPort = 2, + FundamentalPort = 3, + BandwidthPort = 4, + HarmonicsPort = 5, + ReductionPort = 6, + PortCount = 7, }; static const char *const portNames[PortCount]; @@ -46,6 +48,7 @@ void runImpl(unsigned long); void processFrame(); void processSpectralFrame(); + void updateParameters(); static void fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io); @@ -53,14 +56,20 @@ int m_sampleRate; float *m_input; float *m_output; - float *m_low; - float *m_high; - float *m_fundamental; - float *m_bandwidth; - float *m_harmonics; - float *m_reduction; + + float m_fundamental; + float m_bandwidth; + float m_harmonics; + float m_reduction; + + float *m_platency; + float *m_pfundamental; + float *m_pbandwidth; + float *m_pharmonics; + float *m_preduction; const int m_fftsize; + const int m_winsize; const int m_increment; int m_fill; int m_read; @@ -69,6 +78,7 @@ double *m_real; double *m_imag; double *m_window; + MedianFilter<double> **m_medians; }; const char *const @@ -76,12 +86,11 @@ { "Input", "Output", - "Low threshold (dB)", - "High threshold (dB)", + "latency", "Fundamental frequency (Hz)", "Bandwidth of fundamental (Hz)", "Number of partials", - "Reduction (dB)", + "Reduction (%)", }; const LADSPA_PortDescriptor @@ -89,8 +98,7 @@ { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, - LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, - LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, @@ -102,18 +110,15 @@ { { 0, 0, 0 }, { 0, 0, 0 }, - { LADSPA_HINT_DEFAULT_MIDDLE | - LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 }, - { LADSPA_HINT_DEFAULT_HIGH | - LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 }, + { 0, 0, 0 }, { LADSPA_HINT_DEFAULT_LOW | - LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 110, 550 }, + LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 50, 770 }, { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 20, 100 }, { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 6 }, { LADSPA_HINT_DEFAULT_MIDDLE | - LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 20 }, + LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 100 }, }; const LADSPA_Properties @@ -154,22 +159,37 @@ m_sampleRate(sampleRate), m_input(0), m_output(0), - m_low(0), - m_high(0), + m_platency(0), + m_pfundamental(0), + m_pbandwidth(0), + m_pharmonics(0), + m_preduction(0), m_fftsize(FFTSIZE), - m_increment(m_fftsize/4), + m_winsize(WINSIZE), + m_increment(m_winsize/2), m_fill(0), m_read(0) { - m_buffer = new float[m_fftsize]; - m_outacc = new float[m_fftsize * 2]; + m_buffer = new float[m_winsize]; + m_outacc = new float[m_winsize * 2]; + m_window = new double[m_winsize]; m_real = new double[m_fftsize]; m_imag = new double[m_fftsize]; - m_window = new double[m_fftsize]; + m_medians = new MedianFilter<double> *[m_fftsize/2+1]; - for (int i = 0; i < m_fftsize; ++i) { - m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize); + for (int i = 0; i < m_winsize; ++i) { + m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_winsize); } + for (int i = 0; i < m_fftsize/2+1; ++i) { + m_medians[i] = 0; + } + + m_fundamental = 230; + m_bandwidth = 60; + m_harmonics = 3; + m_reduction = 50; + + std::cerr << "note: latency = " << (float(m_winsize) / m_sampleRate)*1000 << " msec" << std::endl; reset(); } @@ -181,6 +201,10 @@ delete[] m_real; delete[] m_imag; delete[] m_window; + for (int i = 0; i < m_fftsize/2+1; ++i) { + delete m_medians[i]; + } + delete[] m_medians; } LADSPA_Handle @@ -199,12 +223,11 @@ float **ports[PortCount] = { &devuvu->m_input, &devuvu->m_output, - &devuvu->m_low, - &devuvu->m_high, - &devuvu->m_fundamental, - &devuvu->m_bandwidth, - &devuvu->m_harmonics, - &devuvu->m_reduction, + &devuvu->m_platency, + &devuvu->m_pfundamental, + &devuvu->m_pbandwidth, + &devuvu->m_pharmonics, + &devuvu->m_preduction, }; *ports[port] = (float *)location; @@ -239,20 +262,34 @@ void Devuvuzelator::reset() { - for (int i = 0; i < m_fftsize; ++i) { + for (int i = 0; i < m_winsize; ++i) { m_buffer[i] = 0.f; } - for (int i = 0; i < m_fftsize*2; ++i) { + for (int i = 0; i < m_winsize*2; ++i) { m_outacc[i] = 0.f; } m_fill = 0; m_read = 0; + for (int i = 0; i < m_fftsize/2+1; ++i) { + if (m_medians[i]) m_medians[i]->reset(); + } +} + +void +Devuvuzelator::updateParameters() +{ + if (m_platency) *m_platency = m_winsize; + if (m_pfundamental) m_fundamental = *m_pfundamental; + if (m_pbandwidth) m_bandwidth = *m_pbandwidth; + if (m_pharmonics) m_harmonics = *m_pharmonics; + if (m_preduction) m_reduction = *m_preduction; } void Devuvuzelator::runImpl(unsigned long sampleCount) { if (!m_input || !m_output) return; + updateParameters(); int ii = 0; int oi = 0; @@ -260,21 +297,21 @@ while (ii < sc) { - m_output[oi++] = m_outacc[m_read++] / 1.5f; + m_output[oi++] = m_outacc[m_read++]; - if (m_fill == m_fftsize) { + if (m_fill == m_winsize) { processFrame(); - for (int j = m_increment; j < m_fftsize; ++j) { + for (int j = m_increment; j < m_winsize; ++j) { m_buffer[j - m_increment] = m_buffer[j]; } - for (int j = m_increment; j < m_fftsize*2; ++j) { + for (int j = m_increment; j < m_winsize*2; ++j) { m_outacc[j - m_increment] = m_outacc[j]; } - for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) { + for (int j = m_winsize*2 - m_increment; j < m_winsize*2; ++j) { m_outacc[j] = 0.f; } @@ -290,9 +327,14 @@ Devuvuzelator::processFrame() { double *frame = (double *)alloca(m_fftsize * sizeof(double)); - int ix = m_fftsize/2; for (int i = 0; i < m_fftsize; ++i) { - frame[ix++] = m_buffer[i] * m_window[i]; + frame[i] = 0.0; + } + + int ix = m_fftsize - m_winsize/2; + while (ix < 0) ix += m_fftsize; + for (int i = 0; i < m_winsize; ++i) { + frame[ix++] += m_buffer[i] * m_window[i]; if (ix == m_fftsize) ix = 0; } @@ -308,9 +350,10 @@ double *spare = (double *)alloca(m_fftsize * sizeof(double)); fft(m_fftsize, true, m_real, m_imag, frame, spare); - ix = m_fftsize/2; - for (int i = 0; i < m_fftsize; ++i) { - m_outacc[m_fftsize + i] += frame[ix++] * m_window[i]; + ix = m_fftsize - m_winsize/2; + while (ix < 0) ix += m_fftsize; + for (int i = 0; i < m_winsize; ++i) { + m_outacc[m_winsize + i] += frame[ix++]; if (ix == m_fftsize) ix = 0; } }
--- a/devuvuzelator-vst.cpp Fri Jun 11 11:45:41 2010 +0100 +++ b/devuvuzelator-vst.cpp Fri Jun 11 15:38:44 2010 +0100 @@ -12,18 +12,19 @@ #define snprintf _snprintf #define alloca _alloca -#define FFTSIZE 1024 +#define FFTSIZE 4096 +#define WINSIZE 1024 + +#include "median.h" class Devuvuzelator : public AudioEffect { enum { - LowParam = 0, - HighParam = 1, - FundamentalParam = 2, - BandwidthParam = 3, - HarmonicsParam = 4, - ReductionParam = 5, - NumParams = 6 + FundamentalParam = 1, + BandwidthParam = 2, + HarmonicsParam = 3, + ReductionParam = 4, + NumParams = 5 }; public: @@ -78,6 +79,7 @@ float m_reduction; const int m_fftsize; + const int m_winsize; const int m_increment; int m_fill; int m_read; @@ -86,6 +88,7 @@ double *m_real; double *m_imag; double *m_window; + MedianFilter<double> **m_medians; }; // VST params 0->1 @@ -94,12 +97,10 @@ Devuvuzelator::setParameter(VstInt32 index, float value) { switch (index) { - case 0: m_low = -80 + 80 * value; break; - case 1: m_high = -80 + 80 * value; break; - case 2: m_fundamental = 110 + 440 * value; break; - case 3: m_bandwidth = 20 + 80 * value; break; - case 4: m_harmonics = int(value * 6 + 0.5); break; - case 5: m_reduction = 20 * value; break; + case 0: m_fundamental = 50 + 720 * value; break; + case 1: m_bandwidth = 20 + 80 * value; break; + case 2: m_harmonics = int(value * 6 + 0.5); break; + case 3: m_reduction = 100 * value; break; } } @@ -107,14 +108,12 @@ Devuvuzelator::getParameter(VstInt32 index) { switch (index) { - case 0: return (m_low + 80) / 80; - case 1: return (m_high + 80) / 80; - case 2: return (m_fundamental - 110) / 440; - case 3: return (m_bandwidth - 20) / 80; - case 4: return (m_harmonics / 6.f); - case 5: return m_reduction / 20; + case 0: return (m_fundamental - 50) / 720; + case 1: return (m_bandwidth - 20) / 80; + case 2: return (m_harmonics / 6.f); + case 3: return m_reduction / 100; } - return 0; + return 0; } // NB! The max name length for VST parameter names, labels @@ -125,12 +124,10 @@ Devuvuzelator::getParameterLabel(VstInt32 index, char *label) { const char *units[NumParams] = { - "dB", - "dB", "Hz", "Hz", "", - "dB", + "%", }; vst_strncpy(label, units[index], kVstMaxParamStrLen); @@ -139,22 +136,18 @@ void Devuvuzelator::getParameterDisplay(VstInt32 index, char *label) { - switch (index) { - case 0: snprintf(label, kVstMaxParamStrLen, "%f", m_low); break; - case 1: snprintf(label, kVstMaxParamStrLen, "%f", m_high); break; - case 2: snprintf(label, kVstMaxParamStrLen, "%f", m_fundamental); break; - case 3: snprintf(label, kVstMaxParamStrLen, "%f", m_bandwidth); break; - case 4: snprintf(label, kVstMaxParamStrLen, "%d", m_harmonics); break; - case 5: snprintf(label, kVstMaxParamStrLen, "%f", m_reduction); break; - } + switch (index) { + case 0: snprintf(label, kVstMaxParamStrLen, "%f", m_fundamental); break; + case 1: snprintf(label, kVstMaxParamStrLen, "%f", m_bandwidth); break; + case 2: snprintf(label, kVstMaxParamStrLen, "%d", m_harmonics); break; + case 3: snprintf(label, kVstMaxParamStrLen, "%f", m_reduction); break; + } } void Devuvuzelator::getParameterName(VstInt32 index, char *label) { const char *names[NumParams] = { - "Floor", - "Ceiling", "Pitch", "B/W", "Partials", @@ -169,29 +162,29 @@ m_sampleRate(0), m_input(0), m_output(0), - m_low(0), - m_high(0), m_fftsize(FFTSIZE), m_increment(m_fftsize/4), m_fill(0), m_read(0) { - m_buffer = new float[m_fftsize]; - m_outacc = new float[m_fftsize * 2]; + m_buffer = new float[m_winsize]; + m_outacc = new float[m_winsize * 2]; + m_window = new double[m_winsize]; m_real = new double[m_fftsize]; m_imag = new double[m_fftsize]; - m_window = new double[m_fftsize]; + m_medians = new MedianFilter<double> *[m_fftsize/2+1]; - for (int i = 0; i < m_fftsize; ++i) { - m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize); + for (int i = 0; i < m_winsize; ++i) { + m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_winsize); + } + for (int i = 0; i < m_fftsize/2+1; ++i) { + m_medians[i] = 0; } - m_low = -40; - m_high = -20; - m_fundamental = 220; + m_fundamental = 230; m_bandwidth = 60; m_harmonics = 3; - m_reduction = 10; + m_reduction = 30; setUniqueID('qmvz'); setNumInputs(1); @@ -209,19 +202,26 @@ delete[] m_real; delete[] m_imag; delete[] m_window; + for (int i = 0; i < m_fftsize/2+1; ++i) { + delete m_medians[i]; + } + delete[] m_medians; } void Devuvuzelator::reset() { - for (int i = 0; i < m_fftsize; ++i) { + for (int i = 0; i < m_winsize; ++i) { m_buffer[i] = 0.f; } - for (int i = 0; i < m_fftsize*2; ++i) { + for (int i = 0; i < m_winsize*2; ++i) { m_outacc[i] = 0.f; } m_fill = 0; m_read = 0; + for (int i = 0; i < m_fftsize/2+1; ++i) { + if (m_medians[i]) m_medians[i]->reset(); + } } void @@ -231,25 +231,25 @@ int ii = 0; int oi = 0; - const int sc = sampleCount; + const int sc = sampleCount; while (ii < sc) { - m_output[oi++] = m_outacc[m_read++] / 1.5f; + m_output[oi++] = m_outacc[m_read++]; - if (m_fill == m_fftsize) { + if (m_fill == m_winsize) { processFrame(); - for (int j = m_increment; j < m_fftsize; ++j) { + for (int j = m_increment; j < m_winsize; ++j) { m_buffer[j - m_increment] = m_buffer[j]; } - for (int j = m_increment; j < m_fftsize*2; ++j) { + for (int j = m_increment; j < m_winsize*2; ++j) { m_outacc[j - m_increment] = m_outacc[j]; } - for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) { + for (int j = m_winsize*2 - m_increment; j < m_winsize*2; ++j) { m_outacc[j] = 0.f; } @@ -265,9 +265,14 @@ Devuvuzelator::processFrame() { double *frame = (double *)alloca(m_fftsize * sizeof(double)); - int ix = m_fftsize/2; for (int i = 0; i < m_fftsize; ++i) { - frame[ix++] = m_buffer[i] * m_window[i]; + frame[i] = 0.0; + } + + int ix = m_fftsize - m_winsize/2; + while (ix < 0) ix += m_fftsize; + for (int i = 0; i < m_winsize; ++i) { + frame[ix++] += m_buffer[i] * m_window[i]; if (ix == m_fftsize) ix = 0; } @@ -283,9 +288,10 @@ double *spare = (double *)alloca(m_fftsize * sizeof(double)); fft(m_fftsize, true, m_real, m_imag, frame, spare); - ix = m_fftsize/2; - for (int i = 0; i < m_fftsize; ++i) { - m_outacc[m_fftsize + i] += frame[ix++] * m_window[i]; + ix = m_fftsize - m_winsize/2; + while (ix < 0) ix += m_fftsize; + for (int i = 0; i < m_winsize; ++i) { + m_outacc[m_winsize + i] += frame[ix++]; if (ix == m_fftsize) ix = 0; } }
--- a/devuvuzelator.cpp Fri Jun 11 11:45:41 2010 +0100 +++ b/devuvuzelator.cpp Fri Jun 11 15:38:44 2010 +0100 @@ -22,29 +22,25 @@ int lowbin = 0.5 + (m_fftsize * lowfreq) / m_sampleRate; int highbin = 0.5 + (m_fftsize * highfreq) / m_sampleRate; -// std::cerr << "partials " << h << ": freqs " << lowfreq << "->" -// << highfreq << ", bins " << lowbin << "->" << highbin << std::endl; + for (int i = lowbin; i <= highbin; ++i) { - for (int i = lowbin; i <= highbin; ++i) { -// std::cerr << "bin " << i << " freq " << (m_sampleRate * i) / m_fftsize << std::endl; - ratios[i] = 1.0; - double db = 10 * log10(mags[i]); - if (db > m_low && db < m_high) { - double r = m_reduction; - ratios[i] = pow(10, -r / 10); + if (!m_medians[i]) { + const float filtersecs = 5.f; + int filterlen = (filtersecs * m_sampleRate) / m_increment; + m_medians[i] = new MedianFilter<double>(filterlen); + } + + m_medians[i]->push(mags[i]); + double threshold = m_medians[i]->getAt(m_reduction); + if (mags[i] > threshold) { + double target = mags[i] - threshold; + ratios[i] = (target / mags[i]); + } else { + ratios[i] = 0.0; } } } -/* - for (int i = 0; i < hs-1; ++i) { - if (ratios[i] == 1.0 && ratios[i+1] < 1.0) { - ratios[i] = (ratios[i+1] + 1) / 2; - } else if (ratios[i] < 1.0 && ratios[i+1] == 1.0) { - ratios[i+1] = (ratios[i] + 1) / 2; - ++i; - } - } -*/ + for (int i = 0; i < hs; ++i) { m_real[i] *= ratios[i]; m_imag[i] *= ratios[i];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/median.h Fri Jun 11 15:38:44 2010 +0100 @@ -0,0 +1,88 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _MEDIAN_H_ +#define _MEDIAN_H_ + +#include <algorithm> +#include <cassert> + +template <typename T> +class MedianFilter +{ +public: + MedianFilter(int size, float percentile = 50.f) : + m_size(size), + m_frame(new T[size]), + m_sorted(new T[size]), + m_sortend(m_sorted + size - 1) { + setPercentile(percentile); + reset(); + } + + ~MedianFilter() { + delete[] m_frame; + delete[] m_sorted; + } + + void setPercentile(float p) { + m_index = int((m_size * p) / 100.f); + if (m_index >= m_size) m_index = m_size-1; + if (m_index < 0) m_index = 0; + } + + void push(T value) { + drop(m_frame[0]); + const int sz1 = m_size-1; + for (int i = 0; i < sz1; ++i) m_frame[i] = m_frame[i+1]; + m_frame[m_size-1] = value; + put(value); + } + + T get() const { + return m_sorted[m_index]; + } + + T getAt(float percentile) { + int ix = int((m_size * percentile) / 100.f); + if (ix >= m_size) ix = m_size-1; + if (ix < 0) ix = 0; + return m_sorted[ix]; + } + + void reset() { + for (int i = 0; i < m_size; ++i) m_frame[i] = 0; + for (int i = 0; i < m_size; ++i) m_sorted[i] = 0; + } + +private: + const int m_size; + T *const m_frame; + T *const m_sorted; + T *const m_sortend; + int m_index; + + void put(T value) { + // precondition: m_sorted contains m_size-1 values, packed at start + // postcondition: m_sorted contains m_size values, one of which is value + T *point = std::lower_bound(m_sorted, m_sortend, value); + const int n = m_sortend - point; + for (int i = n; i > 0; --i) point[i] = point[i-1]; + *point = value; + } + + void drop(T value) { + // precondition: m_sorted contains m_size values, one of which is value + // postcondition: m_sorted contains m_size-1 values, packed at start + T *point = std::lower_bound(m_sorted, m_sortend + 1, value); + if (*point != value) { + std::cerr << "WARNING: MedianFilter::drop: *point is " << *point + << ", expected " << value << std::endl; + } + const int n = m_sortend - point; + for (int i = 0; i < n; ++i) point[i] = point[i+1]; + *m_sortend = T(0); + } +}; + +#endif +