dev@1430: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ dev@1430: dev@1430: /* dev@1430: Sonic Visualiser dev@1430: An audio file viewer and annotation editor. dev@1430: Centre for Digital Music, Queen Mary, University of London. dev@1438: This file copyright 2017 Queen Mary, University of London. dev@1430: dev@1430: This program is free software; you can redistribute it and/or dev@1430: modify it under the terms of the GNU General Public License as dev@1430: published by the Free Software Foundation; either version 2 of the dev@1430: License, or (at your option) any later version. See the file dev@1430: COPYING included with this distribution for more information. dev@1430: */ dev@1430: cannam@1452: #ifndef SV_CSV_STREAM_WRITER_H cannam@1452: #define SV_CSV_STREAM_WRITER_H dev@1430: dev@1434: #include "base/BaseTypes.h" dev@1434: #include "base/Selection.h" dev@1434: #include "base/ProgressReporter.h" dev@1434: #include "base/DataExportOptions.h" Chris@1833: #include "base/StringBits.h" dev@1434: #include "data/model/Model.h" Chris@1833: dev@1434: #include dev@1434: #include dev@1445: #include dev@1441: dev@1438: namespace CSVStreamWriter dev@1430: { dev@1430: dev@1445: template dev@1445: bool dev@1445: writeInChunks(OutStream& oss, dev@1445: const Model& model, dev@1445: const MultiSelection& regions, dev@1445: ProgressReporter* reporter = nullptr, dev@1445: QString delimiter = ",", dev@1445: DataExportOptions options = DataExportDefaults, dev@1445: const sv_frame_t blockSize = 16384) dev@1445: { dev@1447: const auto selections = regions.getSelections(); dev@1447: if (blockSize <= 0 || selections.empty()) return false; dev@1445: dev@1445: // TODO, some form of checking validity of selections? dev@1445: const auto nFramesToWrite = std::accumulate( dev@1447: selections.begin(), dev@1447: selections.end(), dev@1445: 0, dev@1445: [](sv_frame_t acc, const Selection& current) -> sv_frame_t { dev@1445: return acc + (current.getEndFrame() - current.getStartFrame()); dev@1445: } dev@1445: ); dev@1445: dev@1445: const auto wasCancelled = [&reporter]() { dev@1445: return reporter && reporter->wasCancelled(); dev@1445: }; dev@1445: dev@1445: sv_frame_t nFramesWritten = 0; dev@1445: int previousProgress = 0; Chris@1609: bool started = false; dev@1445: dev@1447: for (const auto& extents : selections) { dev@1445: const auto startFrame = extents.getStartFrame(); dev@1445: const auto endFrame = extents.getEndFrame(); dev@1445: auto readPtr = startFrame; dev@1445: while (readPtr < endFrame) { dev@1445: if (wasCancelled()) return false; dev@1445: dev@1445: const auto start = readPtr; dev@1445: const auto end = std::min(start + blockSize, endFrame); Chris@1833: const auto data = model.toStringExportRows( dev@1445: options, dev@1445: start, Chris@1679: end - start Chris@1833: ); dev@1449: Chris@1833: if (!data.empty()) { Chris@1833: for (const auto &row: data) { Chris@1833: if (started) { Chris@1833: oss << "\n"; Chris@1833: } else { Chris@1833: started = true; Chris@1833: } Chris@1833: oss << StringBits::joinDelimited(row, delimiter); Chris@1609: } dev@1449: } dev@1449: dev@1445: nFramesWritten += end - start; Chris@1454: const int currentProgress = Chris@1454: int(100 * nFramesWritten / nFramesToWrite); dev@1445: const bool hasIncreased = currentProgress > previousProgress; dev@1445: if (hasIncreased) { dev@1445: if (reporter) reporter->setProgress(currentProgress); dev@1445: previousProgress = currentProgress; dev@1445: } dev@1445: readPtr = end; dev@1445: } dev@1445: } dev@1445: return !wasCancelled(); // setProgress could process event loop dev@1445: } dev@1445: dev@1445: template dev@1441: bool dev@1441: writeInChunks(OutStream& oss, dev@1441: const Model& model, dev@1441: const Selection& extents, dev@1441: ProgressReporter* reporter = nullptr, dev@1441: QString delimiter = ",", dev@1441: DataExportOptions options = DataExportDefaults, dev@1445: const sv_frame_t blockSize = 16384) dev@1434: { dev@1441: const auto startFrame = extents.isEmpty() ? dev@1434: model.getStartFrame() : extents.getStartFrame(); dev@1441: const auto endFrame = extents.isEmpty() ? dev@1434: model.getEndFrame() : extents.getEndFrame(); dev@1441: const auto hasValidExtents = startFrame >= 0 && endFrame > startFrame; dev@1441: if (!hasValidExtents) return false; dev@1445: Selection all { dev@1445: startFrame, dev@1445: endFrame dev@1434: }; dev@1445: MultiSelection regions; dev@1445: regions.addSelection(all); Chris@1833: return writeInChunks( dev@1445: oss, dev@1445: model, dev@1445: regions, dev@1445: reporter, dev@1445: delimiter, dev@1445: options, dev@1445: blockSize dev@1445: ); dev@1434: } dev@1434: dev@1434: template dev@1441: bool dev@1441: writeInChunks(OutStream& oss, dev@1441: const Model& model, dev@1441: ProgressReporter* reporter = nullptr, dev@1441: QString delimiter = ",", dev@1441: DataExportOptions options = DataExportDefaults, dev@1441: const sv_frame_t blockSize = 16384) dev@1434: { dev@1434: const Selection empty; Chris@1833: return writeInChunks( dev@1434: oss, dev@1434: model, dev@1434: empty, dev@1434: reporter, dev@1434: delimiter, dev@1434: options, dev@1434: blockSize dev@1434: ); dev@1434: } dev@1438: } // namespace CSVStreamWriter cannam@1452: #endif