Mercurial > hg > svgui
diff layer/Colour3DPlotExporter.cpp @ 1566:1f80a514ce29
Merge from branch spectrogram-export
author | Chris Cannam |
---|---|
date | Fri, 10 Jan 2020 14:54:27 +0000 |
parents | a6a31908bd13 |
children | 77ffd5421627 3943553b95b0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/Colour3DPlotExporter.cpp Fri Jan 10 14:54:27 2020 +0000 @@ -0,0 +1,244 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "Colour3DPlotExporter.h" + +#include "data/model/EditableDenseThreeDimensionalModel.h" +#include "data/model/FFTModel.h" + +#include "VerticalBinLayer.h" + +Colour3DPlotExporter::Colour3DPlotExporter(Sources sources, Parameters params) : + m_sources(sources), + m_params(params) +{ + SVCERR << "Colour3DPlotExporter::Colour3DPlotExporter: constructed at " + << this << endl; +} + +Colour3DPlotExporter::~Colour3DPlotExporter() +{ + SVCERR << "Colour3DPlotExporter[" << this << "]::~Colour3DPlotExporter" + << endl; +} + +void +Colour3DPlotExporter::discardSources() +{ + SVCERR << "Colour3DPlotExporter[" << this << "]::discardSources" + << endl; + QMutexLocker locker(&m_mutex); + m_sources.verticalBinLayer = nullptr; + m_sources.source = {}; + m_sources.fft = {}; + m_sources.provider = nullptr; +} + +QString +Colour3DPlotExporter::getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions) const +{ + auto model = + ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source); + + auto layer = m_sources.verticalBinLayer; + auto provider = m_sources.provider; + + if (!model || !layer) { + SVCERR << "ERROR: Colour3DPlotExporter::getDelimitedDataHeaderLine: Source model and layer required" << endl; + return {}; + } + + int minbin = 0; + int sh = model->getHeight(); + int nbins = sh; + + if (provider) { + + minbin = layer->getIBinForY(provider, provider->getPaintHeight()); + if (minbin >= sh) minbin = sh - 1; + if (minbin < 0) minbin = 0; + + nbins = layer->getIBinForY(provider, 0) - minbin + 1; + if (minbin + nbins > sh) nbins = sh - minbin; + } + + QStringList list; + + switch (m_params.timestampFormat) { + case TimestampFormat::None: + break; + case TimestampFormat::Frames: + list << "FRAME"; + break; + case TimestampFormat::Seconds: + list << "TIME"; + break; + } + + if (m_params.binDisplay == BinDisplay::PeakFrequencies) { + for (int i = 0; i < nbins/4; ++i) { + list << QString("FREQ %1").arg(i+1) + << QString("MAG %1").arg(i+1); + } + } else { + bool hasValues = model->hasBinValues(); + QString unit = (hasValues ? model->getBinValueUnit() : ""); + for (int i = minbin; i < minbin + nbins; ++i) { + QString name = model->getBinName(i); + if (name == "") { + if (hasValues) { + if (unit != "") { + name = QString("BIN %1: %2 %3") + .arg(i+1) + .arg(model->getBinValue(i)) + .arg(unit); + } else { + name = QString("BIN %1: %2") + .arg(i+1) + .arg(model->getBinValue(i)); + } + } else { + name = QString("BIN %1") + .arg(i+1); + } + } + list << name; + } + } + + return list.join(delimiter); +} + +QString +Colour3DPlotExporter::toDelimitedDataString(QString delimiter, + DataExportOptions, + sv_frame_t startFrame, + sv_frame_t duration) const +{ + QMutexLocker locker(&m_mutex); + + BinDisplay binDisplay = m_params.binDisplay; + + auto model = + ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source); + auto fftModel = + ModelById::getAs<FFTModel>(m_sources.fft); + + auto layer = m_sources.verticalBinLayer; + auto provider = m_sources.provider; + + if (!model || !layer) { + SVCERR << "ERROR: Colour3DPlotExporter::toDelimitedDataString: Source model and layer required" << endl; + return {}; + } + if ((binDisplay == BinDisplay::PeakFrequencies) && !fftModel) { + SVCERR << "ERROR: Colour3DPlotExporter::toDelimitedDataString: FFT model required in peak frequencies mode" << endl; + return {}; + } + + int minbin = 0; + int sh = model->getHeight(); + int nbins = sh; + + if (provider) { + + minbin = layer->getIBinForY(provider, provider->getPaintHeight()); + if (minbin >= sh) minbin = sh - 1; + if (minbin < 0) minbin = 0; + + nbins = layer->getIBinForY(provider, 0) - minbin + 1; + if (minbin + nbins > sh) nbins = sh - minbin; + } + + int w = model->getWidth(); + + QString s; + + for (int i = 0; i < w; ++i) { + + sv_frame_t fr = model->getStartFrame() + i * model->getResolution(); + if (fr < startFrame || fr >= startFrame + duration) { + continue; + } + + //!!! (+ phase layer type) + + auto column = model->getColumn(i); + column = ColumnOp::Column(column.data() + minbin, + column.data() + minbin + nbins); + + // The scale factor is always applied + column = ColumnOp::applyGain(column, m_params.scaleFactor); + + QStringList list; + + switch (m_params.timestampFormat) { + case TimestampFormat::None: + break; + case TimestampFormat::Frames: + list << QString("%1").arg(fr); + break; + case TimestampFormat::Seconds: + list << RealTime::frame2RealTime(fr, model->getSampleRate()) + .toString().c_str(); + break; + } + + if (binDisplay == BinDisplay::PeakFrequencies) { + + FFTModel::PeakSet peaks = fftModel->getPeakFrequencies + (FFTModel::AllPeaks, i, minbin, minbin + nbins - 1); + + // We don't apply normalisation or gain to the output, but + // we *do* perform thresholding when exporting the + // peak-frequency spectrogram, to give the user an + // opportunity to cut irrelevant peaks. And to make that + // match the display, we have to apply both normalisation + // and gain locally for thresholding + + auto toTest = ColumnOp::normalize(column, m_params.normalization); + toTest = ColumnOp::applyGain(toTest, m_params.gain); + + for (const auto &p: peaks) { + + int bin = p.first; + + if (toTest[bin - minbin] < m_params.threshold) { + continue; + } + + double freq = p.second; + double value = column[bin - minbin]; + + list << QString("%1").arg(freq) << QString("%1").arg(value); + } + + } else { + + if (binDisplay == BinDisplay::PeakBins) { + column = ColumnOp::peakPick(column); + } + + for (auto value: column) { + list << QString("%1").arg(value); + } + } + + s += list.join(delimiter) + "\n"; + } + + return s; +} +