annotate data/fft/FFTFileCache.cpp @ 184:5a916fee6d2d

* Handle generator transforms (plugins whose channel count isn't dependent on number of audio inputs, as they have none) * Be less keen to suspend writing FFT data in spectrogram repaint -- only do it if we find we actually need to query the FFT data (i.e. we aren't repainting an area that hasn't been generated at all yet)
author Chris Cannam
date Tue, 10 Oct 2006 19:04:57 +0000
parents 146eb9e35baa
children 91fdc752e540
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #include "FFTFileCache.h"
Chris@148 17
Chris@150 18 #include "fileio/MatrixFile.h"
Chris@148 19
Chris@148 20 #include "base/Profiler.h"
Chris@148 21
Chris@148 22 #include <iostream>
Chris@148 23
Chris@148 24 #include <QMutexLocker>
Chris@148 25
Chris@148 26 // The underlying matrix has height (m_height * 2 + 1). In each
Chris@148 27 // column we store magnitude at [0], [2] etc and phase at [1], [3]
Chris@148 28 // etc, and then store the normalization factor (maximum magnitude) at
Chris@148 29 // [m_height * 2].
Chris@148 30
Chris@148 31 FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode,
Chris@148 32 StorageType storageType) :
Chris@148 33 m_writebuf(0),
Chris@148 34 m_readbuf(0),
Chris@148 35 m_readbufCol(0),
Chris@148 36 m_readbufWidth(0),
Chris@148 37 m_mfc(new MatrixFile
Chris@148 38 (fileBase, mode,
Chris@148 39 storageType == Compact ? sizeof(uint16_t) : sizeof(float),
Chris@148 40 mode == MatrixFile::ReadOnly)),
Chris@148 41 m_storageType(storageType)
Chris@148 42 {
Chris@148 43 std::cerr << "FFTFileCache: storage type is " << (storageType == Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << std::endl;
Chris@148 44 }
Chris@148 45
Chris@148 46 FFTFileCache::~FFTFileCache()
Chris@148 47 {
Chris@148 48 if (m_readbuf) delete[] m_readbuf;
Chris@148 49 if (m_writebuf) delete[] m_writebuf;
Chris@148 50 delete m_mfc;
Chris@148 51 }
Chris@148 52
Chris@148 53 size_t
Chris@148 54 FFTFileCache::getWidth() const
Chris@148 55 {
Chris@148 56 return m_mfc->getWidth();
Chris@148 57 }
Chris@148 58
Chris@148 59 size_t
Chris@148 60 FFTFileCache::getHeight() const
Chris@148 61 {
Chris@148 62 size_t mh = m_mfc->getHeight();
Chris@148 63 if (mh > 0) return (mh - 1) / 2;
Chris@148 64 else return 0;
Chris@148 65 }
Chris@148 66
Chris@148 67 void
Chris@148 68 FFTFileCache::resize(size_t width, size_t height)
Chris@148 69 {
Chris@148 70 QMutexLocker locker(&m_writeMutex);
Chris@148 71
Chris@148 72 m_mfc->resize(width, height * 2 + 1);
Chris@148 73 if (m_readbuf) {
Chris@148 74 delete[] m_readbuf;
Chris@148 75 m_readbuf = 0;
Chris@148 76 }
Chris@148 77 if (m_writebuf) {
Chris@148 78 delete[] m_writebuf;
Chris@148 79 }
Chris@148 80 m_writebuf = new char[(height * 2 + 1) * m_mfc->getCellSize()];
Chris@148 81 }
Chris@148 82
Chris@148 83 void
Chris@148 84 FFTFileCache::reset()
Chris@148 85 {
Chris@148 86 m_mfc->reset();
Chris@148 87 }
Chris@148 88
Chris@148 89 float
Chris@148 90 FFTFileCache::getMagnitudeAt(size_t x, size_t y) const
Chris@148 91 {
Chris@183 92 Profiler profiler("FFTFileCache::getMagnitudeAt", false);
Chris@183 93
Chris@148 94 float value = 0.f;
Chris@148 95
Chris@148 96 switch (m_storageType) {
Chris@148 97
Chris@148 98 case Compact:
Chris@148 99 value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0)
Chris@148 100 * getNormalizationFactor(x);
Chris@148 101 break;
Chris@148 102
Chris@148 103 case Rectangular:
Chris@148 104 {
Chris@148 105 float real, imag;
Chris@148 106 getValuesAt(x, y, real, imag);
Chris@148 107 value = sqrtf(real * real + imag * imag);
Chris@148 108 break;
Chris@148 109 }
Chris@148 110
Chris@148 111 case Polar:
Chris@148 112 value = getFromReadBufStandard(x, y * 2);
Chris@148 113 break;
Chris@148 114 }
Chris@148 115
Chris@148 116 return value;
Chris@148 117 }
Chris@148 118
Chris@148 119 float
Chris@148 120 FFTFileCache::getNormalizedMagnitudeAt(size_t x, size_t y) const
Chris@148 121 {
Chris@148 122 float value = 0.f;
Chris@148 123
Chris@148 124 switch (m_storageType) {
Chris@148 125
Chris@148 126 case Compact:
Chris@148 127 value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.0;
Chris@148 128 break;
Chris@148 129
Chris@148 130 default:
Chris@148 131 {
Chris@148 132 float mag = getMagnitudeAt(x, y);
Chris@148 133 float factor = getNormalizationFactor(x);
Chris@148 134 if (factor != 0) value = mag / factor;
Chris@148 135 else value = 0.f;
Chris@148 136 break;
Chris@148 137 }
Chris@148 138 }
Chris@148 139
Chris@148 140 return value;
Chris@148 141 }
Chris@148 142
Chris@148 143 float
Chris@148 144 FFTFileCache::getMaximumMagnitudeAt(size_t x) const
Chris@148 145 {
Chris@148 146 return getNormalizationFactor(x);
Chris@148 147 }
Chris@148 148
Chris@148 149 float
Chris@148 150 FFTFileCache::getPhaseAt(size_t x, size_t y) const
Chris@148 151 {
Chris@148 152 float value = 0.f;
Chris@148 153
Chris@148 154 switch (m_storageType) {
Chris@148 155
Chris@148 156 case Compact:
Chris@148 157 value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.0) * M_PI;
Chris@148 158 break;
Chris@148 159
Chris@148 160 case Rectangular:
Chris@148 161 {
Chris@148 162 float real, imag;
Chris@148 163 getValuesAt(x, y, real, imag);
Chris@148 164 value = princargf(atan2f(imag, real));
Chris@148 165 break;
Chris@148 166 }
Chris@148 167
Chris@148 168 case Polar:
Chris@148 169 value = getFromReadBufStandard(x, y * 2 + 1);
Chris@148 170 break;
Chris@148 171 }
Chris@148 172
Chris@148 173 return value;
Chris@148 174 }
Chris@148 175
Chris@148 176 void
Chris@148 177 FFTFileCache::getValuesAt(size_t x, size_t y, float &real, float &imag) const
Chris@148 178 {
Chris@148 179 switch (m_storageType) {
Chris@148 180
Chris@148 181 case Rectangular:
Chris@148 182 real = getFromReadBufStandard(x, y * 2);
Chris@148 183 imag = getFromReadBufStandard(x, y * 2 + 1);
Chris@148 184 return;
Chris@148 185
Chris@148 186 default:
Chris@148 187 float mag = getMagnitudeAt(x, y);
Chris@148 188 float phase = getPhaseAt(x, y);
Chris@148 189 real = mag * cosf(phase);
Chris@148 190 imag = mag * sinf(phase);
Chris@148 191 return;
Chris@148 192 }
Chris@148 193 }
Chris@148 194
Chris@148 195 bool
Chris@148 196 FFTFileCache::haveSetColumnAt(size_t x) const
Chris@148 197 {
Chris@148 198 return m_mfc->haveSetColumnAt(x);
Chris@148 199 }
Chris@148 200
Chris@148 201 void
Chris@148 202 FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor)
Chris@148 203 {
Chris@148 204 QMutexLocker locker(&m_writeMutex);
Chris@148 205
Chris@148 206 size_t h = getHeight();
Chris@148 207
Chris@148 208 switch (m_storageType) {
Chris@148 209
Chris@148 210 case Compact:
Chris@148 211 for (size_t y = 0; y < h; ++y) {
Chris@148 212 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
Chris@148 213 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
Chris@148 214 }
Chris@148 215 break;
Chris@148 216
Chris@148 217 case Rectangular:
Chris@148 218 for (size_t y = 0; y < h; ++y) {
Chris@148 219 ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
Chris@148 220 ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
Chris@148 221 }
Chris@148 222 break;
Chris@148 223
Chris@148 224 case Polar:
Chris@148 225 for (size_t y = 0; y < h; ++y) {
Chris@148 226 ((float *)m_writebuf)[y * 2] = mags[y];
Chris@148 227 ((float *)m_writebuf)[y * 2 + 1] = phases[y];
Chris@148 228 }
Chris@148 229 break;
Chris@148 230 }
Chris@148 231
Chris@148 232 static float maxFactor = 0;
Chris@148 233 if (factor > maxFactor) maxFactor = factor;
Chris@148 234 // std::cerr << "Normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
Chris@148 235
Chris@148 236 if (m_storageType == Compact) {
Chris@148 237 ((uint16_t *)m_writebuf)[h * 2] = factor * 65535.0;
Chris@148 238 } else {
Chris@148 239 ((float *)m_writebuf)[h * 2] = factor;
Chris@148 240 }
Chris@148 241 m_mfc->setColumnAt(x, m_writebuf);
Chris@148 242 }
Chris@148 243
Chris@148 244 void
Chris@148 245 FFTFileCache::setColumnAt(size_t x, float *real, float *imag)
Chris@148 246 {
Chris@148 247 QMutexLocker locker(&m_writeMutex);
Chris@148 248
Chris@148 249 size_t h = getHeight();
Chris@148 250
Chris@148 251 float max = 0.0f;
Chris@148 252
Chris@148 253 switch (m_storageType) {
Chris@148 254
Chris@148 255 case Compact:
Chris@148 256 for (size_t y = 0; y < h; ++y) {
Chris@148 257 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@148 258 if (mag > max) max = mag;
Chris@148 259 }
Chris@148 260 for (size_t y = 0; y < h; ++y) {
Chris@148 261 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@148 262 float phase = princargf(atan2f(imag[y], real[y]));
Chris@148 263 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / max) * 65535.0);
Chris@148 264 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI));
Chris@148 265 }
Chris@148 266 break;
Chris@148 267
Chris@148 268 case Rectangular:
Chris@148 269 for (size_t y = 0; y < h; ++y) {
Chris@148 270 ((float *)m_writebuf)[y * 2] = real[y];
Chris@148 271 ((float *)m_writebuf)[y * 2 + 1] = imag[y];
Chris@148 272 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@148 273 if (mag > max) max = mag;
Chris@148 274 }
Chris@148 275 break;
Chris@148 276
Chris@148 277 case Polar:
Chris@148 278 for (size_t y = 0; y < h; ++y) {
Chris@148 279 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@148 280 if (mag > max) max = mag;
Chris@148 281 ((float *)m_writebuf)[y * 2] = mag;
Chris@148 282 ((float *)m_writebuf)[y * 2 + 1] = princargf(atan2f(imag[y], real[y]));
Chris@148 283 }
Chris@148 284 break;
Chris@148 285 }
Chris@148 286
Chris@148 287 ((float *)m_writebuf)[h * 2] = max;
Chris@148 288 m_mfc->setColumnAt(x, m_writebuf);
Chris@148 289 }
Chris@148 290
Chris@170 291 size_t
Chris@170 292 FFTFileCache::getCacheSize(size_t width, size_t height, StorageType type)
Chris@170 293 {
Chris@170 294 return (height * 2 + 1) * width *
Chris@170 295 (type == Compact ? sizeof(uint16_t) : sizeof(float)) +
Chris@170 296 2 * sizeof(size_t); // matrix file header size
Chris@170 297 }
Chris@170 298
Chris@183 299 void
Chris@183 300 FFTFileCache::populateReadBuf(size_t x) const
Chris@183 301 {
Chris@183 302 Profiler profiler("FFTFileCache::populateReadBuf", false);
Chris@183 303
Chris@183 304 if (!m_readbuf) {
Chris@183 305 m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
Chris@183 306 }
Chris@183 307 m_mfc->getColumnAt(x, m_readbuf);
Chris@183 308 if (m_mfc->haveSetColumnAt(x + 1)) {
Chris@183 309 m_mfc->getColumnAt
Chris@183 310 (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
Chris@183 311 m_readbufWidth = 2;
Chris@183 312 } else {
Chris@183 313 m_readbufWidth = 1;
Chris@183 314 }
Chris@183 315 m_readbufCol = x;
Chris@183 316 }
Chris@183 317