Chris@152: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@152: 
Chris@152: /*
Chris@152:     Sonic Visualiser
Chris@152:     An audio file viewer and annotation editor.
Chris@152:     Centre for Digital Music, Queen Mary, University of London.
Chris@152:     This file copyright 2006 Chris Cannam.
Chris@152:     
Chris@152:     This program is free software; you can redistribute it and/or
Chris@152:     modify it under the terms of the GNU General Public License as
Chris@152:     published by the Free Software Foundation; either version 2 of the
Chris@152:     License, or (at your option) any later version.  See the file
Chris@152:     COPYING included with this distribution for more information.
Chris@152: */
Chris@152: 
Chris@1086: #ifndef FFT_MODEL_H
Chris@1086: #define FFT_MODEL_H
Chris@152: 
Chris@152: #include "DenseThreeDimensionalModel.h"
Chris@1090: #include "DenseTimeValueModel.h"
Chris@1090: 
Chris@1090: #include "base/Window.h"
Chris@152: 
Chris@1270: #include <bqfft/FFT.h>
Chris@1326: #include <bqvec/Allocators.h>
Chris@1091: 
Chris@275: #include <set>
Chris@1091: #include <vector>
Chris@1091: #include <complex>
Chris@275: 
Chris@254: /**
Chris@254:  * An implementation of DenseThreeDimensionalModel that makes FFT data
Chris@387:  * derived from a DenseTimeValueModel available as a generic data
Chris@1090:  * grid.
Chris@254:  */
Chris@152: class FFTModel : public DenseThreeDimensionalModel
Chris@152: {
Chris@247:     Q_OBJECT
Chris@247: 
Chris@1092:     //!!! threading requirements?
Chris@1092:     //!!! doubles? since we're not caching much
Chris@1092: 
Chris@152: public:
Chris@254:     /**
Chris@254:      * Construct an FFT model derived from the given
Chris@254:      * DenseTimeValueModel, with the given window parameters and FFT
Chris@254:      * size (which may exceed the window size, for zero-padded FFTs).
Chris@254:      * 
Chris@254:      * If the model has multiple channels use only the given channel,
Chris@254:      * unless the channel is -1 in which case merge all available
Chris@254:      * channels.
Chris@254:      */
Chris@152:     FFTModel(const DenseTimeValueModel *model,
Chris@152:              int channel,
Chris@152:              WindowType windowType,
Chris@929:              int windowSize,
Chris@929:              int windowIncrement,
Chris@1090:              int fftSize);
Chris@152:     ~FFTModel();
Chris@152: 
Chris@152:     // DenseThreeDimensionalModel and Model methods:
Chris@152:     //
Chris@1090:     virtual int getWidth() const;
Chris@1090:     virtual int getHeight() const;
Chris@1090:     virtual float getValueAt(int x, int y) const { return getMagnitudeAt(x, y); }
Chris@1090:     virtual bool isOK() const { return m_model && m_model->isOK(); }
Chris@1090:     virtual sv_frame_t getStartFrame() const { return 0; }
Chris@1038:     virtual sv_frame_t getEndFrame() const {
Chris@1038:         return sv_frame_t(getWidth()) * getResolution() + getResolution();
Chris@152:     }
Chris@1090:     virtual sv_samplerate_t getSampleRate() const {
Chris@1090:         return isOK() ? m_model->getSampleRate() : 0;
Chris@152:     }
Chris@1090:     virtual int getResolution() const { return m_windowIncrement; }
Chris@1090:     virtual int getYBinCount() const { return getHeight(); }
Chris@1090:     virtual float getMinimumLevel() const { return 0.f; } // Can't provide
Chris@1090:     virtual float getMaximumLevel() const { return 1.f; } // Can't provide
Chris@1090:     virtual Column getColumn(int x) const; // magnitudes
Chris@1200:     virtual Column getPhases(int x) const;
Chris@1090:     virtual QString getBinName(int n) const;
Chris@1090:     virtual bool shouldUseLogValueScale() const { return true; }
Chris@1090:     virtual int getCompletion() const {
Chris@1090:         int c = 100;
Chris@1093:         if (m_model) {
Chris@1093:             if (m_model->isReady(&c)) return 100;
Chris@1093:         }
Chris@1090:         return c;
Chris@152:     }
Chris@1090:     virtual QString getError() const { return ""; } //!!!???
Chris@1090:     virtual sv_frame_t getFillExtent() const { return getEndFrame(); }
Chris@152: 
Chris@1090:     // FFTModel methods:
Chris@1090:     //
Chris@1090:     int getChannel() const { return m_channel; }
Chris@1090:     WindowType getWindowType() const { return m_windowType; }
Chris@1090:     int getWindowSize() const { return m_windowSize; }
Chris@1090:     int getWindowIncrement() const { return m_windowIncrement; }
Chris@1090:     int getFFTSize() const { return m_fftSize; }
Chris@1200: 
Chris@1200: //!!! review which of these are ever actually called
Chris@1090:     
Chris@1090:     float getMagnitudeAt(int x, int y) const;
Chris@1090:     float getMaximumMagnitudeAt(int x) const;
Chris@1090:     float getPhaseAt(int x, int y) const;
Chris@1090:     void getValuesAt(int x, int y, float &real, float &imaginary) const;
Chris@1090:     bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const;
Chris@1090:     bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) const;
Chris@1090:     bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) const;
Chris@478: 
Chris@275:     /**
Chris@275:      * Calculate an estimated frequency for a stable signal in this
Chris@275:      * bin, using phase unwrapping.  This will be completely wrong if
Chris@275:      * the signal is not stable here.
Chris@275:      */
Chris@1045:     virtual bool estimateStableFrequency(int x, int y, double &frequency);
Chris@275: 
Chris@275:     enum PeakPickType
Chris@275:     {
Chris@275:         AllPeaks,                /// Any bin exceeding its immediate neighbours
Chris@275:         MajorPeaks,              /// Peaks picked using sliding median window
Chris@275:         MajorPitchAdaptivePeaks  /// Bigger window for higher frequencies
Chris@275:     };
Chris@275: 
Chris@929:     typedef std::set<int> PeakLocationSet; // bin
Chris@1045:     typedef std::map<int, double> PeakSet; // bin -> freq
Chris@275: 
Chris@275:     /**
Chris@275:      * Return locations of peak bins in the range [ymin,ymax].  If
Chris@275:      * ymax is zero, getHeight()-1 will be used.
Chris@275:      */
Chris@929:     virtual PeakLocationSet getPeaks(PeakPickType type, int x,
Chris@1191:                                      int ymin = 0, int ymax = 0) const;
Chris@275: 
Chris@275:     /**
Chris@275:      * Return locations and estimated stable frequencies of peak bins.
Chris@275:      */
Chris@929:     virtual PeakSet getPeakFrequencies(PeakPickType type, int x,
Chris@1191:                                        int ymin = 0, int ymax = 0) const;
Chris@273: 
Chris@345:     QString getTypeName() const { return tr("FFT"); }
Chris@345: 
Chris@360: public slots:
Chris@360:     void sourceModelAboutToBeDeleted();
Chris@360: 
Chris@152: private:
Chris@297:     FFTModel(const FFTModel &); // not implemented
Chris@152:     FFTModel &operator=(const FFTModel &); // not implemented
Chris@152: 
Chris@1090:     const DenseTimeValueModel *m_model;
Chris@1090:     int m_channel;
Chris@1090:     WindowType m_windowType;
Chris@1090:     int m_windowSize;
Chris@1090:     int m_windowIncrement;
Chris@1090:     int m_fftSize;
Chris@1090:     Window<float> m_windower;
Chris@1270:     mutable breakfastquay::FFT m_fft;
Chris@1090:     
Chris@1040:     int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate,
Chris@1040:                               int bin, float &percentile) const;
Chris@1091: 
Chris@1091:     std::pair<sv_frame_t, sv_frame_t> getSourceSampleRange(int column) const {
Chris@1091:         sv_frame_t startFrame = m_windowIncrement * sv_frame_t(column);
Chris@1091:         sv_frame_t endFrame = startFrame + m_windowSize;
Chris@1091:         // Cols are centred on the audio sample (e.g. col 0 is centred at sample 0)
Chris@1091:         startFrame -= m_windowSize / 2;
Chris@1091:         endFrame -= m_windowSize / 2;
Chris@1091:         return { startFrame, endFrame };
Chris@1091:     }
Chris@1091: 
Chris@1326:     typedef std::vector<float, breakfastquay::StlAllocator<float>> fvec;
Chris@1326:     typedef std::vector<std::complex<float>,
Chris@1326:                         breakfastquay::StlAllocator<std::complex<float>>> cvec;
Chris@1326:     
Chris@1371:     const cvec &getFFTColumn(int column) const; // returns ref for immediate use only
Chris@1326:     fvec getSourceSamples(int column) const;
Chris@1326:     fvec getSourceData(std::pair<sv_frame_t, sv_frame_t>) const;
Chris@1326:     fvec getSourceDataUncached(std::pair<sv_frame_t, sv_frame_t>) const;
Chris@1093: 
Chris@1094:     struct SavedSourceData {
Chris@1094:         std::pair<sv_frame_t, sv_frame_t> range;
Chris@1326:         fvec data;
Chris@1094:     };
Chris@1094:     mutable SavedSourceData m_savedData;
Chris@1371: 
Chris@1093:     struct SavedColumn {
Chris@1093:         int n;
Chris@1326:         cvec col;
Chris@1093:     };
Chris@1371:     mutable std::vector<SavedColumn> m_cached;
Chris@1371:     mutable size_t m_cacheWriteIndex;
Chris@1093:     size_t m_cacheSize;
Chris@152: };
Chris@152: 
Chris@152: #endif