Mercurial > hg > svcore
comparison data/model/WritableWaveFileModel.cpp @ 1520:954d0cf29ca7 import-audio-data
Switch the normalisation option in WritableWaveFileModel from normalising on read to normalising on write, so that the saved file is already normalised and therefore can be read again without having to remember to normalise it
author | Chris Cannam |
---|---|
date | Wed, 12 Sep 2018 13:56:56 +0100 |
parents | 925d205c39b4 |
children | 70e172e6cc59 |
comparison
equal
deleted
inserted
replaced
1519:fbe8afdfa8a6 | 1520:954d0cf29ca7 |
---|---|
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 sv_samplerate_t sampleRate, | |
40 int channels, | 41 int channels, |
41 QString path, | 42 Normalisation norm) : |
42 bool normaliseOnRead) : | |
43 m_model(0), | 43 m_model(0), |
44 m_writer(0), | 44 m_temporaryWriter(0), |
45 m_targetWriter(0), | |
45 m_reader(0), | 46 m_reader(0), |
47 m_normalisation(norm), | |
46 m_sampleRate(sampleRate), | 48 m_sampleRate(sampleRate), |
47 m_channels(channels), | 49 m_channels(channels), |
48 m_frameCount(0), | 50 m_frameCount(0), |
49 m_startFrame(0), | 51 m_startFrame(0), |
50 m_proportion(PROPORTION_UNKNOWN) | 52 m_proportion(PROPORTION_UNKNOWN) |
51 { | 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 { | |
52 if (path.isEmpty()) { | 93 if (path.isEmpty()) { |
53 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 | |
54 QDir dir(TempDirectory::getInstance()->getPath()); | 98 QDir dir(TempDirectory::getInstance()->getPath()); |
55 path = dir.filePath(QString("written_%1.wav") | 99 path = dir.filePath(QString("written_%1.wav").arg(getId())); |
56 .arg((intptr_t)this)); | |
57 } catch (const DirectoryCreationFailed &f) { | 100 } catch (const DirectoryCreationFailed &f) { |
58 SVCERR << "WritableWaveFileModel: Failed to create temporary directory" << endl; | 101 SVCERR << "WritableWaveFileModel: Failed to create temporary directory" << endl; |
59 return; | 102 return; |
60 } | 103 } |
61 } | 104 } |
62 | 105 |
63 // Write directly to the target file, so that we can do | 106 m_targetPath = path; |
64 // incremental writes and concurrent reads | 107 m_temporaryPath = ""; |
65 m_writer = new WavFileWriter(path, sampleRate, channels, | 108 |
66 WavFileWriter::WriteToTarget); | 109 // We don't delete or null-out writer/reader members after |
67 if (!m_writer->isOK()) { | 110 // failures here - they are all deleted in the dtor, and the |
68 SVCERR << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl; | 111 // presence/existence of the model is what's used to determine |
69 delete m_writer; | 112 // whether to go ahead, not the writer/readers. If the model is |
70 m_writer = 0; | 113 // non-null, then the necessary writer/readers must be OK, as the |
71 return; | 114 // model is the last thing initialised |
72 } | 115 |
73 | 116 m_targetWriter = new WavFileWriter(m_targetPath, m_sampleRate, m_channels, |
74 FileSource source(m_writer->getPath()); | 117 WavFileWriter::WriteToTarget); |
75 | 118 |
76 m_reader = new WavFileReader(source, true, normaliseOnRead); | 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); | |
142 | |
143 m_reader = new WavFileReader(source, true); | |
77 if (!m_reader->getError().isEmpty()) { | 144 if (!m_reader->getError().isEmpty()) { |
78 SVCERR << "WritableWaveFileModel: Error in creating wave file reader" << endl; | 145 SVCERR << "WritableWaveFileModel: Error in creating wave file reader: " << m_reader->getError() << endl; |
79 delete m_reader; | |
80 m_reader = 0; | |
81 return; | 146 return; |
82 } | 147 } |
83 | 148 |
84 m_model = new ReadOnlyWaveFileModel(source, m_reader); | 149 m_model = new ReadOnlyWaveFileModel(source, m_reader); |
85 if (!m_model->isOK()) { | 150 if (!m_model->isOK()) { |
86 SVCERR << "WritableWaveFileModel: Error in creating wave file model" << endl; | 151 SVCERR << "WritableWaveFileModel: Error in creating wave file model" << endl; |
87 delete m_model; | 152 delete m_model; |
88 m_model = 0; | 153 m_model = 0; |
89 delete m_reader; | |
90 m_reader = 0; | |
91 return; | 154 return; |
92 } | 155 } |
93 m_model->setStartFrame(m_startFrame); | 156 m_model->setStartFrame(m_startFrame); |
94 | 157 |
95 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | 158 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); |
98 } | 161 } |
99 | 162 |
100 WritableWaveFileModel::~WritableWaveFileModel() | 163 WritableWaveFileModel::~WritableWaveFileModel() |
101 { | 164 { |
102 delete m_model; | 165 delete m_model; |
103 delete m_writer; | 166 delete m_targetWriter; |
167 delete m_temporaryWriter; | |
104 delete m_reader; | 168 delete m_reader; |
105 } | 169 } |
106 | 170 |
107 void | 171 void |
108 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame) | 172 WritableWaveFileModel::setStartFrame(sv_frame_t startFrame) |
109 { | 173 { |
110 m_startFrame = startFrame; | 174 m_startFrame = startFrame; |
111 if (m_model) m_model->setStartFrame(startFrame); | 175 if (m_model) { |
176 m_model->setStartFrame(startFrame); | |
177 } | |
112 } | 178 } |
113 | 179 |
114 bool | 180 bool |
115 WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t count) | 181 WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t count) |
116 { | 182 { |
117 if (!m_writer) return false; | 183 if (!m_model) return false; |
118 | 184 |
119 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL | 185 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL |
120 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl; | 186 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl; |
121 #endif | 187 #endif |
122 | 188 |
123 if (!m_writer->writeSamples(samples, count)) { | 189 WavFileWriter *writer = m_targetWriter; |
124 SVCERR << "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; | |
125 return false; | 196 return false; |
126 } | 197 } |
127 | 198 |
128 m_frameCount += count; | 199 m_frameCount += count; |
129 | 200 |
130 if (m_reader && m_reader->getChannelCount() == 0) { | 201 if (m_normalisation == Normalisation::None) { |
131 m_reader->updateFrameCount(); | 202 if (m_reader->getChannelCount() == 0) { |
203 m_reader->updateFrameCount(); | |
204 } | |
132 } | 205 } |
133 | 206 |
134 return true; | 207 return true; |
135 } | 208 } |
136 | 209 |
137 void | 210 void |
138 WritableWaveFileModel::updateModel() | 211 WritableWaveFileModel::updateModel() |
139 { | 212 { |
140 if (m_reader) { | 213 if (!m_model) return; |
141 m_reader->updateFrameCount(); | 214 |
142 } | 215 m_reader->updateFrameCount(); |
143 } | 216 } |
144 | 217 |
145 bool | 218 bool |
146 WritableWaveFileModel::isOK() const | 219 WritableWaveFileModel::isOK() const |
147 { | 220 { |
148 bool ok = (m_writer && m_writer->isOK()); | 221 return (m_model && m_model->isOK()); |
149 // SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl; | |
150 return ok; | |
151 } | 222 } |
152 | 223 |
153 bool | 224 bool |
154 WritableWaveFileModel::isReady(int *completion) const | 225 WritableWaveFileModel::isReady(int *completion) const |
155 { | 226 { |
172 } | 243 } |
173 | 244 |
174 void | 245 void |
175 WritableWaveFileModel::writeComplete() | 246 WritableWaveFileModel::writeComplete() |
176 { | 247 { |
177 m_writer->close(); | 248 if (!m_model) return; |
178 if (m_reader) m_reader->updateDone(); | 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(); | |
179 m_proportion = 100; | 258 m_proportion = 100; |
180 emit modelChanged(); | 259 emit modelChanged(); |
260 } | |
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); | |
181 } | 296 } |
182 | 297 |
183 sv_frame_t | 298 sv_frame_t |
184 WritableWaveFileModel::getFrameCount() const | 299 WritableWaveFileModel::getFrameCount() const |
185 { | 300 { |
238 // 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. |
239 | 354 |
240 Model::toXml | 355 Model::toXml |
241 (out, indent, | 356 (out, indent, |
242 QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2") | 357 QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2") |
243 .arg(encodeEntities(m_writer->getPath())) | 358 .arg(encodeEntities(m_targetPath)) |
244 .arg(extraAttributes)); | 359 .arg(extraAttributes)); |
245 } | 360 } |
246 | 361 |