annotate data/fft/FFTFileCacheReader.cpp @ 683:f84f147572b9

Avoid crash when generating/processing a very short file
author Chris Cannam
date Wed, 11 May 2011 11:04:02 +0100
parents 8accc7969c1c
children 06f13a3b9e9e
rev   line source
Chris@537 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@537 2
Chris@537 3 /*
Chris@537 4 Sonic Visualiser
Chris@537 5 An audio file viewer and annotation editor.
Chris@537 6 Centre for Digital Music, Queen Mary, University of London.
Chris@537 7 This file copyright 2006-2009 Chris Cannam and QMUL.
Chris@537 8
Chris@537 9 This program is free software; you can redistribute it and/or
Chris@537 10 modify it under the terms of the GNU General Public License as
Chris@537 11 published by the Free Software Foundation; either version 2 of the
Chris@537 12 License, or (at your option) any later version. See the file
Chris@537 13 COPYING included with this distribution for more information.
Chris@537 14 */
Chris@537 15
Chris@537 16 #include "FFTFileCacheReader.h"
Chris@537 17 #include "FFTFileCacheWriter.h"
Chris@537 18
Chris@537 19 #include "fileio/MatrixFile.h"
Chris@537 20
Chris@537 21 #include "base/Profiler.h"
Chris@537 22 #include "base/Thread.h"
Chris@537 23 #include "base/Exceptions.h"
Chris@537 24
Chris@537 25 #include <iostream>
Chris@537 26
Chris@537 27
Chris@537 28 // The underlying matrix has height (m_height * 2 + 1). In each
Chris@537 29 // column we store magnitude at [0], [2] etc and phase at [1], [3]
Chris@537 30 // etc, and then store the normalization factor (maximum magnitude) at
Chris@537 31 // [m_height * 2]. In compact mode, the factor takes two cells.
Chris@537 32
Chris@537 33 FFTFileCacheReader::FFTFileCacheReader(FFTFileCacheWriter *writer) :
Chris@537 34 m_readbuf(0),
Chris@537 35 m_readbufCol(0),
Chris@537 36 m_readbufWidth(0),
Chris@555 37 m_readbufGood(false),
Chris@537 38 m_storageType(writer->getStorageType()),
Chris@537 39 m_factorSize(m_storageType == FFTCache::Compact ? 2 : 1),
Chris@537 40 m_mfc(new MatrixFile
Chris@537 41 (writer->getFileBase(),
Chris@537 42 MatrixFile::ReadOnly,
Chris@537 43 m_storageType == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float),
Chris@537 44 writer->getWidth(),
Chris@537 45 writer->getHeight() * 2 + m_factorSize))
Chris@537 46 {
Chris@537 47 // std::cerr << "FFTFileCacheReader: storage type is " << (storageType == FFTCache::Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << std::endl;
Chris@537 48 }
Chris@537 49
Chris@537 50 FFTFileCacheReader::~FFTFileCacheReader()
Chris@537 51 {
Chris@537 52 if (m_readbuf) delete[] m_readbuf;
Chris@537 53 delete m_mfc;
Chris@537 54 }
Chris@537 55
Chris@537 56 size_t
Chris@537 57 FFTFileCacheReader::getWidth() const
Chris@537 58 {
Chris@537 59 return m_mfc->getWidth();
Chris@537 60 }
Chris@537 61
Chris@537 62 size_t
Chris@537 63 FFTFileCacheReader::getHeight() const
Chris@537 64 {
Chris@537 65 size_t mh = m_mfc->getHeight();
Chris@537 66 if (mh > m_factorSize) return (mh - m_factorSize) / 2;
Chris@537 67 else return 0;
Chris@537 68 }
Chris@537 69
Chris@537 70 float
Chris@537 71 FFTFileCacheReader::getMagnitudeAt(size_t x, size_t y) const
Chris@537 72 {
Chris@537 73 Profiler profiler("FFTFileCacheReader::getMagnitudeAt", false);
Chris@537 74
Chris@537 75 float value = 0.f;
Chris@537 76
Chris@537 77 switch (m_storageType) {
Chris@537 78
Chris@537 79 case FFTCache::Compact:
Chris@537 80 value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0)
Chris@537 81 * getNormalizationFactor(x);
Chris@537 82 break;
Chris@537 83
Chris@537 84 case FFTCache::Rectangular:
Chris@537 85 {
Chris@537 86 float real, imag;
Chris@537 87 getValuesAt(x, y, real, imag);
Chris@537 88 value = sqrtf(real * real + imag * imag);
Chris@537 89 break;
Chris@537 90 }
Chris@537 91
Chris@537 92 case FFTCache::Polar:
Chris@537 93 value = getFromReadBufStandard(x, y * 2);
Chris@537 94 break;
Chris@537 95 }
Chris@537 96
Chris@537 97 return value;
Chris@537 98 }
Chris@537 99
Chris@537 100 float
Chris@537 101 FFTFileCacheReader::getNormalizedMagnitudeAt(size_t x, size_t y) const
Chris@537 102 {
Chris@537 103 float value = 0.f;
Chris@537 104
Chris@537 105 switch (m_storageType) {
Chris@537 106
Chris@537 107 case FFTCache::Compact:
Chris@537 108 value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.0;
Chris@537 109 break;
Chris@537 110
Chris@537 111 default:
Chris@537 112 {
Chris@537 113 float mag = getMagnitudeAt(x, y);
Chris@537 114 float factor = getNormalizationFactor(x);
Chris@537 115 if (factor != 0) value = mag / factor;
Chris@537 116 else value = 0.f;
Chris@537 117 break;
Chris@537 118 }
Chris@537 119 }
Chris@537 120
Chris@537 121 return value;
Chris@537 122 }
Chris@537 123
Chris@537 124 float
Chris@537 125 FFTFileCacheReader::getMaximumMagnitudeAt(size_t x) const
Chris@537 126 {
Chris@537 127 return getNormalizationFactor(x);
Chris@537 128 }
Chris@537 129
Chris@537 130 float
Chris@537 131 FFTFileCacheReader::getPhaseAt(size_t x, size_t y) const
Chris@537 132 {
Chris@537 133 float value = 0.f;
Chris@537 134
Chris@537 135 switch (m_storageType) {
Chris@537 136
Chris@537 137 case FFTCache::Compact:
Chris@537 138 value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.0) * M_PI;
Chris@537 139 break;
Chris@537 140
Chris@537 141 case FFTCache::Rectangular:
Chris@537 142 {
Chris@537 143 float real, imag;
Chris@537 144 getValuesAt(x, y, real, imag);
Chris@537 145 value = atan2f(imag, real);
Chris@537 146 break;
Chris@537 147 }
Chris@537 148
Chris@537 149 case FFTCache::Polar:
Chris@537 150 value = getFromReadBufStandard(x, y * 2 + 1);
Chris@537 151 break;
Chris@537 152 }
Chris@537 153
Chris@537 154 return value;
Chris@537 155 }
Chris@537 156
Chris@537 157 void
Chris@537 158 FFTFileCacheReader::getValuesAt(size_t x, size_t y, float &real, float &imag) const
Chris@537 159 {
Chris@555 160 // std::cerr << "FFTFileCacheReader::getValuesAt(" << x << "," << y << ")" << std::endl;
Chris@555 161
Chris@537 162 switch (m_storageType) {
Chris@537 163
Chris@537 164 case FFTCache::Rectangular:
Chris@537 165 real = getFromReadBufStandard(x, y * 2);
Chris@537 166 imag = getFromReadBufStandard(x, y * 2 + 1);
Chris@537 167 return;
Chris@537 168
Chris@537 169 default:
Chris@537 170 float mag = getMagnitudeAt(x, y);
Chris@537 171 float phase = getPhaseAt(x, y);
Chris@537 172 real = mag * cosf(phase);
Chris@537 173 imag = mag * sinf(phase);
Chris@537 174 return;
Chris@537 175 }
Chris@537 176 }
Chris@537 177
Chris@537 178 void
Chris@537 179 FFTFileCacheReader::getMagnitudesAt(size_t x, float *values, size_t minbin, size_t count, size_t step) const
Chris@537 180 {
Chris@537 181 Profiler profiler("FFTFileCacheReader::getMagnitudesAt");
Chris@537 182
Chris@537 183 switch (m_storageType) {
Chris@537 184
Chris@537 185 case FFTCache::Compact:
Chris@537 186 for (size_t i = 0; i < count; ++i) {
Chris@537 187 size_t y = minbin + i * step;
Chris@537 188 values[i] = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0)
Chris@537 189 * getNormalizationFactor(x);
Chris@537 190 }
Chris@537 191 break;
Chris@537 192
Chris@537 193 case FFTCache::Rectangular:
Chris@537 194 {
Chris@537 195 float real, imag;
Chris@537 196 for (size_t i = 0; i < count; ++i) {
Chris@537 197 size_t y = minbin + i * step;
Chris@537 198 real = getFromReadBufStandard(x, y * 2);
Chris@537 199 imag = getFromReadBufStandard(x, y * 2 + 1);
Chris@537 200 values[i] = sqrtf(real * real + imag * imag);
Chris@537 201 }
Chris@537 202 break;
Chris@537 203 }
Chris@537 204
Chris@537 205 case FFTCache::Polar:
Chris@537 206 for (size_t i = 0; i < count; ++i) {
Chris@537 207 size_t y = minbin + i * step;
Chris@537 208 values[i] = getFromReadBufStandard(x, y * 2);
Chris@537 209 }
Chris@537 210 break;
Chris@537 211 }
Chris@537 212 }
Chris@537 213
Chris@537 214 bool
Chris@537 215 FFTFileCacheReader::haveSetColumnAt(size_t x) const
Chris@537 216 {
Chris@555 217 if (m_readbuf && m_readbufGood &&
Chris@555 218 (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) {
Chris@555 219 // std::cerr << "FFTFileCacheReader::haveSetColumnAt: short-circuiting; we know about this one" << std::endl;
Chris@555 220 return true;
Chris@555 221 }
Chris@537 222 return m_mfc->haveSetColumnAt(x);
Chris@537 223 }
Chris@537 224
Chris@537 225 size_t
Chris@537 226 FFTFileCacheReader::getCacheSize(size_t width, size_t height,
Chris@537 227 FFTCache::StorageType type)
Chris@537 228 {
Chris@537 229 return (height * 2 + (type == FFTCache::Compact ? 2 : 1)) * width *
Chris@537 230 (type == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float)) +
Chris@537 231 2 * sizeof(size_t); // matrix file header size
Chris@537 232 }
Chris@537 233
Chris@537 234 void
Chris@537 235 FFTFileCacheReader::populateReadBuf(size_t x) const
Chris@537 236 {
Chris@537 237 Profiler profiler("FFTFileCacheReader::populateReadBuf", false);
Chris@537 238
Chris@555 239 // std::cerr << "FFTFileCacheReader::populateReadBuf(" << x << ")" << std::endl;
Chris@554 240
Chris@537 241 if (!m_readbuf) {
Chris@537 242 m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
Chris@537 243 }
Chris@537 244
Chris@555 245 m_readbufGood = false;
Chris@555 246
Chris@537 247 try {
Chris@555 248 bool good = false;
Chris@555 249 if (m_mfc->haveSetColumnAt(x)) {
Chris@555 250 // If the column is not available, we have no obligation
Chris@555 251 // to do anything with the readbuf -- we can cheerfully
Chris@555 252 // return garbage. It's the responsibility of the caller
Chris@555 253 // to check haveSetColumnAt before trusting any retrieved
Chris@555 254 // data. However, we do record whether the data in the
Chris@555 255 // readbuf is good or not, because we can use that to
Chris@555 256 // return an immediate result for haveSetColumnAt if the
Chris@555 257 // column is right.
Chris@555 258 good = true;
Chris@555 259 m_mfc->getColumnAt(x, m_readbuf);
Chris@555 260 }
Chris@537 261 if (m_mfc->haveSetColumnAt(x + 1)) {
Chris@537 262 m_mfc->getColumnAt
Chris@537 263 (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
Chris@537 264 m_readbufWidth = 2;
Chris@537 265 } else {
Chris@537 266 m_readbufWidth = 1;
Chris@537 267 }
Chris@555 268 m_readbufGood = good;
Chris@537 269 } catch (FileReadFailed f) {
Chris@537 270 std::cerr << "ERROR: FFTFileCacheReader::populateReadBuf: File read failed: "
Chris@537 271 << f.what() << std::endl;
Chris@537 272 memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize());
Chris@537 273 }
Chris@537 274 m_readbufCol = x;
Chris@537 275 }
Chris@537 276