BasicCompressedDenseThreeDimensionalModel.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  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
17 
18 #include "base/LogRange.h"
19 
20 #include <QTextStream>
21 #include <QStringList>
22 #include <QReadLocker>
23 #include <QWriteLocker>
24 
25 #include <iostream>
26 
27 #include <cmath>
28 #include <cassert>
29 
30 using std::vector;
31 
32 #include "system/System.h"
33 
35  int resolution,
36  int yBinCount,
37  bool notifyOnAdd) :
38  m_startFrame(0),
39  m_sampleRate(sampleRate),
40  m_resolution(resolution),
41  m_yBinCount(yBinCount),
42  m_minimum(0.0),
43  m_maximum(0.0),
44  m_haveExtents(false),
45  m_notifyOnAdd(notifyOnAdd),
46  m_sinceLastNotifyMin(-1),
47  m_sinceLastNotifyMax(-1),
48  m_completion(100)
49 {
50 }
51 
52 bool
54 {
55  return true;
56 }
57 
58 bool
60 {
61  if (completion) *completion = getCompletion();
62  return true;
63 }
64 
67 {
68  return m_sampleRate;
69 }
70 
73 {
74  return m_startFrame;
75 }
76 
77 void
79 {
80  m_startFrame = f;
81 }
82 
85 {
86  return m_resolution * m_data.size() + (m_resolution - 1);
87 }
88 
89 int
91 {
92  return m_resolution;
93 }
94 
95 void
97 {
98  m_resolution = sz;
99 }
100 
101 int
103 {
104  return int(m_data.size());
105 }
106 
107 int
109 {
110  return m_yBinCount;
111 }
112 
113 void
115 {
116  m_yBinCount = sz;
117 }
118 
119 float
121 {
122  return m_minimum;
123 }
124 
125 void
127 {
128  m_minimum = level;
129 }
130 
131 float
133 {
134  return m_maximum;
135 }
136 
137 void
139 {
140  m_maximum = level;
141 }
142 
145 {
146  QReadLocker locker(&m_lock);
147  if (in_range_for(m_data, index)) return expandAndRetrieve(index);
148  else return Column();
149 }
150 
151 float
153 {
154  Column c = getColumn(index);
155  if (in_range_for(c, n)) return c.at(n);
156  return m_minimum;
157 }
158 
159 //static int given = 0, stored = 0;
160 
161 void
163  const Column &values)
164 {
165  assert(in_range_for(m_data, index));
166 
167  //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl;
168 
169  // The default case is to store the entire column at m_data[index]
170  // and place 0 at m_trunc[index] to indicate that it has not been
171  // truncated. We only do clever stuff if one of the clever-stuff
172  // tests works out.
173 
174  m_trunc[index] = 0;
175  if (index == 0 ||
176  int(values.size()) != m_yBinCount) {
177 // given += values.size();
178 // stored += values.size();
179  m_data[index] = values;
180  return;
181  }
182 
183  // Maximum distance between a column and the one we refer to as
184  // the source of its truncated values. Limited by having to fit
185  // in a signed char, but in any case small values are usually
186  // better
187  static int maxdist = 6;
188 
189  bool known = false; // do we know whether to truncate at top or bottom?
190  bool top = false; // if we do know, will we truncate at top?
191 
192  // If the previous column is not truncated, then it is the only
193  // candidate for comparison. If it is truncated, then the column
194  // that it refers to is the only candidate. Either way, we only
195  // have one possible column to compare against here, and we are
196  // being careful to ensure it is not a truncated one (to avoid
197  // doing more work recursively when uncompressing).
198  int tdist = 1;
199  int ptrunc = m_trunc[index-1];
200  if (ptrunc < 0) {
201  top = false;
202  known = true;
203  tdist = -ptrunc + 1;
204  } else if (ptrunc > 0) {
205  top = true;
206  known = true;
207  tdist = ptrunc + 1;
208  }
209 
210  Column p = expandAndRetrieve(index - tdist);
211  int h = m_yBinCount;
212 
213  if (int(p.size()) == h && tdist <= maxdist) {
214 
215  int bcount = 0, tcount = 0;
216  if (!known || !top) {
217  // count how many identical values there are at the bottom
218  for (int i = 0; i < h; ++i) {
219  if (values.at(i) == p.at(i)) ++bcount;
220  else break;
221  }
222  }
223  if (!known || top) {
224  // count how many identical values there are at the top
225  for (int i = h; i > 0; --i) {
226  if (values.at(i-1) == p.at(i-1)) ++tcount;
227  else break;
228  }
229  }
230  if (!known) top = (tcount > bcount);
231 
232  int limit = h / 4; // don't bother unless we have at least this many
233  if ((top ? tcount : bcount) > limit) {
234 
235  if (!top) {
236  // create a new column with h - bcount values from bcount up
237  Column tcol(h - bcount);
238 // given += values.size();
239 // stored += h - bcount;
240  for (int i = bcount; i < h; ++i) {
241  tcol[i - bcount] = values.at(i);
242  }
243  m_data[index] = tcol;
244  m_trunc[index] = (signed char)(-tdist);
245  return;
246  } else {
247  // create a new column with h - tcount values from 0 up
248  Column tcol(h - tcount);
249 // given += values.size();
250 // stored += h - tcount;
251  for (int i = 0; i < h - tcount; ++i) {
252  tcol[i] = values.at(i);
253  }
254  m_data[index] = tcol;
255  m_trunc[index] = (signed char)(tdist);
256  return;
257  }
258  }
259  }
260 
261 // given += values.size();
262 // stored += values.size();
263 // cout << "given: " << given << ", stored: " << stored << " ("
264 // << ((float(stored) / float(given)) * 100.f) << "%)" << endl;
265 
266  // default case if nothing wacky worked out
267  m_data[index] = values;
268  return;
269 }
270 
273 {
274  if (int(c.size()) == m_yBinCount) return c;
275  else {
276  Column cc(c);
277  cc.resize(m_yBinCount, 0.0);
278  return cc;
279  }
280 }
281 
284 {
285  // See comment above m_trunc declaration in header
286 
287  assert(index >= 0 && index < int(m_data.size()));
288  Column c = m_data.at(index);
289  if (index == 0) {
290  return rightHeight(c);
291  }
292  int trunc = (int)m_trunc[index];
293  if (trunc == 0) {
294  return rightHeight(c);
295  }
296  bool top = true;
297  int tdist = trunc;
298  if (trunc < 0) { top = false; tdist = -trunc; }
299  Column p = expandAndRetrieve(index - tdist);
300  int psize = int(p.size()), csize = int(c.size());
301  if (psize != m_yBinCount) {
302  cerr << "WARNING: BasicCompressedDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << endl;
303  }
304  if (top) {
305  for (int i = csize; i < psize; ++i) {
306  c.push_back(p.at(i));
307  }
308  } else {
309  Column cc(psize);
310  for (int i = 0; i < psize - csize; ++i) {
311  cc[i] = p.at(i);
312  }
313  for (int i = 0; i < csize; ++i) {
314  cc[i + (psize - csize)] = c.at(i);
315  }
316  return cc;
317  }
318  return c;
319 }
320 
321 void
323  const Column &values)
324 {
325  QWriteLocker locker(&m_lock);
326 
327  while (index >= int(m_data.size())) {
328  m_data.push_back(Column());
329  m_trunc.push_back(0);
330  }
331 
332  bool allChange = false;
333 
334  for (int i = 0; in_range_for(values, i); ++i) {
335  float value = values[i];
336  if (ISNAN(value) || ISINF(value)) {
337  continue;
338  }
339  if (!m_haveExtents || value < m_minimum) {
340  m_minimum = value;
341  allChange = true;
342  }
343  if (!m_haveExtents || value > m_maximum) {
344  m_maximum = value;
345  allChange = true;
346  }
347  m_haveExtents = true;
348  }
349 
350  truncateAndStore(index, values);
351 
352 // assert(values == expandAndRetrieve(index));
353 
354  sv_frame_t windowStart = index;
355  windowStart *= m_resolution;
356 
357  if (m_notifyOnAdd) {
358  if (allChange) {
359  emit modelChanged(getId());
360  } else {
361  emit modelChangedWithin(getId(),
362  windowStart, windowStart + m_resolution);
363  }
364  } else {
365  if (allChange) {
368  emit modelChanged(getId());
369  } else {
370  if (m_sinceLastNotifyMin == -1 ||
371  windowStart < m_sinceLastNotifyMin) {
372  m_sinceLastNotifyMin = windowStart;
373  }
374  if (m_sinceLastNotifyMax == -1 ||
375  windowStart > m_sinceLastNotifyMax) {
376  m_sinceLastNotifyMax = windowStart;
377  }
378  }
379  }
380 }
381 
382 QString
384 {
385  if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
386  else return "";
387 }
388 
389 void
391 {
392  while ((int)m_binNames.size() <= n) m_binNames.push_back("");
393  m_binNames[n] = name;
394  emit modelChanged(getId());
395 }
396 
397 void
399 {
400  m_binNames = names;
401  emit modelChanged(getId());
402 }
403 
404 bool
406 {
407  return !m_binValues.empty();
408 }
409 
410 float
412 {
413  if (n < (int)m_binValues.size()) return m_binValues[n];
414  else return 0.f;
415 }
416 
417 void
419 {
420  m_binValues = values;
421 }
422 
423 QString
425 {
426  return m_binValueUnit;
427 }
428 
429 void
431 {
432  m_binValueUnit = unit;
433 }
434 
435 bool
437 {
438  QReadLocker locker(&m_lock);
439 
440  vector<double> sample;
441  vector<int> n;
442 
443  for (int i = 0; i < 10; ++i) {
444  int index = i * 10;
445  if (in_range_for(m_data, index)) {
446  const Column &c = m_data.at(index);
447  while (c.size() > sample.size()) {
448  sample.push_back(0.0);
449  n.push_back(0);
450  }
451  for (int j = 0; in_range_for(c, j); ++j) {
452  sample[j] += c.at(j);
453  ++n[j];
454  }
455  }
456  }
457 
458  if (sample.empty()) return false;
459  for (decltype(sample)::size_type j = 0; j < sample.size(); ++j) {
460  if (n[j]) sample[j] /= n[j];
461  }
462 
463  return LogRange::shouldUseLogScale(sample);
464 }
465 
466 void
468 {
469  if (m_completion != completion) {
470  m_completion = completion;
471 
472  if (completion == 100) {
473 
474  m_notifyOnAdd = true; // henceforth
475  emit modelChanged(getId());
476 
477  } else if (!m_notifyOnAdd) {
478 
479  if (update &&
480  m_sinceLastNotifyMin >= 0 &&
481  m_sinceLastNotifyMax >= 0) {
482  emit modelChangedWithin(getId(),
486  } else {
487  emit completionChanged(getId());
488  }
489  } else {
490  emit completionChanged(getId());
491  }
492  }
493 }
494 
495 int
497 {
498  return m_completion;
499 }
500 
501 QVector<QString>
503  const
504 {
505  QVector<QString> sv;
506  for (int i = 0; i < m_yBinCount; ++i) {
507  sv.push_back(QString("Bin%1").arg(i+1));
508  }
509  return sv;
510 }
511 
512 QVector<QVector<QString>>
514  sv_frame_t startFrame,
515  sv_frame_t duration)
516  const
517 {
518  QReadLocker locker(&m_lock);
519 
520  QVector<QVector<QString>> rows;
521 
522  for (int i = 0; in_range_for(m_data, i); ++i) {
523  Column c = getColumn(i);
525  if (fr >= startFrame && fr < startFrame + duration) {
526  QVector<QString> row;
527  for (int j = 0; in_range_for(c, j); ++j) {
528  row << QString("%1").arg(c.at(j));
529  }
530  rows.push_back(row);
531  }
532  }
533 
534  return rows;
535 }
536 
537 void
539  QString indent,
540  QString extraAttributes) const
541 {
542  QReadLocker locker(&m_lock);
543 
544  // For historical reasons we read and write "resolution" as "windowSize".
545 
546  // Our dataset doesn't have its own export ID, we just use
547  // ours. Actually any model could do that, since datasets aren't
548  // in the same id-space as models when re-read
549 
550  SVDEBUG << "BasicCompressedDenseThreeDimensionalModel::toXml" << endl;
551 
553  (out, indent,
554  QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
555  .arg(m_resolution)
556  .arg(m_yBinCount)
557  .arg(m_minimum)
558  .arg(m_maximum)
559  .arg(getExportId())
560  .arg(m_startFrame)
561  .arg(extraAttributes));
562 
563  out << indent;
564  out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
565  .arg(getExportId());
566 
567  for (int i = 0; in_range_for(m_binNames, i); ++i) {
568  if (m_binNames[i] != "") {
569  out << indent + " ";
570  out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
571  .arg(i).arg(m_binNames[i]);
572  }
573  }
574 
575  for (int i = 0; in_range_for(m_data, i); ++i) {
576  Column c = getColumn(i);
577  out << indent + " ";
578  out << QString("<row n=\"%1\">").arg(i);
579  for (int j = 0; in_range_for(c, j); ++j) {
580  if (j > 0) out << " ";
581  out << c.at(j);
582  }
583  out << QString("</row>\n");
584  out.flush();
585  }
586 
587  out << indent + "</dataset>\n";
588 }
589 
590 
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
Definition: Model.cpp:204
QVector< QVector< QString > > toStringExportRows(DataExportOptions options, sv_frame_t startFrame, sv_frame_t duration) const override
Emit events starting within the given range as string rows ready for conversion to an e...
virtual void setBinValues(std::vector< float > values)
Set the values of all bins (separate from their labels).
sv_samplerate_t getSampleRate() const override
Return the frame rate in frames per second.
bool isOK() const override
Return true if the model was constructed successfully.
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
void toXml(QTextStream &out, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
float getMaximumLevel() const override
Return the maximum value of the value in each bin.
#define ISNAN
Definition: System.h:103
virtual void setResolution(int sz)
Set the number of sample frames covered by each set of bins.
virtual void setBinValueUnit(QString unit)
Set the name of the unit of the values return from getBinValue() if any.
virtual void setBinName(int n, QString)
Set the name of bin n.
virtual void setStartFrame(sv_frame_t)
Set the frame offset of the first column.
virtual void setBinNames(std::vector< QString > names)
Set the names of all bins.
QVector< QString > getStringExportHeaders(DataExportOptions options) const override
Return a label for each column that would be written by toStringExportRows.
Id getId() const
Return an id for this object.
Definition: ById.h:193
float getMinimumLevel() const override
Return the minimum value of the value in each bin.
float getValueAt(int x, int n) const override
Get a single value, from the n&#39;th bin of the given column.
QString getBinName(int n) const override
Return the name of bin n.
int getResolution() const override
Return the number of sample frames covered by each set of bins.
bool hasBinValues() const override
Return true if the bins have values as well as names.
bool shouldUseLogValueScale() const override
Return true if the distribution of values in the bins is such as to suggest a log scale (mapping to c...
sv_frame_t getTrueEndFrame() const override
Return the audio frame at the end of the model.
virtual void setMinimumLevel(float sz)
Set the minimum value of the value in a bin.
int getWidth() const override
Return the number of columns.
Column getColumn(int x) const override
Get the set of bin values at the given column.
void completionChanged(ModelId myId)
Emitted when some internal processing has advanced a stage, but the model has not changed externally...
virtual void setHeight(int sz)
Set the number of bins in each column.
static bool shouldUseLogScale(std::vector< double > values)
Estimate whether a set of values would be more properly shown using a logarithmic than a linear scale...
Definition: LogRange.cpp:93
ExportId getExportId() const
Return the numerical export identifier for this object.
bool in_range_for(const C &container, T i)
Check whether an integer index is in range for a container, avoiding overflows and signed/unsigned co...
Definition: BaseTypes.h:37
void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
#define SVDEBUG
Definition: Debug.h:106
virtual void setMaximumLevel(float sz)
Set the maximum value of the value in a bin.
QString getBinValueUnit() const override
Obtain the name of the unit of the values returned from getBinValue(), if any.
#define ISINF
Definition: System.h:104
virtual void setColumn(int x, const Column &values)
Set the entire set of bin values at the given column.
sv_frame_t getStartFrame() const override
Return the first audio frame spanned by the model.
float getBinValue(int n) const override
Return the value of bin n, if any.
int getCompletion() const override
Return an estimated percentage value showing how far through any background operation used to calcula...
BasicCompressedDenseThreeDimensionalModel(sv_samplerate_t sampleRate, int resolution, int height, bool notifyOnAdd=true)
void modelChanged(ModelId myId)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
bool isReady(int *completion=0) const override
Return true if the model has finished loading or calculating all its data, for a model that is capabl...
int getHeight() const override
Return the number of bins in each column.
int DataExportOptions