Mercurial > hg > svgui
comparison 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 |
comparison
equal
deleted
inserted
replaced
1553:76e4302a3fc2 | 1566:1f80a514ce29 |
---|---|
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 | |
22 Colour3DPlotExporter::Colour3DPlotExporter(Sources sources, Parameters params) : | |
23 m_sources(sources), | |
24 m_params(params) | |
25 { | |
26 SVCERR << "Colour3DPlotExporter::Colour3DPlotExporter: constructed at " | |
27 << this << endl; | |
28 } | |
29 | |
30 Colour3DPlotExporter::~Colour3DPlotExporter() | |
31 { | |
32 SVCERR << "Colour3DPlotExporter[" << this << "]::~Colour3DPlotExporter" | |
33 << endl; | |
34 } | |
35 | |
36 void | |
37 Colour3DPlotExporter::discardSources() | |
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 QString | |
49 Colour3DPlotExporter::getDelimitedDataHeaderLine(QString delimiter, | |
50 DataExportOptions) const | |
51 { | |
52 auto model = | |
53 ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source); | |
54 | |
55 auto layer = m_sources.verticalBinLayer; | |
56 auto provider = m_sources.provider; | |
57 | |
58 if (!model || !layer) { | |
59 SVCERR << "ERROR: Colour3DPlotExporter::getDelimitedDataHeaderLine: Source model and layer required" << endl; | |
60 return {}; | |
61 } | |
62 | |
63 int minbin = 0; | |
64 int sh = model->getHeight(); | |
65 int nbins = sh; | |
66 | |
67 if (provider) { | |
68 | |
69 minbin = layer->getIBinForY(provider, provider->getPaintHeight()); | |
70 if (minbin >= sh) minbin = sh - 1; | |
71 if (minbin < 0) minbin = 0; | |
72 | |
73 nbins = layer->getIBinForY(provider, 0) - minbin + 1; | |
74 if (minbin + nbins > sh) nbins = sh - minbin; | |
75 } | |
76 | |
77 QStringList list; | |
78 | |
79 switch (m_params.timestampFormat) { | |
80 case TimestampFormat::None: | |
81 break; | |
82 case TimestampFormat::Frames: | |
83 list << "FRAME"; | |
84 break; | |
85 case TimestampFormat::Seconds: | |
86 list << "TIME"; | |
87 break; | |
88 } | |
89 | |
90 if (m_params.binDisplay == BinDisplay::PeakFrequencies) { | |
91 for (int i = 0; i < nbins/4; ++i) { | |
92 list << QString("FREQ %1").arg(i+1) | |
93 << QString("MAG %1").arg(i+1); | |
94 } | |
95 } else { | |
96 bool hasValues = model->hasBinValues(); | |
97 QString unit = (hasValues ? model->getBinValueUnit() : ""); | |
98 for (int i = minbin; i < minbin + nbins; ++i) { | |
99 QString name = model->getBinName(i); | |
100 if (name == "") { | |
101 if (hasValues) { | |
102 if (unit != "") { | |
103 name = QString("BIN %1: %2 %3") | |
104 .arg(i+1) | |
105 .arg(model->getBinValue(i)) | |
106 .arg(unit); | |
107 } else { | |
108 name = QString("BIN %1: %2") | |
109 .arg(i+1) | |
110 .arg(model->getBinValue(i)); | |
111 } | |
112 } else { | |
113 name = QString("BIN %1") | |
114 .arg(i+1); | |
115 } | |
116 } | |
117 list << name; | |
118 } | |
119 } | |
120 | |
121 return list.join(delimiter); | |
122 } | |
123 | |
124 QString | |
125 Colour3DPlotExporter::toDelimitedDataString(QString delimiter, | |
126 DataExportOptions, | |
127 sv_frame_t startFrame, | |
128 sv_frame_t duration) const | |
129 { | |
130 QMutexLocker locker(&m_mutex); | |
131 | |
132 BinDisplay binDisplay = m_params.binDisplay; | |
133 | |
134 auto model = | |
135 ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source); | |
136 auto fftModel = | |
137 ModelById::getAs<FFTModel>(m_sources.fft); | |
138 | |
139 auto layer = m_sources.verticalBinLayer; | |
140 auto provider = m_sources.provider; | |
141 | |
142 if (!model || !layer) { | |
143 SVCERR << "ERROR: Colour3DPlotExporter::toDelimitedDataString: Source model and layer required" << endl; | |
144 return {}; | |
145 } | |
146 if ((binDisplay == BinDisplay::PeakFrequencies) && !fftModel) { | |
147 SVCERR << "ERROR: Colour3DPlotExporter::toDelimitedDataString: FFT model required in peak frequencies mode" << endl; | |
148 return {}; | |
149 } | |
150 | |
151 int minbin = 0; | |
152 int sh = model->getHeight(); | |
153 int nbins = sh; | |
154 | |
155 if (provider) { | |
156 | |
157 minbin = layer->getIBinForY(provider, provider->getPaintHeight()); | |
158 if (minbin >= sh) minbin = sh - 1; | |
159 if (minbin < 0) minbin = 0; | |
160 | |
161 nbins = layer->getIBinForY(provider, 0) - minbin + 1; | |
162 if (minbin + nbins > sh) nbins = sh - minbin; | |
163 } | |
164 | |
165 int w = model->getWidth(); | |
166 | |
167 QString s; | |
168 | |
169 for (int i = 0; i < w; ++i) { | |
170 | |
171 sv_frame_t fr = model->getStartFrame() + i * model->getResolution(); | |
172 if (fr < startFrame || fr >= startFrame + duration) { | |
173 continue; | |
174 } | |
175 | |
176 //!!! (+ phase layer type) | |
177 | |
178 auto column = model->getColumn(i); | |
179 column = ColumnOp::Column(column.data() + minbin, | |
180 column.data() + minbin + nbins); | |
181 | |
182 // The scale factor is always applied | |
183 column = ColumnOp::applyGain(column, m_params.scaleFactor); | |
184 | |
185 QStringList list; | |
186 | |
187 switch (m_params.timestampFormat) { | |
188 case TimestampFormat::None: | |
189 break; | |
190 case TimestampFormat::Frames: | |
191 list << QString("%1").arg(fr); | |
192 break; | |
193 case TimestampFormat::Seconds: | |
194 list << RealTime::frame2RealTime(fr, model->getSampleRate()) | |
195 .toString().c_str(); | |
196 break; | |
197 } | |
198 | |
199 if (binDisplay == BinDisplay::PeakFrequencies) { | |
200 | |
201 FFTModel::PeakSet peaks = fftModel->getPeakFrequencies | |
202 (FFTModel::AllPeaks, i, minbin, minbin + nbins - 1); | |
203 | |
204 // We don't apply normalisation or gain to the output, but | |
205 // we *do* perform thresholding when exporting the | |
206 // peak-frequency spectrogram, to give the user an | |
207 // opportunity to cut irrelevant peaks. And to make that | |
208 // match the display, we have to apply both normalisation | |
209 // and gain locally for thresholding | |
210 | |
211 auto toTest = ColumnOp::normalize(column, m_params.normalization); | |
212 toTest = ColumnOp::applyGain(toTest, m_params.gain); | |
213 | |
214 for (const auto &p: peaks) { | |
215 | |
216 int bin = p.first; | |
217 | |
218 if (toTest[bin - minbin] < m_params.threshold) { | |
219 continue; | |
220 } | |
221 | |
222 double freq = p.second; | |
223 double value = column[bin - minbin]; | |
224 | |
225 list << QString("%1").arg(freq) << QString("%1").arg(value); | |
226 } | |
227 | |
228 } else { | |
229 | |
230 if (binDisplay == BinDisplay::PeakBins) { | |
231 column = ColumnOp::peakPick(column); | |
232 } | |
233 | |
234 for (auto value: column) { | |
235 list << QString("%1").arg(value); | |
236 } | |
237 } | |
238 | |
239 s += list.join(delimiter) + "\n"; | |
240 } | |
241 | |
242 return s; | |
243 } | |
244 |