svcore  1.9
EditableDenseThreeDimensionalModel.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "EditableDenseThreeDimensionalModel.h"
00017 
00018 #include "base/LogRange.h"
00019 
00020 #include <QTextStream>
00021 #include <QStringList>
00022 #include <QReadLocker>
00023 #include <QWriteLocker>
00024 
00025 #include <iostream>
00026 
00027 #include <cmath>
00028 #include <cassert>
00029 
00030 #include "system/System.h"
00031 
00032 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(int sampleRate,
00033                                                                        int resolution,
00034                                                                        int yBinCount,
00035                                                                        CompressionType compression,
00036                                                                        bool notifyOnAdd) :
00037     m_startFrame(0),
00038     m_sampleRate(sampleRate),
00039     m_resolution(resolution),
00040     m_yBinCount(yBinCount),
00041     m_compression(compression),
00042     m_minimum(0.0),
00043     m_maximum(0.0),
00044     m_haveExtents(false),
00045     m_notifyOnAdd(notifyOnAdd),
00046     m_sinceLastNotifyMin(-1),
00047     m_sinceLastNotifyMax(-1),
00048     m_completion(100)
00049 {
00050 }    
00051 
00052 bool
00053 EditableDenseThreeDimensionalModel::isOK() const
00054 {
00055     return true;
00056 }
00057 
00058 int
00059 EditableDenseThreeDimensionalModel::getSampleRate() const
00060 {
00061     return m_sampleRate;
00062 }
00063 
00064 int
00065 EditableDenseThreeDimensionalModel::getStartFrame() const
00066 {
00067     return m_startFrame;
00068 }
00069 
00070 void
00071 EditableDenseThreeDimensionalModel::setStartFrame(int f)
00072 {
00073     m_startFrame = f; 
00074 }
00075 
00076 int
00077 EditableDenseThreeDimensionalModel::getEndFrame() const
00078 {
00079     return m_resolution * m_data.size() + (m_resolution - 1);
00080 }
00081 
00082 Model *
00083 EditableDenseThreeDimensionalModel::clone() const
00084 {
00085     QReadLocker locker(&m_lock);
00086 
00087     EditableDenseThreeDimensionalModel *model =
00088         new EditableDenseThreeDimensionalModel
00089         (m_sampleRate, m_resolution, m_yBinCount, m_compression);
00090 
00091     model->m_minimum = m_minimum;
00092     model->m_maximum = m_maximum;
00093     model->m_haveExtents = m_haveExtents;
00094 
00095     for (int i = 0; i < m_data.size(); ++i) {
00096         model->setColumn(i, m_data.at(i));
00097     }
00098 
00099     return model;
00100 }
00101 
00102 int
00103 EditableDenseThreeDimensionalModel::getResolution() const
00104 {
00105     return m_resolution;
00106 }
00107 
00108 void
00109 EditableDenseThreeDimensionalModel::setResolution(int sz)
00110 {
00111     m_resolution = sz;
00112 }
00113 
00114 int
00115 EditableDenseThreeDimensionalModel::getWidth() const
00116 {
00117     return m_data.size();
00118 }
00119 
00120 int
00121 EditableDenseThreeDimensionalModel::getHeight() const
00122 {
00123     return m_yBinCount;
00124 }
00125 
00126 void
00127 EditableDenseThreeDimensionalModel::setHeight(int sz)
00128 {
00129     m_yBinCount = sz;
00130 }
00131 
00132 float
00133 EditableDenseThreeDimensionalModel::getMinimumLevel() const
00134 {
00135     return m_minimum;
00136 }
00137 
00138 void
00139 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
00140 {
00141     m_minimum = level;
00142 }
00143 
00144 float
00145 EditableDenseThreeDimensionalModel::getMaximumLevel() const
00146 {
00147     return m_maximum;
00148 }
00149 
00150 void
00151 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
00152 {
00153     m_maximum = level;
00154 }
00155 
00156 EditableDenseThreeDimensionalModel::Column
00157 EditableDenseThreeDimensionalModel::getColumn(int index) const
00158 {
00159     QReadLocker locker(&m_lock);
00160     if (index < 0 || index >= m_data.size()) return Column();
00161     return expandAndRetrieve(index);
00162 }
00163 
00164 float
00165 EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const
00166 {
00167     Column c = getColumn(index);
00168     if (int(n) < c.size()) return c.at(n);
00169     return m_minimum;
00170 }
00171 
00172 //static int given = 0, stored = 0;
00173 
00174 void
00175 EditableDenseThreeDimensionalModel::truncateAndStore(int index,
00176                                                      const Column &values)
00177 {
00178     assert(int(index) < m_data.size());
00179 
00180     //cout << "truncateAndStore(" << index << ", " << values.size() << ")" << endl;
00181 
00182     // The default case is to store the entire column at m_data[index]
00183     // and place 0 at m_trunc[index] to indicate that it has not been
00184     // truncated.  We only do clever stuff if one of the clever-stuff
00185     // tests works out.
00186 
00187     m_trunc[index] = 0;
00188     if (index == 0 ||
00189         m_compression == NoCompression ||
00190         values.size() != int(m_yBinCount)) {
00191 //        given += values.size();
00192 //        stored += values.size();
00193         m_data[index] = values;
00194         return;
00195     }
00196 
00197     // Maximum distance between a column and the one we refer to as
00198     // the source of its truncated values.  Limited by having to fit
00199     // in a signed char, but in any case small values are usually
00200     // better
00201     static int maxdist = 6;
00202 
00203     bool known = false; // do we know whether to truncate at top or bottom?
00204     bool top = false;   // if we do know, will we truncate at top?
00205 
00206     // If the previous column is not truncated, then it is the only
00207     // candidate for comparison.  If it is truncated, then the column
00208     // that it refers to is the only candidate.  Either way, we only
00209     // have one possible column to compare against here, and we are
00210     // being careful to ensure it is not a truncated one (to avoid
00211     // doing more work recursively when uncompressing).
00212     int tdist = 1;
00213     int ptrunc = m_trunc[index-1];
00214     if (ptrunc < 0) {
00215         top = false;
00216         known = true;
00217         tdist = -ptrunc + 1;
00218     } else if (ptrunc > 0) {
00219         top = true;
00220         known = true;
00221         tdist = ptrunc + 1;
00222     }
00223 
00224     Column p = expandAndRetrieve(index - tdist);
00225     int h = m_yBinCount;
00226 
00227     if (p.size() == h && tdist <= maxdist) {
00228 
00229         int bcount = 0, tcount = 0;
00230         if (!known || !top) {
00231             // count how many identical values there are at the bottom
00232             for (int i = 0; i < h; ++i) {
00233                 if (values.at(i) == p.at(i)) ++bcount;
00234                 else break;
00235             }
00236         }
00237         if (!known || top) {
00238             // count how many identical values there are at the top
00239             for (int i = h; i > 0; --i) {
00240                 if (values.at(i-1) == p.at(i-1)) ++tcount;
00241                 else break;
00242             }
00243         }
00244         if (!known) top = (tcount > bcount);
00245 
00246         int limit = h / 4; // don't bother unless we have at least this many
00247         if ((top ? tcount : bcount) > limit) {
00248         
00249             if (!top) {
00250                 // create a new column with h - bcount values from bcount up
00251                 Column tcol(h - bcount);
00252 //                given += values.size();
00253 //                stored += h - bcount;
00254                 for (int i = bcount; i < h; ++i) {
00255                     tcol[i - bcount] = values.at(i);
00256                 }
00257                 m_data[index] = tcol;
00258                 m_trunc[index] = -tdist;
00259                 return;
00260             } else {
00261                 // create a new column with h - tcount values from 0 up
00262                 Column tcol(h - tcount);
00263 //                given += values.size();
00264 //                stored += h - tcount;
00265                 for (int i = 0; i < h - tcount; ++i) {
00266                     tcol[i] = values.at(i);
00267                 }
00268                 m_data[index] = tcol;
00269                 m_trunc[index] = tdist;
00270                 return;
00271             }
00272         }
00273     }                
00274 
00275 //    given += values.size();
00276 //    stored += values.size();
00277 //    cout << "given: " << given << ", stored: " << stored << " (" 
00278 //              << ((float(stored) / float(given)) * 100.f) << "%)" << endl;
00279 
00280     // default case if nothing wacky worked out
00281     m_data[index] = values;
00282     return;
00283 }
00284 
00285 EditableDenseThreeDimensionalModel::Column
00286 EditableDenseThreeDimensionalModel::expandAndRetrieve(int index) const
00287 {
00288     // See comment above m_trunc declaration in header
00289 
00290     assert(index >= 0 && index < int(m_data.size()));
00291     Column c = m_data.at(index);
00292     if (index == 0) {
00293         return c;
00294     }
00295     int trunc = (int)m_trunc[index];
00296     if (trunc == 0) {
00297         return c;
00298     }
00299     bool top = true;
00300     int tdist = trunc;
00301     if (trunc < 0) { top = false; tdist = -trunc; }
00302     Column p = expandAndRetrieve(index - tdist);
00303     int psize = p.size(), csize = c.size();
00304     if (psize != m_yBinCount) {
00305         cerr << "WARNING: EditableDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << endl;
00306     }
00307     if (top) {
00308         for (int i = csize; i < psize; ++i) {
00309             c.push_back(p.at(i));
00310         }
00311     } else {
00312         // push_front is very slow on QVector -- but not enough to
00313         // make it desirable to choose a different container, since
00314         // QVector has all the other advantages for us.  easier to
00315         // write the whole array out to a new vector
00316         Column cc(psize);
00317         for (int i = 0; i < psize - csize; ++i) {
00318             cc[i] = p.at(i);
00319         }
00320         for (int i = 0; i < csize; ++i) {
00321             cc[i + (psize - csize)] = c.at(i);
00322         }
00323         return cc;
00324     }
00325     return c;
00326 }
00327 
00328 void
00329 EditableDenseThreeDimensionalModel::setColumn(int index,
00330                                               const Column &values)
00331 {
00332     QWriteLocker locker(&m_lock);
00333 
00334     while (int(index) >= m_data.size()) {
00335         m_data.push_back(Column());
00336         m_trunc.push_back(0);
00337     }
00338 
00339     bool allChange = false;
00340 
00341 //    if (values.size() > m_yBinCount) m_yBinCount = values.size();
00342 
00343     for (int i = 0; i < values.size(); ++i) {
00344         float value = values[i];
00345         if (ISNAN(value) || ISINF(value)) {
00346             continue;
00347         }
00348         if (!m_haveExtents || value < m_minimum) {
00349             m_minimum = value;
00350             allChange = true;
00351         }
00352         if (!m_haveExtents || value > m_maximum) {
00353             m_maximum = value;
00354             allChange = true;
00355         }
00356         m_haveExtents = true;
00357     }
00358 
00359     truncateAndStore(index, values);
00360 
00361 //    assert(values == expandAndRetrieve(index));
00362 
00363     long windowStart = index;
00364     windowStart *= m_resolution;
00365 
00366     if (m_notifyOnAdd) {
00367         if (allChange) {
00368             emit modelChanged();
00369         } else {
00370             emit modelChangedWithin(windowStart, windowStart + m_resolution);
00371         }
00372     } else {
00373         if (allChange) {
00374             m_sinceLastNotifyMin = -1;
00375             m_sinceLastNotifyMax = -1;
00376             emit modelChanged();
00377         } else {
00378             if (m_sinceLastNotifyMin == -1 ||
00379                 windowStart < m_sinceLastNotifyMin) {
00380                 m_sinceLastNotifyMin = windowStart;
00381             }
00382             if (m_sinceLastNotifyMax == -1 ||
00383                 windowStart > m_sinceLastNotifyMax) {
00384                 m_sinceLastNotifyMax = windowStart;
00385             }
00386         }
00387     }
00388 }
00389 
00390 QString
00391 EditableDenseThreeDimensionalModel::getBinName(int n) const
00392 {
00393     if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n];
00394     else return "";
00395 }
00396 
00397 void
00398 EditableDenseThreeDimensionalModel::setBinName(int n, QString name)
00399 {
00400     while ((int)m_binNames.size() <= n) m_binNames.push_back("");
00401     m_binNames[n] = name;
00402     emit modelChanged();
00403 }
00404 
00405 void
00406 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
00407 {
00408     m_binNames = names;
00409     emit modelChanged();
00410 }
00411 
00412 bool
00413 EditableDenseThreeDimensionalModel::hasBinValues() const
00414 {
00415     return !m_binValues.empty();
00416 }
00417 
00418 float
00419 EditableDenseThreeDimensionalModel::getBinValue(int n) const
00420 {
00421     if (n < (int)m_binValues.size()) return m_binValues[n];
00422     else return 0.f;
00423 }
00424 
00425 void
00426 EditableDenseThreeDimensionalModel::setBinValues(std::vector<float> values)
00427 {
00428     m_binValues = values;
00429 }
00430 
00431 QString
00432 EditableDenseThreeDimensionalModel::getBinValueUnit() const
00433 {
00434     return m_binValueUnit;
00435 }
00436 
00437 void
00438 EditableDenseThreeDimensionalModel::setBinValueUnit(QString unit)
00439 {
00440     m_binValueUnit = unit;
00441 }
00442 
00443 bool
00444 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
00445 {
00446     QReadLocker locker(&m_lock);
00447 
00448     QVector<float> sample;
00449     QVector<int> n;
00450     
00451     for (int i = 0; i < 10; ++i) {
00452         int index = i * 10;
00453         if (index < m_data.size()) {
00454             const Column &c = m_data.at(index);
00455             while (c.size() > sample.size()) {
00456                 sample.push_back(0.f);
00457                 n.push_back(0);
00458             }
00459             for (int j = 0; j < c.size(); ++j) {
00460                 sample[j] += c.at(j);
00461                 ++n[j];
00462             }
00463         }
00464     }
00465 
00466     if (sample.empty()) return false;
00467     for (int j = 0; j < sample.size(); ++j) {
00468         if (n[j]) sample[j] /= n[j];
00469     }
00470     
00471     return LogRange::useLogScale(sample.toStdVector());
00472 }
00473 
00474 void
00475 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
00476 {
00477     if (m_completion != completion) {
00478         m_completion = completion;
00479 
00480         if (completion == 100) {
00481 
00482             m_notifyOnAdd = true; // henceforth
00483             emit modelChanged();
00484 
00485         } else if (!m_notifyOnAdd) {
00486 
00487             if (update &&
00488                 m_sinceLastNotifyMin >= 0 &&
00489                 m_sinceLastNotifyMax >= 0) {
00490                 emit modelChangedWithin(m_sinceLastNotifyMin,
00491                                         m_sinceLastNotifyMax + m_resolution);
00492                 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
00493             } else {
00494                 emit completionChanged();
00495             }
00496         } else {
00497             emit completionChanged();
00498         }           
00499     }
00500 }
00501 
00502 QString
00503 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter) const
00504 {
00505     QReadLocker locker(&m_lock);
00506     QString s;
00507     for (int i = 0; i < m_data.size(); ++i) {
00508         QStringList list;
00509         for (int j = 0; j < m_data.at(i).size(); ++j) {
00510             list << QString("%1").arg(m_data.at(i).at(j));
00511         }
00512         s += list.join(delimiter) + "\n";
00513     }
00514     return s;
00515 }
00516 
00517 QString
00518 EditableDenseThreeDimensionalModel::toDelimitedDataStringSubset(QString delimiter, int f0, int f1) const
00519 {
00520     QReadLocker locker(&m_lock);
00521     QString s;
00522     for (int i = 0; i < m_data.size(); ++i) {
00523         int fr = m_startFrame + i * m_resolution;
00524         if (fr >= int(f0) && fr < int(f1)) {
00525             QStringList list;
00526             for (int j = 0; j < m_data.at(i).size(); ++j) {
00527                 list << QString("%1").arg(m_data.at(i).at(j));
00528             }
00529             s += list.join(delimiter) + "\n";
00530         }
00531     }
00532     return s;
00533 }
00534 
00535 void
00536 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
00537                                           QString indent,
00538                                           QString extraAttributes) const
00539 {
00540     QReadLocker locker(&m_lock);
00541 
00542     // For historical reasons we read and write "resolution" as "windowSize"
00543 
00544     SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl;
00545 
00546     Model::toXml
00547         (out, indent,
00548          QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7")
00549          .arg(m_resolution)
00550          .arg(m_yBinCount)
00551          .arg(m_minimum)
00552          .arg(m_maximum)
00553          .arg(getObjectExportId(&m_data))
00554          .arg(m_startFrame)
00555          .arg(extraAttributes));
00556 
00557     out << indent;
00558     out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
00559         .arg(getObjectExportId(&m_data));
00560 
00561     for (int i = 0; i < (int)m_binNames.size(); ++i) {
00562         if (m_binNames[i] != "") {
00563             out << indent + "  ";
00564             out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
00565                 .arg(i).arg(m_binNames[i]);
00566         }
00567     }
00568 
00569     for (int i = 0; i < (int)m_data.size(); ++i) {
00570         out << indent + "  ";
00571         out << QString("<row n=\"%1\">").arg(i);
00572         for (int j = 0; j < (int)m_data.at(i).size(); ++j) {
00573             if (j > 0) out << " ";
00574             out << m_data.at(i).at(j);
00575         }
00576         out << QString("</row>\n");
00577         out.flush();
00578     }
00579 
00580     out << indent + "</dataset>\n";
00581 }
00582 
00583