| Chris@152 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@152 | 2 | 
| Chris@152 | 3 /* | 
| Chris@152 | 4     Sonic Visualiser | 
| Chris@152 | 5     An audio file viewer and annotation editor. | 
| Chris@152 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@202 | 7     This file copyright 2006 Chris Cannam and QMUL. | 
| Chris@152 | 8 | 
| Chris@152 | 9     This program is free software; you can redistribute it and/or | 
| Chris@152 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@152 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@152 | 12     License, or (at your option) any later version.  See the file | 
| Chris@152 | 13     COPYING included with this distribution for more information. | 
| Chris@152 | 14 */ | 
| Chris@152 | 15 | 
| Chris@152 | 16 #include "EditableDenseThreeDimensionalModel.h" | 
| Chris@152 | 17 | 
| Chris@478 | 18 #include "base/LogRange.h" | 
| Chris@478 | 19 | 
| Chris@152 | 20 #include <QTextStream> | 
| Chris@387 | 21 #include <QStringList> | 
| Chris@387 | 22 | 
| Chris@181 | 23 #include <iostream> | 
| Chris@181 | 24 | 
| Chris@256 | 25 #include <cmath> | 
| Chris@256 | 26 | 
| Chris@152 | 27 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(size_t sampleRate, | 
| Chris@152 | 28                                                                        size_t resolution, | 
| Chris@152 | 29                                                                        size_t yBinCount, | 
| Chris@152 | 30                                                                        bool notifyOnAdd) : | 
| Chris@152 | 31     m_sampleRate(sampleRate), | 
| Chris@152 | 32     m_resolution(resolution), | 
| Chris@152 | 33     m_yBinCount(yBinCount), | 
| Chris@152 | 34     m_minimum(0.0), | 
| Chris@152 | 35     m_maximum(0.0), | 
| Chris@256 | 36     m_haveExtents(false), | 
| Chris@152 | 37     m_notifyOnAdd(notifyOnAdd), | 
| Chris@152 | 38     m_sinceLastNotifyMin(-1), | 
| Chris@152 | 39     m_sinceLastNotifyMax(-1), | 
| Chris@152 | 40     m_completion(100) | 
| Chris@152 | 41 { | 
| Chris@152 | 42 } | 
| Chris@152 | 43 | 
| Chris@152 | 44 bool | 
| Chris@152 | 45 EditableDenseThreeDimensionalModel::isOK() const | 
| Chris@152 | 46 { | 
| Chris@152 | 47     return true; | 
| Chris@152 | 48 } | 
| Chris@152 | 49 | 
| Chris@152 | 50 size_t | 
| Chris@152 | 51 EditableDenseThreeDimensionalModel::getSampleRate() const | 
| Chris@152 | 52 { | 
| Chris@152 | 53     return m_sampleRate; | 
| Chris@152 | 54 } | 
| Chris@152 | 55 | 
| Chris@152 | 56 size_t | 
| Chris@152 | 57 EditableDenseThreeDimensionalModel::getStartFrame() const | 
| Chris@152 | 58 { | 
| Chris@152 | 59     return 0; | 
| Chris@152 | 60 } | 
| Chris@152 | 61 | 
| Chris@152 | 62 size_t | 
| Chris@152 | 63 EditableDenseThreeDimensionalModel::getEndFrame() const | 
| Chris@152 | 64 { | 
| Chris@152 | 65     return m_resolution * m_data.size() + (m_resolution - 1); | 
| Chris@152 | 66 } | 
| Chris@152 | 67 | 
| Chris@152 | 68 Model * | 
| Chris@152 | 69 EditableDenseThreeDimensionalModel::clone() const | 
| Chris@152 | 70 { | 
| Chris@152 | 71     EditableDenseThreeDimensionalModel *model = | 
| Chris@152 | 72         new EditableDenseThreeDimensionalModel | 
| Chris@152 | 73 	(m_sampleRate, m_resolution, m_yBinCount); | 
| Chris@152 | 74 | 
| Chris@152 | 75     model->m_minimum = m_minimum; | 
| Chris@152 | 76     model->m_maximum = m_maximum; | 
| Chris@256 | 77     model->m_haveExtents = m_haveExtents; | 
| Chris@152 | 78 | 
| Chris@152 | 79     for (size_t i = 0; i < m_data.size(); ++i) { | 
| Chris@533 | 80 	model->setColumn(i, m_data.at(i)); | 
| Chris@152 | 81     } | 
| Chris@152 | 82 | 
| Chris@152 | 83     return model; | 
| Chris@152 | 84 } | 
| Chris@152 | 85 | 
| Chris@152 | 86 size_t | 
| Chris@152 | 87 EditableDenseThreeDimensionalModel::getResolution() const | 
| Chris@152 | 88 { | 
| Chris@152 | 89     return m_resolution; | 
| Chris@152 | 90 } | 
| Chris@152 | 91 | 
| Chris@152 | 92 void | 
| Chris@152 | 93 EditableDenseThreeDimensionalModel::setResolution(size_t sz) | 
| Chris@152 | 94 { | 
| Chris@152 | 95     m_resolution = sz; | 
| Chris@152 | 96 } | 
| Chris@152 | 97 | 
| Chris@152 | 98 size_t | 
| Chris@182 | 99 EditableDenseThreeDimensionalModel::getWidth() const | 
| Chris@182 | 100 { | 
| Chris@182 | 101     return m_data.size(); | 
| Chris@182 | 102 } | 
| Chris@182 | 103 | 
| Chris@182 | 104 size_t | 
| Chris@182 | 105 EditableDenseThreeDimensionalModel::getHeight() const | 
| Chris@152 | 106 { | 
| Chris@152 | 107     return m_yBinCount; | 
| Chris@152 | 108 } | 
| Chris@152 | 109 | 
| Chris@152 | 110 void | 
| Chris@182 | 111 EditableDenseThreeDimensionalModel::setHeight(size_t sz) | 
| Chris@152 | 112 { | 
| Chris@152 | 113     m_yBinCount = sz; | 
| Chris@152 | 114 } | 
| Chris@152 | 115 | 
| Chris@152 | 116 float | 
| Chris@152 | 117 EditableDenseThreeDimensionalModel::getMinimumLevel() const | 
| Chris@152 | 118 { | 
| Chris@152 | 119     return m_minimum; | 
| Chris@152 | 120 } | 
| Chris@152 | 121 | 
| Chris@152 | 122 void | 
| Chris@152 | 123 EditableDenseThreeDimensionalModel::setMinimumLevel(float level) | 
| Chris@152 | 124 { | 
| Chris@152 | 125     m_minimum = level; | 
| Chris@152 | 126 } | 
| Chris@152 | 127 | 
| Chris@152 | 128 float | 
| Chris@152 | 129 EditableDenseThreeDimensionalModel::getMaximumLevel() const | 
| Chris@152 | 130 { | 
| Chris@152 | 131     return m_maximum; | 
| Chris@152 | 132 } | 
| Chris@152 | 133 | 
| Chris@152 | 134 void | 
| Chris@152 | 135 EditableDenseThreeDimensionalModel::setMaximumLevel(float level) | 
| Chris@152 | 136 { | 
| Chris@152 | 137     m_maximum = level; | 
| Chris@152 | 138 } | 
| Chris@152 | 139 | 
| Chris@533 | 140 EditableDenseThreeDimensionalModel::Column | 
| Chris@533 | 141 EditableDenseThreeDimensionalModel::getColumn(size_t index) const | 
| Chris@152 | 142 { | 
| Chris@152 | 143     QMutexLocker locker(&m_mutex); | 
| Chris@152 | 144 | 
| Chris@533 | 145     Column result; | 
| Chris@533 | 146 | 
| Chris@182 | 147     if (index < m_data.size()) { | 
| Chris@533 | 148 	result = m_data.at(index); | 
| Chris@152 | 149     } else { | 
| Chris@152 | 150 	result.clear(); | 
| Chris@152 | 151     } | 
| Chris@152 | 152 | 
| Chris@152 | 153     while (result.size() < m_yBinCount) result.push_back(m_minimum); | 
| Chris@533 | 154     return result; | 
| Chris@152 | 155 } | 
| Chris@152 | 156 | 
| Chris@152 | 157 float | 
| Chris@182 | 158 EditableDenseThreeDimensionalModel::getValueAt(size_t index, size_t n) const | 
| Chris@152 | 159 { | 
| Chris@152 | 160     QMutexLocker locker(&m_mutex); | 
| Chris@152 | 161 | 
| Chris@182 | 162     if (index < m_data.size()) { | 
| Chris@533 | 163 	const Column &s = m_data.at(index); | 
| Chris@256 | 164 //        std::cerr << "index " << index << ", n " << n << ", res " << m_resolution << ", size " << s.size() | 
| Chris@256 | 165 //                  << std::endl; | 
| Chris@533 | 166 	if (n < s.size()) return s.at(n); | 
| Chris@152 | 167     } | 
| Chris@152 | 168 | 
| Chris@152 | 169     return m_minimum; | 
| Chris@152 | 170 } | 
| Chris@152 | 171 | 
| Chris@152 | 172 void | 
| Chris@182 | 173 EditableDenseThreeDimensionalModel::setColumn(size_t index, | 
| Chris@182 | 174                                               const Column &values) | 
| Chris@152 | 175 { | 
| Chris@152 | 176     QMutexLocker locker(&m_mutex); | 
| Chris@152 | 177 | 
| Chris@182 | 178     while (index >= m_data.size()) { | 
| Chris@182 | 179 	m_data.push_back(Column()); | 
| Chris@152 | 180     } | 
| Chris@152 | 181 | 
| Chris@152 | 182     bool allChange = false; | 
| Chris@152 | 183 | 
| Chris@439 | 184     if (values.size() > m_yBinCount) m_yBinCount = values.size(); | 
| Chris@439 | 185 | 
| Chris@152 | 186     for (size_t i = 0; i < values.size(); ++i) { | 
| Chris@256 | 187         float value = values[i]; | 
| Chris@257 | 188         if (std::isnan(value) || std::isinf(value)) { | 
| Chris@256 | 189             continue; | 
| Chris@256 | 190         } | 
| Chris@256 | 191 	if (!m_haveExtents || value < m_minimum) { | 
| Chris@256 | 192 	    m_minimum = value; | 
| Chris@152 | 193 	    allChange = true; | 
| Chris@152 | 194 	} | 
| Chris@256 | 195 	if (!m_haveExtents || value > m_maximum) { | 
| Chris@256 | 196 	    m_maximum = value; | 
| Chris@152 | 197 	    allChange = true; | 
| Chris@152 | 198 	} | 
| Chris@256 | 199         m_haveExtents = true; | 
| Chris@152 | 200     } | 
| Chris@152 | 201 | 
| Chris@152 | 202     m_data[index] = values; | 
| Chris@152 | 203 | 
| Chris@182 | 204     long windowStart = index; | 
| Chris@182 | 205     windowStart *= m_resolution; | 
| Chris@182 | 206 | 
| Chris@152 | 207     if (m_notifyOnAdd) { | 
| Chris@152 | 208 	if (allChange) { | 
| Chris@152 | 209 	    emit modelChanged(); | 
| Chris@152 | 210 	} else { | 
| Chris@152 | 211 	    emit modelChanged(windowStart, windowStart + m_resolution); | 
| Chris@152 | 212 	} | 
| Chris@152 | 213     } else { | 
| Chris@152 | 214 	if (allChange) { | 
| Chris@152 | 215 	    m_sinceLastNotifyMin = -1; | 
| Chris@152 | 216 	    m_sinceLastNotifyMax = -1; | 
| Chris@152 | 217 	    emit modelChanged(); | 
| Chris@152 | 218 	} else { | 
| Chris@152 | 219 	    if (m_sinceLastNotifyMin == -1 || | 
| Chris@152 | 220 		windowStart < m_sinceLastNotifyMin) { | 
| Chris@152 | 221 		m_sinceLastNotifyMin = windowStart; | 
| Chris@152 | 222 	    } | 
| Chris@152 | 223 	    if (m_sinceLastNotifyMax == -1 || | 
| Chris@152 | 224 		windowStart > m_sinceLastNotifyMax) { | 
| Chris@152 | 225 		m_sinceLastNotifyMax = windowStart; | 
| Chris@152 | 226 	    } | 
| Chris@152 | 227 	} | 
| Chris@152 | 228     } | 
| Chris@152 | 229 } | 
| Chris@152 | 230 | 
| Chris@152 | 231 QString | 
| Chris@152 | 232 EditableDenseThreeDimensionalModel::getBinName(size_t n) const | 
| Chris@152 | 233 { | 
| Chris@152 | 234     if (m_binNames.size() > n) return m_binNames[n]; | 
| Chris@152 | 235     else return ""; | 
| Chris@152 | 236 } | 
| Chris@152 | 237 | 
| Chris@152 | 238 void | 
| Chris@152 | 239 EditableDenseThreeDimensionalModel::setBinName(size_t n, QString name) | 
| Chris@152 | 240 { | 
| Chris@152 | 241     while (m_binNames.size() <= n) m_binNames.push_back(""); | 
| Chris@152 | 242     m_binNames[n] = name; | 
| Chris@152 | 243     emit modelChanged(); | 
| Chris@152 | 244 } | 
| Chris@152 | 245 | 
| Chris@152 | 246 void | 
| Chris@152 | 247 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names) | 
| Chris@152 | 248 { | 
| Chris@152 | 249     m_binNames = names; | 
| Chris@152 | 250     emit modelChanged(); | 
| Chris@152 | 251 } | 
| Chris@152 | 252 | 
| Chris@478 | 253 bool | 
| Chris@478 | 254 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const | 
| Chris@478 | 255 { | 
| Chris@533 | 256     QVector<float> sample; | 
| Chris@533 | 257     QVector<int> n; | 
| Chris@478 | 258 | 
| Chris@478 | 259     for (int i = 0; i < 10; ++i) { | 
| Chris@478 | 260         size_t index = i * 10; | 
| Chris@478 | 261         if (index < m_data.size()) { | 
| Chris@533 | 262             const Column &c = m_data.at(index); | 
| Chris@478 | 263             while (c.size() > sample.size()) { | 
| Chris@478 | 264                 sample.push_back(0.f); | 
| Chris@478 | 265                 n.push_back(0); | 
| Chris@478 | 266             } | 
| Chris@478 | 267             for (int j = 0; j < c.size(); ++j) { | 
| Chris@533 | 268                 sample[j] += c.at(j); | 
| Chris@478 | 269                 ++n[j]; | 
| Chris@478 | 270             } | 
| Chris@478 | 271         } | 
| Chris@478 | 272     } | 
| Chris@478 | 273 | 
| Chris@478 | 274     if (sample.empty()) return false; | 
| Chris@478 | 275     for (int j = 0; j < sample.size(); ++j) { | 
| Chris@478 | 276         if (n[j]) sample[j] /= n[j]; | 
| Chris@478 | 277     } | 
| Chris@478 | 278 | 
| Chris@533 | 279     return LogRange::useLogScale(sample.toStdVector()); | 
| Chris@478 | 280 } | 
| Chris@478 | 281 | 
| Chris@152 | 282 void | 
| Chris@333 | 283 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update) | 
| Chris@152 | 284 { | 
| Chris@152 | 285     if (m_completion != completion) { | 
| Chris@152 | 286 	m_completion = completion; | 
| Chris@152 | 287 | 
| Chris@152 | 288 	if (completion == 100) { | 
| Chris@152 | 289 | 
| Chris@152 | 290 	    m_notifyOnAdd = true; // henceforth | 
| Chris@152 | 291 	    emit modelChanged(); | 
| Chris@152 | 292 | 
| Chris@152 | 293 	} else if (!m_notifyOnAdd) { | 
| Chris@152 | 294 | 
| Chris@333 | 295 	    if (update && | 
| Chris@333 | 296                 m_sinceLastNotifyMin >= 0 && | 
| Chris@152 | 297 		m_sinceLastNotifyMax >= 0) { | 
| Chris@152 | 298 		emit modelChanged(m_sinceLastNotifyMin, | 
| Chris@152 | 299 				  m_sinceLastNotifyMax + m_resolution); | 
| Chris@152 | 300 		m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; | 
| Chris@152 | 301 	    } else { | 
| Chris@152 | 302 		emit completionChanged(); | 
| Chris@152 | 303 	    } | 
| Chris@152 | 304 	} else { | 
| Chris@152 | 305 	    emit completionChanged(); | 
| Chris@152 | 306 	} | 
| Chris@152 | 307     } | 
| Chris@152 | 308 } | 
| Chris@152 | 309 | 
| Chris@318 | 310 QString | 
| Chris@318 | 311 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter) const | 
| Chris@318 | 312 { | 
| Chris@318 | 313     QString s; | 
| Chris@318 | 314     for (size_t i = 0; i < m_data.size(); ++i) { | 
| Chris@318 | 315         QStringList list; | 
| Chris@533 | 316 	for (size_t j = 0; j < m_data.at(i).size(); ++j) { | 
| Chris@533 | 317             list << QString("%1").arg(m_data.at(i).at(j)); | 
| Chris@318 | 318         } | 
| Chris@318 | 319         s += list.join(delimiter) + "\n"; | 
| Chris@318 | 320     } | 
| Chris@318 | 321     return s; | 
| Chris@318 | 322 } | 
| Chris@318 | 323 | 
| Chris@152 | 324 void | 
| Chris@152 | 325 EditableDenseThreeDimensionalModel::toXml(QTextStream &out, | 
| Chris@314 | 326                                           QString indent, | 
| Chris@314 | 327                                           QString extraAttributes) const | 
| Chris@152 | 328 { | 
| Chris@152 | 329     // For historical reasons we read and write "resolution" as "windowSize" | 
| Chris@152 | 330 | 
| Chris@318 | 331     std::cerr << "EditableDenseThreeDimensionalModel::toXml" << std::endl; | 
| Chris@318 | 332 | 
| Chris@314 | 333     Model::toXml | 
| Chris@314 | 334 	(out, indent, | 
| Chris@314 | 335          QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" %6") | 
| Chris@152 | 336 	 .arg(m_resolution) | 
| Chris@152 | 337 	 .arg(m_yBinCount) | 
| Chris@152 | 338 	 .arg(m_minimum) | 
| Chris@152 | 339 	 .arg(m_maximum) | 
| Chris@152 | 340 	 .arg(getObjectExportId(&m_data)) | 
| Chris@152 | 341 	 .arg(extraAttributes)); | 
| Chris@152 | 342 | 
| Chris@152 | 343     out << indent; | 
| Chris@152 | 344     out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n") | 
| Chris@152 | 345 	.arg(getObjectExportId(&m_data)); | 
| Chris@152 | 346 | 
| Chris@152 | 347     for (size_t i = 0; i < m_binNames.size(); ++i) { | 
| Chris@152 | 348 	if (m_binNames[i] != "") { | 
| Chris@152 | 349 	    out << indent + "  "; | 
| Chris@152 | 350 	    out << QString("<bin number=\"%1\" name=\"%2\"/>\n") | 
| Chris@152 | 351 		.arg(i).arg(m_binNames[i]); | 
| Chris@152 | 352 	} | 
| Chris@152 | 353     } | 
| Chris@152 | 354 | 
| Chris@152 | 355     for (size_t i = 0; i < m_data.size(); ++i) { | 
| Chris@152 | 356 	out << indent + "  "; | 
| Chris@152 | 357 	out << QString("<row n=\"%1\">").arg(i); | 
| Chris@533 | 358 	for (size_t j = 0; j < m_data.at(i).size(); ++j) { | 
| Chris@152 | 359 	    if (j > 0) out << " "; | 
| Chris@533 | 360 	    out << m_data.at(i).at(j); | 
| Chris@152 | 361 	} | 
| Chris@152 | 362 	out << QString("</row>\n"); | 
| Chris@318 | 363         out.flush(); | 
| Chris@152 | 364     } | 
| Chris@152 | 365 | 
| Chris@152 | 366     out << indent + "</dataset>\n"; | 
| Chris@152 | 367 } | 
| Chris@152 | 368 | 
| Chris@152 | 369 |