svcore
1.9
|
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