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@686
|
60 std::cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << 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@687
|
112 // DEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl;
|
Chris@258
|
113 #endif
|
Chris@258
|
114
|
Chris@175
|
115 if (!m_writer->writeSamples(samples, count)) {
|
Chris@686
|
116 std::cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << 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@687
|
126 DEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << 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@687
|
131 DEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << 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@687
|
144 // DEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << 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@687
|
167 // DEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << 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
|