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"
dev@1434: #include "data/model/Model.h"
dev@1434: #include <QString>
dev@1434: #include <algorithm>
dev@1445: #include <numeric>
dev@1441: 
dev@1438: namespace CSVStreamWriter
dev@1430: {
dev@1430: 
dev@1445: template <class OutStream>
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@1447:     const auto finalFrameOfLastRegion = (*selections.crbegin()).getEndFrame();
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;
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);
dev@1449:             const auto data = model.toDelimitedDataStringSubsetWithOptions(
dev@1445:                 delimiter,
dev@1445:                 options,
dev@1445:                 start,
dev@1445:                 end
dev@1449:             ).trimmed();
dev@1449: 
dev@1449:             if ( data != "" ) {
dev@1449:                 oss << data << (end < finalFrameOfLastRegion ? "\n" : "");
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 <class OutStream>
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);
dev@1445:     return CSVStreamWriter::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 <class OutStream>
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;
dev@1444:     return CSVStreamWriter::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