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