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@1122: #include "ReadOnlyWaveFileModel.h" Chris@1122: 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@1096: using namespace std; Chris@1096: Chris@1133: const int WritableWaveFileModel::PROPORTION_UNKNOWN = -1; Chris@1133: Chris@258: //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1 Chris@187: Chris@1040: WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, Chris@1429: int channels, Chris@1429: 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@1133: m_proportion(PROPORTION_UNKNOWN) 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@1465: } catch (const DirectoryCreationFailed &f) { Chris@1428: SVCERR << "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@1428: SVCERR << "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@1428: SVCERR << "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@1122: m_model = new ReadOnlyWaveFileModel(source, m_reader); Chris@187: if (!m_model->isOK()) { Chris@1428: SVCERR << "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@1038: connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), Chris@1038: this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t))); 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@1038: WritableWaveFileModel::setStartFrame(sv_frame_t 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@1325: WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t 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@1337: SVCERR << "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: if (m_reader && m_reader->getChannelCount() == 0) { Chris@187: m_reader->updateFrameCount(); Chris@175: } Chris@175: Chris@175: return true; Chris@175: } Chris@175: Chris@1337: void Chris@1337: WritableWaveFileModel::updateModel() Chris@1337: { Chris@1337: if (m_reader) { Chris@1337: m_reader->updateFrameCount(); Chris@1337: } Chris@1337: } Chris@1337: 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@1133: int c = getCompletion(); Chris@1133: if (completion) *completion = c; Chris@1133: if (!isOK()) return false; Chris@1133: return (c == 100); Chris@188: } Chris@188: Chris@188: void Chris@1133: WritableWaveFileModel::setWriteProportion(int proportion) Chris@188: { Chris@1133: m_proportion = proportion; Chris@1133: } Chris@1133: Chris@1133: int Chris@1133: WritableWaveFileModel::getWriteProportion() const Chris@1133: { Chris@1133: return m_proportion; Chris@1133: } Chris@1133: Chris@1133: void Chris@1133: WritableWaveFileModel::writeComplete() Chris@1133: { Chris@1336: m_writer->close(); Chris@1133: if (m_reader) m_reader->updateDone(); Chris@1133: m_proportion = 100; Chris@1133: emit modelChanged(); Chris@175: } Chris@175: Chris@1038: sv_frame_t 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@1326: floatvec_t Chris@1096: WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const Chris@175: { Chris@1096: if (!m_model || m_model->getChannelCount() == 0) return {}; Chris@1096: return m_model->getData(channel, start, count); Chris@175: } Chris@175: Chris@1326: vector Chris@1086: WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel, Chris@1096: sv_frame_t start, sv_frame_t count) const Chris@175: { Chris@1096: if (!m_model || m_model->getChannelCount() == 0) return {}; Chris@1096: return m_model->getMultiChannelData(fromchannel, tochannel, start, count); 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@1038: WritableWaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t 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@1038: WritableWaveFileModel::getSummary(int channel, sv_frame_t start, sv_frame_t 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@1123: // The assumption here is that the underlying wave file has Chris@1123: // already been saved somewhere (its location is available through Chris@1123: // getLocation()) and that the code that uses this class is Chris@1123: // dealing with the problem of making sure it remains available. Chris@1123: // We just write this out as if it were a normal wave file. Chris@187: Chris@188: Model::toXml Chris@188: (out, indent, Chris@1123: QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2") Chris@603: .arg(encodeEntities(m_writer->getPath())) Chris@1123: .arg(extraAttributes)); Chris@175: } Chris@175: