annotate data/fft/FFTFileCache.cpp @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents 3e0f1f7bec85
children 6066bde1c126
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@202 7 This file copyright 2006 Chris Cannam and QMUL.
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@408 21 #include "base/Thread.h"
Chris@455 22 #include "base/Exceptions.h"
Chris@148 23
Chris@148 24 #include <iostream>
Chris@148 25
Chris@374 26
Chris@148 27 // The underlying matrix has height (m_height * 2 + 1). In each
Chris@148 28 // column we store magnitude at [0], [2] etc and phase at [1], [3]
Chris@148 29 // etc, and then store the normalization factor (maximum magnitude) at
Chris@266 30 // [m_height * 2]. In compact mode, the factor takes two cells.
Chris@148 31
Chris@148 32 FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode,
Chris@148 33 StorageType storageType) :
Chris@148 34 m_writebuf(0),
Chris@148 35 m_readbuf(0),
Chris@148 36 m_readbufCol(0),
Chris@148 37 m_readbufWidth(0),
Chris@148 38 m_mfc(new MatrixFile
Chris@148 39 (fileBase, mode,
Chris@148 40 storageType == Compact ? sizeof(uint16_t) : sizeof(float),
Chris@148 41 mode == MatrixFile::ReadOnly)),
Chris@266 42 m_storageType(storageType),
Chris@266 43 m_factorSize(storageType == Compact ? 2 : 1)
Chris@148 44 {
Chris@259 45 // std::cerr << "FFTFileCache: storage type is " << (storageType == Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << std::endl;
Chris@148 46 }
Chris@148 47
Chris@148 48 FFTFileCache::~FFTFileCache()
Chris@148 49 {
Chris@148 50 if (m_readbuf) delete[] m_readbuf;
Chris@148 51 if (m_writebuf) delete[] m_writebuf;
Chris@148 52 delete m_mfc;
Chris@148 53 }
Chris@148 54
Chris@148 55 size_t
Chris@148 56 FFTFileCache::getWidth() const
Chris@148 57 {
Chris@148 58 return m_mfc->getWidth();
Chris@148 59 }
Chris@148 60
Chris@148 61 size_t
Chris@148 62 FFTFileCache::getHeight() const
Chris@148 63 {
Chris@148 64 size_t mh = m_mfc->getHeight();
Chris@266 65 if (mh > m_factorSize) return (mh - m_factorSize) / 2;
Chris@148 66 else return 0;
Chris@148 67 }
Chris@148 68
Chris@148 69 void
Chris@148 70 FFTFileCache::resize(size_t width, size_t height)
Chris@148 71 {
Chris@408 72 MutexLocker locker(&m_writeMutex, "FFTFileCache::resize::m_writeMutex");
Chris@148 73
Chris@266 74 m_mfc->resize(width, height * 2 + m_factorSize);
Chris@458 75
Chris@458 76 {
Chris@458 77 MutexLocker locker(&m_readbufMutex, "FFTFileCache::resize::m_readMutex");
Chris@458 78 if (m_readbuf) {
Chris@458 79 delete[] m_readbuf;
Chris@458 80 m_readbuf = 0;
Chris@458 81 }
Chris@148 82 }
Chris@458 83
Chris@148 84 if (m_writebuf) {
Chris@148 85 delete[] m_writebuf;
Chris@148 86 }
Chris@266 87 m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()];
Chris@148 88 }
Chris@458 89
Chris@148 90 void
Chris@148 91 FFTFileCache::reset()
Chris@148 92 {
Chris@148 93 m_mfc->reset();
Chris@148 94 }
Chris@148 95
Chris@148 96 float
Chris@148 97 FFTFileCache::getMagnitudeAt(size_t x, size_t y) const
Chris@148 98 {
Chris@183 99 Profiler profiler("FFTFileCache::getMagnitudeAt", false);
Chris@183 100
Chris@148 101 float value = 0.f;
Chris@148 102
Chris@148 103 switch (m_storageType) {
Chris@148 104
Chris@148 105 case Compact:
Chris@148 106 value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0)
Chris@148 107 * getNormalizationFactor(x);
Chris@148 108 break;
Chris@148 109
Chris@148 110 case Rectangular:
Chris@148 111 {
Chris@148 112 float real, imag;
Chris@148 113 getValuesAt(x, y, real, imag);
Chris@148 114 value = sqrtf(real * real + imag * imag);
Chris@148 115 break;
Chris@148 116 }
Chris@148 117
Chris@148 118 case Polar:
Chris@148 119 value = getFromReadBufStandard(x, y * 2);
Chris@148 120 break;
Chris@148 121 }
Chris@148 122
Chris@148 123 return value;
Chris@148 124 }
Chris@148 125
Chris@148 126 float
Chris@148 127 FFTFileCache::getNormalizedMagnitudeAt(size_t x, size_t y) const
Chris@148 128 {
Chris@148 129 float value = 0.f;
Chris@148 130
Chris@148 131 switch (m_storageType) {
Chris@148 132
Chris@148 133 case Compact:
Chris@148 134 value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.0;
Chris@148 135 break;
Chris@148 136
Chris@148 137 default:
Chris@148 138 {
Chris@148 139 float mag = getMagnitudeAt(x, y);
Chris@148 140 float factor = getNormalizationFactor(x);
Chris@148 141 if (factor != 0) value = mag / factor;
Chris@148 142 else value = 0.f;
Chris@148 143 break;
Chris@148 144 }
Chris@148 145 }
Chris@148 146
Chris@148 147 return value;
Chris@148 148 }
Chris@148 149
Chris@148 150 float
Chris@148 151 FFTFileCache::getMaximumMagnitudeAt(size_t x) const
Chris@148 152 {
Chris@148 153 return getNormalizationFactor(x);
Chris@148 154 }
Chris@148 155
Chris@148 156 float
Chris@148 157 FFTFileCache::getPhaseAt(size_t x, size_t y) const
Chris@148 158 {
Chris@148 159 float value = 0.f;
Chris@148 160
Chris@148 161 switch (m_storageType) {
Chris@148 162
Chris@148 163 case Compact:
Chris@148 164 value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.0) * M_PI;
Chris@148 165 break;
Chris@148 166
Chris@148 167 case Rectangular:
Chris@148 168 {
Chris@148 169 float real, imag;
Chris@148 170 getValuesAt(x, y, real, imag);
Chris@408 171 value = atan2f(imag, real);
Chris@148 172 break;
Chris@148 173 }
Chris@148 174
Chris@148 175 case Polar:
Chris@148 176 value = getFromReadBufStandard(x, y * 2 + 1);
Chris@148 177 break;
Chris@148 178 }
Chris@148 179
Chris@148 180 return value;
Chris@148 181 }
Chris@148 182
Chris@148 183 void
Chris@148 184 FFTFileCache::getValuesAt(size_t x, size_t y, float &real, float &imag) const
Chris@148 185 {
Chris@148 186 switch (m_storageType) {
Chris@148 187
Chris@148 188 case Rectangular:
Chris@148 189 real = getFromReadBufStandard(x, y * 2);
Chris@148 190 imag = getFromReadBufStandard(x, y * 2 + 1);
Chris@148 191 return;
Chris@148 192
Chris@148 193 default:
Chris@148 194 float mag = getMagnitudeAt(x, y);
Chris@148 195 float phase = getPhaseAt(x, y);
Chris@148 196 real = mag * cosf(phase);
Chris@148 197 imag = mag * sinf(phase);
Chris@148 198 return;
Chris@148 199 }
Chris@148 200 }
Chris@148 201
Chris@148 202 bool
Chris@148 203 FFTFileCache::haveSetColumnAt(size_t x) const
Chris@148 204 {
Chris@148 205 return m_mfc->haveSetColumnAt(x);
Chris@148 206 }
Chris@148 207
Chris@148 208 void
Chris@148 209 FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor)
Chris@148 210 {
Chris@408 211 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
Chris@148 212
Chris@148 213 size_t h = getHeight();
Chris@148 214
Chris@148 215 switch (m_storageType) {
Chris@148 216
Chris@148 217 case Compact:
Chris@148 218 for (size_t y = 0; y < h; ++y) {
Chris@148 219 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
Chris@148 220 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
Chris@148 221 }
Chris@148 222 break;
Chris@148 223
Chris@148 224 case Rectangular:
Chris@148 225 for (size_t y = 0; y < h; ++y) {
Chris@148 226 ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
Chris@148 227 ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
Chris@148 228 }
Chris@148 229 break;
Chris@148 230
Chris@148 231 case Polar:
Chris@148 232 for (size_t y = 0; y < h; ++y) {
Chris@148 233 ((float *)m_writebuf)[y * 2] = mags[y];
Chris@148 234 ((float *)m_writebuf)[y * 2 + 1] = phases[y];
Chris@148 235 }
Chris@148 236 break;
Chris@148 237 }
Chris@148 238
Chris@266 239 // static float maxFactor = 0;
Chris@266 240 // if (factor > maxFactor) maxFactor = factor;
Chris@266 241 // std::cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
Chris@148 242
Chris@266 243 setNormalizationFactorToWritebuf(factor);
Chris@266 244
Chris@148 245 m_mfc->setColumnAt(x, m_writebuf);
Chris@148 246 }
Chris@148 247
Chris@148 248 void
Chris@148 249 FFTFileCache::setColumnAt(size_t x, float *real, float *imag)
Chris@148 250 {
Chris@408 251 MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex");
Chris@148 252
Chris@148 253 size_t h = getHeight();
Chris@148 254
Chris@266 255 float factor = 0.0f;
Chris@148 256
Chris@148 257 switch (m_storageType) {
Chris@148 258
Chris@148 259 case Compact:
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@266 262 if (mag > factor) factor = mag;
Chris@148 263 }
Chris@148 264 for (size_t y = 0; y < h; ++y) {
Chris@148 265 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@408 266 float phase = atan2f(imag[y], real[y]);
Chris@266 267 ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0);
Chris@148 268 ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI));
Chris@148 269 }
Chris@148 270 break;
Chris@148 271
Chris@148 272 case Rectangular:
Chris@148 273 for (size_t y = 0; y < h; ++y) {
Chris@148 274 ((float *)m_writebuf)[y * 2] = real[y];
Chris@148 275 ((float *)m_writebuf)[y * 2 + 1] = imag[y];
Chris@148 276 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@266 277 if (mag > factor) factor = mag;
Chris@148 278 }
Chris@148 279 break;
Chris@148 280
Chris@148 281 case Polar:
Chris@148 282 for (size_t y = 0; y < h; ++y) {
Chris@148 283 float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@266 284 if (mag > factor) factor = mag;
Chris@148 285 ((float *)m_writebuf)[y * 2] = mag;
Chris@408 286 float phase = atan2f(imag[y], real[y]);
Chris@290 287 ((float *)m_writebuf)[y * 2 + 1] = phase;
Chris@148 288 }
Chris@148 289 break;
Chris@148 290 }
Chris@148 291
Chris@266 292 // static float maxFactor = 0;
Chris@266 293 // if (factor > maxFactor) maxFactor = factor;
Chris@266 294 // std::cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
Chris@266 295
Chris@266 296 setNormalizationFactorToWritebuf(factor);
Chris@266 297
Chris@148 298 m_mfc->setColumnAt(x, m_writebuf);
Chris@148 299 }
Chris@148 300
Chris@170 301 size_t
Chris@170 302 FFTFileCache::getCacheSize(size_t width, size_t height, StorageType type)
Chris@170 303 {
Chris@266 304 return (height * 2 + (type == Compact ? 2 : 1)) * width *
Chris@170 305 (type == Compact ? sizeof(uint16_t) : sizeof(float)) +
Chris@170 306 2 * sizeof(size_t); // matrix file header size
Chris@170 307 }
Chris@170 308
Chris@183 309 void
Chris@458 310 FFTFileCache::populateReadBuf(size_t x) const // m_readbufMutex already held
Chris@183 311 {
Chris@183 312 Profiler profiler("FFTFileCache::populateReadBuf", false);
Chris@183 313
Chris@183 314 if (!m_readbuf) {
Chris@183 315 m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
Chris@183 316 }
Chris@455 317 try {
Chris@455 318 m_mfc->getColumnAt(x, m_readbuf);
Chris@455 319 if (m_mfc->haveSetColumnAt(x + 1)) {
Chris@455 320 m_mfc->getColumnAt
Chris@455 321 (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
Chris@455 322 m_readbufWidth = 2;
Chris@455 323 } else {
Chris@455 324 m_readbufWidth = 1;
Chris@455 325 }
Chris@455 326 } catch (FileReadFailed f) {
Chris@455 327 std::cerr << "ERROR: FFTFileCache::populateReadBuf: File read failed: "
Chris@455 328 << f.what() << std::endl;
Chris@455 329 memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize());
Chris@183 330 }
Chris@183 331 m_readbufCol = x;
Chris@183 332 }
Chris@183 333