| 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@175 | 55     m_writer = new WavFileWriter(path, sampleRate, channels); | 
| Chris@175 | 56     if (!m_writer->isOK()) { | 
| Chris@175 | 57         std::cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError().toStdString() << std::endl; | 
| Chris@175 | 58         delete m_writer; | 
| Chris@175 | 59         m_writer = 0; | 
| Chris@175 | 60         return; | 
| Chris@175 | 61     } | 
| Chris@187 | 62 | 
| Chris@290 | 63     m_reader = new WavFileReader(m_writer->getPath(), true); | 
| Chris@290 | 64     if (!m_reader->getError().isEmpty()) { | 
| Chris@187 | 65         std::cerr << "WritableWaveFileModel: Error in creating wave file reader" << std::endl; | 
| Chris@187 | 66         delete m_reader; | 
| Chris@187 | 67         m_reader = 0; | 
| Chris@187 | 68         return; | 
| Chris@187 | 69     } | 
| Chris@187 | 70 | 
| Chris@187 | 71     m_model = new WaveFileModel(m_writer->getPath(), m_reader); | 
| Chris@187 | 72     if (!m_model->isOK()) { | 
| Chris@187 | 73         std::cerr << "WritableWaveFileModel: Error in creating wave file model" << std::endl; | 
| Chris@187 | 74         delete m_model; | 
| Chris@187 | 75         m_model = 0; | 
| Chris@187 | 76         delete m_reader; | 
| Chris@187 | 77         m_reader = 0; | 
| Chris@187 | 78         return; | 
| Chris@187 | 79     } | 
| Chris@300 | 80     m_model->setStartFrame(m_startFrame); | 
| Chris@187 | 81 | 
| Chris@258 | 82     connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | 
| Chris@258 | 83     connect(m_model, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@187 | 84             this, SIGNAL(modelChanged(size_t, size_t))); | 
| Chris@175 | 85 } | 
| Chris@175 | 86 | 
| Chris@175 | 87 WritableWaveFileModel::~WritableWaveFileModel() | 
| Chris@175 | 88 { | 
| Chris@175 | 89     delete m_model; | 
| Chris@175 | 90     delete m_writer; | 
| Chris@175 | 91     delete m_reader; | 
| Chris@175 | 92 } | 
| Chris@175 | 93 | 
| Chris@300 | 94 void | 
| Chris@300 | 95 WritableWaveFileModel::setStartFrame(size_t startFrame) | 
| Chris@300 | 96 { | 
| Chris@300 | 97     m_startFrame = startFrame; | 
| Chris@300 | 98     if (m_model) m_model->setStartFrame(startFrame); | 
| Chris@300 | 99 } | 
| Chris@300 | 100 | 
| Chris@175 | 101 bool | 
| Chris@175 | 102 WritableWaveFileModel::addSamples(float **samples, size_t count) | 
| Chris@175 | 103 { | 
| Chris@175 | 104     if (!m_writer) return false; | 
| Chris@175 | 105 | 
| Chris@258 | 106 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | 
| Chris@258 | 107 //    std::cerr << "WritableWaveFileModel::addSamples(" << count << ")" << std::endl; | 
| Chris@258 | 108 #endif | 
| Chris@258 | 109 | 
| Chris@175 | 110     if (!m_writer->writeSamples(samples, count)) { | 
| Chris@175 | 111         std::cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError().toStdString() << std::endl; | 
| Chris@175 | 112         return false; | 
| Chris@175 | 113     } | 
| Chris@175 | 114 | 
| Chris@175 | 115     m_frameCount += count; | 
| Chris@175 | 116 | 
| Chris@187 | 117     static int updateCounter = 0; | 
| Chris@175 | 118 | 
| Chris@187 | 119     if (m_reader && m_reader->getChannelCount() == 0) { | 
| Chris@258 | 120 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | 
| Chris@258 | 121         std::cerr << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << std::endl; | 
| Chris@258 | 122 #endif | 
| Chris@187 | 123         m_reader->updateFrameCount(); | 
| Chris@187 | 124     } else if (++updateCounter == 100) { | 
| Chris@258 | 125 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | 
| Chris@258 | 126         std::cerr << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << std::endl; | 
| Chris@258 | 127 #endif | 
| Chris@175 | 128         if (m_reader) m_reader->updateFrameCount(); | 
| Chris@175 | 129         updateCounter = 0; | 
| Chris@175 | 130     } | 
| Chris@175 | 131 | 
| Chris@175 | 132     return true; | 
| Chris@175 | 133 } | 
| Chris@175 | 134 | 
| Chris@175 | 135 bool | 
| Chris@175 | 136 WritableWaveFileModel::isOK() const | 
| Chris@175 | 137 { | 
| Chris@187 | 138     bool ok = (m_writer && m_writer->isOK()); | 
| Chris@188 | 139 //    std::cerr << "WritableWaveFileModel::isOK(): ok = " << ok << std::endl; | 
| Chris@175 | 140     return ok; | 
| Chris@175 | 141 } | 
| Chris@175 | 142 | 
| Chris@175 | 143 bool | 
| Chris@175 | 144 WritableWaveFileModel::isReady(int *completion) const | 
| Chris@175 | 145 { | 
| Chris@188 | 146     if (completion) *completion = m_completion; | 
| Chris@188 | 147     return (m_completion == 100); | 
| Chris@188 | 148 } | 
| Chris@188 | 149 | 
| Chris@188 | 150 void | 
| Chris@188 | 151 WritableWaveFileModel::setCompletion(int completion) | 
| Chris@188 | 152 { | 
| Chris@188 | 153     m_completion = completion; | 
| Chris@188 | 154     if (completion == 100) { | 
| Chris@188 | 155         if (m_reader) m_reader->updateDone(); | 
| Chris@188 | 156     } | 
| Chris@175 | 157 } | 
| Chris@175 | 158 | 
| Chris@175 | 159 size_t | 
| Chris@175 | 160 WritableWaveFileModel::getFrameCount() const | 
| Chris@175 | 161 { | 
| Chris@188 | 162 //    std::cerr << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << std::endl; | 
| Chris@175 | 163     return m_frameCount; | 
| Chris@175 | 164 } | 
| Chris@175 | 165 | 
| Chris@175 | 166 Model * | 
| Chris@175 | 167 WritableWaveFileModel::clone() const | 
| Chris@175 | 168 { | 
| Chris@175 | 169     assert(0); //!!! | 
| Chris@188 | 170     return 0; | 
| Chris@175 | 171 } | 
| Chris@175 | 172 | 
| Chris@175 | 173 size_t | 
| Chris@300 | 174 WritableWaveFileModel::getData(int channel, size_t start, size_t count, | 
| Chris@300 | 175                                float *buffer) const | 
| Chris@175 | 176 { | 
| Chris@187 | 177     if (!m_model || m_model->getChannelCount() == 0) return 0; | 
| Chris@300 | 178     return m_model->getData(channel, start, count, buffer); | 
| Chris@175 | 179 } | 
| Chris@175 | 180 | 
| Chris@175 | 181 size_t | 
| Chris@300 | 182 WritableWaveFileModel::getData(int channel, size_t start, size_t count, | 
| Chris@300 | 183                                double *buffer) const | 
| Chris@175 | 184 { | 
| Chris@187 | 185     if (!m_model || m_model->getChannelCount() == 0) return 0; | 
| Chris@175 | 186 //    std::cerr << "WritableWaveFileModel::getValues(" << channel << ", " | 
| Chris@175 | 187 //              << start << ", " << end << "): calling model" << std::endl; | 
| Chris@300 | 188     return m_model->getData(channel, start, count, buffer); | 
| Chris@175 | 189 } | 
| Chris@175 | 190 | 
| Chris@225 | 191 void | 
| Chris@300 | 192 WritableWaveFileModel::getSummaries(size_t channel, size_t start, size_t count, | 
| Chris@300 | 193                                     RangeBlock &ranges, | 
| Chris@300 | 194                                     size_t &blockSize) const | 
| Chris@175 | 195 { | 
| Chris@225 | 196     ranges.clear(); | 
| Chris@225 | 197     if (!m_model || m_model->getChannelCount() == 0) return; | 
| Chris@300 | 198     m_model->getSummaries(channel, start, count, ranges, blockSize); | 
| Chris@175 | 199 } | 
| Chris@175 | 200 | 
| Chris@175 | 201 WritableWaveFileModel::Range | 
| Chris@300 | 202 WritableWaveFileModel::getSummary(size_t channel, size_t start, size_t count) const | 
| Chris@175 | 203 { | 
| Chris@187 | 204     if (!m_model || m_model->getChannelCount() == 0) return Range(); | 
| Chris@300 | 205     return m_model->getSummary(channel, start, count); | 
| Chris@175 | 206 } | 
| Chris@175 | 207 | 
| Chris@175 | 208 void | 
| Chris@175 | 209 WritableWaveFileModel::toXml(QTextStream &out, | 
| Chris@175 | 210                              QString indent, | 
| Chris@175 | 211                              QString extraAttributes) const | 
| Chris@175 | 212 { | 
| Chris@188 | 213     // We don't actually write the data to XML.  We just write a brief | 
| Chris@188 | 214     // description of the model.  Any code that uses this class is | 
| Chris@188 | 215     // going to need to be aware that it will have to make separate | 
| Chris@188 | 216     // arrangements for the audio file itself. | 
| Chris@187 | 217 | 
| Chris@188 | 218     Model::toXml | 
| Chris@188 | 219         (out, indent, | 
| Chris@188 | 220          QString("type=\"writablewavefile\" file=\"%1\" channels=\"%2\" %3") | 
| Chris@188 | 221          .arg(m_writer->getPath()).arg(m_model->getChannelCount()).arg(extraAttributes)); | 
| Chris@175 | 222 } | 
| Chris@175 | 223 |