annotate data/model/WritableWaveFileModel.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents cd42620e3f40
children cc27f35aa75c
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@175 18 #include "base/TempDirectory.h"
Chris@175 19 #include "base/Exceptions.h"
Chris@175 20
Chris@175 21 #include "fileio/WavFileWriter.h"
Chris@175 22 #include "fileio/WavFileReader.h"
Chris@175 23
Chris@175 24 #include <QDir>
Chris@314 25 #include <QTextStream>
Chris@175 26
Chris@175 27 #include <cassert>
Chris@175 28 #include <iostream>
Chris@723 29 #include <stdint.h>
Chris@175 30
Chris@258 31 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
Chris@187 32
Chris@929 33 WritableWaveFileModel::WritableWaveFileModel(int sampleRate,
Chris@929 34 int channels,
Chris@175 35 QString path) :
Chris@175 36 m_model(0),
Chris@175 37 m_writer(0),
Chris@175 38 m_reader(0),
Chris@175 39 m_sampleRate(sampleRate),
Chris@175 40 m_channels(channels),
Chris@188 41 m_frameCount(0),
Chris@300 42 m_startFrame(0),
Chris@188 43 m_completion(0)
Chris@175 44 {
Chris@175 45 if (path.isEmpty()) {
Chris@175 46 try {
Chris@175 47 QDir dir(TempDirectory::getInstance()->getPath());
Chris@175 48 path = dir.filePath(QString("written_%1.wav")
Chris@175 49 .arg((intptr_t)this));
Chris@175 50 } catch (DirectoryCreationFailed f) {
Chris@843 51 cerr << "WritableWaveFileModel: Failed to create temporary directory" << endl;
Chris@175 52 return;
Chris@175 53 }
Chris@175 54 }
Chris@175 55
Chris@684 56 // Write directly to the target file, so that we can do
Chris@684 57 // incremental writes and concurrent reads
Chris@684 58 m_writer = new WavFileWriter(path, sampleRate, channels,
Chris@684 59 WavFileWriter::WriteToTarget);
Chris@175 60 if (!m_writer->isOK()) {
Chris@843 61 cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl;
Chris@175 62 delete m_writer;
Chris@175 63 m_writer = 0;
Chris@175 64 return;
Chris@175 65 }
Chris@187 66
Chris@317 67 FileSource source(m_writer->getPath());
Chris@316 68
Chris@316 69 m_reader = new WavFileReader(source, true);
Chris@290 70 if (!m_reader->getError().isEmpty()) {
Chris@843 71 cerr << "WritableWaveFileModel: Error in creating wave file reader" << endl;
Chris@187 72 delete m_reader;
Chris@187 73 m_reader = 0;
Chris@187 74 return;
Chris@187 75 }
Chris@187 76
Chris@316 77 m_model = new WaveFileModel(source, m_reader);
Chris@187 78 if (!m_model->isOK()) {
Chris@843 79 cerr << "WritableWaveFileModel: Error in creating wave file model" << endl;
Chris@187 80 delete m_model;
Chris@187 81 m_model = 0;
Chris@187 82 delete m_reader;
Chris@187 83 m_reader = 0;
Chris@187 84 return;
Chris@187 85 }
Chris@300 86 m_model->setStartFrame(m_startFrame);
Chris@187 87
Chris@258 88 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@947 89 connect(m_model, SIGNAL(modelChangedWithin(int, int)),
Chris@947 90 this, SIGNAL(modelChangedWithin(int, int)));
Chris@175 91 }
Chris@175 92
Chris@175 93 WritableWaveFileModel::~WritableWaveFileModel()
Chris@175 94 {
Chris@175 95 delete m_model;
Chris@175 96 delete m_writer;
Chris@175 97 delete m_reader;
Chris@175 98 }
Chris@175 99
Chris@300 100 void
Chris@929 101 WritableWaveFileModel::setStartFrame(int startFrame)
Chris@300 102 {
Chris@300 103 m_startFrame = startFrame;
Chris@300 104 if (m_model) m_model->setStartFrame(startFrame);
Chris@300 105 }
Chris@300 106
Chris@175 107 bool
Chris@929 108 WritableWaveFileModel::addSamples(float **samples, int count)
Chris@175 109 {
Chris@175 110 if (!m_writer) return false;
Chris@175 111
Chris@258 112 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
Chris@690 113 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl;
Chris@258 114 #endif
Chris@258 115
Chris@175 116 if (!m_writer->writeSamples(samples, count)) {
Chris@843 117 cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl;
Chris@175 118 return false;
Chris@175 119 }
Chris@175 120
Chris@175 121 m_frameCount += count;
Chris@175 122
Chris@187 123 static int updateCounter = 0;
Chris@175 124
Chris@187 125 if (m_reader && m_reader->getChannelCount() == 0) {
Chris@258 126 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
Chris@690 127 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << endl;
Chris@258 128 #endif
Chris@187 129 m_reader->updateFrameCount();
Chris@187 130 } else if (++updateCounter == 100) {
Chris@258 131 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
Chris@690 132 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << endl;
Chris@258 133 #endif
Chris@175 134 if (m_reader) m_reader->updateFrameCount();
Chris@175 135 updateCounter = 0;
Chris@175 136 }
Chris@175 137
Chris@175 138 return true;
Chris@175 139 }
Chris@175 140
Chris@175 141 bool
Chris@175 142 WritableWaveFileModel::isOK() const
Chris@175 143 {
Chris@187 144 bool ok = (m_writer && m_writer->isOK());
Chris@690 145 // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl;
Chris@175 146 return ok;
Chris@175 147 }
Chris@175 148
Chris@175 149 bool
Chris@175 150 WritableWaveFileModel::isReady(int *completion) const
Chris@175 151 {
Chris@188 152 if (completion) *completion = m_completion;
Chris@188 153 return (m_completion == 100);
Chris@188 154 }
Chris@188 155
Chris@188 156 void
Chris@188 157 WritableWaveFileModel::setCompletion(int completion)
Chris@188 158 {
Chris@188 159 m_completion = completion;
Chris@188 160 if (completion == 100) {
Chris@188 161 if (m_reader) m_reader->updateDone();
Chris@188 162 }
Chris@175 163 }
Chris@175 164
Chris@929 165 int
Chris@175 166 WritableWaveFileModel::getFrameCount() const
Chris@175 167 {
Chris@690 168 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl;
Chris@175 169 return m_frameCount;
Chris@175 170 }
Chris@175 171
Chris@175 172 Model *
Chris@175 173 WritableWaveFileModel::clone() const
Chris@175 174 {
Chris@175 175 assert(0); //!!!
Chris@188 176 return 0;
Chris@175 177 }
Chris@175 178
Chris@929 179 int
Chris@929 180 WritableWaveFileModel::getData(int channel, int start, int count,
Chris@300 181 float *buffer) const
Chris@175 182 {
Chris@187 183 if (!m_model || m_model->getChannelCount() == 0) return 0;
Chris@300 184 return m_model->getData(channel, start, count, buffer);
Chris@175 185 }
Chris@175 186
Chris@929 187 int
Chris@929 188 WritableWaveFileModel::getData(int channel, int start, int count,
Chris@300 189 double *buffer) const
Chris@175 190 {
Chris@187 191 if (!m_model || m_model->getChannelCount() == 0) return 0;
Chris@300 192 return m_model->getData(channel, start, count, buffer);
Chris@175 193 }
Chris@175 194
Chris@929 195 int
Chris@929 196 WritableWaveFileModel::getData(int fromchannel, int tochannel,
Chris@929 197 int start, int count,
Chris@363 198 float **buffers) const
Chris@363 199 {
Chris@363 200 if (!m_model || m_model->getChannelCount() == 0) return 0;
Chris@363 201 return m_model->getData(fromchannel, tochannel, start, count, buffers);
Chris@363 202 }
Chris@363 203
Chris@929 204 int
Chris@929 205 WritableWaveFileModel::getSummaryBlockSize(int desired) const
Chris@377 206 {
Chris@377 207 if (!m_model) return desired;
Chris@377 208 return m_model->getSummaryBlockSize(desired);
Chris@377 209 }
Chris@377 210
Chris@225 211 void
Chris@929 212 WritableWaveFileModel::getSummaries(int channel, int start, int count,
Chris@300 213 RangeBlock &ranges,
Chris@929 214 int &blockSize) const
Chris@175 215 {
Chris@225 216 ranges.clear();
Chris@225 217 if (!m_model || m_model->getChannelCount() == 0) return;
Chris@300 218 m_model->getSummaries(channel, start, count, ranges, blockSize);
Chris@175 219 }
Chris@175 220
Chris@175 221 WritableWaveFileModel::Range
Chris@929 222 WritableWaveFileModel::getSummary(int channel, int start, int count) const
Chris@175 223 {
Chris@187 224 if (!m_model || m_model->getChannelCount() == 0) return Range();
Chris@300 225 return m_model->getSummary(channel, start, count);
Chris@175 226 }
Chris@175 227
Chris@175 228 void
Chris@175 229 WritableWaveFileModel::toXml(QTextStream &out,
Chris@175 230 QString indent,
Chris@175 231 QString extraAttributes) const
Chris@175 232 {
Chris@188 233 // We don't actually write the data to XML. We just write a brief
Chris@188 234 // description of the model. Any code that uses this class is
Chris@188 235 // going to need to be aware that it will have to make separate
Chris@188 236 // arrangements for the audio file itself.
Chris@187 237
Chris@188 238 Model::toXml
Chris@188 239 (out, indent,
Chris@188 240 QString("type=\"writablewavefile\" file=\"%1\" channels=\"%2\" %3")
Chris@603 241 .arg(encodeEntities(m_writer->getPath()))
Chris@603 242 .arg(m_model->getChannelCount()).arg(extraAttributes));
Chris@175 243 }
Chris@175 244