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 <QDir>
Chris@314: #include <QTextStream>
Chris@175: 
Chris@175: #include <cassert>
Chris@175: #include <iostream>
Chris@723: #include <stdint.h>
Chris@175: 
Chris@258: //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
Chris@187: 
Chris@175: WritableWaveFileModel::WritableWaveFileModel(size_t sampleRate,
Chris@175: 					     size_t 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@258:     connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@187:             this, SIGNAL(modelChanged(size_t, size_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@300: WritableWaveFileModel::setStartFrame(size_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@175: WritableWaveFileModel::addSamples(float **samples, size_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@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@175: size_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@175: Model *
Chris@175: WritableWaveFileModel::clone() const
Chris@175: {
Chris@175:     assert(0); //!!!
Chris@188:     return 0;
Chris@175: }
Chris@175: 
Chris@175: size_t
Chris@300: WritableWaveFileModel::getData(int channel, size_t start, size_t 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@175: size_t
Chris@300: WritableWaveFileModel::getData(int channel, size_t start, size_t 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@363: size_t
Chris@363: WritableWaveFileModel::getData(size_t fromchannel, size_t tochannel,
Chris@363:                                size_t start, size_t 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@377: size_t
Chris@377: WritableWaveFileModel::getSummaryBlockSize(size_t 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@300: WritableWaveFileModel::getSummaries(size_t channel, size_t start, size_t count,
Chris@300:                                     RangeBlock &ranges,
Chris@300:                                     size_t &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@300: WritableWaveFileModel::getSummary(size_t channel, size_t start, size_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@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: