Chris@148: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@148: Chris@148: /* Chris@148: Sonic Visualiser Chris@148: An audio file viewer and annotation editor. Chris@148: Centre for Digital Music, Queen Mary, University of London. Chris@202: This file copyright 2006 Chris Cannam and QMUL. Chris@148: Chris@148: This program is free software; you can redistribute it and/or Chris@148: modify it under the terms of the GNU General Public License as Chris@148: published by the Free Software Foundation; either version 2 of the Chris@148: License, or (at your option) any later version. See the file Chris@148: COPYING included with this distribution for more information. Chris@148: */ Chris@148: Chris@148: #include "FFTFileCache.h" Chris@148: Chris@150: #include "fileio/MatrixFile.h" Chris@148: Chris@148: #include "base/Profiler.h" Chris@408: #include "base/Thread.h" Chris@455: #include "base/Exceptions.h" Chris@148: Chris@148: #include Chris@148: Chris@374: Chris@148: // The underlying matrix has height (m_height * 2 + 1). In each Chris@148: // column we store magnitude at [0], [2] etc and phase at [1], [3] Chris@148: // etc, and then store the normalization factor (maximum magnitude) at Chris@266: // [m_height * 2]. In compact mode, the factor takes two cells. Chris@148: Chris@148: FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode, Chris@148: StorageType storageType) : Chris@148: m_writebuf(0), Chris@148: m_readbuf(0), Chris@148: m_readbufCol(0), Chris@148: m_readbufWidth(0), Chris@148: m_mfc(new MatrixFile Chris@148: (fileBase, mode, Chris@148: storageType == Compact ? sizeof(uint16_t) : sizeof(float), Chris@148: mode == MatrixFile::ReadOnly)), Chris@266: m_storageType(storageType), Chris@266: m_factorSize(storageType == Compact ? 2 : 1) Chris@148: { Chris@259: // std::cerr << "FFTFileCache: storage type is " << (storageType == Compact ? "Compact" : storageType == Polar ? "Polar" : "Rectangular") << std::endl; Chris@148: } Chris@148: Chris@148: FFTFileCache::~FFTFileCache() Chris@148: { Chris@148: if (m_readbuf) delete[] m_readbuf; Chris@148: if (m_writebuf) delete[] m_writebuf; Chris@148: delete m_mfc; Chris@148: } Chris@148: Chris@148: size_t Chris@148: FFTFileCache::getWidth() const Chris@148: { Chris@148: return m_mfc->getWidth(); Chris@148: } Chris@148: Chris@148: size_t Chris@148: FFTFileCache::getHeight() const Chris@148: { Chris@148: size_t mh = m_mfc->getHeight(); Chris@266: if (mh > m_factorSize) return (mh - m_factorSize) / 2; Chris@148: else return 0; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTFileCache::resize(size_t width, size_t height) Chris@148: { Chris@408: MutexLocker locker(&m_writeMutex, "FFTFileCache::resize::m_writeMutex"); Chris@148: Chris@266: m_mfc->resize(width, height * 2 + m_factorSize); Chris@148: if (m_readbuf) { Chris@148: delete[] m_readbuf; Chris@148: m_readbuf = 0; Chris@148: } Chris@148: if (m_writebuf) { Chris@148: delete[] m_writebuf; Chris@148: } Chris@266: m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()]; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTFileCache::reset() Chris@148: { Chris@148: m_mfc->reset(); Chris@148: } Chris@148: Chris@148: float Chris@148: FFTFileCache::getMagnitudeAt(size_t x, size_t y) const Chris@148: { Chris@183: Profiler profiler("FFTFileCache::getMagnitudeAt", false); Chris@183: Chris@148: float value = 0.f; Chris@148: Chris@148: switch (m_storageType) { Chris@148: Chris@148: case Compact: Chris@148: value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.0) Chris@148: * getNormalizationFactor(x); Chris@148: break; Chris@148: Chris@148: case Rectangular: Chris@148: { Chris@148: float real, imag; Chris@148: getValuesAt(x, y, real, imag); Chris@148: value = sqrtf(real * real + imag * imag); Chris@148: break; Chris@148: } Chris@148: Chris@148: case Polar: Chris@148: value = getFromReadBufStandard(x, y * 2); Chris@148: break; Chris@148: } Chris@148: Chris@148: return value; Chris@148: } Chris@148: Chris@148: float Chris@148: FFTFileCache::getNormalizedMagnitudeAt(size_t x, size_t y) const Chris@148: { Chris@148: float value = 0.f; Chris@148: Chris@148: switch (m_storageType) { Chris@148: Chris@148: case Compact: Chris@148: value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.0; Chris@148: break; Chris@148: Chris@148: default: Chris@148: { Chris@148: float mag = getMagnitudeAt(x, y); Chris@148: float factor = getNormalizationFactor(x); Chris@148: if (factor != 0) value = mag / factor; Chris@148: else value = 0.f; Chris@148: break; Chris@148: } Chris@148: } Chris@148: Chris@148: return value; Chris@148: } Chris@148: Chris@148: float Chris@148: FFTFileCache::getMaximumMagnitudeAt(size_t x) const Chris@148: { Chris@148: return getNormalizationFactor(x); Chris@148: } Chris@148: Chris@148: float Chris@148: FFTFileCache::getPhaseAt(size_t x, size_t y) const Chris@148: { Chris@148: float value = 0.f; Chris@148: Chris@148: switch (m_storageType) { Chris@148: Chris@148: case Compact: Chris@148: value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.0) * M_PI; Chris@148: break; Chris@148: Chris@148: case Rectangular: Chris@148: { Chris@148: float real, imag; Chris@148: getValuesAt(x, y, real, imag); Chris@408: value = atan2f(imag, real); Chris@148: break; Chris@148: } Chris@148: Chris@148: case Polar: Chris@148: value = getFromReadBufStandard(x, y * 2 + 1); Chris@148: break; Chris@148: } Chris@148: Chris@148: return value; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTFileCache::getValuesAt(size_t x, size_t y, float &real, float &imag) const Chris@148: { Chris@148: switch (m_storageType) { Chris@148: Chris@148: case Rectangular: Chris@148: real = getFromReadBufStandard(x, y * 2); Chris@148: imag = getFromReadBufStandard(x, y * 2 + 1); Chris@148: return; Chris@148: Chris@148: default: Chris@148: float mag = getMagnitudeAt(x, y); Chris@148: float phase = getPhaseAt(x, y); Chris@148: real = mag * cosf(phase); Chris@148: imag = mag * sinf(phase); Chris@148: return; Chris@148: } Chris@148: } Chris@148: Chris@148: bool Chris@148: FFTFileCache::haveSetColumnAt(size_t x) const Chris@148: { Chris@148: return m_mfc->haveSetColumnAt(x); Chris@148: } Chris@148: Chris@148: void Chris@148: FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor) Chris@148: { Chris@408: MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex"); Chris@148: Chris@148: size_t h = getHeight(); Chris@148: Chris@148: switch (m_storageType) { Chris@148: Chris@148: case Compact: Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0); Chris@148: ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI)); Chris@148: } Chris@148: break; Chris@148: Chris@148: case Rectangular: Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]); Chris@148: ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]); Chris@148: } Chris@148: break; Chris@148: Chris@148: case Polar: Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: ((float *)m_writebuf)[y * 2] = mags[y]; Chris@148: ((float *)m_writebuf)[y * 2 + 1] = phases[y]; Chris@148: } Chris@148: break; Chris@148: } Chris@148: Chris@266: // static float maxFactor = 0; Chris@266: // if (factor > maxFactor) maxFactor = factor; Chris@266: // std::cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl; Chris@148: Chris@266: setNormalizationFactorToWritebuf(factor); Chris@266: Chris@148: m_mfc->setColumnAt(x, m_writebuf); Chris@148: } Chris@148: Chris@148: void Chris@148: FFTFileCache::setColumnAt(size_t x, float *real, float *imag) Chris@148: { Chris@408: MutexLocker locker(&m_writeMutex, "FFTFileCache::setColumnAt::m_writeMutex"); Chris@148: Chris@148: size_t h = getHeight(); Chris@148: Chris@266: float factor = 0.0f; Chris@148: Chris@148: switch (m_storageType) { Chris@148: Chris@148: case Compact: Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); Chris@266: if (mag > factor) factor = mag; Chris@148: } Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); Chris@408: float phase = atan2f(imag[y], real[y]); Chris@266: ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0); Chris@148: ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI)); Chris@148: } Chris@148: break; Chris@148: Chris@148: case Rectangular: Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: ((float *)m_writebuf)[y * 2] = real[y]; Chris@148: ((float *)m_writebuf)[y * 2 + 1] = imag[y]; Chris@148: float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); Chris@266: if (mag > factor) factor = mag; Chris@148: } Chris@148: break; Chris@148: Chris@148: case Polar: Chris@148: for (size_t y = 0; y < h; ++y) { Chris@148: float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); Chris@266: if (mag > factor) factor = mag; Chris@148: ((float *)m_writebuf)[y * 2] = mag; Chris@408: float phase = atan2f(imag[y], real[y]); Chris@290: ((float *)m_writebuf)[y * 2 + 1] = phase; Chris@148: } Chris@148: break; Chris@148: } Chris@148: Chris@266: // static float maxFactor = 0; Chris@266: // if (factor > maxFactor) maxFactor = factor; Chris@266: // std::cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl; Chris@266: Chris@266: setNormalizationFactorToWritebuf(factor); Chris@266: Chris@148: m_mfc->setColumnAt(x, m_writebuf); Chris@148: } Chris@148: Chris@170: size_t Chris@170: FFTFileCache::getCacheSize(size_t width, size_t height, StorageType type) Chris@170: { Chris@266: return (height * 2 + (type == Compact ? 2 : 1)) * width * Chris@170: (type == Compact ? sizeof(uint16_t) : sizeof(float)) + Chris@170: 2 * sizeof(size_t); // matrix file header size Chris@170: } Chris@170: Chris@183: void Chris@183: FFTFileCache::populateReadBuf(size_t x) const Chris@183: { Chris@183: Profiler profiler("FFTFileCache::populateReadBuf", false); Chris@183: Chris@183: if (!m_readbuf) { Chris@183: m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()]; Chris@183: } Chris@455: try { Chris@455: m_mfc->getColumnAt(x, m_readbuf); Chris@455: if (m_mfc->haveSetColumnAt(x + 1)) { Chris@455: m_mfc->getColumnAt Chris@455: (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight()); Chris@455: m_readbufWidth = 2; Chris@455: } else { Chris@455: m_readbufWidth = 1; Chris@455: } Chris@455: } catch (FileReadFailed f) { Chris@455: std::cerr << "ERROR: FFTFileCache::populateReadBuf: File read failed: " Chris@455: << f.what() << std::endl; Chris@455: memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize()); Chris@183: } Chris@183: m_readbufCol = x; Chris@183: } Chris@183: