44 m_windowType(windowType),
45 m_windowSize(windowSize),
46 m_windowIncrement(windowIncrement),
48 m_windower(windowType, windowSize),
50 m_maximumFrequency(0.0),
58 <<
") may not exceed FFT size (" <<
m_fftSize <<
")" << endl;
59 throw invalid_argument(
"FFTModel window size may not exceed FFT size");
64 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
95 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
100 if (!model->isOK()) {
111 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
113 if (model->isReady(&c))
return 100;
128 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
129 if (!model)
return 0;
130 return int((model->getEndFrame() - model->getStartFrame())
140 if (maxBin >= 0 && maxBin < height) {
164 col.reserve(cplx.size());
165 for (
auto c: cplx) col.push_back(abs(c));
174 col.reserve(cplx.size());
176 col.push_back(arg(c));
196 int n = int(col.size());
197 for (
int i = 0; i < n; ++i) {
198 if (col[i] > max) max = col[i];
230 for (
int i = 0; i < count; ++i) {
231 values[i] = abs(col[minbin + i]);
241 for (
int i = 0; i < count; ++i) {
242 values[i] = arg(col[minbin + i]);
252 for (
int i = 0; i < count; ++i) {
253 reals[i] = col[minbin + i].real();
255 for (
int i = 0; i < count; ++i) {
256 imags[i] = col[minbin + i].imag();
276 vector<float> pad(off, 0.f);
279 padded.insert(padded.end(), pad.begin(), pad.end());
280 padded.insert(padded.end(), data.begin(), data.end());
281 padded.insert(padded.end(), pad.begin(), pad.end());
298 Profiler profiler(
"FFTModel::getSourceData (cache miss)");
309 data.reserve(range.second - range.first);
311 data.insert(data.end(),
318 data.insert(data.end(), rest.begin(), rest.end());
336 Profiler profiler(
"FFTModel::getSourceDataUncached");
338 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
339 if (!model)
return {};
341 decltype(range.first) pfx = 0;
342 if (range.first < 0) {
344 range = { 0, range.second };
349 range.second - range.first);
359 data.resize(range.second - range.first, 0.f);
362 vector<float> pad(pfx, 0.f);
363 data.insert(data.begin(), pad.begin(), pad.end());
367 int channels = model->getChannelCount();
369 int n = int(data.size());
370 float factor = 1.f / float(channels);
372 for (
int i = 0; i < n; ++i) {
390 for (
const auto &incache :
m_cached) {
391 if (incache.n == n) {
398 Profiler profiler(
"FFTModel::getFFTColumn (cache miss)");
402 breakfastquay::v_fftshift(samples.data(),
m_fftSize);
409 m_fft.forwardInterleaved(samples.data(),
410 reinterpret_cast<float *
>(col.data()));
426 if (!
isOK())
return false;
430 if (x+1 >=
getWidth())
return false;
446 double expectedPhase = oldPhase + (2.0 *
M_PI * y * incr) /
m_fftSize;
448 double phaseError =
princarg(newPhase - expectedPhase);
454 (
getSampleRate() * (expectedPhase + phaseError - oldPhase)) /
463 Profiler profiler(
"FFTModel::getPeaks");
466 if (!
isOK())
return peaks;
468 if (ymax == 0 || ymax >
getHeight() - 1) {
474 if (minbin > 0) minbin = minbin - 1;
476 if (maxbin <
getHeight() - 1) maxbin = maxbin + 1;
477 const int n = maxbin - minbin + 1;
478 float *values =
new float[n];
480 for (
int bin = ymin; bin <= ymax; ++bin) {
481 if (bin == minbin || bin == maxbin)
continue;
482 if (values[bin - minbin] > values[bin - minbin - 1] &&
483 values[bin - minbin] > values[bin - minbin + 1]) {
492 int nv = int(values.size());
495 for (
int i = 0; i < nv; ++i) mean += values[i];
496 if (nv > 0) mean = mean / float(values.size());
509 int halfWin = medianWinSize/2;
514 if (ymin > halfWin) binmin = ymin - halfWin;
518 if (ymax + halfWin < nv) binmax = ymax + halfWin;
519 else binmax = nv - 1;
523 for (
int bin = binmin; bin <= binmax; ++bin) {
525 float value = values[bin];
529 halfWin = medianWinSize/2;
531 int actualSize = std::min(medianWinSize, bin - binmin + 1);
532 window.
resize(actualSize);
537 if (ymax + halfWin < nv) binmax = ymax + halfWin;
538 else binmax = nv - 1;
541 float median = window.
get();
544 if (bin > actualSize/2) centrebin = bin - actualSize/2;
546 while (centrebin > prevcentre || bin == binmin) {
548 if (centrebin > prevcentre) ++prevcentre;
550 float centre = values[prevcentre];
552 if (centre > median) {
553 inrange.push_back(centrebin);
556 if (centre <= median || centrebin+1 == nv) {
557 if (!inrange.empty()) {
560 for (
int i = 0; i < (int)inrange.size(); ++i) {
561 if (i == 0 || values[inrange[i]] > peakval) {
562 peakval = values[inrange[i]];
563 peakbin = inrange[i];
567 if (peakbin >= ymin && peakbin <= ymax) {
568 peaks.insert(peakbin);
573 if (bin == binmin)
break;
582 int bin,
double &dist)
const 586 if (bin == 0)
return 3;
588 double binfreq = (sampleRate * bin) /
m_fftSize;
591 int hibin = int(lrint((hifreq *
m_fftSize) / sampleRate));
592 int medianWinSize = hibin - bin;
594 if (medianWinSize < 3) {
601 if (medianWinSize > 20) {
602 medianWinSize = (1 + medianWinSize / 10) * 10;
604 if (medianWinSize > 200) {
605 medianWinSize = (1 + medianWinSize / 100) * 100;
607 if (medianWinSize > 2000) {
608 medianWinSize = (1 + medianWinSize / 1000) * 1000;
610 if (medianWinSize > 20000) {
611 medianWinSize = 20000;
614 if (medianWinSize < 100) {
615 dist = 1.0 - (4.0 / medianWinSize);
617 dist = 1.0 - (8.0 / medianWinSize);
619 if (dist < 0.5) dist = 0.5;
621 return medianWinSize;
626 int ymin,
int ymax)
const 628 Profiler profiler(
"FFTModel::getPeakFrequencies");
631 if (!
isOK())
return peaks;
642 vector<float> phases;
643 for (PeakLocationSet::iterator i = locations.begin();
644 i != locations.end(); ++i) {
649 for (PeakLocationSet::iterator i = locations.begin();
650 i != locations.end(); ++i) {
651 double oldPhase = phases[phaseIndex];
653 double expectedPhase = oldPhase + (2.0 *
M_PI * *i * incr) /
m_fftSize;
654 double phaseError =
princarg(newPhase - expectedPhase);
656 (sampleRate * (expectedPhase + phaseError - oldPhase))
658 peaks[*i] = frequency;
Column getPhases(int x) const
double sv_samplerate_t
Sample rate.
sv_samplerate_t getSampleRate() const override
Return the frame rate in frames per second.
bool isOK() const override
Return true if the model was constructed successfully.
static HitCount inSmallCache("FFTModel: Small FFT cache")
static double getFrequencyForPitch(int midiPitch, double centsOffset=0, double concertA=0.0)
Return the frequency at the given MIDI pitch plus centsOffset cents (1/100ths of a semitone)...
void setMaximumFrequency(double freq)
static HitCount inSourceCache("FFTModel: Source data cache")
std::pair< sv_frame_t, sv_frame_t > getSourceSampleRange(int column) const
void getValuesAt(int x, int y, float &real, float &imaginary) const
int64_t sv_frame_t
Frame index, the unit of our time axis.
float getMagnitudeAt(int x, int y) const
!! review which of these are ever actually called
floatvec_t getSourceData(std::pair< sv_frame_t, sv_frame_t >) const
int getResolution() const override
Return the number of sample frames covered by each column of bins.
void cut(T *const BQ_R__ block) const
virtual bool estimateStableFrequency(int x, int y, double &frequency)
Calculate an estimated frequency for a stable signal in this bin, using phase unwrapping.
virtual PeakLocationSet getPeaks(PeakPickType type, int x, int ymin=0, int ymax=0) const
Return locations of peak bins in the range [ymin,ymax].
const complexvec_t & getFFTColumn(int column) const
Any bin exceeding its immediate neighbours.
double m_maximumFrequency
std::vector< float, breakfastquay::StlAllocator< float > > floatvec_t
bool getMagnitudesAt(int x, float *values, int minbin=0, int count=0) const
Profile class for counting cache hits and the like.
double princarg(double a)
Window< float > m_windower
int getCompletion() const override
Return an estimated percentage value showing how far through any background operation used to calcula...
std::pair< sv_frame_t, sv_frame_t > range
virtual PeakSet getPeakFrequencies(PeakPickType type, int x, int ymin=0, int ymax=0) const
Return locations and estimated stable frequencies of peak bins.
QString getBinName(int n) const override
Get the name of a given bin (i.e.
std::map< int, double > PeakSet
sv_samplerate_t m_sampleRate
float getMaximumMagnitudeAt(int x) const
int getHeight() const override
Return the number of bins in each column.
void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
Peaks picked using sliding median window.
FFTModel(ModelId model, int channel, WindowType windowType, int windowSize, int windowIncrement, int fftSize)
!! threading requirements? !! doubles? since we're not caching much
floatvec_t getSourceDataUncached(std::pair< sv_frame_t, sv_frame_t >) const
bool getPhasesAt(int x, float *values, int minbin=0, int count=0) const
Column getColumn(int x) const override
Get data from the given column of bin values.
float getPhaseAt(int x, int y) const
std::vector< SavedColumn > m_cached
std::vector< std::complex< float >, breakfastquay::StlAllocator< std::complex< float > > > complexvec_t
SavedSourceData m_savedData
int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate, int bin, double &dist) const
int getWidth() const override
Return the number of columns of bins in the model.
float getBinValue(int n) const override
Return the value of bin n, if any.
floatvec_t getSourceSamples(int column) const
void modelChanged(ModelId myId)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
std::set< int > PeakLocationSet
Profile point instance class.