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@723
|
29 #include <stdint.h>
|
Chris@175
|
30
|
Chris@258
|
31 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
|
Chris@187
|
32
|
Chris@1040
|
33 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate,
|
Chris@929
|
34 int channels,
|
Chris@175
|
35 QString path) :
|
Chris@175
|
36 m_model(0),
|
Chris@175
|
37 m_writer(0),
|
Chris@175
|
38 m_reader(0),
|
Chris@175
|
39 m_sampleRate(sampleRate),
|
Chris@175
|
40 m_channels(channels),
|
Chris@188
|
41 m_frameCount(0),
|
Chris@300
|
42 m_startFrame(0),
|
Chris@188
|
43 m_completion(0)
|
Chris@175
|
44 {
|
Chris@175
|
45 if (path.isEmpty()) {
|
Chris@175
|
46 try {
|
Chris@175
|
47 QDir dir(TempDirectory::getInstance()->getPath());
|
Chris@175
|
48 path = dir.filePath(QString("written_%1.wav")
|
Chris@175
|
49 .arg((intptr_t)this));
|
Chris@175
|
50 } catch (DirectoryCreationFailed f) {
|
Chris@843
|
51 cerr << "WritableWaveFileModel: Failed to create temporary directory" << endl;
|
Chris@175
|
52 return;
|
Chris@175
|
53 }
|
Chris@175
|
54 }
|
Chris@175
|
55
|
Chris@684
|
56 // Write directly to the target file, so that we can do
|
Chris@684
|
57 // incremental writes and concurrent reads
|
Chris@684
|
58 m_writer = new WavFileWriter(path, sampleRate, channels,
|
Chris@684
|
59 WavFileWriter::WriteToTarget);
|
Chris@175
|
60 if (!m_writer->isOK()) {
|
Chris@843
|
61 cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl;
|
Chris@175
|
62 delete m_writer;
|
Chris@175
|
63 m_writer = 0;
|
Chris@175
|
64 return;
|
Chris@175
|
65 }
|
Chris@187
|
66
|
Chris@317
|
67 FileSource source(m_writer->getPath());
|
Chris@316
|
68
|
Chris@316
|
69 m_reader = new WavFileReader(source, true);
|
Chris@290
|
70 if (!m_reader->getError().isEmpty()) {
|
Chris@843
|
71 cerr << "WritableWaveFileModel: Error in creating wave file reader" << endl;
|
Chris@187
|
72 delete m_reader;
|
Chris@187
|
73 m_reader = 0;
|
Chris@187
|
74 return;
|
Chris@187
|
75 }
|
Chris@187
|
76
|
Chris@316
|
77 m_model = new WaveFileModel(source, m_reader);
|
Chris@187
|
78 if (!m_model->isOK()) {
|
Chris@843
|
79 cerr << "WritableWaveFileModel: Error in creating wave file model" << endl;
|
Chris@187
|
80 delete m_model;
|
Chris@187
|
81 m_model = 0;
|
Chris@187
|
82 delete m_reader;
|
Chris@187
|
83 m_reader = 0;
|
Chris@187
|
84 return;
|
Chris@187
|
85 }
|
Chris@300
|
86 m_model->setStartFrame(m_startFrame);
|
Chris@187
|
87
|
Chris@258
|
88 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
|
Chris@1038
|
89 connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
|
Chris@1038
|
90 this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)));
|
Chris@175
|
91 }
|
Chris@175
|
92
|
Chris@175
|
93 WritableWaveFileModel::~WritableWaveFileModel()
|
Chris@175
|
94 {
|
Chris@175
|
95 delete m_model;
|
Chris@175
|
96 delete m_writer;
|
Chris@175
|
97 delete m_reader;
|
Chris@175
|
98 }
|
Chris@175
|
99
|
Chris@300
|
100 void
|
Chris@1038
|
101 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame)
|
Chris@300
|
102 {
|
Chris@300
|
103 m_startFrame = startFrame;
|
Chris@300
|
104 if (m_model) m_model->setStartFrame(startFrame);
|
Chris@300
|
105 }
|
Chris@300
|
106
|
Chris@175
|
107 bool
|
Chris@1038
|
108 WritableWaveFileModel::addSamples(float **samples, sv_frame_t count)
|
Chris@175
|
109 {
|
Chris@175
|
110 if (!m_writer) return false;
|
Chris@175
|
111
|
Chris@258
|
112 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
|
Chris@690
|
113 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl;
|
Chris@258
|
114 #endif
|
Chris@258
|
115
|
Chris@175
|
116 if (!m_writer->writeSamples(samples, count)) {
|
Chris@843
|
117 cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl;
|
Chris@175
|
118 return false;
|
Chris@175
|
119 }
|
Chris@175
|
120
|
Chris@175
|
121 m_frameCount += count;
|
Chris@175
|
122
|
Chris@187
|
123 static int updateCounter = 0;
|
Chris@175
|
124
|
Chris@187
|
125 if (m_reader && m_reader->getChannelCount() == 0) {
|
Chris@258
|
126 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
|
Chris@690
|
127 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << endl;
|
Chris@258
|
128 #endif
|
Chris@187
|
129 m_reader->updateFrameCount();
|
Chris@187
|
130 } else if (++updateCounter == 100) {
|
Chris@258
|
131 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
|
Chris@690
|
132 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << endl;
|
Chris@258
|
133 #endif
|
Chris@175
|
134 if (m_reader) m_reader->updateFrameCount();
|
Chris@175
|
135 updateCounter = 0;
|
Chris@175
|
136 }
|
Chris@175
|
137
|
Chris@175
|
138 return true;
|
Chris@175
|
139 }
|
Chris@175
|
140
|
Chris@175
|
141 bool
|
Chris@175
|
142 WritableWaveFileModel::isOK() const
|
Chris@175
|
143 {
|
Chris@187
|
144 bool ok = (m_writer && m_writer->isOK());
|
Chris@690
|
145 // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl;
|
Chris@175
|
146 return ok;
|
Chris@175
|
147 }
|
Chris@175
|
148
|
Chris@175
|
149 bool
|
Chris@175
|
150 WritableWaveFileModel::isReady(int *completion) const
|
Chris@175
|
151 {
|
Chris@188
|
152 if (completion) *completion = m_completion;
|
Chris@188
|
153 return (m_completion == 100);
|
Chris@188
|
154 }
|
Chris@188
|
155
|
Chris@188
|
156 void
|
Chris@188
|
157 WritableWaveFileModel::setCompletion(int completion)
|
Chris@188
|
158 {
|
Chris@188
|
159 m_completion = completion;
|
Chris@188
|
160 if (completion == 100) {
|
Chris@188
|
161 if (m_reader) m_reader->updateDone();
|
Chris@188
|
162 }
|
Chris@175
|
163 }
|
Chris@175
|
164
|
Chris@1038
|
165 sv_frame_t
|
Chris@175
|
166 WritableWaveFileModel::getFrameCount() const
|
Chris@175
|
167 {
|
Chris@690
|
168 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl;
|
Chris@175
|
169 return m_frameCount;
|
Chris@175
|
170 }
|
Chris@175
|
171
|
Chris@1038
|
172 sv_frame_t
|
Chris@1038
|
173 WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count,
|
Chris@300
|
174 float *buffer) const
|
Chris@175
|
175 {
|
Chris@187
|
176 if (!m_model || m_model->getChannelCount() == 0) return 0;
|
Chris@300
|
177 return m_model->getData(channel, start, count, buffer);
|
Chris@175
|
178 }
|
Chris@175
|
179
|
Chris@1038
|
180 sv_frame_t
|
Chris@1086
|
181 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
|
Chris@1086
|
182 sv_frame_t start, sv_frame_t count,
|
Chris@1086
|
183 float **buffers) const
|
Chris@175
|
184 {
|
Chris@187
|
185 if (!m_model || m_model->getChannelCount() == 0) return 0;
|
Chris@1086
|
186 return m_model->getMultiChannelData(fromchannel, tochannel, start, count, buffers);
|
Chris@363
|
187 }
|
Chris@363
|
188
|
Chris@929
|
189 int
|
Chris@929
|
190 WritableWaveFileModel::getSummaryBlockSize(int desired) const
|
Chris@377
|
191 {
|
Chris@377
|
192 if (!m_model) return desired;
|
Chris@377
|
193 return m_model->getSummaryBlockSize(desired);
|
Chris@377
|
194 }
|
Chris@377
|
195
|
Chris@225
|
196 void
|
Chris@1038
|
197 WritableWaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t count,
|
Chris@300
|
198 RangeBlock &ranges,
|
Chris@929
|
199 int &blockSize) const
|
Chris@175
|
200 {
|
Chris@225
|
201 ranges.clear();
|
Chris@225
|
202 if (!m_model || m_model->getChannelCount() == 0) return;
|
Chris@300
|
203 m_model->getSummaries(channel, start, count, ranges, blockSize);
|
Chris@175
|
204 }
|
Chris@175
|
205
|
Chris@175
|
206 WritableWaveFileModel::Range
|
Chris@1038
|
207 WritableWaveFileModel::getSummary(int channel, sv_frame_t start, sv_frame_t count) const
|
Chris@175
|
208 {
|
Chris@187
|
209 if (!m_model || m_model->getChannelCount() == 0) return Range();
|
Chris@300
|
210 return m_model->getSummary(channel, start, count);
|
Chris@175
|
211 }
|
Chris@175
|
212
|
Chris@175
|
213 void
|
Chris@175
|
214 WritableWaveFileModel::toXml(QTextStream &out,
|
Chris@175
|
215 QString indent,
|
Chris@175
|
216 QString extraAttributes) const
|
Chris@175
|
217 {
|
Chris@188
|
218 // We don't actually write the data to XML. We just write a brief
|
Chris@188
|
219 // description of the model. Any code that uses this class is
|
Chris@188
|
220 // going to need to be aware that it will have to make separate
|
Chris@188
|
221 // arrangements for the audio file itself.
|
Chris@187
|
222
|
Chris@188
|
223 Model::toXml
|
Chris@188
|
224 (out, indent,
|
Chris@188
|
225 QString("type=\"writablewavefile\" file=\"%1\" channels=\"%2\" %3")
|
Chris@603
|
226 .arg(encodeEntities(m_writer->getPath()))
|
Chris@603
|
227 .arg(m_model->getChannelCount()).arg(extraAttributes));
|
Chris@175
|
228 }
|
Chris@175
|
229
|