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 "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 <iostream>
Chris@537: 
Chris@537: //#define DEBUG_FFT_FILE_CACHE_WRITER 1
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: FFTFileCacheWriter::FFTFileCacheWriter(QString fileBase,
Chris@537:                                        FFTCache::StorageType storageType,
Chris@537:                                        size_t width, size_t height) :
Chris@537:     m_writebuf(0),
Chris@537:     m_fileBase(fileBase),
Chris@537:     m_storageType(storageType),
Chris@537:     m_factorSize(storageType == FFTCache::Compact ? 2 : 1),
Chris@537:     m_mfc(new MatrixFile
Chris@537:           (fileBase, MatrixFile::WriteOnly, 
Chris@537:            storageType == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float),
Chris@537:            width, height * 2 + m_factorSize))
Chris@537: {
Chris@577: #ifdef DEBUG_FFT_FILE_CACHE_WRITER
Chris@537:     std::cerr << "FFTFileCacheWriter: storage type is " << (storageType == FFTCache::Compact ? "Compact" : storageType == FFTCache::Polar ? "Polar" : "Rectangular") << ", size " << width << "x" << height << std::endl;
Chris@577: #endif
Chris@550:     m_mfc->setAutoClose(true);
Chris@537:     m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()];
Chris@537: }
Chris@537: 
Chris@537: FFTFileCacheWriter::~FFTFileCacheWriter()
Chris@537: {
Chris@537:     if (m_writebuf) delete[] m_writebuf;
Chris@537:     delete m_mfc;
Chris@537: }
Chris@537: 
Chris@537: QString
Chris@537: FFTFileCacheWriter::getFileBase() const
Chris@537: {
Chris@537:     return m_fileBase;
Chris@537: }
Chris@537: 
Chris@537: size_t
Chris@537: FFTFileCacheWriter::getWidth() const
Chris@537: {
Chris@537:     return m_mfc->getWidth();
Chris@537: }
Chris@537: 
Chris@537: size_t
Chris@537: FFTFileCacheWriter::getHeight() const
Chris@537: {
Chris@537:     size_t 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@550: bool
Chris@550: FFTFileCacheWriter::haveSetColumnAt(size_t x) const
Chris@550: {
Chris@550:     return m_mfc->haveSetColumnAt(x);
Chris@550: }
Chris@550: 
Chris@537: void
Chris@537: FFTFileCacheWriter::setColumnAt(size_t x, float *mags, float *phases, float factor)
Chris@537: {
Chris@537:     size_t h = getHeight();
Chris@537: 
Chris@537:     switch (m_storageType) {
Chris@537: 
Chris@537:     case FFTCache::Compact:
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
Chris@537:             ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
Chris@537:         }
Chris@537:         break;
Chris@537: 
Chris@537:     case FFTCache::Rectangular:
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
Chris@537:             ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
Chris@537:         }
Chris@537:         break;
Chris@537: 
Chris@537:     case FFTCache::Polar:
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             ((float *)m_writebuf)[y * 2] = mags[y];
Chris@537:             ((float *)m_writebuf)[y * 2 + 1] = phases[y];
Chris@537:         }
Chris@537:         break;
Chris@537:     }
Chris@537: 
Chris@537:     static float maxFactor = 0;
Chris@537:     if (factor > maxFactor) maxFactor = factor;
Chris@537: #ifdef DEBUG_FFT_FILE_CACHE_WRITER
Chris@537:     std::cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
Chris@537: #endif
Chris@537: 
Chris@537:     setNormalizationFactorToWritebuf(factor);
Chris@537: 
Chris@537:     m_mfc->setColumnAt(x, m_writebuf);
Chris@537: }
Chris@537: 
Chris@537: void
Chris@537: FFTFileCacheWriter::setColumnAt(size_t x, float *real, float *imag)
Chris@537: {
Chris@537:     size_t h = getHeight();
Chris@537: 
Chris@537:     float factor = 0.0f;
Chris@537: 
Chris@537:     switch (m_storageType) {
Chris@537: 
Chris@537:     case FFTCache::Compact:
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@537:             if (mag > factor) factor = mag;
Chris@537:         }
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@537:             float phase = atan2f(imag[y], real[y]);
Chris@537:             ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0);
Chris@537:             ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI));
Chris@537:         }
Chris@537:         break;
Chris@537: 
Chris@537:     case FFTCache::Rectangular:
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             ((float *)m_writebuf)[y * 2] = real[y];
Chris@537:             ((float *)m_writebuf)[y * 2 + 1] = imag[y];
Chris@537:             float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@537:             if (mag > factor) factor = mag;
Chris@537:         }
Chris@537:         break;
Chris@537: 
Chris@537:     case FFTCache::Polar:
Chris@537:         for (size_t y = 0; y < h; ++y) {
Chris@537:             float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
Chris@537:             if (mag > factor) factor = mag;
Chris@537:             ((float *)m_writebuf)[y * 2] = mag;
Chris@537:             float phase = atan2f(imag[y], real[y]);
Chris@537:             ((float *)m_writebuf)[y * 2 + 1] = phase;
Chris@537:         }
Chris@537:         break;
Chris@537:     }
Chris@537: 
Chris@537:     static float maxFactor = 0;
Chris@537:     if (factor > maxFactor) maxFactor = factor;
Chris@537: #ifdef DEBUG_FFT_FILE_CACHE_WRITER
Chris@537:     std::cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << std::endl;
Chris@537: #endif
Chris@537: 
Chris@537:     setNormalizationFactorToWritebuf(factor);
Chris@537: 
Chris@537:     m_mfc->setColumnAt(x, m_writebuf);
Chris@537: }
Chris@537: 
Chris@537: size_t
Chris@537: FFTFileCacheWriter::getCacheSize(size_t width, size_t 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@537:         2 * sizeof(size_t); // matrix file header size
Chris@537: }
Chris@537: 
Chris@537: void
Chris@537: FFTFileCacheWriter::allColumnsWritten()
Chris@537: {
Chris@537: #ifdef DEBUG_FFT_FILE_CACHE_WRITER
Chris@537:     std::cerr << "FFTFileCacheWriter::allColumnsWritten" << std::endl;
Chris@537: #endif
Chris@537:     m_mfc->close();
Chris@537: }
Chris@537: