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 Chris@1326: #include Chris@1091: Chris@275: #include Chris@1091: #include Chris@1091: #include 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@1744: FFTModel(ModelId model, // a DenseTimeValueModel 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@1790: bool isOK() const override; Chris@1790: int getCompletion() const override; Chris@1790: Chris@1580: int getWidth() const override; Chris@1580: int getHeight() const override; Chris@1790: Chris@1790: float getValueAt(int x, int y) const override { Chris@1790: return getMagnitudeAt(x, y); Chris@1790: } Chris@1790: sv_frame_t getStartFrame() const override { Chris@1790: return 0; Chris@1790: } Chris@1725: sv_frame_t getTrueEndFrame() const override { Chris@1038: return sv_frame_t(getWidth()) * getResolution() + getResolution(); Chris@152: } Chris@1790: sv_samplerate_t getSampleRate() const override { Chris@1790: return m_sampleRate; Chris@1790: } Chris@1790: int getResolution() const override { Chris@1790: return m_windowIncrement; Chris@1790: } Chris@1790: Chris@1580: float getMinimumLevel() const override { return 0.f; } // Can't provide Chris@1580: float getMaximumLevel() const override { return 1.f; } // Can't provide Chris@1790: Chris@1580: Column getColumn(int x) const override; // magnitudes Chris@1790: Chris@1790: bool hasBinValues() const override { Chris@1790: return true; Chris@1790: } Chris@1790: QString getBinValueUnit() const override { Chris@1790: return "Hz"; Chris@1790: } Chris@1790: bool shouldUseLogValueScale() const override { Chris@1790: return true; Chris@1790: } Chris@1790: float getBinValue(int n) const override; Chris@1580: QString getBinName(int n) const override; Chris@1790: Chris@1833: QVector Chris@1833: getStringExportHeaders(DataExportOptions) const override { Chris@1833: return {}; Chris@1815: } Chris@1833: Chris@1833: QVector> Chris@1833: toStringExportRows(DataExportOptions, sv_frame_t, sv_frame_t) const override { Chris@1833: return {}; Chris@1679: } Chris@152: Chris@1090: // FFTModel methods: Chris@1090: // Chris@1790: QString getError() const { return m_error; } Chris@1790: 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@1780: void setMaximumFrequency(double freq); Chris@1780: double getMaximumFrequency() const { return m_maximumFrequency; } Chris@1780: 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@1790: Column getPhases(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 PeakLocationSet; // bin Chris@1045: typedef std::map 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@1580: QString getTypeName() const override { return tr("FFT"); } Chris@345: Chris@1744: private: Chris@1744: FFTModel(const FFTModel &) =delete; Chris@1744: FFTModel &operator=(const FFTModel &) =delete; Chris@360: Chris@1744: const ModelId m_model; // a DenseTimeValueModel Chris@1780: sv_samplerate_t m_sampleRate; 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 m_windower; Chris@1270: mutable breakfastquay::FFT m_fft; Chris@1780: double m_maximumFrequency; Chris@1784: mutable QString m_error; Chris@1090: Chris@1040: int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate, Chris@1576: int bin, double &dist) const; Chris@1091: Chris@1091: std::pair 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> fvec; Chris@1326: typedef std::vector, Chris@1326: breakfastquay::StlAllocator>> cvec; Chris@1780: Chris@1780: cvec getFFTColumn(int column) const; Chris@1326: fvec getSourceSamples(int column) const; Chris@1326: fvec getSourceData(std::pair) const; Chris@1326: fvec getSourceDataUncached(std::pair) const; Chris@1093: Chris@1094: struct SavedSourceData { Chris@1094: std::pair 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 m_cached; Chris@1371: mutable size_t m_cacheWriteIndex; Chris@1093: size_t m_cacheSize; Chris@152: }; Chris@152: Chris@152: #endif