Chris@537: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@537: Chris@537: /* Chris@537: Sonic Visualiser Chris@537: An audio file viewer and annotation editor. Chris@537: Centre for Digital Music, Queen Mary, University of London. Chris@537: This file copyright 2006-2009 Chris Cannam and QMUL. Chris@537: Chris@537: This program is free software; you can redistribute it and/or Chris@537: modify it under the terms of the GNU General Public License as Chris@537: published by the Free Software Foundation; either version 2 of the Chris@537: License, or (at your option) any later version. See the file Chris@537: COPYING included with this distribution for more information. Chris@537: */ Chris@537: Chris@537: #include "FFTFileCacheReader.h" Chris@537: #include "FFTFileCacheWriter.h" Chris@537: Chris@537: #include "fileio/MatrixFile.h" Chris@537: Chris@537: #include "base/Profiler.h" Chris@537: #include "base/Thread.h" Chris@537: #include "base/Exceptions.h" Chris@537: Chris@537: #include Chris@537: Chris@537: Chris@537: // The underlying matrix has height (m_height * 2 + 1). In each Chris@537: // column we store magnitude at [0], [2] etc and phase at [1], [3] Chris@537: // etc, and then store the normalization factor (maximum magnitude) at Chris@537: // [m_height * 2]. In compact mode, the factor takes two cells. Chris@537: Chris@537: FFTFileCacheReader::FFTFileCacheReader(FFTFileCacheWriter *writer) : Chris@537: m_readbuf(0), Chris@537: m_readbufCol(0), Chris@537: m_readbufWidth(0), Chris@555: m_readbufGood(false), Chris@537: m_storageType(writer->getStorageType()), Chris@537: m_factorSize(m_storageType == FFTCache::Compact ? 2 : 1), Chris@537: m_mfc(new MatrixFile Chris@537: (writer->getFileBase(), Chris@537: MatrixFile::ReadOnly, Chris@1038: int((m_storageType == FFTCache::Compact) ? sizeof(uint16_t) : sizeof(float)), Chris@537: writer->getWidth(), Chris@537: writer->getHeight() * 2 + m_factorSize)) Chris@537: { Chris@843: // cerr << "FFTFileCacheReader: storage type is " << (storageType == FFTCache::Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << endl; Chris@537: } Chris@537: Chris@537: FFTFileCacheReader::~FFTFileCacheReader() Chris@537: { Chris@537: if (m_readbuf) delete[] m_readbuf; Chris@537: delete m_mfc; Chris@537: } Chris@537: Chris@929: int Chris@537: FFTFileCacheReader::getWidth() const Chris@537: { Chris@537: return m_mfc->getWidth(); Chris@537: } Chris@537: Chris@929: int Chris@537: FFTFileCacheReader::getHeight() const Chris@537: { Chris@929: int mh = m_mfc->getHeight(); Chris@537: if (mh > m_factorSize) return (mh - m_factorSize) / 2; Chris@537: else return 0; Chris@537: } Chris@537: Chris@537: float Chris@929: FFTFileCacheReader::getMagnitudeAt(int x, int y) const Chris@537: { Chris@537: Profiler profiler("FFTFileCacheReader::getMagnitudeAt", false); Chris@537: Chris@537: float value = 0.f; Chris@537: Chris@537: switch (m_storageType) { Chris@537: Chris@537: case FFTCache::Compact: Chris@1038: value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.f) Chris@537: * getNormalizationFactor(x); Chris@537: break; Chris@537: Chris@537: case FFTCache::Rectangular: Chris@537: { Chris@537: float real, imag; Chris@537: getValuesAt(x, y, real, imag); Chris@537: value = sqrtf(real * real + imag * imag); Chris@537: break; Chris@537: } Chris@537: Chris@537: case FFTCache::Polar: Chris@537: value = getFromReadBufStandard(x, y * 2); Chris@537: break; Chris@537: } Chris@537: Chris@537: return value; Chris@537: } Chris@537: Chris@537: float Chris@929: FFTFileCacheReader::getNormalizedMagnitudeAt(int x, int y) const Chris@537: { Chris@537: float value = 0.f; Chris@537: Chris@537: switch (m_storageType) { Chris@537: Chris@537: case FFTCache::Compact: Chris@1038: value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.f; Chris@537: break; Chris@537: Chris@929: case FFTCache::Rectangular: Chris@929: case FFTCache::Polar: Chris@537: { Chris@537: float mag = getMagnitudeAt(x, y); Chris@537: float factor = getNormalizationFactor(x); Chris@537: if (factor != 0) value = mag / factor; Chris@537: else value = 0.f; Chris@537: break; Chris@537: } Chris@537: } Chris@537: Chris@537: return value; Chris@537: } Chris@537: Chris@537: float Chris@929: FFTFileCacheReader::getMaximumMagnitudeAt(int x) const Chris@537: { Chris@537: return getNormalizationFactor(x); Chris@537: } Chris@537: Chris@537: float Chris@929: FFTFileCacheReader::getPhaseAt(int x, int y) const Chris@537: { Chris@537: float value = 0.f; Chris@537: Chris@537: switch (m_storageType) { Chris@537: Chris@537: case FFTCache::Compact: Chris@1038: value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.f) * float(M_PI); Chris@537: break; Chris@537: Chris@537: case FFTCache::Rectangular: Chris@537: { Chris@537: float real, imag; Chris@537: getValuesAt(x, y, real, imag); Chris@537: value = atan2f(imag, real); Chris@537: break; Chris@537: } Chris@537: Chris@537: case FFTCache::Polar: Chris@537: value = getFromReadBufStandard(x, y * 2 + 1); Chris@537: break; Chris@537: } Chris@537: Chris@537: return value; Chris@537: } Chris@537: Chris@537: void Chris@929: FFTFileCacheReader::getValuesAt(int x, int y, float &real, float &imag) const Chris@537: { Chris@690: // SVDEBUG << "FFTFileCacheReader::getValuesAt(" << x << "," << y << ")" << endl; Chris@555: Chris@537: switch (m_storageType) { Chris@537: Chris@537: case FFTCache::Rectangular: Chris@537: real = getFromReadBufStandard(x, y * 2); Chris@537: imag = getFromReadBufStandard(x, y * 2 + 1); Chris@537: return; Chris@537: Chris@929: case FFTCache::Compact: Chris@929: case FFTCache::Polar: Chris@537: float mag = getMagnitudeAt(x, y); Chris@537: float phase = getPhaseAt(x, y); Chris@537: real = mag * cosf(phase); Chris@537: imag = mag * sinf(phase); Chris@537: return; Chris@537: } Chris@537: } Chris@537: Chris@537: void Chris@929: FFTFileCacheReader::getMagnitudesAt(int x, float *values, int minbin, int count, int step) const Chris@537: { Chris@537: Profiler profiler("FFTFileCacheReader::getMagnitudesAt"); Chris@537: Chris@537: switch (m_storageType) { Chris@537: Chris@537: case FFTCache::Compact: Chris@929: for (int i = 0; i < count; ++i) { Chris@929: int y = minbin + i * step; Chris@1038: values[i] = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.f) Chris@537: * getNormalizationFactor(x); Chris@537: } Chris@537: break; Chris@537: Chris@537: case FFTCache::Rectangular: Chris@537: { Chris@537: float real, imag; Chris@929: for (int i = 0; i < count; ++i) { Chris@929: int y = minbin + i * step; Chris@537: real = getFromReadBufStandard(x, y * 2); Chris@537: imag = getFromReadBufStandard(x, y * 2 + 1); Chris@537: values[i] = sqrtf(real * real + imag * imag); Chris@537: } Chris@537: break; Chris@537: } Chris@537: Chris@537: case FFTCache::Polar: Chris@929: for (int i = 0; i < count; ++i) { Chris@929: int y = minbin + i * step; Chris@537: values[i] = getFromReadBufStandard(x, y * 2); Chris@537: } Chris@537: break; Chris@537: } Chris@537: } Chris@537: Chris@537: bool Chris@929: FFTFileCacheReader::haveSetColumnAt(int x) const Chris@537: { Chris@555: if (m_readbuf && m_readbufGood && Chris@555: (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) { Chris@690: // SVDEBUG << "FFTFileCacheReader::haveSetColumnAt: short-circuiting; we know about this one" << endl; Chris@555: return true; Chris@555: } Chris@537: return m_mfc->haveSetColumnAt(x); Chris@537: } Chris@537: Chris@1038: size_t Chris@929: FFTFileCacheReader::getCacheSize(int width, int height, Chris@537: FFTCache::StorageType type) Chris@537: { Chris@537: return (height * 2 + (type == FFTCache::Compact ? 2 : 1)) * width * Chris@537: (type == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float)) + Chris@929: 2 * sizeof(int); // matrix file header size Chris@537: } Chris@537: Chris@537: void Chris@929: FFTFileCacheReader::populateReadBuf(int x) const Chris@537: { Chris@537: Profiler profiler("FFTFileCacheReader::populateReadBuf", false); Chris@537: Chris@690: // SVDEBUG << "FFTFileCacheReader::populateReadBuf(" << x << ")" << endl; Chris@554: Chris@537: if (!m_readbuf) { Chris@537: m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()]; Chris@537: } Chris@537: Chris@555: m_readbufGood = false; Chris@555: Chris@537: try { Chris@555: bool good = false; Chris@555: if (m_mfc->haveSetColumnAt(x)) { Chris@555: // If the column is not available, we have no obligation Chris@555: // to do anything with the readbuf -- we can cheerfully Chris@555: // return garbage. It's the responsibility of the caller Chris@555: // to check haveSetColumnAt before trusting any retrieved Chris@555: // data. However, we do record whether the data in the Chris@555: // readbuf is good or not, because we can use that to Chris@555: // return an immediate result for haveSetColumnAt if the Chris@555: // column is right. Chris@555: good = true; Chris@555: m_mfc->getColumnAt(x, m_readbuf); Chris@555: } Chris@537: if (m_mfc->haveSetColumnAt(x + 1)) { Chris@537: m_mfc->getColumnAt Chris@537: (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight()); Chris@537: m_readbufWidth = 2; Chris@537: } else { Chris@537: m_readbufWidth = 1; Chris@537: } Chris@555: m_readbufGood = good; Chris@537: } catch (FileReadFailed f) { Chris@843: cerr << "ERROR: FFTFileCacheReader::populateReadBuf: File read failed: " Chris@843: << f.what() << endl; Chris@537: memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize()); Chris@537: } Chris@537: m_readbufCol = x; Chris@537: } Chris@537: