comparison 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
comparison
equal deleted inserted replaced
1090:420fc961c0c4 1091:bdebff3265ae
40 m_channel(channel), 40 m_channel(channel),
41 m_windowType(windowType), 41 m_windowType(windowType),
42 m_windowSize(windowSize), 42 m_windowSize(windowSize),
43 m_windowIncrement(windowIncrement), 43 m_windowIncrement(windowIncrement),
44 m_fftSize(fftSize), 44 m_fftSize(fftSize),
45 m_windower(windowType, windowSize) 45 m_windower(windowType, windowSize),
46 { 46 m_fft(fftSize)
47 {
48 if (m_windowSize > m_fftSize) {
49 cerr << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize
50 << ") must be at least FFT size (" << m_fftSize << ")" << endl;
51 throw invalid_argument("FFTModel window size must be at least FFT size");
52 }
47 } 53 }
48 54
49 FFTModel::~FFTModel() 55 FFTModel::~FFTModel()
50 { 56 {
51 } 57 }
55 { 61 {
56 if (m_model) { 62 if (m_model) {
57 cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl; 63 cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl;
58 m_model = 0; 64 m_model = 0;
59 } 65 }
66 }
67
68 int
69 FFTModel::getWidth() const
70 {
71 if (!m_model) return 0;
72 return int((m_model->getEndFrame() - m_model->getStartFrame())
73 / m_windowIncrement) + 1;
74 }
75
76 int
77 FFTModel::getHeight() const
78 {
79 return m_fftSize / 2 + 1;
60 } 80 }
61 81
62 QString 82 QString
63 FFTModel::getBinName(int n) const 83 FFTModel::getBinName(int n) const
64 { 84 {
65 sv_samplerate_t sr = getSampleRate(); 85 sv_samplerate_t sr = getSampleRate();
66 if (!sr) return ""; 86 if (!sr) return "";
67 QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2)); 87 QString name = tr("%1 Hz").arg((n * sr) / ((getHeight()-1) * 2));
68 return name; 88 return name;
89 }
90
91 FFTModel::Column
92 FFTModel::getColumn(int x) const
93 {
94 auto cplx = getFFTColumn(x);
95 Column col;
96 col.reserve(int(cplx.size()));
97 for (auto c: cplx) col.push_back(abs(c));
98 return col;
99 }
100
101 float
102 FFTModel::getMagnitudeAt(int x, int y) const
103 {
104 //!!!
105 return abs(getFFTColumn(x)[y]);
106 }
107
108 float
109 FFTModel::getMaximumMagnitudeAt(int x) const
110 {
111 Column col(getColumn(x));
112 auto itr = max_element(col.begin(), col.end());
113 if (itr == col.end()) return 0.f;
114 else return *itr;
115 }
116
117 float
118 FFTModel::getPhaseAt(int x, int y) const
119 {
120 //!!!
121 return arg(getFFTColumn(x)[y]);
122 }
123
124 void
125 FFTModel::getValuesAt(int x, int y, float &re, float &im) const
126 {
127 auto col = getFFTColumn(x);
128 re = col[y].real();
129 im = col[y].imag();
130 }
131
132 bool
133 FFTModel::isColumnAvailable(int ) const
134 {
135 //!!!
136 return true;
137 }
138
139 bool
140 FFTModel::getMagnitudesAt(int x, float *values, int minbin, int count) const
141 {
142 if (count == 0) count = getHeight();
143 auto col = getFFTColumn(x);
144 for (int i = 0; i < count; ++i) {
145 values[i] = abs(col[minbin + i]);
146 }
147 return true;
148 }
149
150 bool
151 FFTModel::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count) const
152 {
153 //!!! WRONG
154 return getMagnitudesAt(x, values, minbin, count);
155 }
156
157 bool
158 FFTModel::getPhasesAt(int x, float *values, int minbin, int count) const
159 {
160 if (count == 0) count = getHeight();
161 auto col = getFFTColumn(x);
162 for (int i = 0; i < count; ++i) {
163 values[i] = arg(col[minbin + i]);
164 }
165 return true;
166 }
167
168 bool
169 FFTModel::getValuesAt(int x, float *reals, float *imags, int minbin, int count) const
170 {
171 if (count == 0) count = getHeight();
172 auto col = getFFTColumn(x);
173 for (int i = 0; i < count; ++i) {
174 reals[i] = col[minbin + i].real();
175 }
176 for (int i = 0; i < count; ++i) {
177 imags[i] = col[minbin + i].imag();
178 }
179 return true;
180 }
181
182 vector<float>
183 FFTModel::getSourceSamples(int column) const
184 {
185 auto range = getSourceSampleRange(column);
186 vector<float> samples(m_fftSize, 0.f);
187 int off = (m_fftSize - m_windowSize) / 2;
188 decltype(range.first) pfx = 0;
189 if (range.first < 0) {
190 pfx = -range.first;
191 range = { 0, range.second };
192 }
193 (void) m_model->getData(m_channel,
194 range.first,
195 range.second - range.first,
196 &samples[off + pfx]);
197 if (m_channel == -1) {
198 int channels = m_model->getChannelCount();
199 if (channels > 1) {
200 for (int i = 0; i < range.second - range.first; ++i) {
201 samples[off + pfx + i] /= float(channels);
202 }
203 }
204 }
205 return samples;
206 }
207
208 vector<complex<float>>
209 FFTModel::getFFTColumn(int column) const
210 {
211 auto samples = getSourceSamples(column);
212 m_windower.cut(&samples[0]);
213 return m_fft.process(samples);
69 } 214 }
70 215
71 bool 216 bool
72 FFTModel::estimateStableFrequency(int x, int y, double &frequency) 217 FFTModel::estimateStableFrequency(int x, int y, double &frequency)
73 { 218 {
237 { 382 {
238 percentile = 0.5; 383 percentile = 0.5;
239 if (type == MajorPeaks) return 10; 384 if (type == MajorPeaks) return 10;
240 if (bin == 0) return 3; 385 if (bin == 0) return 3;
241 386
242 double binfreq = (getSampleRate() * bin) / m_fftSize; 387 double binfreq = (sampleRate * bin) / m_fftSize;
243 double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq); 388 double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq);
244 389
245 int hibin = int(lrint((hifreq * m_fftSize) / getSampleRate())); 390 int hibin = int(lrint((hifreq * m_fftSize) / sampleRate));
246 int medianWinSize = hibin - bin; 391 int medianWinSize = hibin - bin;
247 if (medianWinSize < 3) medianWinSize = 3; 392 if (medianWinSize < 3) medianWinSize = 3;
248 393
249 percentile = 0.5f + float(binfreq / getSampleRate()); 394 percentile = 0.5f + float(binfreq / sampleRate);
250 395
251 return medianWinSize; 396 return medianWinSize;
252 } 397 }
253 398
254 FFTModel::PeakSet 399 FFTModel::PeakSet