svcore  1.9
EditableDenseThreeDimensionalModel.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 #include "system/System.h"
31 
33  int resolution,
34  int yBinCount,
35  CompressionType compression,
36  bool notifyOnAdd) :
37  m_startFrame(0),
38  m_sampleRate(sampleRate),
39  m_resolution(resolution),
40  m_yBinCount(yBinCount),
41  m_compression(compression),
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 int
60 {
61  return m_sampleRate;
62 }
63 
64 int
66 {
67  return m_startFrame;
68 }
69 
70 void
72 {
73  m_startFrame = f;
74 }
75 
76 int
78 {
79  return m_resolution * m_data.size() + (m_resolution - 1);
80 }
81 
82 Model *
84 {
85  QReadLocker locker(&m_lock);
86 
90 
91  model->m_minimum = m_minimum;
92  model->m_maximum = m_maximum;
94 
95  for (int i = 0; i < m_data.size(); ++i) {
96  model->setColumn(i, m_data.at(i));
97  }
98 
99  return model;
100 }
101 
102 int
104 {
105  return m_resolution;
106 }
107 
108 void
110 {
111  m_resolution = sz;
112 }
113 
114 int
116 {
117  return m_data.size();
118 }
119 
120 int
122 {
123  return m_yBinCount;
124 }
125 
126 void
128 {
129  m_yBinCount = sz;
130 }
131 
132 float
134 {
135  return m_minimum;
136 }
137 
138 void
140 {
141  m_minimum = level;
142 }
143 
144 float
146 {
147  return m_maximum;
148 }
149 
150 void
152 {
153  m_maximum = level;
154 }
155 
158 {
159  QReadLocker locker(&m_lock);
160  if (index < 0 || index >= m_data.size()) return Column();
161  return expandAndRetrieve(index);
162 }
163 
164 float
166 {
167  Column c = getColumn(index);
168  if (int(n) < c.size()) return c.at(n);
169  return m_minimum;
170 }
171 
172 //static int given = 0, stored = 0;
173 
174 void
176  const Column &values)
177 {
178  assert(int(index) < m_data.size());
179 
180  //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl;
181 
182  // The default case is to store the entire column at m_data[index]
183  // and place 0 at m_trunc[index] to indicate that it has not been
184  // truncated. We only do clever stuff if one of the clever-stuff
185  // tests works out.
186 
187  m_trunc[index] = 0;
188  if (index == 0 ||
190  values.size() != int(m_yBinCount)) {
191 // given += values.size();
192 // stored += values.size();
193  m_data[index] = values;
194  return;
195  }
196 
197  // Maximum distance between a column and the one we refer to as
198  // the source of its truncated values. Limited by having to fit
199  // in a signed char, but in any case small values are usually
200  // better
201  static int maxdist = 6;
202 
203  bool known = false; // do we know whether to truncate at top or bottom?
204  bool top = false; // if we do know, will we truncate at top?
205 
206  // If the previous column is not truncated, then it is the only
207  // candidate for comparison. If it is truncated, then the column
208  // that it refers to is the only candidate. Either way, we only
209  // have one possible column to compare against here, and we are
210  // being careful to ensure it is not a truncated one (to avoid
211  // doing more work recursively when uncompressing).
212  int tdist = 1;
213  int ptrunc = m_trunc[index-1];
214  if (ptrunc < 0) {
215  top = false;
216  known = true;
217  tdist = -ptrunc + 1;
218  } else if (ptrunc > 0) {
219  top = true;
220  known = true;
221  tdist = ptrunc + 1;
222  }
223 
224  Column p = expandAndRetrieve(index - tdist);
225  int h = m_yBinCount;
226 
227  if (p.size() == h && tdist <= maxdist) {
228 
229  int bcount = 0, tcount = 0;
230  if (!known || !top) {
231  // count how many identical values there are at the bottom
232  for (int i = 0; i < h; ++i) {
233  if (values.at(i) == p.at(i)) ++bcount;
234  else break;
235  }
236  }
237  if (!known || top) {
238  // count how many identical values there are at the top
239  for (int i = h; i > 0; --i) {
240  if (values.at(i-1) == p.at(i-1)) ++tcount;
241  else break;
242  }
243  }
244  if (!known) top = (tcount > bcount);
245 
246  int limit = h / 4; // don't bother unless we have at least this many
247  if ((top ? tcount : bcount) > limit) {
248 
249  if (!top) {
250  // create a new column with h - bcount values from bcount up
251  Column tcol(h - bcount);
252 // given += values.size();
253 // stored += h - bcount;
254  for (int i = bcount; i < h; ++i) {
255  tcol[i - bcount] = values.at(i);
256  }
257  m_data[index] = tcol;
258  m_trunc[index] = -tdist;
259  return;
260  } else {
261  // create a new column with h - tcount values from 0 up
262  Column tcol(h - tcount);
263 // given += values.size();
264 // stored += h - tcount;
265  for (int i = 0; i < h - tcount; ++i) {
266  tcol[i] = values.at(i);
267  }
268  m_data[index] = tcol;
269  m_trunc[index] = tdist;
270  return;
271  }
272  }
273  }
274 
275 // given += values.size();
276 // stored += values.size();
277 // cout << "given: " << given << ", stored: " << stored << " ("
278 // << ((float(stored) / float(given)) * 100.f) << "%)" << endl;
279 
280  // default case if nothing wacky worked out
281  m_data[index] = values;
282  return;
283 }
284 
287 {
288  // See comment above m_trunc declaration in header
289 
290  assert(index >= 0 && index < int(m_data.size()));
291  Column c = m_data.at(index);
292  if (index == 0) {
293  return c;
294  }
295  int trunc = (int)m_trunc[index];
296  if (trunc == 0) {
297  return c;
298  }
299  bool top = true;
300  int tdist = trunc;
301  if (trunc < 0) { top = false; tdist = -trunc; }
302  Column p = expandAndRetrieve(index - tdist);
303  int psize = p.size(), csize = c.size();
304  if (psize != m_yBinCount) {
305  cerr << "WARNING: EditableDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << endl;
306  }
307  if (top) {
308  for (int i = csize; i < psize; ++i) {
309  c.push_back(p.at(i));
310  }
311  } else {
312  // push_front is very slow on QVector -- but not enough to
313  // make it desirable to choose a different container, since
314  // QVector has all the other advantages for us. easier to
315  // write the whole array out to a new vector
316  Column cc(psize);
317  for (int i = 0; i < psize - csize; ++i) {
318  cc[i] = p.at(i);
319  }
320  for (int i = 0; i < csize; ++i) {
321  cc[i + (psize - csize)] = c.at(i);
322  }
323  return cc;
324  }
325  return c;
326 }
327 
328 void
330  const Column &values)
331 {
332  QWriteLocker locker(&m_lock);
333 
334  while (int(index) >= m_data.size()) {
335  m_data.push_back(Column());
336  m_trunc.push_back(0);
337  }
338 
339  bool allChange = false;
340 
341 // if (values.size() > m_yBinCount) m_yBinCount = values.size();
342 
343  for (int i = 0; i < values.size(); ++i) {
344  float value = values[i];
345  if (ISNAN(value) || ISINF(value)) {
346  continue;
347  }
348  if (!m_haveExtents || value < m_minimum) {
349  m_minimum = value;
350  allChange = true;
351  }
352  if (!m_haveExtents || value > m_maximum) {
353  m_maximum = value;
354  allChange = true;
355  }
356  m_haveExtents = true;
357  }
358 
359  truncateAndStore(index, values);
360 
361 // assert(values == expandAndRetrieve(index));
362 
363  long windowStart = index;
364  windowStart *= m_resolution;
365 
366  if (m_notifyOnAdd) {
367  if (allChange) {
368  emit modelChanged();
369  } else {
370  emit modelChangedWithin(windowStart, windowStart + m_resolution);
371  }
372  } else {
373  if (allChange) {
376  emit modelChanged();
377  } else {
378  if (m_sinceLastNotifyMin == -1 ||
379  windowStart < m_sinceLastNotifyMin) {
380  m_sinceLastNotifyMin = windowStart;
381  }
382  if (m_sinceLastNotifyMax == -1 ||
383  windowStart > m_sinceLastNotifyMax) {
384  m_sinceLastNotifyMax = windowStart;
385  }
386  }
387  }
388 }
389 
390 QString
392 {
393  if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
394  else return "";
395 }
396 
397 void
399 {
400  while ((int)m_binNames.size() <= n) m_binNames.push_back("");
401  m_binNames[n] = name;
402  emit modelChanged();
403 }
404 
405 void
407 {
408  m_binNames = names;
409  emit modelChanged();
410 }
411 
412 bool
414 {
415  return !m_binValues.empty();
416 }
417 
418 float
420 {
421  if (n < (int)m_binValues.size()) return m_binValues[n];
422  else return 0.f;
423 }
424 
425 void
427 {
428  m_binValues = values;
429 }
430 
431 QString
433 {
434  return m_binValueUnit;
435 }
436 
437 void
439 {
440  m_binValueUnit = unit;
441 }
442 
443 bool
445 {
446  QReadLocker locker(&m_lock);
447 
448  QVector<float> sample;
449  QVector<int> n;
450 
451  for (int i = 0; i < 10; ++i) {
452  int index = i * 10;
453  if (index < m_data.size()) {
454  const Column &c = m_data.at(index);
455  while (c.size() > sample.size()) {
456  sample.push_back(0.f);
457  n.push_back(0);
458  }
459  for (int j = 0; j < c.size(); ++j) {
460  sample[j] += c.at(j);
461  ++n[j];
462  }
463  }
464  }
465 
466  if (sample.empty()) return false;
467  for (int j = 0; j < sample.size(); ++j) {
468  if (n[j]) sample[j] /= n[j];
469  }
470 
471  return LogRange::useLogScale(sample.toStdVector());
472 }
473 
474 void
476 {
477  if (m_completion != completion) {
478  m_completion = completion;
479 
480  if (completion == 100) {
481 
482  m_notifyOnAdd = true; // henceforth
483  emit modelChanged();
484 
485  } else if (!m_notifyOnAdd) {
486 
487  if (update &&
488  m_sinceLastNotifyMin >= 0 &&
489  m_sinceLastNotifyMax >= 0) {
493  } else {
494  emit completionChanged();
495  }
496  } else {
497  emit completionChanged();
498  }
499  }
500 }
501 
502 QString
504 {
505  QReadLocker locker(&m_lock);
506  QString s;
507  for (int i = 0; i < m_data.size(); ++i) {
508  QStringList list;
509  for (int j = 0; j < m_data.at(i).size(); ++j) {
510  list << QString("%1").arg(m_data.at(i).at(j));
511  }
512  s += list.join(delimiter) + "\n";
513  }
514  return s;
515 }
516 
517 QString
519 {
520  QReadLocker locker(&m_lock);
521  QString s;
522  for (int i = 0; i < m_data.size(); ++i) {
523  int fr = m_startFrame + i * m_resolution;
524  if (fr >= int(f0) && fr < int(f1)) {
525  QStringList list;
526  for (int j = 0; j < m_data.at(i).size(); ++j) {
527  list << QString("%1").arg(m_data.at(i).at(j));
528  }
529  s += list.join(delimiter) + "\n";
530  }
531  }
532  return s;
533 }
534 
535 void
537  QString indent,
538  QString extraAttributes) const
539 {
540  QReadLocker locker(&m_lock);
541 
542  // For historical reasons we read and write "resolution" as "windowSize"
543 
544  SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
545 
547  (out, indent,
548  QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
549  .arg(m_resolution)
550  .arg(m_yBinCount)
551  .arg(m_minimum)
552  .arg(m_maximum)
553  .arg(getObjectExportId(&m_data))
554  .arg(m_startFrame)
555  .arg(extraAttributes));
556 
557  out << indent;
558  out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
559  .arg(getObjectExportId(&m_data));
560 
561  for (int i = 0; i < (int)m_binNames.size(); ++i) {
562  if (m_binNames[i] != "") {
563  out << indent + " ";
564  out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
565  .arg(i).arg(m_binNames[i]);
566  }
567  }
568 
569  for (int i = 0; i < (int)m_data.size(); ++i) {
570  out << indent + " ";
571  out << QString("<row n=\"%1\">").arg(i);
572  for (int j = 0; j < (int)m_data.at(i).size(); ++j) {
573  if (j > 0) out << " ";
574  out << m_data.at(i).at(j);
575  }
576  out << QString("</row>\n");
577  out.flush();
578  }
579 
580  out << indent + "</dataset>\n";
581 }
582 
583 
virtual bool isOK() const
Return true if the model was constructed successfully.
virtual void toXml(QTextStream &out, QString indent="", QString extraAttributes="") const
Stream this exportable object out to XML on a text stream.
virtual void setMinimumLevel(float sz)
Set the minimum value of the value in a bin.
virtual QString toDelimitedDataStringSubset(QString delimiter, int f0, int f1) const
virtual QString getBinValueUnit() const
Obtain the name of the unit of the values returned from getBinValue(), if any.
virtual QString getBinName(int n) const
Return the name of bin n.
virtual void setBinName(int n, QString)
Set the name of bin n.
virtual float getValueAt(int x, int n) const
Get a single value, from the n'th bin of the given column.
#define ISNAN
Definition: System.h:92
void completionChanged()
Emitted when some internal processing has advanced a stage, but the model has not changed externally.
static int getObjectExportId(const void *)
void modelChangedWithin(int startFrame, int endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
virtual QString toDelimitedDataString(QString delimiter) const
virtual void setCompletion(int completion, bool update=true)
virtual int getStartFrame() const
Return the first audio frame spanned by the model.
virtual float getMinimumLevel() const
Return the minimum value of the value in each bin.
virtual void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const
Stream this exportable object out to XML on a text stream.
Definition: Model.cpp:174
EditableDenseThreeDimensionalModel(int sampleRate, int resolution, int yBinCount, CompressionType compression, bool notifyOnAdd=true)
virtual float getMaximumLevel() const
Return the maximum value of the value in each bin.
bool shouldUseLogValueScale() const
Return true if the distribution of values in the bins is such as to suggest a log scale (mapping to c...
virtual int getHeight() const
Return the number of bins in 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 setStartFrame(int)
Set the frame offset of the first column.
static bool useLogScale(std::vector< float > values)
Estimate whether a set of values would be more properly shown using a logarithmic than a linear scale...
Definition: LogRange.cpp:94
void modelChanged()
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
virtual Column getColumn(int x) const
Get the set of bin values at the given column.
virtual float getBinValue(int n) const
Return the value of bin n, if any.
virtual void setBinNames(std::vector< QString > names)
Set the names of all bins.
virtual int getResolution() const
Return the number of sample frames covered by each set of bins.
Model is the base class for all data models that represent any sort of data on a time scale based on ...
Definition: Model.h:35
virtual void setMaximumLevel(float sz)
Set the maximum value of the value in a bin.
void truncateAndStore(int index, const Column &values)
virtual int getSampleRate() const
Return the frame rate in frames per second.
virtual int getEndFrame() const
Return the last audio frame spanned by the model.
#define SVDEBUG
Definition: Debug.h:42
#define ISINF
Definition: System.h:93
virtual Model * clone() const
Return a copy of this model.
virtual void setResolution(int sz)
Set the number of sample frames covered by each set of bins.
virtual void setBinValues(std::vector< float > values)
Set the values of all bins (separate from their labels).
virtual void setHeight(int sz)
Set the number of bins in each set of bins.
virtual void setColumn(int x, const Column &values)
Set the entire set of bin values at the given column.
virtual bool hasBinValues() const
Return true if the bins have values as well as names.
virtual int getWidth() const
Return the number of columns.