annotate data/model/FFTModel.h @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 59e7fe1b1003
children cc27f35aa75c
rev   line source
Chris@152 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@152 2
Chris@152 3 /*
Chris@152 4 Sonic Visualiser
Chris@152 5 An audio file viewer and annotation editor.
Chris@152 6 Centre for Digital Music, Queen Mary, University of London.
Chris@152 7 This file copyright 2006 Chris Cannam.
Chris@152 8
Chris@152 9 This program is free software; you can redistribute it and/or
Chris@152 10 modify it under the terms of the GNU General Public License as
Chris@152 11 published by the Free Software Foundation; either version 2 of the
Chris@152 12 License, or (at your option) any later version. See the file
Chris@152 13 COPYING included with this distribution for more information.
Chris@152 14 */
Chris@152 15
Chris@152 16 #ifndef _FFT_MODEL_H_
Chris@152 17 #define _FFT_MODEL_H_
Chris@152 18
Chris@152 19 #include "data/fft/FFTDataServer.h"
Chris@152 20 #include "DenseThreeDimensionalModel.h"
Chris@152 21
Chris@275 22 #include <set>
Chris@275 23 #include <map>
Chris@275 24
Chris@254 25 /**
Chris@254 26 * An implementation of DenseThreeDimensionalModel that makes FFT data
Chris@387 27 * derived from a DenseTimeValueModel available as a generic data
Chris@387 28 * grid. The FFT data is acquired using FFTDataServer. Note that any
Chris@387 29 * of the accessor functions may throw AllocationFailed if a cache
Chris@387 30 * resize fails.
Chris@254 31 */
Chris@254 32
Chris@152 33 class FFTModel : public DenseThreeDimensionalModel
Chris@152 34 {
Chris@247 35 Q_OBJECT
Chris@247 36
Chris@152 37 public:
Chris@254 38 /**
Chris@254 39 * Construct an FFT model derived from the given
Chris@254 40 * DenseTimeValueModel, with the given window parameters and FFT
Chris@254 41 * size (which may exceed the window size, for zero-padded FFTs).
Chris@254 42 *
Chris@254 43 * If the model has multiple channels use only the given channel,
Chris@254 44 * unless the channel is -1 in which case merge all available
Chris@254 45 * channels.
Chris@254 46 *
Chris@254 47 * If polar is true, the data will normally be retrieved from the
Chris@254 48 * FFT model in magnitude/phase form; otherwise it will normally
Chris@254 49 * be retrieved in "cartesian" real/imaginary form. The results
Chris@254 50 * should be the same either way, but a "polar" model addressed in
Chris@254 51 * "cartesian" form or vice versa may suffer a performance
Chris@254 52 * penalty.
Chris@254 53 *
Chris@254 54 * The fillFromColumn argument gives a hint that the FFT data
Chris@254 55 * server should aim to start calculating FFT data at that column
Chris@254 56 * number if possible, as that is likely to be requested first.
Chris@254 57 */
Chris@152 58 FFTModel(const DenseTimeValueModel *model,
Chris@152 59 int channel,
Chris@152 60 WindowType windowType,
Chris@929 61 int windowSize,
Chris@929 62 int windowIncrement,
Chris@929 63 int fftSize,
Chris@152 64 bool polar,
Chris@334 65 StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria,
Chris@929 66 int fillFromColumn = 0);
Chris@152 67 ~FFTModel();
Chris@152 68
Chris@929 69 inline float getMagnitudeAt(int x, int y) {
Chris@152 70 return m_server->getMagnitudeAt(x << m_xshift, y << m_yshift);
Chris@152 71 }
Chris@929 72 inline float getNormalizedMagnitudeAt(int x, int y) {
Chris@152 73 return m_server->getNormalizedMagnitudeAt(x << m_xshift, y << m_yshift);
Chris@152 74 }
Chris@929 75 inline float getMaximumMagnitudeAt(int x) {
Chris@152 76 return m_server->getMaximumMagnitudeAt(x << m_xshift);
Chris@152 77 }
Chris@929 78 inline float getPhaseAt(int x, int y) {
Chris@152 79 return m_server->getPhaseAt(x << m_xshift, y << m_yshift);
Chris@152 80 }
Chris@929 81 inline void getValuesAt(int x, int y, float &real, float &imaginary) {
Chris@152 82 m_server->getValuesAt(x << m_xshift, y << m_yshift, real, imaginary);
Chris@152 83 }
Chris@929 84 inline bool isColumnAvailable(int x) const {
Chris@152 85 return m_server->isColumnReady(x << m_xshift);
Chris@152 86 }
Chris@152 87
Chris@929 88 inline bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) {
Chris@408 89 return m_server->getMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio());
Chris@408 90 }
Chris@929 91 inline bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) {
Chris@408 92 return m_server->getNormalizedMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio());
Chris@408 93 }
Chris@929 94 inline bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) {
Chris@408 95 return m_server->getPhasesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio());
Chris@408 96 }
Chris@929 97 inline bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) {
Chris@556 98 return m_server->getValuesAt(x << m_xshift, reals, imaginaries, minbin << m_yshift, count, getYRatio());
Chris@556 99 }
Chris@408 100
Chris@929 101 inline int getFillExtent() const { return m_server->getFillExtent(); }
Chris@152 102
Chris@152 103 // DenseThreeDimensionalModel and Model methods:
Chris@152 104 //
Chris@929 105 inline virtual int getWidth() const {
Chris@182 106 return m_server->getWidth() >> m_xshift;
Chris@182 107 }
Chris@929 108 inline virtual int getHeight() const {
Chris@212 109 // If there is no y-shift, the server's height (based on its
Chris@212 110 // fftsize/2 + 1) is correct. If there is a shift, then the
Chris@212 111 // server is using a larger fft size than we want, so we shift
Chris@212 112 // it right as many times as necessary, but then we need to
Chris@212 113 // re-add the "+1" part (because ((fftsize*2)/2 + 1) / 2 !=
Chris@212 114 // fftsize/2 + 1).
Chris@212 115 return (m_server->getHeight() >> m_yshift) + (m_yshift > 0 ? 1 : 0);
Chris@182 116 }
Chris@929 117 virtual float getValueAt(int x, int y) const {
Chris@182 118 return const_cast<FFTModel *>(this)->getMagnitudeAt(x, y);
Chris@182 119 }
Chris@152 120 virtual bool isOK() const {
Chris@152 121 return m_server && m_server->getModel();
Chris@152 122 }
Chris@929 123 virtual int getStartFrame() const {
Chris@152 124 return 0;
Chris@152 125 }
Chris@929 126 virtual int getEndFrame() const {
Chris@152 127 return getWidth() * getResolution() + getResolution();
Chris@152 128 }
Chris@929 129 virtual int getSampleRate() const;
Chris@929 130 virtual int getResolution() const {
Chris@152 131 return m_server->getWindowIncrement() << m_xshift;
Chris@152 132 }
Chris@929 133 virtual int getYBinCount() const {
Chris@152 134 return getHeight();
Chris@152 135 }
Chris@152 136 virtual float getMinimumLevel() const {
Chris@152 137 return 0.f; // Can't provide
Chris@152 138 }
Chris@152 139 virtual float getMaximumLevel() const {
Chris@152 140 return 1.f; // Can't provide
Chris@152 141 }
Chris@929 142 virtual Column getColumn(int x) const;
Chris@929 143 virtual QString getBinName(int n) const;
Chris@152 144
Chris@478 145 virtual bool shouldUseLogValueScale() const {
Chris@478 146 return true; // Although obviously it's up to the user...
Chris@478 147 }
Chris@478 148
Chris@275 149 /**
Chris@275 150 * Calculate an estimated frequency for a stable signal in this
Chris@275 151 * bin, using phase unwrapping. This will be completely wrong if
Chris@275 152 * the signal is not stable here.
Chris@275 153 */
Chris@929 154 virtual bool estimateStableFrequency(int x, int y, float &frequency);
Chris@275 155
Chris@275 156 enum PeakPickType
Chris@275 157 {
Chris@275 158 AllPeaks, /// Any bin exceeding its immediate neighbours
Chris@275 159 MajorPeaks, /// Peaks picked using sliding median window
Chris@275 160 MajorPitchAdaptivePeaks /// Bigger window for higher frequencies
Chris@275 161 };
Chris@275 162
Chris@929 163 typedef std::set<int> PeakLocationSet; // bin
Chris@929 164 typedef std::map<int, float> PeakSet; // bin -> freq
Chris@275 165
Chris@275 166 /**
Chris@275 167 * Return locations of peak bins in the range [ymin,ymax]. If
Chris@275 168 * ymax is zero, getHeight()-1 will be used.
Chris@275 169 */
Chris@929 170 virtual PeakLocationSet getPeaks(PeakPickType type, int x,
Chris@929 171 int ymin = 0, int ymax = 0);
Chris@275 172
Chris@275 173 /**
Chris@275 174 * Return locations and estimated stable frequencies of peak bins.
Chris@275 175 */
Chris@929 176 virtual PeakSet getPeakFrequencies(PeakPickType type, int x,
Chris@929 177 int ymin = 0, int ymax = 0);
Chris@273 178
Chris@152 179 virtual int getCompletion() const { return m_server->getFillCompletion(); }
Chris@678 180 virtual QString getError() const { return m_server->getError(); }
Chris@152 181
Chris@152 182 virtual Model *clone() const;
Chris@152 183
Chris@154 184 virtual void suspend() { m_server->suspend(); }
Chris@155 185 virtual void suspendWrites() { m_server->suspendWrites(); }
Chris@154 186 virtual void resume() { m_server->resume(); }
Chris@154 187
Chris@345 188 QString getTypeName() const { return tr("FFT"); }
Chris@345 189
Chris@360 190 public slots:
Chris@360 191 void sourceModelAboutToBeDeleted();
Chris@360 192
Chris@152 193 private:
Chris@297 194 FFTModel(const FFTModel &); // not implemented
Chris@152 195 FFTModel &operator=(const FFTModel &); // not implemented
Chris@152 196
Chris@152 197 FFTDataServer *m_server;
Chris@152 198 int m_xshift;
Chris@152 199 int m_yshift;
Chris@275 200
Chris@297 201 FFTDataServer *getServer(const DenseTimeValueModel *,
Chris@929 202 int, WindowType, int, int, int,
Chris@929 203 bool, StorageAdviser::Criteria, int);
Chris@297 204
Chris@929 205 int getPeakPickWindowSize(PeakPickType type, int sampleRate,
Chris@929 206 int bin, float &percentile) const;
Chris@408 207
Chris@929 208 int getYRatio() {
Chris@929 209 int ys = m_yshift;
Chris@929 210 int r = 1;
Chris@408 211 while (ys) { --ys; r <<= 1; }
Chris@408 212 return r;
Chris@408 213 }
Chris@152 214 };
Chris@152 215
Chris@152 216 #endif