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@148: 
Chris@148: #include <iostream>
Chris@148: 
Chris@148: #include <QMutexLocker>
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@148:     QMutexLocker locker(&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@148:         value = princargf(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@148:     QMutexLocker locker(&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@148:     QMutexLocker locker(&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@148:             float phase = princargf(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@290:             float phase = princargf(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@183:     m_mfc->getColumnAt(x, m_readbuf);
Chris@183:     if (m_mfc->haveSetColumnAt(x + 1)) {
Chris@183:         m_mfc->getColumnAt
Chris@183:             (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
Chris@183:         m_readbufWidth = 2;
Chris@183:     } else {
Chris@183:         m_readbufWidth = 1;
Chris@183:     }
Chris@183:     m_readbufCol = x;
Chris@183: }
Chris@183: