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@1122
|
18 #include "ReadOnlyWaveFileModel.h"
|
Chris@1122
|
19
|
Chris@175
|
20 #include "base/TempDirectory.h"
|
Chris@175
|
21 #include "base/Exceptions.h"
|
Chris@175
|
22
|
Chris@175
|
23 #include "fileio/WavFileWriter.h"
|
Chris@175
|
24 #include "fileio/WavFileReader.h"
|
Chris@175
|
25
|
Chris@175
|
26 #include <QDir>
|
Chris@314
|
27 #include <QTextStream>
|
Chris@175
|
28
|
Chris@175
|
29 #include <cassert>
|
Chris@175
|
30 #include <iostream>
|
Chris@723
|
31 #include <stdint.h>
|
Chris@175
|
32
|
Chris@1096
|
33 using namespace std;
|
Chris@1096
|
34
|
Chris@1133
|
35 const int WritableWaveFileModel::PROPORTION_UNKNOWN = -1;
|
Chris@1133
|
36
|
Chris@258
|
37 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
|
Chris@187
|
38
|
Chris@1040
|
39 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate,
|
Chris@1429
|
40 int channels,
|
Chris@1517
|
41 QString path,
|
Chris@1517
|
42 bool normaliseOnRead) :
|
Chris@175
|
43 m_model(0),
|
Chris@175
|
44 m_writer(0),
|
Chris@175
|
45 m_reader(0),
|
Chris@175
|
46 m_sampleRate(sampleRate),
|
Chris@175
|
47 m_channels(channels),
|
Chris@188
|
48 m_frameCount(0),
|
Chris@300
|
49 m_startFrame(0),
|
Chris@1133
|
50 m_proportion(PROPORTION_UNKNOWN)
|
Chris@175
|
51 {
|
Chris@175
|
52 if (path.isEmpty()) {
|
Chris@175
|
53 try {
|
Chris@175
|
54 QDir dir(TempDirectory::getInstance()->getPath());
|
Chris@175
|
55 path = dir.filePath(QString("written_%1.wav")
|
Chris@175
|
56 .arg((intptr_t)this));
|
Chris@1465
|
57 } catch (const DirectoryCreationFailed &f) {
|
Chris@1428
|
58 SVCERR << "WritableWaveFileModel: Failed to create temporary directory" << endl;
|
Chris@175
|
59 return;
|
Chris@175
|
60 }
|
Chris@175
|
61 }
|
Chris@175
|
62
|
Chris@684
|
63 // Write directly to the target file, so that we can do
|
Chris@684
|
64 // incremental writes and concurrent reads
|
Chris@684
|
65 m_writer = new WavFileWriter(path, sampleRate, channels,
|
Chris@684
|
66 WavFileWriter::WriteToTarget);
|
Chris@175
|
67 if (!m_writer->isOK()) {
|
Chris@1428
|
68 SVCERR << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl;
|
Chris@175
|
69 delete m_writer;
|
Chris@175
|
70 m_writer = 0;
|
Chris@175
|
71 return;
|
Chris@175
|
72 }
|
Chris@187
|
73
|
Chris@317
|
74 FileSource source(m_writer->getPath());
|
Chris@316
|
75
|
Chris@1517
|
76 m_reader = new WavFileReader(source, true, normaliseOnRead);
|
Chris@290
|
77 if (!m_reader->getError().isEmpty()) {
|
Chris@1428
|
78 SVCERR << "WritableWaveFileModel: Error in creating wave file reader" << endl;
|
Chris@187
|
79 delete m_reader;
|
Chris@187
|
80 m_reader = 0;
|
Chris@187
|
81 return;
|
Chris@187
|
82 }
|
Chris@187
|
83
|
Chris@1122
|
84 m_model = new ReadOnlyWaveFileModel(source, m_reader);
|
Chris@187
|
85 if (!m_model->isOK()) {
|
Chris@1428
|
86 SVCERR << "WritableWaveFileModel: Error in creating wave file model" << endl;
|
Chris@187
|
87 delete m_model;
|
Chris@187
|
88 m_model = 0;
|
Chris@187
|
89 delete m_reader;
|
Chris@187
|
90 m_reader = 0;
|
Chris@187
|
91 return;
|
Chris@187
|
92 }
|
Chris@300
|
93 m_model->setStartFrame(m_startFrame);
|
Chris@187
|
94
|
Chris@258
|
95 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
|
Chris@1038
|
96 connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
|
Chris@1038
|
97 this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)));
|
Chris@175
|
98 }
|
Chris@175
|
99
|
Chris@175
|
100 WritableWaveFileModel::~WritableWaveFileModel()
|
Chris@175
|
101 {
|
Chris@175
|
102 delete m_model;
|
Chris@175
|
103 delete m_writer;
|
Chris@175
|
104 delete m_reader;
|
Chris@175
|
105 }
|
Chris@175
|
106
|
Chris@300
|
107 void
|
Chris@1038
|
108 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame)
|
Chris@300
|
109 {
|
Chris@300
|
110 m_startFrame = startFrame;
|
Chris@300
|
111 if (m_model) m_model->setStartFrame(startFrame);
|
Chris@300
|
112 }
|
Chris@300
|
113
|
Chris@175
|
114 bool
|
Chris@1325
|
115 WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t count)
|
Chris@175
|
116 {
|
Chris@175
|
117 if (!m_writer) return false;
|
Chris@175
|
118
|
Chris@258
|
119 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
|
Chris@690
|
120 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl;
|
Chris@258
|
121 #endif
|
Chris@258
|
122
|
Chris@175
|
123 if (!m_writer->writeSamples(samples, count)) {
|
Chris@1337
|
124 SVCERR << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl;
|
Chris@175
|
125 return false;
|
Chris@175
|
126 }
|
Chris@175
|
127
|
Chris@175
|
128 m_frameCount += count;
|
Chris@175
|
129
|
Chris@187
|
130 if (m_reader && m_reader->getChannelCount() == 0) {
|
Chris@187
|
131 m_reader->updateFrameCount();
|
Chris@175
|
132 }
|
Chris@175
|
133
|
Chris@175
|
134 return true;
|
Chris@175
|
135 }
|
Chris@175
|
136
|
Chris@1337
|
137 void
|
Chris@1337
|
138 WritableWaveFileModel::updateModel()
|
Chris@1337
|
139 {
|
Chris@1337
|
140 if (m_reader) {
|
Chris@1337
|
141 m_reader->updateFrameCount();
|
Chris@1337
|
142 }
|
Chris@1337
|
143 }
|
Chris@1337
|
144
|
Chris@175
|
145 bool
|
Chris@175
|
146 WritableWaveFileModel::isOK() const
|
Chris@175
|
147 {
|
Chris@187
|
148 bool ok = (m_writer && m_writer->isOK());
|
Chris@690
|
149 // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl;
|
Chris@175
|
150 return ok;
|
Chris@175
|
151 }
|
Chris@175
|
152
|
Chris@175
|
153 bool
|
Chris@175
|
154 WritableWaveFileModel::isReady(int *completion) const
|
Chris@175
|
155 {
|
Chris@1133
|
156 int c = getCompletion();
|
Chris@1133
|
157 if (completion) *completion = c;
|
Chris@1133
|
158 if (!isOK()) return false;
|
Chris@1133
|
159 return (c == 100);
|
Chris@188
|
160 }
|
Chris@188
|
161
|
Chris@188
|
162 void
|
Chris@1133
|
163 WritableWaveFileModel::setWriteProportion(int proportion)
|
Chris@188
|
164 {
|
Chris@1133
|
165 m_proportion = proportion;
|
Chris@1133
|
166 }
|
Chris@1133
|
167
|
Chris@1133
|
168 int
|
Chris@1133
|
169 WritableWaveFileModel::getWriteProportion() const
|
Chris@1133
|
170 {
|
Chris@1133
|
171 return m_proportion;
|
Chris@1133
|
172 }
|
Chris@1133
|
173
|
Chris@1133
|
174 void
|
Chris@1133
|
175 WritableWaveFileModel::writeComplete()
|
Chris@1133
|
176 {
|
Chris@1336
|
177 m_writer->close();
|
Chris@1133
|
178 if (m_reader) m_reader->updateDone();
|
Chris@1133
|
179 m_proportion = 100;
|
Chris@1133
|
180 emit modelChanged();
|
Chris@175
|
181 }
|
Chris@175
|
182
|
Chris@1038
|
183 sv_frame_t
|
Chris@175
|
184 WritableWaveFileModel::getFrameCount() const
|
Chris@175
|
185 {
|
Chris@690
|
186 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl;
|
Chris@175
|
187 return m_frameCount;
|
Chris@175
|
188 }
|
Chris@175
|
189
|
Chris@1326
|
190 floatvec_t
|
Chris@1096
|
191 WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
|
Chris@175
|
192 {
|
Chris@1096
|
193 if (!m_model || m_model->getChannelCount() == 0) return {};
|
Chris@1096
|
194 return m_model->getData(channel, start, count);
|
Chris@175
|
195 }
|
Chris@175
|
196
|
Chris@1326
|
197 vector<floatvec_t>
|
Chris@1086
|
198 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
|
Chris@1096
|
199 sv_frame_t start, sv_frame_t count) const
|
Chris@175
|
200 {
|
Chris@1096
|
201 if (!m_model || m_model->getChannelCount() == 0) return {};
|
Chris@1096
|
202 return m_model->getMultiChannelData(fromchannel, tochannel, start, count);
|
Chris@363
|
203 }
|
Chris@363
|
204
|
Chris@929
|
205 int
|
Chris@929
|
206 WritableWaveFileModel::getSummaryBlockSize(int desired) const
|
Chris@377
|
207 {
|
Chris@377
|
208 if (!m_model) return desired;
|
Chris@377
|
209 return m_model->getSummaryBlockSize(desired);
|
Chris@377
|
210 }
|
Chris@377
|
211
|
Chris@225
|
212 void
|
Chris@1038
|
213 WritableWaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t count,
|
Chris@300
|
214 RangeBlock &ranges,
|
Chris@929
|
215 int &blockSize) const
|
Chris@175
|
216 {
|
Chris@225
|
217 ranges.clear();
|
Chris@225
|
218 if (!m_model || m_model->getChannelCount() == 0) return;
|
Chris@300
|
219 m_model->getSummaries(channel, start, count, ranges, blockSize);
|
Chris@175
|
220 }
|
Chris@175
|
221
|
Chris@175
|
222 WritableWaveFileModel::Range
|
Chris@1038
|
223 WritableWaveFileModel::getSummary(int channel, sv_frame_t start, sv_frame_t count) const
|
Chris@175
|
224 {
|
Chris@187
|
225 if (!m_model || m_model->getChannelCount() == 0) return Range();
|
Chris@300
|
226 return m_model->getSummary(channel, start, count);
|
Chris@175
|
227 }
|
Chris@175
|
228
|
Chris@175
|
229 void
|
Chris@175
|
230 WritableWaveFileModel::toXml(QTextStream &out,
|
Chris@175
|
231 QString indent,
|
Chris@175
|
232 QString extraAttributes) const
|
Chris@175
|
233 {
|
Chris@1123
|
234 // The assumption here is that the underlying wave file has
|
Chris@1123
|
235 // already been saved somewhere (its location is available through
|
Chris@1123
|
236 // getLocation()) and that the code that uses this class is
|
Chris@1123
|
237 // dealing with the problem of making sure it remains available.
|
Chris@1123
|
238 // We just write this out as if it were a normal wave file.
|
Chris@187
|
239
|
Chris@188
|
240 Model::toXml
|
Chris@188
|
241 (out, indent,
|
Chris@1123
|
242 QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2")
|
Chris@603
|
243 .arg(encodeEntities(m_writer->getPath()))
|
Chris@1123
|
244 .arg(extraAttributes));
|
Chris@175
|
245 }
|
Chris@175
|
246
|