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