Mercurial > hg > svcore
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 |