annotate data/model/WritableWaveFileModel.cpp @ 1305:9f9f55a8af92 mp3-gapless

Add gapless flag to MP3FileReader, and implement trimming the delay samples from the start (padding is not yet trimmed from end)
author Chris Cannam
date Tue, 29 Nov 2016 11:35:56 +0000
parents e994747fb9dd
children 3aea4f7617bb
rev   line source
Chris@175 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@175 2
Chris@175 3 /*
Chris@175 4 Sonic Visualiser
Chris@175 5 An audio file viewer and annotation editor.
Chris@175 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 QMUL.
Chris@175 8
Chris@175 9 This program is free software; you can redistribute it and/or
Chris@175 10 modify it under the terms of the GNU General Public License as
Chris@175 11 published by the Free Software Foundation; either version 2 of the
Chris@175 12 License, or (at your option) any later version. See the file
Chris@175 13 COPYING included with this distribution for more information.
Chris@175 14 */
Chris@175 15
Chris@175 16 #include "WritableWaveFileModel.h"
Chris@175 17
Chris@1122 18 #include "ReadOnlyWaveFileModel.h"
Chris@1122 19
Chris@175 20 #include "base/TempDirectory.h"
Chris@175 21 #include "base/Exceptions.h"
Chris@175 22
Chris@175 23 #include "fileio/WavFileWriter.h"
Chris@175 24 #include "fileio/WavFileReader.h"
Chris@175 25
Chris@175 26 #include <QDir>
Chris@314 27 #include <QTextStream>
Chris@175 28
Chris@175 29 #include <cassert>
Chris@175 30 #include <iostream>
Chris@723 31 #include <stdint.h>
Chris@175 32
Chris@1096 33 using namespace std;
Chris@1096 34
Chris@1133 35 const int WritableWaveFileModel::PROPORTION_UNKNOWN = -1;
Chris@1133 36
Chris@258 37 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
Chris@187 38
Chris@1040 39 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate,
Chris@929 40 int channels,
Chris@175 41 QString path) :
Chris@175 42 m_model(0),
Chris@175 43 m_writer(0),
Chris@175 44 m_reader(0),
Chris@175 45 m_sampleRate(sampleRate),
Chris@175 46 m_channels(channels),
Chris@188 47 m_frameCount(0),
Chris@300 48 m_startFrame(0),
Chris@1133 49 m_proportion(PROPORTION_UNKNOWN)
Chris@175 50 {
Chris@175 51 if (path.isEmpty()) {
Chris@175 52 try {
Chris@175 53 QDir dir(TempDirectory::getInstance()->getPath());
Chris@175 54 path = dir.filePath(QString("written_%1.wav")
Chris@175 55 .arg((intptr_t)this));
Chris@175 56 } catch (DirectoryCreationFailed f) {
Chris@843 57 cerr << "WritableWaveFileModel: Failed to create temporary directory" << endl;
Chris@175 58 return;
Chris@175 59 }
Chris@175 60 }
Chris@175 61
Chris@684 62 // Write directly to the target file, so that we can do
Chris@684 63 // incremental writes and concurrent reads
Chris@684 64 m_writer = new WavFileWriter(path, sampleRate, channels,
Chris@684 65 WavFileWriter::WriteToTarget);
Chris@175 66 if (!m_writer->isOK()) {
Chris@843 67 cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl;
Chris@175 68 delete m_writer;
Chris@175 69 m_writer = 0;
Chris@175 70 return;
Chris@175 71 }
Chris@187 72
Chris@317 73 FileSource source(m_writer->getPath());
Chris@316 74
Chris@316 75 m_reader = new WavFileReader(source, true);
Chris@290 76 if (!m_reader->getError().isEmpty()) {
Chris@843 77 cerr << "WritableWaveFileModel: Error in creating wave file reader" << endl;
Chris@187 78 delete m_reader;
Chris@187 79 m_reader = 0;
Chris@187 80 return;
Chris@187 81 }
Chris@187 82
Chris@1122 83 m_model = new ReadOnlyWaveFileModel(source, m_reader);
Chris@187 84 if (!m_model->isOK()) {
Chris@843 85 cerr << "WritableWaveFileModel: Error in creating wave file model" << endl;
Chris@187 86 delete m_model;
Chris@187 87 m_model = 0;
Chris@187 88 delete m_reader;
Chris@187 89 m_reader = 0;
Chris@187 90 return;
Chris@187 91 }
Chris@300 92 m_model->setStartFrame(m_startFrame);
Chris@187 93
Chris@258 94 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@1038 95 connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
Chris@1038 96 this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)));
Chris@175 97 }
Chris@175 98
Chris@175 99 WritableWaveFileModel::~WritableWaveFileModel()
Chris@175 100 {
Chris@175 101 delete m_model;
Chris@175 102 delete m_writer;
Chris@175 103 delete m_reader;
Chris@175 104 }
Chris@175 105
Chris@300 106 void
Chris@1038 107 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame)
Chris@300 108 {
Chris@300 109 m_startFrame = startFrame;
Chris@300 110 if (m_model) m_model->setStartFrame(startFrame);
Chris@300 111 }
Chris@300 112
Chris@175 113 bool
Chris@1038 114 WritableWaveFileModel::addSamples(float **samples, sv_frame_t count)
Chris@175 115 {
Chris@175 116 if (!m_writer) return false;
Chris@175 117
Chris@258 118 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
Chris@690 119 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl;
Chris@258 120 #endif
Chris@258 121
Chris@175 122 if (!m_writer->writeSamples(samples, count)) {
Chris@843 123 cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl;
Chris@175 124 return false;
Chris@175 125 }
Chris@175 126
Chris@175 127 m_frameCount += count;
Chris@175 128
Chris@187 129 static int updateCounter = 0;
Chris@175 130
Chris@187 131 if (m_reader && m_reader->getChannelCount() == 0) {
Chris@258 132 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
Chris@690 133 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << endl;
Chris@258 134 #endif
Chris@187 135 m_reader->updateFrameCount();
Chris@187 136 } else if (++updateCounter == 100) {
Chris@258 137 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
Chris@690 138 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << endl;
Chris@258 139 #endif
Chris@175 140 if (m_reader) m_reader->updateFrameCount();
Chris@175 141 updateCounter = 0;
Chris@175 142 }
Chris@175 143
Chris@175 144 return true;
Chris@175 145 }
Chris@175 146
Chris@175 147 bool
Chris@175 148 WritableWaveFileModel::isOK() const
Chris@175 149 {
Chris@187 150 bool ok = (m_writer && m_writer->isOK());
Chris@690 151 // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl;
Chris@175 152 return ok;
Chris@175 153 }
Chris@175 154
Chris@175 155 bool
Chris@175 156 WritableWaveFileModel::isReady(int *completion) const
Chris@175 157 {
Chris@1133 158 int c = getCompletion();
Chris@1133 159 if (completion) *completion = c;
Chris@1133 160 if (!isOK()) return false;
Chris@1133 161 return (c == 100);
Chris@188 162 }
Chris@188 163
Chris@188 164 void
Chris@1133 165 WritableWaveFileModel::setWriteProportion(int proportion)
Chris@188 166 {
Chris@1133 167 m_proportion = proportion;
Chris@1133 168 }
Chris@1133 169
Chris@1133 170 int
Chris@1133 171 WritableWaveFileModel::getWriteProportion() const
Chris@1133 172 {
Chris@1133 173 return m_proportion;
Chris@1133 174 }
Chris@1133 175
Chris@1133 176 void
Chris@1133 177 WritableWaveFileModel::writeComplete()
Chris@1133 178 {
Chris@1133 179 if (m_reader) m_reader->updateDone();
Chris@1133 180 m_proportion = 100;
Chris@1133 181 emit modelChanged();
Chris@175 182 }
Chris@175 183
Chris@1038 184 sv_frame_t
Chris@175 185 WritableWaveFileModel::getFrameCount() const
Chris@175 186 {
Chris@690 187 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl;
Chris@175 188 return m_frameCount;
Chris@175 189 }
Chris@175 190
Chris@1096 191 vector<float>
Chris@1096 192 WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
Chris@175 193 {
Chris@1096 194 if (!m_model || m_model->getChannelCount() == 0) return {};
Chris@1096 195 return m_model->getData(channel, start, count);
Chris@175 196 }
Chris@175 197
Chris@1096 198 vector<vector<float>>
Chris@1086 199 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
Chris@1096 200 sv_frame_t start, sv_frame_t count) const
Chris@175 201 {
Chris@1096 202 if (!m_model || m_model->getChannelCount() == 0) return {};
Chris@1096 203 return m_model->getMultiChannelData(fromchannel, tochannel, start, count);
Chris@363 204 }
Chris@363 205
Chris@929 206 int
Chris@929 207 WritableWaveFileModel::getSummaryBlockSize(int desired) const
Chris@377 208 {
Chris@377 209 if (!m_model) return desired;
Chris@377 210 return m_model->getSummaryBlockSize(desired);
Chris@377 211 }
Chris@377 212
Chris@225 213 void
Chris@1038 214 WritableWaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t count,
Chris@300 215 RangeBlock &ranges,
Chris@929 216 int &blockSize) const
Chris@175 217 {
Chris@225 218 ranges.clear();
Chris@225 219 if (!m_model || m_model->getChannelCount() == 0) return;
Chris@300 220 m_model->getSummaries(channel, start, count, ranges, blockSize);
Chris@175 221 }
Chris@175 222
Chris@175 223 WritableWaveFileModel::Range
Chris@1038 224 WritableWaveFileModel::getSummary(int channel, sv_frame_t start, sv_frame_t count) const
Chris@175 225 {
Chris@187 226 if (!m_model || m_model->getChannelCount() == 0) return Range();
Chris@300 227 return m_model->getSummary(channel, start, count);
Chris@175 228 }
Chris@175 229
Chris@175 230 void
Chris@175 231 WritableWaveFileModel::toXml(QTextStream &out,
Chris@175 232 QString indent,
Chris@175 233 QString extraAttributes) const
Chris@175 234 {
Chris@1123 235 // The assumption here is that the underlying wave file has
Chris@1123 236 // already been saved somewhere (its location is available through
Chris@1123 237 // getLocation()) and that the code that uses this class is
Chris@1123 238 // dealing with the problem of making sure it remains available.
Chris@1123 239 // We just write this out as if it were a normal wave file.
Chris@187 240
Chris@188 241 Model::toXml
Chris@188 242 (out, indent,
Chris@1123 243 QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2")
Chris@603 244 .arg(encodeEntities(m_writer->getPath()))
Chris@1123 245 .arg(extraAttributes));
Chris@175 246 }
Chris@175 247