annotate layer/Colour3DPlotExporter.cpp @ 1605:ae2d5f8ff005

When asked to render the whole view width, we need to wait for the layers to be ready before we can determine what the width is
author Chris Cannam
date Thu, 30 Apr 2020 14:47:13 +0100
parents 32171776fcc9
children
rev   line source
Chris@1554 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1554 2
Chris@1554 3 /*
Chris@1554 4 Sonic Visualiser
Chris@1554 5 An audio file viewer and annotation editor.
Chris@1554 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1554 7
Chris@1554 8 This program is free software; you can redistribute it and/or
Chris@1554 9 modify it under the terms of the GNU General Public License as
Chris@1554 10 published by the Free Software Foundation; either version 2 of the
Chris@1554 11 License, or (at your option) any later version. See the file
Chris@1554 12 COPYING included with this distribution for more information.
Chris@1554 13 */
Chris@1554 14
Chris@1554 15 #include "Colour3DPlotExporter.h"
Chris@1554 16
Chris@1554 17 #include "data/model/EditableDenseThreeDimensionalModel.h"
Chris@1554 18 #include "data/model/FFTModel.h"
Chris@1554 19
Chris@1554 20 #include "VerticalBinLayer.h"
Chris@1554 21
Chris@1556 22 Colour3DPlotExporter::Colour3DPlotExporter(Sources sources, Parameters params) :
Chris@1556 23 m_sources(sources),
Chris@1556 24 m_params(params)
Chris@1556 25 {
Chris@1556 26 SVCERR << "Colour3DPlotExporter::Colour3DPlotExporter: constructed at "
Chris@1556 27 << this << endl;
Chris@1556 28 }
Chris@1556 29
Chris@1556 30 Colour3DPlotExporter::~Colour3DPlotExporter()
Chris@1556 31 {
Chris@1556 32 SVCERR << "Colour3DPlotExporter[" << this << "]::~Colour3DPlotExporter"
Chris@1556 33 << endl;
Chris@1556 34 }
Chris@1556 35
Chris@1556 36 void
Chris@1556 37 Colour3DPlotExporter::discardSources()
Chris@1556 38 {
Chris@1556 39 SVCERR << "Colour3DPlotExporter[" << this << "]::discardSources"
Chris@1556 40 << endl;
Chris@1556 41 QMutexLocker locker(&m_mutex);
Chris@1556 42 m_sources.verticalBinLayer = nullptr;
Chris@1556 43 m_sources.source = {};
Chris@1556 44 m_sources.fft = {};
Chris@1556 45 m_sources.provider = nullptr;
Chris@1556 46 }
Chris@1556 47
Chris@1593 48 QVector<QString>
Chris@1593 49 Colour3DPlotExporter::getStringExportHeaders(DataExportOptions opts) const
Chris@1565 50 {
Chris@1565 51 auto model =
Chris@1565 52 ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1565 53
Chris@1565 54 auto layer = m_sources.verticalBinLayer;
Chris@1565 55 auto provider = m_sources.provider;
Chris@1565 56
Chris@1565 57 if (!model || !layer) {
Chris@1565 58 SVCERR << "ERROR: Colour3DPlotExporter::getDelimitedDataHeaderLine: Source model and layer required" << endl;
Chris@1565 59 return {};
Chris@1565 60 }
Chris@1565 61
Chris@1565 62 int minbin = 0;
Chris@1565 63 int sh = model->getHeight();
Chris@1565 64 int nbins = sh;
Chris@1565 65
Chris@1565 66 if (provider) {
Chris@1565 67
Chris@1565 68 minbin = layer->getIBinForY(provider, provider->getPaintHeight());
Chris@1565 69 if (minbin >= sh) minbin = sh - 1;
Chris@1565 70 if (minbin < 0) minbin = 0;
Chris@1565 71
Chris@1565 72 nbins = layer->getIBinForY(provider, 0) - minbin + 1;
Chris@1565 73 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1565 74 }
Chris@1565 75
Chris@1593 76 QVector<QString> headers;
Chris@1565 77
Chris@1568 78 if (opts & DataExportAlwaysIncludeTimestamp) {
Chris@1568 79 if (opts & DataExportWriteTimeInFrames) {
Chris@1593 80 headers << "FRAME";
Chris@1568 81 } else {
Chris@1593 82 headers << "TIME";
Chris@1568 83 }
Chris@1565 84 }
Chris@1565 85
Chris@1565 86 if (m_params.binDisplay == BinDisplay::PeakFrequencies) {
Chris@1565 87 for (int i = 0; i < nbins/4; ++i) {
Chris@1593 88 headers << QString("FREQ %1").arg(i+1)
Chris@1565 89 << QString("MAG %1").arg(i+1);
Chris@1565 90 }
Chris@1565 91 } else {
Chris@1565 92 bool hasValues = model->hasBinValues();
Chris@1565 93 QString unit = (hasValues ? model->getBinValueUnit() : "");
Chris@1565 94 for (int i = minbin; i < minbin + nbins; ++i) {
Chris@1565 95 QString name = model->getBinName(i);
Chris@1565 96 if (name == "") {
Chris@1565 97 if (hasValues) {
Chris@1565 98 if (unit != "") {
Chris@1565 99 name = QString("BIN %1: %2 %3")
Chris@1565 100 .arg(i+1)
Chris@1565 101 .arg(model->getBinValue(i))
Chris@1565 102 .arg(unit);
Chris@1565 103 } else {
Chris@1565 104 name = QString("BIN %1: %2")
Chris@1565 105 .arg(i+1)
Chris@1565 106 .arg(model->getBinValue(i));
Chris@1565 107 }
Chris@1565 108 } else {
Chris@1565 109 name = QString("BIN %1")
Chris@1565 110 .arg(i+1);
Chris@1565 111 }
Chris@1565 112 }
Chris@1593 113 headers << name;
Chris@1565 114 }
Chris@1565 115 }
Chris@1565 116
Chris@1593 117 return headers;
Chris@1565 118 }
Chris@1565 119
Chris@1593 120 QVector<QVector<QString>>
Chris@1593 121 Colour3DPlotExporter::toStringExportRows(DataExportOptions opts,
Chris@1593 122 sv_frame_t startFrame,
Chris@1593 123 sv_frame_t duration) const
Chris@1554 124 {
Chris@1554 125 QMutexLocker locker(&m_mutex);
Chris@1554 126
Chris@1554 127 BinDisplay binDisplay = m_params.binDisplay;
Chris@1554 128
Chris@1554 129 auto model =
Chris@1554 130 ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1554 131 auto fftModel =
Chris@1554 132 ModelById::getAs<FFTModel>(m_sources.fft);
Chris@1554 133
Chris@1554 134 auto layer = m_sources.verticalBinLayer;
Chris@1554 135 auto provider = m_sources.provider;
Chris@1554 136
Chris@1554 137 if (!model || !layer) {
Chris@1554 138 SVCERR << "ERROR: Colour3DPlotExporter::toDelimitedDataString: Source model and layer required" << endl;
Chris@1554 139 return {};
Chris@1554 140 }
Chris@1556 141 if ((binDisplay == BinDisplay::PeakFrequencies) && !fftModel) {
Chris@1556 142 SVCERR << "ERROR: Colour3DPlotExporter::toDelimitedDataString: FFT model required in peak frequencies mode" << endl;
Chris@1556 143 return {};
Chris@1556 144 }
Chris@1554 145
Chris@1554 146 int minbin = 0;
Chris@1554 147 int sh = model->getHeight();
Chris@1554 148 int nbins = sh;
Chris@1554 149
Chris@1554 150 if (provider) {
Chris@1554 151
Chris@1554 152 minbin = layer->getIBinForY(provider, provider->getPaintHeight());
Chris@1554 153 if (minbin >= sh) minbin = sh - 1;
Chris@1554 154 if (minbin < 0) minbin = 0;
Chris@1554 155
Chris@1554 156 nbins = layer->getIBinForY(provider, 0) - minbin + 1;
Chris@1554 157 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1554 158 }
Chris@1554 159
Chris@1554 160 int w = model->getWidth();
Chris@1554 161
Chris@1593 162 QVector<QVector<QString>> rows;
Chris@1554 163
Chris@1554 164 for (int i = 0; i < w; ++i) {
Chris@1556 165
Chris@1554 166 sv_frame_t fr = model->getStartFrame() + i * model->getResolution();
Chris@1554 167 if (fr < startFrame || fr >= startFrame + duration) {
Chris@1554 168 continue;
Chris@1554 169 }
Chris@1556 170
Chris@1561 171 //!!! (+ phase layer type)
Chris@1556 172
Chris@1556 173 auto column = model->getColumn(i);
Chris@1556 174 column = ColumnOp::Column(column.data() + minbin,
Chris@1556 175 column.data() + minbin + nbins);
Chris@1561 176
Chris@1561 177 // The scale factor is always applied
Chris@1561 178 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1556 179
Chris@1593 180 QVector<QString> row;
Chris@1593 181
Chris@1568 182 if (opts & DataExportAlwaysIncludeTimestamp) {
Chris@1568 183 if (opts & DataExportWriteTimeInFrames) {
Chris@1593 184 row << QString("%1").arg(fr);
Chris@1568 185 } else {
Chris@1593 186 row << RealTime::frame2RealTime(fr, model->getSampleRate())
Chris@1568 187 .toString().c_str();
Chris@1568 188 }
Chris@1564 189 }
Chris@1564 190
Chris@1556 191 if (binDisplay == BinDisplay::PeakFrequencies) {
Chris@1556 192
Chris@1556 193 FFTModel::PeakSet peaks = fftModel->getPeakFrequencies
Chris@1558 194 (FFTModel::AllPeaks, i, minbin, minbin + nbins - 1);
Chris@1554 195
Chris@1561 196 // We don't apply normalisation or gain to the output, but
Chris@1561 197 // we *do* perform thresholding when exporting the
Chris@1561 198 // peak-frequency spectrogram, to give the user an
Chris@1561 199 // opportunity to cut irrelevant peaks. And to make that
Chris@1561 200 // match the display, we have to apply both normalisation
Chris@1561 201 // and gain locally for thresholding
Chris@1561 202
Chris@1561 203 auto toTest = ColumnOp::normalize(column, m_params.normalization);
Chris@1561 204 toTest = ColumnOp::applyGain(toTest, m_params.gain);
Chris@1561 205
Chris@1556 206 for (const auto &p: peaks) {
Chris@1556 207
Chris@1556 208 int bin = p.first;
Chris@1561 209
Chris@1561 210 if (toTest[bin - minbin] < m_params.threshold) {
Chris@1561 211 continue;
Chris@1561 212 }
Chris@1561 213
Chris@1556 214 double freq = p.second;
Chris@1561 215 double value = column[bin - minbin];
Chris@1561 216
Chris@1593 217 row << QString("%1").arg(freq) << QString("%1").arg(value);
Chris@1556 218 }
Chris@1556 219
Chris@1556 220 } else {
Chris@1556 221
Chris@1556 222 if (binDisplay == BinDisplay::PeakBins) {
Chris@1556 223 column = ColumnOp::peakPick(column);
Chris@1556 224 }
Chris@1556 225
Chris@1556 226 for (auto value: column) {
Chris@1593 227 row << QString("%1").arg(value);
Chris@1556 228 }
Chris@1556 229 }
Chris@1567 230
Chris@1593 231 if (!row.empty()) {
Chris@1593 232 rows.push_back(row);
Chris@1567 233 }
Chris@1554 234 }
Chris@1554 235
Chris@1593 236 return rows;
Chris@1554 237 }
Chris@1554 238