Mercurial > hg > svcore
diff data/model/FFTModel.cpp @ 1091:bdebff3265ae simple-fft-model
Simplest naive FFTModel implementation (+ fill in tests)
author | Chris Cannam |
---|---|
date | Fri, 12 Jun 2015 18:08:57 +0100 |
parents | 420fc961c0c4 |
children | 70f18770b72d |
line wrap: on
line diff
--- a/data/model/FFTModel.cpp Fri Jun 12 14:51:46 2015 +0100 +++ b/data/model/FFTModel.cpp Fri Jun 12 18:08:57 2015 +0100 @@ -42,8 +42,14 @@ m_windowSize(windowSize), m_windowIncrement(windowIncrement), m_fftSize(fftSize), - m_windower(windowType, windowSize) + m_windower(windowType, windowSize), + m_fft(fftSize) { + if (m_windowSize > m_fftSize) { + cerr << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize + << ") must be at least FFT size (" << m_fftSize << ")" << endl; + throw invalid_argument("FFTModel window size must be at least FFT size"); + } } FFTModel::~FFTModel() @@ -59,6 +65,20 @@ } } +int +FFTModel::getWidth() const +{ + if (!m_model) return 0; + return int((m_model->getEndFrame() - m_model->getStartFrame()) + / m_windowIncrement) + 1; +} + +int +FFTModel::getHeight() const +{ + return m_fftSize / 2 + 1; +} + QString FFTModel::getBinName(int n) const { @@ -68,6 +88,131 @@ return name; } +FFTModel::Column +FFTModel::getColumn(int x) const +{ + auto cplx = getFFTColumn(x); + Column col; + col.reserve(int(cplx.size())); + for (auto c: cplx) col.push_back(abs(c)); + return col; +} + +float +FFTModel::getMagnitudeAt(int x, int y) const +{ + //!!! + return abs(getFFTColumn(x)[y]); +} + +float +FFTModel::getMaximumMagnitudeAt(int x) const +{ + Column col(getColumn(x)); + auto itr = max_element(col.begin(), col.end()); + if (itr == col.end()) return 0.f; + else return *itr; +} + +float +FFTModel::getPhaseAt(int x, int y) const +{ + //!!! + return arg(getFFTColumn(x)[y]); +} + +void +FFTModel::getValuesAt(int x, int y, float &re, float &im) const +{ + auto col = getFFTColumn(x); + re = col[y].real(); + im = col[y].imag(); +} + +bool +FFTModel::isColumnAvailable(int ) const +{ + //!!! + return true; +} + +bool +FFTModel::getMagnitudesAt(int x, float *values, int minbin, int count) const +{ + if (count == 0) count = getHeight(); + auto col = getFFTColumn(x); + for (int i = 0; i < count; ++i) { + values[i] = abs(col[minbin + i]); + } + return true; +} + +bool +FFTModel::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count) const +{ + //!!! WRONG + return getMagnitudesAt(x, values, minbin, count); +} + +bool +FFTModel::getPhasesAt(int x, float *values, int minbin, int count) const +{ + if (count == 0) count = getHeight(); + auto col = getFFTColumn(x); + for (int i = 0; i < count; ++i) { + values[i] = arg(col[minbin + i]); + } + return true; +} + +bool +FFTModel::getValuesAt(int x, float *reals, float *imags, int minbin, int count) const +{ + if (count == 0) count = getHeight(); + auto col = getFFTColumn(x); + for (int i = 0; i < count; ++i) { + reals[i] = col[minbin + i].real(); + } + for (int i = 0; i < count; ++i) { + imags[i] = col[minbin + i].imag(); + } + return true; +} + +vector<float> +FFTModel::getSourceSamples(int column) const +{ + auto range = getSourceSampleRange(column); + vector<float> samples(m_fftSize, 0.f); + int off = (m_fftSize - m_windowSize) / 2; + decltype(range.first) pfx = 0; + if (range.first < 0) { + pfx = -range.first; + range = { 0, range.second }; + } + (void) m_model->getData(m_channel, + range.first, + range.second - range.first, + &samples[off + pfx]); + if (m_channel == -1) { + int channels = m_model->getChannelCount(); + if (channels > 1) { + for (int i = 0; i < range.second - range.first; ++i) { + samples[off + pfx + i] /= float(channels); + } + } + } + return samples; +} + +vector<complex<float>> +FFTModel::getFFTColumn(int column) const +{ + auto samples = getSourceSamples(column); + m_windower.cut(&samples[0]); + return m_fft.process(samples); +} + bool FFTModel::estimateStableFrequency(int x, int y, double &frequency) { @@ -239,14 +384,14 @@ if (type == MajorPeaks) return 10; if (bin == 0) return 3; - double binfreq = (getSampleRate() * bin) / m_fftSize; + double binfreq = (sampleRate * bin) / m_fftSize; double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq); - int hibin = int(lrint((hifreq * m_fftSize) / getSampleRate())); + int hibin = int(lrint((hifreq * m_fftSize) / sampleRate)); int medianWinSize = hibin - bin; if (medianWinSize < 3) medianWinSize = 3; - percentile = 0.5f + float(binfreq / getSampleRate()); + percentile = 0.5f + float(binfreq / sampleRate); return medianWinSize; }