Chris@175: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@175: Chris@175: /* Chris@175: Sonic Visualiser Chris@175: An audio file viewer and annotation editor. Chris@175: Centre for Digital Music, Queen Mary, University of London. Chris@202: This file copyright 2006 QMUL. Chris@175: Chris@175: This program is free software; you can redistribute it and/or Chris@175: modify it under the terms of the GNU General Public License as Chris@175: published by the Free Software Foundation; either version 2 of the Chris@175: License, or (at your option) any later version. See the file Chris@175: COPYING included with this distribution for more information. Chris@175: */ Chris@175: Chris@175: #include "WritableWaveFileModel.h" Chris@175: Chris@175: #include "base/TempDirectory.h" Chris@175: #include "base/Exceptions.h" Chris@175: Chris@175: #include "fileio/WavFileWriter.h" Chris@175: #include "fileio/WavFileReader.h" Chris@175: Chris@175: #include Chris@314: #include Chris@175: Chris@175: #include Chris@175: #include Chris@723: #include Chris@175: Chris@258: //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1 Chris@187: Chris@929: WritableWaveFileModel::WritableWaveFileModel(int sampleRate, Chris@929: int channels, Chris@175: QString path) : Chris@175: m_model(0), Chris@175: m_writer(0), Chris@175: m_reader(0), Chris@175: m_sampleRate(sampleRate), Chris@175: m_channels(channels), Chris@188: m_frameCount(0), Chris@300: m_startFrame(0), Chris@188: m_completion(0) Chris@175: { Chris@175: if (path.isEmpty()) { Chris@175: try { Chris@175: QDir dir(TempDirectory::getInstance()->getPath()); Chris@175: path = dir.filePath(QString("written_%1.wav") Chris@175: .arg((intptr_t)this)); Chris@175: } catch (DirectoryCreationFailed f) { Chris@843: cerr << "WritableWaveFileModel: Failed to create temporary directory" << endl; Chris@175: return; Chris@175: } Chris@175: } Chris@175: Chris@684: // Write directly to the target file, so that we can do Chris@684: // incremental writes and concurrent reads Chris@684: m_writer = new WavFileWriter(path, sampleRate, channels, Chris@684: WavFileWriter::WriteToTarget); Chris@175: if (!m_writer->isOK()) { Chris@843: cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl; Chris@175: delete m_writer; Chris@175: m_writer = 0; Chris@175: return; Chris@175: } Chris@187: Chris@317: FileSource source(m_writer->getPath()); Chris@316: Chris@316: m_reader = new WavFileReader(source, true); Chris@290: if (!m_reader->getError().isEmpty()) { Chris@843: cerr << "WritableWaveFileModel: Error in creating wave file reader" << endl; Chris@187: delete m_reader; Chris@187: m_reader = 0; Chris@187: return; Chris@187: } Chris@187: Chris@316: m_model = new WaveFileModel(source, m_reader); Chris@187: if (!m_model->isOK()) { Chris@843: cerr << "WritableWaveFileModel: Error in creating wave file model" << endl; Chris@187: delete m_model; Chris@187: m_model = 0; Chris@187: delete m_reader; Chris@187: m_reader = 0; Chris@187: return; Chris@187: } Chris@300: m_model->setStartFrame(m_startFrame); Chris@187: Chris@258: connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); Chris@947: connect(m_model, SIGNAL(modelChangedWithin(int, int)), Chris@947: this, SIGNAL(modelChangedWithin(int, int))); Chris@175: } Chris@175: Chris@175: WritableWaveFileModel::~WritableWaveFileModel() Chris@175: { Chris@175: delete m_model; Chris@175: delete m_writer; Chris@175: delete m_reader; Chris@175: } Chris@175: Chris@300: void Chris@929: WritableWaveFileModel::setStartFrame(int startFrame) Chris@300: { Chris@300: m_startFrame = startFrame; Chris@300: if (m_model) m_model->setStartFrame(startFrame); Chris@300: } Chris@300: Chris@175: bool Chris@929: WritableWaveFileModel::addSamples(float **samples, int count) Chris@175: { Chris@175: if (!m_writer) return false; Chris@175: Chris@258: #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL Chris@690: // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl; Chris@258: #endif Chris@258: Chris@175: if (!m_writer->writeSamples(samples, count)) { Chris@843: cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl; Chris@175: return false; Chris@175: } Chris@175: Chris@175: m_frameCount += count; Chris@175: Chris@187: static int updateCounter = 0; Chris@175: Chris@187: if (m_reader && m_reader->getChannelCount() == 0) { Chris@258: #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL Chris@690: SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << endl; Chris@258: #endif Chris@187: m_reader->updateFrameCount(); Chris@187: } else if (++updateCounter == 100) { Chris@258: #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL Chris@690: SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << endl; Chris@258: #endif Chris@175: if (m_reader) m_reader->updateFrameCount(); Chris@175: updateCounter = 0; Chris@175: } Chris@175: Chris@175: return true; Chris@175: } Chris@175: Chris@175: bool Chris@175: WritableWaveFileModel::isOK() const Chris@175: { Chris@187: bool ok = (m_writer && m_writer->isOK()); Chris@690: // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl; Chris@175: return ok; Chris@175: } Chris@175: Chris@175: bool Chris@175: WritableWaveFileModel::isReady(int *completion) const Chris@175: { Chris@188: if (completion) *completion = m_completion; Chris@188: return (m_completion == 100); Chris@188: } Chris@188: Chris@188: void Chris@188: WritableWaveFileModel::setCompletion(int completion) Chris@188: { Chris@188: m_completion = completion; Chris@188: if (completion == 100) { Chris@188: if (m_reader) m_reader->updateDone(); Chris@188: } Chris@175: } Chris@175: Chris@929: int Chris@175: WritableWaveFileModel::getFrameCount() const Chris@175: { Chris@690: // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl; Chris@175: return m_frameCount; Chris@175: } Chris@175: Chris@175: Model * Chris@175: WritableWaveFileModel::clone() const Chris@175: { Chris@175: assert(0); //!!! Chris@188: return 0; Chris@175: } Chris@175: Chris@929: int Chris@929: WritableWaveFileModel::getData(int channel, int start, int count, Chris@300: float *buffer) const Chris@175: { Chris@187: if (!m_model || m_model->getChannelCount() == 0) return 0; Chris@300: return m_model->getData(channel, start, count, buffer); Chris@175: } Chris@175: Chris@929: int Chris@929: WritableWaveFileModel::getData(int channel, int start, int count, Chris@300: double *buffer) const Chris@175: { Chris@187: if (!m_model || m_model->getChannelCount() == 0) return 0; Chris@300: return m_model->getData(channel, start, count, buffer); Chris@175: } Chris@175: Chris@929: int Chris@929: WritableWaveFileModel::getData(int fromchannel, int tochannel, Chris@929: int start, int count, Chris@363: float **buffers) const Chris@363: { Chris@363: if (!m_model || m_model->getChannelCount() == 0) return 0; Chris@363: return m_model->getData(fromchannel, tochannel, start, count, buffers); Chris@363: } Chris@363: Chris@929: int Chris@929: WritableWaveFileModel::getSummaryBlockSize(int desired) const Chris@377: { Chris@377: if (!m_model) return desired; Chris@377: return m_model->getSummaryBlockSize(desired); Chris@377: } Chris@377: Chris@225: void Chris@929: WritableWaveFileModel::getSummaries(int channel, int start, int count, Chris@300: RangeBlock &ranges, Chris@929: int &blockSize) const Chris@175: { Chris@225: ranges.clear(); Chris@225: if (!m_model || m_model->getChannelCount() == 0) return; Chris@300: m_model->getSummaries(channel, start, count, ranges, blockSize); Chris@175: } Chris@175: Chris@175: WritableWaveFileModel::Range Chris@929: WritableWaveFileModel::getSummary(int channel, int start, int count) const Chris@175: { Chris@187: if (!m_model || m_model->getChannelCount() == 0) return Range(); Chris@300: return m_model->getSummary(channel, start, count); Chris@175: } Chris@175: Chris@175: void Chris@175: WritableWaveFileModel::toXml(QTextStream &out, Chris@175: QString indent, Chris@175: QString extraAttributes) const Chris@175: { Chris@188: // We don't actually write the data to XML. We just write a brief Chris@188: // description of the model. Any code that uses this class is Chris@188: // going to need to be aware that it will have to make separate Chris@188: // arrangements for the audio file itself. Chris@187: Chris@188: Model::toXml Chris@188: (out, indent, Chris@188: QString("type=\"writablewavefile\" file=\"%1\" channels=\"%2\" %3") Chris@603: .arg(encodeEntities(m_writer->getPath())) Chris@603: .arg(m_model->getChannelCount()).arg(extraAttributes)); Chris@175: } Chris@175: