Mercurial > hg > svcore
comparison data/model/WritableWaveFileModel.cpp @ 1527:710e6250a401 zoom
Merge from default branch
author | Chris Cannam |
---|---|
date | Mon, 17 Sep 2018 13:51:14 +0100 |
parents | 954d0cf29ca7 |
children | 70e172e6cc59 |
comparison
equal
deleted
inserted
replaced
1324:d4a28d1479a8 | 1527:710e6250a401 |
---|---|
34 | 34 |
35 const int WritableWaveFileModel::PROPORTION_UNKNOWN = -1; | 35 const int WritableWaveFileModel::PROPORTION_UNKNOWN = -1; |
36 | 36 |
37 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1 | 37 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1 |
38 | 38 |
39 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, | 39 WritableWaveFileModel::WritableWaveFileModel(QString path, |
40 int channels, | 40 sv_samplerate_t sampleRate, |
41 QString path) : | 41 int channels, |
42 Normalisation norm) : | |
42 m_model(0), | 43 m_model(0), |
43 m_writer(0), | 44 m_temporaryWriter(0), |
45 m_targetWriter(0), | |
44 m_reader(0), | 46 m_reader(0), |
47 m_normalisation(norm), | |
45 m_sampleRate(sampleRate), | 48 m_sampleRate(sampleRate), |
46 m_channels(channels), | 49 m_channels(channels), |
47 m_frameCount(0), | 50 m_frameCount(0), |
48 m_startFrame(0), | 51 m_startFrame(0), |
49 m_proportion(PROPORTION_UNKNOWN) | 52 m_proportion(PROPORTION_UNKNOWN) |
50 { | 53 { |
54 init(path); | |
55 } | |
56 | |
57 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, | |
58 int channels, | |
59 Normalisation norm) : | |
60 m_model(0), | |
61 m_temporaryWriter(0), | |
62 m_targetWriter(0), | |
63 m_reader(0), | |
64 m_normalisation(norm), | |
65 m_sampleRate(sampleRate), | |
66 m_channels(channels), | |
67 m_frameCount(0), | |
68 m_startFrame(0), | |
69 m_proportion(PROPORTION_UNKNOWN) | |
70 { | |
71 init(); | |
72 } | |
73 | |
74 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, | |
75 int channels) : | |
76 m_model(0), | |
77 m_temporaryWriter(0), | |
78 m_targetWriter(0), | |
79 m_reader(0), | |
80 m_normalisation(Normalisation::None), | |
81 m_sampleRate(sampleRate), | |
82 m_channels(channels), | |
83 m_frameCount(0), | |
84 m_startFrame(0), | |
85 m_proportion(PROPORTION_UNKNOWN) | |
86 { | |
87 init(); | |
88 } | |
89 | |
90 void | |
91 WritableWaveFileModel::init(QString path) | |
92 { | |
51 if (path.isEmpty()) { | 93 if (path.isEmpty()) { |
52 try { | 94 try { |
95 // Temp dir is exclusive to this run of the application, | |
96 // so the filename only needs to be unique within that - | |
97 // model ID should be ok | |
53 QDir dir(TempDirectory::getInstance()->getPath()); | 98 QDir dir(TempDirectory::getInstance()->getPath()); |
54 path = dir.filePath(QString("written_%1.wav") | 99 path = dir.filePath(QString("written_%1.wav").arg(getId())); |
55 .arg((intptr_t)this)); | 100 } catch (const DirectoryCreationFailed &f) { |
56 } catch (DirectoryCreationFailed f) { | 101 SVCERR << "WritableWaveFileModel: Failed to create temporary directory" << endl; |
57 cerr << "WritableWaveFileModel: Failed to create temporary directory" << endl; | |
58 return; | 102 return; |
59 } | 103 } |
60 } | 104 } |
61 | 105 |
62 // Write directly to the target file, so that we can do | 106 m_targetPath = path; |
63 // incremental writes and concurrent reads | 107 m_temporaryPath = ""; |
64 m_writer = new WavFileWriter(path, sampleRate, channels, | 108 |
65 WavFileWriter::WriteToTarget); | 109 // We don't delete or null-out writer/reader members after |
66 if (!m_writer->isOK()) { | 110 // failures here - they are all deleted in the dtor, and the |
67 cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl; | 111 // presence/existence of the model is what's used to determine |
68 delete m_writer; | 112 // whether to go ahead, not the writer/readers. If the model is |
69 m_writer = 0; | 113 // non-null, then the necessary writer/readers must be OK, as the |
70 return; | 114 // model is the last thing initialised |
71 } | 115 |
72 | 116 m_targetWriter = new WavFileWriter(m_targetPath, m_sampleRate, m_channels, |
73 FileSource source(m_writer->getPath()); | 117 WavFileWriter::WriteToTarget); |
118 | |
119 if (!m_targetWriter->isOK()) { | |
120 SVCERR << "WritableWaveFileModel: Error in creating WAV file writer: " << m_targetWriter->getError() << endl; | |
121 return; | |
122 } | |
123 | |
124 if (m_normalisation != Normalisation::None) { | |
125 | |
126 // Temp dir is exclusive to this run of the application, so | |
127 // the filename only needs to be unique within that | |
128 QDir dir(TempDirectory::getInstance()->getPath()); | |
129 m_temporaryPath = dir.filePath(QString("prenorm_%1.wav").arg(getId())); | |
130 | |
131 m_temporaryWriter = new WavFileWriter | |
132 (m_temporaryPath, m_sampleRate, m_channels, | |
133 WavFileWriter::WriteToTarget); | |
134 | |
135 if (!m_temporaryWriter->isOK()) { | |
136 SVCERR << "WritableWaveFileModel: Error in creating temporary WAV file writer: " << m_temporaryWriter->getError() << endl; | |
137 return; | |
138 } | |
139 } | |
140 | |
141 FileSource source(m_targetPath); | |
74 | 142 |
75 m_reader = new WavFileReader(source, true); | 143 m_reader = new WavFileReader(source, true); |
76 if (!m_reader->getError().isEmpty()) { | 144 if (!m_reader->getError().isEmpty()) { |
77 cerr << "WritableWaveFileModel: Error in creating wave file reader" << endl; | 145 SVCERR << "WritableWaveFileModel: Error in creating wave file reader: " << m_reader->getError() << endl; |
78 delete m_reader; | |
79 m_reader = 0; | |
80 return; | 146 return; |
81 } | 147 } |
82 | 148 |
83 m_model = new ReadOnlyWaveFileModel(source, m_reader); | 149 m_model = new ReadOnlyWaveFileModel(source, m_reader); |
84 if (!m_model->isOK()) { | 150 if (!m_model->isOK()) { |
85 cerr << "WritableWaveFileModel: Error in creating wave file model" << endl; | 151 SVCERR << "WritableWaveFileModel: Error in creating wave file model" << endl; |
86 delete m_model; | 152 delete m_model; |
87 m_model = 0; | 153 m_model = 0; |
88 delete m_reader; | |
89 m_reader = 0; | |
90 return; | 154 return; |
91 } | 155 } |
92 m_model->setStartFrame(m_startFrame); | 156 m_model->setStartFrame(m_startFrame); |
93 | 157 |
94 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | 158 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); |
97 } | 161 } |
98 | 162 |
99 WritableWaveFileModel::~WritableWaveFileModel() | 163 WritableWaveFileModel::~WritableWaveFileModel() |
100 { | 164 { |
101 delete m_model; | 165 delete m_model; |
102 delete m_writer; | 166 delete m_targetWriter; |
167 delete m_temporaryWriter; | |
103 delete m_reader; | 168 delete m_reader; |
104 } | 169 } |
105 | 170 |
106 void | 171 void |
107 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame) | 172 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame) |
108 { | 173 { |
109 m_startFrame = startFrame; | 174 m_startFrame = startFrame; |
110 if (m_model) m_model->setStartFrame(startFrame); | 175 if (m_model) { |
176 m_model->setStartFrame(startFrame); | |
177 } | |
111 } | 178 } |
112 | 179 |
113 bool | 180 bool |
114 WritableWaveFileModel::addSamples(float **samples, sv_frame_t count) | 181 WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t count) |
115 { | 182 { |
116 if (!m_writer) return false; | 183 if (!m_model) return false; |
117 | 184 |
118 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | 185 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL |
119 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl; | 186 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl; |
120 #endif | 187 #endif |
121 | 188 |
122 if (!m_writer->writeSamples(samples, count)) { | 189 WavFileWriter *writer = m_targetWriter; |
123 cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl; | 190 if (m_normalisation != Normalisation::None) { |
191 writer = m_temporaryWriter; | |
192 } | |
193 | |
194 if (!writer->writeSamples(samples, count)) { | |
195 SVCERR << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << writer->getError() << endl; | |
124 return false; | 196 return false; |
125 } | 197 } |
126 | 198 |
127 m_frameCount += count; | 199 m_frameCount += count; |
128 | 200 |
129 static int updateCounter = 0; | 201 if (m_normalisation == Normalisation::None) { |
130 | 202 if (m_reader->getChannelCount() == 0) { |
131 if (m_reader && m_reader->getChannelCount() == 0) { | 203 m_reader->updateFrameCount(); |
132 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | 204 } |
133 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << endl; | |
134 #endif | |
135 m_reader->updateFrameCount(); | |
136 } else if (++updateCounter == 100) { | |
137 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | |
138 SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << endl; | |
139 #endif | |
140 if (m_reader) m_reader->updateFrameCount(); | |
141 updateCounter = 0; | |
142 } | 205 } |
143 | 206 |
144 return true; | 207 return true; |
208 } | |
209 | |
210 void | |
211 WritableWaveFileModel::updateModel() | |
212 { | |
213 if (!m_model) return; | |
214 | |
215 m_reader->updateFrameCount(); | |
145 } | 216 } |
146 | 217 |
147 bool | 218 bool |
148 WritableWaveFileModel::isOK() const | 219 WritableWaveFileModel::isOK() const |
149 { | 220 { |
150 bool ok = (m_writer && m_writer->isOK()); | 221 return (m_model && m_model->isOK()); |
151 // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl; | |
152 return ok; | |
153 } | 222 } |
154 | 223 |
155 bool | 224 bool |
156 WritableWaveFileModel::isReady(int *completion) const | 225 WritableWaveFileModel::isReady(int *completion) const |
157 { | 226 { |
174 } | 243 } |
175 | 244 |
176 void | 245 void |
177 WritableWaveFileModel::writeComplete() | 246 WritableWaveFileModel::writeComplete() |
178 { | 247 { |
179 if (m_reader) m_reader->updateDone(); | 248 if (!m_model) return; |
249 | |
250 if (m_normalisation == Normalisation::None) { | |
251 m_targetWriter->close(); | |
252 } else { | |
253 m_temporaryWriter->close(); | |
254 normaliseToTarget(); | |
255 } | |
256 | |
257 m_reader->updateDone(); | |
180 m_proportion = 100; | 258 m_proportion = 100; |
181 emit modelChanged(); | 259 emit modelChanged(); |
182 } | 260 } |
183 | 261 |
262 void | |
263 WritableWaveFileModel::normaliseToTarget() | |
264 { | |
265 if (m_temporaryPath == "") { | |
266 SVCERR << "WritableWaveFileModel::normaliseToTarget: No temporary path available" << endl; | |
267 return; | |
268 } | |
269 | |
270 WavFileReader normalisingReader(m_temporaryPath, false, | |
271 WavFileReader::Normalisation::Peak); | |
272 | |
273 if (!normalisingReader.getError().isEmpty()) { | |
274 SVCERR << "WritableWaveFileModel: Error in creating normalising reader: " << normalisingReader.getError() << endl; | |
275 return; | |
276 } | |
277 | |
278 sv_frame_t frame = 0; | |
279 sv_frame_t block = 65536; | |
280 sv_frame_t count = normalisingReader.getFrameCount(); | |
281 | |
282 while (frame < count) { | |
283 auto frames = normalisingReader.getInterleavedFrames(frame, block); | |
284 if (!m_targetWriter->putInterleavedFrames(frames)) { | |
285 SVCERR << "ERROR: WritableWaveFileModel::normaliseToTarget: writer failed: " << m_targetWriter->getError() << endl; | |
286 return; | |
287 } | |
288 frame += block; | |
289 } | |
290 | |
291 m_targetWriter->close(); | |
292 | |
293 delete m_temporaryWriter; | |
294 m_temporaryWriter = 0; | |
295 QFile::remove(m_temporaryPath); | |
296 } | |
297 | |
184 sv_frame_t | 298 sv_frame_t |
185 WritableWaveFileModel::getFrameCount() const | 299 WritableWaveFileModel::getFrameCount() const |
186 { | 300 { |
187 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl; | 301 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl; |
188 return m_frameCount; | 302 return m_frameCount; |
189 } | 303 } |
190 | 304 |
191 vector<float> | 305 floatvec_t |
192 WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const | 306 WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const |
193 { | 307 { |
194 if (!m_model || m_model->getChannelCount() == 0) return {}; | 308 if (!m_model || m_model->getChannelCount() == 0) return {}; |
195 return m_model->getData(channel, start, count); | 309 return m_model->getData(channel, start, count); |
196 } | 310 } |
197 | 311 |
198 vector<vector<float>> | 312 vector<floatvec_t> |
199 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel, | 313 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel, |
200 sv_frame_t start, sv_frame_t count) const | 314 sv_frame_t start, sv_frame_t count) const |
201 { | 315 { |
202 if (!m_model || m_model->getChannelCount() == 0) return {}; | 316 if (!m_model || m_model->getChannelCount() == 0) return {}; |
203 return m_model->getMultiChannelData(fromchannel, tochannel, start, count); | 317 return m_model->getMultiChannelData(fromchannel, tochannel, start, count); |
239 // We just write this out as if it were a normal wave file. | 353 // We just write this out as if it were a normal wave file. |
240 | 354 |
241 Model::toXml | 355 Model::toXml |
242 (out, indent, | 356 (out, indent, |
243 QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2") | 357 QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2") |
244 .arg(encodeEntities(m_writer->getPath())) | 358 .arg(encodeEntities(m_targetPath)) |
245 .arg(extraAttributes)); | 359 .arg(extraAttributes)); |
246 } | 360 } |
247 | 361 |