Chris@152: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@152: Chris@152: /* Chris@152: Sonic Visualiser Chris@152: An audio file viewer and annotation editor. Chris@152: Centre for Digital Music, Queen Mary, University of London. Chris@202: This file copyright 2006 Chris Cannam and QMUL. Chris@152: Chris@152: This program is free software; you can redistribute it and/or Chris@152: modify it under the terms of the GNU General Public License as Chris@152: published by the Free Software Foundation; either version 2 of the Chris@152: License, or (at your option) any later version. See the file Chris@152: COPYING included with this distribution for more information. Chris@152: */ Chris@152: Chris@152: #include "EditableDenseThreeDimensionalModel.h" Chris@152: Chris@478: #include "base/LogRange.h" Chris@478: Chris@152: #include Chris@387: #include Chris@1777: #include Chris@387: Chris@181: #include Chris@181: Chris@256: #include Chris@534: #include Chris@256: Chris@1044: using std::vector; Chris@1044: Chris@607: #include "system/System.h" Chris@607: Chris@1040: EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(sv_samplerate_t sampleRate, Chris@929: int resolution, Chris@929: int yBinCount, Chris@152: bool notifyOnAdd) : Chris@611: m_startFrame(0), Chris@152: m_sampleRate(sampleRate), Chris@152: m_resolution(resolution), Chris@152: m_yBinCount(yBinCount), Chris@152: m_minimum(0.0), Chris@152: m_maximum(0.0), Chris@256: m_haveExtents(false), Chris@152: m_notifyOnAdd(notifyOnAdd), Chris@152: m_sinceLastNotifyMin(-1), Chris@152: m_sinceLastNotifyMax(-1), Chris@152: m_completion(100) Chris@152: { Chris@152: } Chris@152: Chris@152: bool Chris@152: EditableDenseThreeDimensionalModel::isOK() const Chris@152: { Chris@152: return true; Chris@152: } Chris@152: Chris@1701: bool Chris@1701: EditableDenseThreeDimensionalModel::isReady(int *completion) const Chris@1701: { Chris@1701: if (completion) *completion = getCompletion(); Chris@1701: return true; Chris@1701: } Chris@1701: Chris@1040: sv_samplerate_t Chris@152: EditableDenseThreeDimensionalModel::getSampleRate() const Chris@152: { Chris@152: return m_sampleRate; Chris@152: } Chris@152: Chris@1038: sv_frame_t Chris@152: EditableDenseThreeDimensionalModel::getStartFrame() const Chris@152: { Chris@611: return m_startFrame; Chris@611: } Chris@611: Chris@611: void Chris@1038: EditableDenseThreeDimensionalModel::setStartFrame(sv_frame_t f) Chris@611: { Chris@611: m_startFrame = f; Chris@152: } Chris@152: Chris@1038: sv_frame_t Chris@1725: EditableDenseThreeDimensionalModel::getTrueEndFrame() const Chris@152: { Chris@152: return m_resolution * m_data.size() + (m_resolution - 1); Chris@152: } Chris@152: Chris@929: int Chris@152: EditableDenseThreeDimensionalModel::getResolution() const Chris@152: { Chris@152: return m_resolution; Chris@152: } Chris@152: Chris@152: void Chris@929: EditableDenseThreeDimensionalModel::setResolution(int sz) Chris@152: { Chris@152: m_resolution = sz; Chris@152: } Chris@152: Chris@929: int Chris@182: EditableDenseThreeDimensionalModel::getWidth() const Chris@182: { Chris@1154: return int(m_data.size()); Chris@182: } Chris@182: Chris@929: int Chris@182: EditableDenseThreeDimensionalModel::getHeight() const Chris@152: { Chris@152: return m_yBinCount; Chris@152: } Chris@152: Chris@152: void Chris@929: EditableDenseThreeDimensionalModel::setHeight(int sz) Chris@152: { Chris@152: m_yBinCount = sz; Chris@152: } Chris@152: Chris@152: float Chris@152: EditableDenseThreeDimensionalModel::getMinimumLevel() const Chris@152: { Chris@152: return m_minimum; Chris@152: } Chris@152: Chris@152: void Chris@152: EditableDenseThreeDimensionalModel::setMinimumLevel(float level) Chris@152: { Chris@152: m_minimum = level; Chris@152: } Chris@152: Chris@152: float Chris@152: EditableDenseThreeDimensionalModel::getMaximumLevel() const Chris@152: { Chris@152: return m_maximum; Chris@152: } Chris@152: Chris@152: void Chris@152: EditableDenseThreeDimensionalModel::setMaximumLevel(float level) Chris@152: { Chris@152: m_maximum = level; Chris@152: } Chris@152: Chris@533: EditableDenseThreeDimensionalModel::Column Chris@929: EditableDenseThreeDimensionalModel::getColumn(int index) const Chris@152: { Chris@1777: QMutexLocker locker(&m_mutex); Chris@1777: if (!in_range_for(m_data, index)) { Chris@1777: return {}; Chris@152: } Chris@1777: Column c = m_data.at(index); Chris@1777: if (int(c.size()) == m_yBinCount) { Chris@1777: return c; Chris@1777: } else { Chris@1252: Column cc(c); Chris@1252: cc.resize(m_yBinCount, 0.0); Chris@1252: return cc; Chris@1252: } Chris@1252: } Chris@1252: Chris@1777: float Chris@1777: EditableDenseThreeDimensionalModel::getValueAt(int index, int n) const Chris@534: { Chris@1777: QMutexLocker locker(&m_mutex); Chris@1777: if (!in_range_for(m_data, index)) { Chris@1777: return m_minimum; Chris@534: } Chris@1777: const Column &c = m_data.at(index); Chris@1777: if (!in_range_for(c, n)) { Chris@1777: return m_minimum; Chris@534: } Chris@1777: return c.at(n); Chris@152: } Chris@152: Chris@152: void Chris@929: EditableDenseThreeDimensionalModel::setColumn(int index, Chris@182: const Column &values) Chris@152: { Chris@152: bool allChange = false; Chris@1110: sv_frame_t windowStart = index; Chris@182: windowStart *= m_resolution; Chris@182: Chris@1777: { Chris@1777: QMutexLocker locker(&m_mutex); Chris@1777: Chris@1777: while (index >= int(m_data.size())) { Chris@1777: m_data.push_back(Column()); Chris@1777: } Chris@1777: Chris@1777: for (int i = 0; in_range_for(values, i); ++i) { Chris@1777: float value = values[i]; Chris@1777: if (ISNAN(value) || ISINF(value)) { Chris@1777: continue; Chris@1777: } Chris@1777: if (!m_haveExtents || value < m_minimum) { Chris@1777: m_minimum = value; Chris@1777: allChange = true; Chris@1777: } Chris@1777: if (!m_haveExtents || value > m_maximum) { Chris@1777: m_maximum = value; Chris@1777: allChange = true; Chris@1777: } Chris@1777: m_haveExtents = true; Chris@1777: } Chris@1777: Chris@1777: m_data[index] = values; Chris@1777: Chris@1777: if (allChange) { Chris@1777: m_sinceLastNotifyMin = -1; Chris@1777: m_sinceLastNotifyMax = -1; Chris@1777: } else { Chris@1777: if (m_sinceLastNotifyMin == -1 || Chris@1777: windowStart < m_sinceLastNotifyMin) { Chris@1777: m_sinceLastNotifyMin = windowStart; Chris@1777: } Chris@1777: if (m_sinceLastNotifyMax == -1 || Chris@1777: windowStart > m_sinceLastNotifyMax) { Chris@1777: m_sinceLastNotifyMax = windowStart; Chris@1777: } Chris@1777: } Chris@1777: } Chris@1777: Chris@152: if (m_notifyOnAdd) { Chris@1429: if (allChange) { Chris@1752: emit modelChanged(getId()); Chris@1429: } else { Chris@1752: emit modelChangedWithin(getId(), Chris@1752: windowStart, windowStart + m_resolution); Chris@1429: } Chris@152: } else { Chris@1429: if (allChange) { Chris@1752: emit modelChanged(getId()); Chris@1429: } Chris@152: } Chris@152: } Chris@152: Chris@152: QString Chris@929: EditableDenseThreeDimensionalModel::getBinName(int n) const Chris@152: { Chris@939: if (n >= 0 && (int)m_binNames.size() > n) return m_binNames[n]; Chris@152: else return ""; Chris@152: } Chris@152: Chris@152: void Chris@929: EditableDenseThreeDimensionalModel::setBinName(int n, QString name) Chris@152: { Chris@929: while ((int)m_binNames.size() <= n) m_binNames.push_back(""); Chris@152: m_binNames[n] = name; Chris@1752: emit modelChanged(getId()); Chris@152: } Chris@152: Chris@152: void Chris@152: EditableDenseThreeDimensionalModel::setBinNames(std::vector names) Chris@152: { Chris@152: m_binNames = names; Chris@1752: emit modelChanged(getId()); Chris@152: } Chris@152: Chris@478: bool Chris@886: EditableDenseThreeDimensionalModel::hasBinValues() const Chris@886: { Chris@886: return !m_binValues.empty(); Chris@886: } Chris@886: Chris@886: float Chris@929: EditableDenseThreeDimensionalModel::getBinValue(int n) const Chris@886: { Chris@929: if (n < (int)m_binValues.size()) return m_binValues[n]; Chris@886: else return 0.f; Chris@886: } Chris@886: Chris@886: void Chris@886: EditableDenseThreeDimensionalModel::setBinValues(std::vector values) Chris@886: { Chris@886: m_binValues = values; Chris@886: } Chris@886: Chris@886: QString Chris@886: EditableDenseThreeDimensionalModel::getBinValueUnit() const Chris@886: { Chris@886: return m_binValueUnit; Chris@886: } Chris@886: Chris@886: void Chris@886: EditableDenseThreeDimensionalModel::setBinValueUnit(QString unit) Chris@886: { Chris@886: m_binValueUnit = unit; Chris@886: } Chris@886: Chris@886: bool Chris@478: EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const Chris@478: { Chris@1777: QMutexLocker locker(&m_mutex); Chris@534: Chris@1044: vector sample; Chris@1044: vector n; Chris@478: Chris@478: for (int i = 0; i < 10; ++i) { Chris@929: int index = i * 10; Chris@1154: if (in_range_for(m_data, index)) { Chris@533: const Column &c = m_data.at(index); Chris@1154: while (c.size() > sample.size()) { Chris@1044: sample.push_back(0.0); Chris@478: n.push_back(0); Chris@478: } Chris@1154: for (int j = 0; in_range_for(c, j); ++j) { Chris@533: sample[j] += c.at(j); Chris@478: ++n[j]; Chris@478: } Chris@478: } Chris@478: } Chris@478: Chris@478: if (sample.empty()) return false; Chris@1044: for (decltype(sample)::size_type j = 0; j < sample.size(); ++j) { Chris@1044: if (n[j]) sample[j] /= n[j]; Chris@478: } Chris@478: Chris@1392: return LogRange::shouldUseLogScale(sample); Chris@478: } Chris@478: Chris@152: void Chris@333: EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update) Chris@152: { Chris@152: if (m_completion != completion) { Chris@1429: m_completion = completion; Chris@152: Chris@1429: if (completion == 100) { Chris@152: Chris@1429: m_notifyOnAdd = true; // henceforth Chris@1752: emit modelChanged(getId()); Chris@152: Chris@1429: } else if (!m_notifyOnAdd) { Chris@152: Chris@1429: if (update && Chris@333: m_sinceLastNotifyMin >= 0 && Chris@1429: m_sinceLastNotifyMax >= 0) { Chris@1752: emit modelChangedWithin(getId(), Chris@1752: m_sinceLastNotifyMin, Chris@931: m_sinceLastNotifyMax + m_resolution); Chris@1429: m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; Chris@1429: } else { Chris@1752: emit completionChanged(getId()); Chris@1429: } Chris@1429: } else { Chris@1752: emit completionChanged(getId()); Chris@1429: } Chris@152: } Chris@152: } Chris@152: Chris@1701: int Chris@1701: EditableDenseThreeDimensionalModel::getCompletion() const Chris@1701: { Chris@1701: return m_completion; Chris@1701: } Chris@1701: Chris@318: QString Chris@1679: EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter, Chris@1679: DataExportOptions, Chris@1679: sv_frame_t startFrame, Chris@1679: sv_frame_t duration) const Chris@838: { Chris@1777: QMutexLocker locker(&m_mutex); Chris@838: QString s; Chris@1154: for (int i = 0; in_range_for(m_data, i); ++i) { Chris@1038: sv_frame_t fr = m_startFrame + i * m_resolution; Chris@1679: if (fr >= startFrame && fr < startFrame + duration) { Chris@838: QStringList list; Chris@1154: for (int j = 0; in_range_for(m_data.at(i), j); ++j) { Chris@838: list << QString("%1").arg(m_data.at(i).at(j)); Chris@838: } Chris@838: s += list.join(delimiter) + "\n"; Chris@838: } Chris@838: } Chris@838: return s; Chris@838: } Chris@838: Chris@152: void Chris@152: EditableDenseThreeDimensionalModel::toXml(QTextStream &out, Chris@314: QString indent, Chris@314: QString extraAttributes) const Chris@152: { Chris@1777: QMutexLocker locker(&m_mutex); Chris@534: Chris@1677: // For historical reasons we read and write "resolution" as "windowSize". Chris@1677: Chris@1677: // Our dataset doesn't have its own export ID, we just use Chris@1677: // ours. Actually any model could do that, since datasets aren't Chris@1677: // in the same id-space as models when re-read Chris@152: Chris@690: SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl; Chris@318: Chris@314: Model::toXml Chris@1429: (out, indent, Chris@611: QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7") Chris@1429: .arg(m_resolution) Chris@1429: .arg(m_yBinCount) Chris@1429: .arg(m_minimum) Chris@1429: .arg(m_maximum) Chris@1677: .arg(getExportId()) Chris@611: .arg(m_startFrame) Chris@1429: .arg(extraAttributes)); Chris@152: Chris@152: out << indent; Chris@152: out << QString("\n") Chris@1677: .arg(getExportId()); Chris@152: Chris@1777: for (int i = 0; in_range_for(m_binNames, i); ++i) { Chris@1429: if (m_binNames[i] != "") { Chris@1429: out << indent + " "; Chris@1429: out << QString("\n") Chris@1429: .arg(i).arg(m_binNames[i]); Chris@1429: } Chris@152: } Chris@152: Chris@1777: for (int i = 0; in_range_for(m_data, i); ++i) { Chris@1777: Column c = getColumn(i); Chris@1429: out << indent + " "; Chris@1429: out << QString("").arg(i); Chris@1777: for (int j = 0; in_range_for(c, j); ++j) { Chris@1429: if (j > 0) out << " "; Chris@1777: out << c.at(j); Chris@1429: } Chris@1429: out << QString("\n"); Chris@318: out.flush(); Chris@152: } Chris@152: Chris@152: out << indent + "\n"; Chris@152: } Chris@152: Chris@152: