Chris@407: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@407: Chris@407: /* Chris@407: Sonic Visualiser Chris@407: An audio file viewer and annotation editor. Chris@407: Centre for Digital Music, Queen Mary, University of London. Chris@407: This file copyright 2007 QMUL. Chris@407: Chris@407: This program is free software; you can redistribute it and/or Chris@407: modify it under the terms of the GNU General Public License as Chris@407: published by the Free Software Foundation; either version 2 of the Chris@407: License, or (at your option) any later version. See the file Chris@407: COPYING included with this distribution for more information. Chris@407: */ Chris@407: Chris@1581: #ifndef SV_PATH_MODEL_H Chris@1581: #define SV_PATH_MODEL_H Chris@407: Chris@407: #include "Model.h" Chris@1662: #include "DeferredNotifier.h" Chris@407: #include "base/RealTime.h" Chris@1040: #include "base/BaseTypes.h" Chris@407: Chris@1662: #include "base/XmlExportable.h" Chris@1662: #include "base/RealTime.h" Chris@1662: Chris@407: #include Chris@1662: #include Chris@407: Chris@407: struct PathPoint Chris@407: { Chris@1662: PathPoint(sv_frame_t _frame) : Chris@1662: frame(_frame), mapframe(_frame) { } Chris@1040: PathPoint(sv_frame_t _frame, sv_frame_t _mapframe) : Chris@407: frame(_frame), mapframe(_mapframe) { } Chris@407: Chris@1040: sv_frame_t frame; Chris@1040: sv_frame_t mapframe; Chris@407: Chris@407: void toXml(QTextStream &stream, QString indent = "", Chris@407: QString extraAttributes = "") const { Chris@407: stream << QString("%1\n") Chris@407: .arg(indent).arg(frame).arg(mapframe).arg(extraAttributes); Chris@407: } Chris@407: Chris@1060: QString toDelimitedDataString(QString delimiter, DataExportOptions, Chris@1040: sv_samplerate_t sampleRate) const { Chris@407: QStringList list; Chris@407: list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); Chris@407: list << QString("%1").arg(mapframe); Chris@407: return list.join(delimiter); Chris@407: } Chris@407: Chris@1662: bool operator<(const PathPoint &p2) const { Chris@1662: if (frame != p2.frame) return frame < p2.frame; Chris@1662: return mapframe < p2.mapframe; Chris@1662: } Chris@407: }; Chris@407: Chris@1662: class PathModel : public Model Chris@407: { Chris@407: public: Chris@1662: typedef std::set PointList; Chris@407: Chris@1662: PathModel(sv_samplerate_t sampleRate, Chris@1662: int resolution, Chris@1662: bool notifyOnAdd = true) : Chris@1662: m_sampleRate(sampleRate), Chris@1662: m_resolution(resolution), Chris@1662: m_notifier(this, Chris@1662: notifyOnAdd ? Chris@1662: DeferredNotifier::NOTIFY_ALWAYS : Chris@1662: DeferredNotifier::NOTIFY_DEFERRED), Chris@1662: m_completion(100), Chris@1662: m_start(0), Chris@1662: m_end(0) { Chris@1662: } Chris@1662: Chris@1667: QString getTypeName() const override { return tr("Path"); } cannam@1695: bool isSparse() const override { return true; } Chris@1662: bool isOK() const override { return true; } Chris@1662: Chris@1662: sv_frame_t getStartFrame() const override { Chris@1662: return m_start; Chris@1662: } Chris@1725: sv_frame_t getTrueEndFrame() const override { Chris@1662: return m_end; Chris@1662: } Chris@1662: Chris@1662: sv_samplerate_t getSampleRate() const override { return m_sampleRate; } Chris@1662: int getResolution() const { return m_resolution; } Chris@1662: cannam@1695: int getCompletion() const override { return m_completion; } Chris@1662: Chris@1662: void setCompletion(int completion, bool update = true) { Chris@1662: Chris@1662: { QMutexLocker locker(&m_mutex); Chris@1662: if (m_completion == completion) return; Chris@1662: m_completion = completion; Chris@1662: } Chris@1662: Chris@1662: if (update) { Chris@1662: m_notifier.makeDeferredNotifications(); Chris@1662: } Chris@1662: Chris@1662: emit completionChanged(); Chris@1662: Chris@1662: if (completion == 100) { Chris@1662: // henceforth: Chris@1662: m_notifier.switchMode(DeferredNotifier::NOTIFY_ALWAYS); Chris@1662: emit modelChanged(); Chris@1662: } Chris@407: } Chris@425: Chris@425: /** Chris@1662: * Query methods. Chris@425: */ Chris@1670: int getPointCount() const { Chris@1670: return int(m_points.size()); Chris@1670: } Chris@1662: const PointList &getPoints() const { Chris@1662: return m_points; Chris@1662: } Chris@425: Chris@1662: /** Chris@1662: * Editing methods. Chris@1662: */ Chris@1662: void add(PathPoint p) { Chris@1662: Chris@1662: { QMutexLocker locker(&m_mutex); Chris@1662: m_points.insert(p); Chris@1662: Chris@1662: if (m_start == m_end) { Chris@1662: m_start = p.frame; Chris@1662: m_end = m_start + m_resolution; Chris@1662: } else { Chris@1662: if (p.frame < m_start) { Chris@1662: m_start = p.frame; Chris@1662: } Chris@1662: if (p.frame + m_resolution > m_end) { Chris@1662: m_end = p.frame + m_resolution; Chris@1662: } Chris@1662: } Chris@1662: } Chris@1662: Chris@1662: m_notifier.update(p.frame, m_resolution); Chris@1662: } Chris@1662: Chris@1662: void remove(PathPoint p) { Chris@1662: { QMutexLocker locker(&m_mutex); Chris@1662: m_points.erase(p); Chris@1662: } Chris@1662: Chris@1662: emit modelChangedWithin(p.frame, p.frame + m_resolution); Chris@1662: } Chris@1662: Chris@1662: void clear() { Chris@1662: { QMutexLocker locker(&m_mutex); Chris@1662: m_start = m_end = 0; Chris@1662: m_points.clear(); Chris@1662: } Chris@1662: } Chris@1662: Chris@1662: /** Chris@1662: * XmlExportable methods. Chris@1662: */ Chris@1662: void toXml(QTextStream &out, Chris@1662: QString indent = "", Chris@1662: QString extraAttributes = "") const override { 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 Chris@1677: // aren't in the same id-space as models when re-read Chris@1662: Chris@1662: Model::toXml Chris@1662: (out, Chris@1662: indent, Chris@1662: QString("type=\"sparse\" dimensions=\"2\" resolution=\"%1\" " Chris@1662: "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"path\" %4") Chris@1662: .arg(m_resolution) Chris@1662: .arg("true") // always true after model reaches 100% - Chris@1662: // subsequent points are always notified Chris@1677: .arg(getExportId()) Chris@1662: .arg(extraAttributes)); Chris@1662: Chris@1675: out << indent << QString("\n") Chris@1677: .arg(getExportId()); Chris@1662: Chris@1675: for (PathPoint p: m_points) { Chris@1675: p.toXml(out, indent + " ", ""); Chris@1675: } Chris@1675: Chris@1675: out << indent << "\n"; Chris@1662: } Chris@1679: Chris@1679: QString toDelimitedDataString(QString delimiter, Chris@1679: DataExportOptions, Chris@1679: sv_frame_t startFrame, Chris@1679: sv_frame_t duration) const override { Chris@1679: Chris@1679: QString s; Chris@1679: for (PathPoint p: m_points) { Chris@1679: if (p.frame < startFrame) continue; Chris@1679: if (p.frame >= startFrame + duration) break; Chris@1679: s += QString("%1%2%3\n") Chris@1679: .arg(p.frame) Chris@1679: .arg(delimiter) Chris@1679: .arg(p.mapframe); Chris@1679: } Chris@1679: Chris@1679: return s; Chris@1679: } Chris@1662: Chris@1662: protected: Chris@1662: sv_samplerate_t m_sampleRate; Chris@1662: int m_resolution; Chris@1662: Chris@1662: DeferredNotifier m_notifier; Chris@1662: int m_completion; Chris@1662: Chris@1662: sv_frame_t m_start; Chris@1662: sv_frame_t m_end; Chris@1662: PointList m_points; Chris@1662: Chris@1662: mutable QMutex m_mutex; Chris@407: }; Chris@407: Chris@407: Chris@407: #endif