annotate data/model/WritableWaveFileModel.cpp @ 684:bcca512445f3

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