1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006-2007 Chris Cannam and QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "CodedAudioFileReader.h"
17
18 #include "WavFileReader.h"
19 #include "base/TempDirectory.h"
20 #include "base/Exceptions.h"
21 #include "base/Profiler.h"
22 #include "base/Serialiser.h"
23 #include "base/StorageAdviser.h"
24
25 #include <bqresample/Resampler.h>
26
27 #include <stdint.h>
28 #include <iostream>
29 #include <QDir>
30 #include <QMutexLocker>
31
32 using namespace std;
33
CodedAudioFileReader(CacheMode cacheMode,sv_samplerate_t targetRate,bool normalised)34 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode,
35 sv_samplerate_t targetRate,
36 bool normalised) :
37 m_cacheMode(cacheMode),
38 m_initialised(false),
39 m_serialiser(nullptr),
40 m_fileRate(0),
41 m_cacheFileWritePtr(nullptr),
42 m_cacheFileReader(nullptr),
43 m_cacheWriteBuffer(nullptr),
44 m_cacheWriteBufferIndex(0),
45 m_cacheWriteBufferFrames(65536),
46 m_resampler(nullptr),
47 m_resampleBuffer(nullptr),
48 m_resampleBufferFrames(0),
49 m_fileFrameCount(0),
50 m_normalised(normalised),
51 m_max(0.f),
52 m_gain(1.f),
53 m_trimFromStart(0),
54 m_trimFromEnd(0),
55 m_clippedCount(0),
56 m_firstNonzero(0),
57 m_lastNonzero(0)
58 {
59 SVDEBUG << "CodedAudioFileReader:: cache mode: " << cacheMode
60 << " (" << (cacheMode == CacheInTemporaryFile
61 ? "CacheInTemporaryFile" : "CacheInMemory") << ")"
62 << ", rate: " << targetRate
63 << (targetRate == 0 ? " (use source rate)" : "")
64 << ", normalised: " << normalised << endl;
65
66 m_frameCount = 0;
67 m_sampleRate = targetRate;
68 }
69
~CodedAudioFileReader()70 CodedAudioFileReader::~CodedAudioFileReader()
71 {
72 QMutexLocker locker(&m_cacheMutex);
73
74 if (m_serialiser) endSerialised();
75
76 if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
77
78 SVDEBUG << "CodedAudioFileReader::~CodedAudioFileReader: deleting cache file reader" << endl;
79
80 delete m_cacheFileReader;
81 delete[] m_cacheWriteBuffer;
82
83 if (m_cacheFileName != "") {
84 SVDEBUG << "CodedAudioFileReader::~CodedAudioFileReader: deleting cache file " << m_cacheFileName << endl;
85 if (!QFile(m_cacheFileName).remove()) {
86 SVDEBUG << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName << "\"" << endl;
87 }
88 }
89
90 delete m_resampler;
91 delete[] m_resampleBuffer;
92
93 if (!m_data.empty()) {
94 StorageAdviser::notifyDoneAllocation
95 (StorageAdviser::MemoryAllocation,
96 (m_data.size() * sizeof(float)) / 1024);
97 }
98 }
99
100 void
setFramesToTrim(sv_frame_t fromStart,sv_frame_t fromEnd)101 CodedAudioFileReader::setFramesToTrim(sv_frame_t fromStart, sv_frame_t fromEnd)
102 {
103 m_trimFromStart = fromStart;
104 m_trimFromEnd = fromEnd;
105 }
106
107 void
startSerialised(QString id)108 CodedAudioFileReader::startSerialised(QString id)
109 {
110 SVDEBUG << "CodedAudioFileReader(" << this << ")::startSerialised: id = " << id << endl;
111
112 delete m_serialiser;
113 m_serialiser = new Serialiser(id);
114 }
115
116 void
endSerialised()117 CodedAudioFileReader::endSerialised()
118 {
119 SVDEBUG << "CodedAudioFileReader(" << this << ")::endSerialised: id = " << (m_serialiser ? m_serialiser->getId() : "(none)") << endl;
120
121 delete m_serialiser;
122 m_serialiser = nullptr;
123 }
124
125 void
initialiseDecodeCache()126 CodedAudioFileReader::initialiseDecodeCache()
127 {
128 QMutexLocker locker(&m_cacheMutex);
129
130 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: file rate = " << m_fileRate << endl;
131
132 if (m_channelCount == 0) {
133 SVCERR << "CodedAudioFileReader::initialiseDecodeCache: No channel count set!" << endl;
134 throw std::logic_error("No channel count set");
135 }
136
137 if (m_fileRate == 0) {
138 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << endl;
139 throw FileOperationFailed("(coded file)", "sample rate unknown (bug in subclass implementation?)");
140 }
141 if (m_sampleRate == 0) {
142 m_sampleRate = m_fileRate;
143 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: rate (from file) = " << m_fileRate << endl;
144 }
145 if (m_fileRate != m_sampleRate) {
146 SVDEBUG << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << endl;
147
148 breakfastquay::Resampler::Parameters params;
149 params.quality = breakfastquay::Resampler::FastestTolerable;
150 params.maxBufferSize = int(m_cacheWriteBufferFrames);
151 params.initialSampleRate = m_fileRate;
152 m_resampler = new breakfastquay::Resampler(params, m_channelCount);
153
154 double ratio = m_sampleRate / m_fileRate;
155 m_resampleBufferFrames = int(ceil(double(m_cacheWriteBufferFrames) *
156 ratio + 1));
157 m_resampleBuffer = new float[m_resampleBufferFrames * m_channelCount];
158 }
159
160 m_cacheWriteBuffer = new float[m_cacheWriteBufferFrames * m_channelCount];
161 m_cacheWriteBufferIndex = 0;
162
163 if (m_cacheMode == CacheInTemporaryFile) {
164
165 try {
166 QDir dir(TempDirectory::getInstance()->getPath());
167 m_cacheFileName = dir.filePath(QString("decoded_%1.w64")
168 .arg((intptr_t)this));
169
170 SF_INFO fileInfo;
171 int fileRate = int(round(m_sampleRate));
172 if (m_sampleRate != sv_samplerate_t(fileRate)) {
173 SVDEBUG << "CodedAudioFileReader: WARNING: Non-integer sample rate "
174 << m_sampleRate << " presented for writing, rounding to " << fileRate
175 << endl;
176 }
177 fileInfo.samplerate = fileRate;
178 fileInfo.channels = m_channelCount;
179
180 // Previously we were writing SF_FORMAT_PCM_16 and in a
181 // comment I wrote: "No point in writing 24-bit or float;
182 // generally this class is used for decoding files that
183 // have come from a 16 bit source or that decode to only
184 // 16 bits anyway." That was naive -- we want to preserve
185 // the original values to the same float precision that we
186 // use internally. Saving PCM_16 obviously doesn't
187 // preserve values for sources at bit depths greater than
188 // 16, but it also doesn't always do so for sources at bit
189 // depths less than 16.
190 //
191 // (This came to light with a bug in libsndfile 1.0.26,
192 // which always reports every file as non-seekable, so
193 // that coded readers were being used even for WAV
194 // files. This changed the values that came from PCM_8 WAV
195 // sources, breaking Sonic Annotator's output comparison
196 // tests.)
197 //
198 // So: now we write floats.
199 fileInfo.format = SF_FORMAT_W64 | SF_FORMAT_FLOAT;
200
201 #ifdef Q_OS_WIN
202 m_cacheFileWritePtr = sf_wchar_open
203 ((LPCWSTR)m_cacheFileName.utf16(), SFM_WRITE, &fileInfo);
204 #else
205 m_cacheFileWritePtr = sf_open
206 (m_cacheFileName.toLocal8Bit(), SFM_WRITE, &fileInfo);
207 #endif
208
209 if (m_cacheFileWritePtr) {
210
211 // Ideally we would do this now only if we were in a
212 // threaded mode -- creating the reader later if we're
213 // not threaded -- but we don't have access to that
214 // information here
215
216 m_cacheFileReader = new WavFileReader(m_cacheFileName);
217
218 if (!m_cacheFileReader->isOK()) {
219 SVDEBUG << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError() << endl;
220 delete m_cacheFileReader;
221 m_cacheFileReader = nullptr;
222 m_cacheMode = CacheInMemory;
223 sf_close(m_cacheFileWritePtr);
224 }
225
226 } else {
227 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: failed to open cache file \"" << m_cacheFileName << "\" (" << m_channelCount << " channels, sample rate " << m_sampleRate << " for writing, falling back to in-memory cache" << endl;
228 m_cacheMode = CacheInMemory;
229 }
230
231 } catch (const DirectoryCreationFailed &f) {
232 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << endl;
233 m_cacheMode = CacheInMemory;
234 }
235 }
236
237 if (m_cacheMode == CacheInMemory) {
238 m_data.clear();
239 }
240
241 if (m_trimFromEnd >= (m_cacheWriteBufferFrames * m_channelCount)) {
242 SVCERR << "WARNING: CodedAudioFileReader::setSamplesToTrim: Can't handle trimming more frames from end (" << m_trimFromEnd << ") than can be stored in cache-write buffer (" << (m_cacheWriteBufferFrames * m_channelCount) << "), won't trim anything from the end after all";
243 m_trimFromEnd = 0;
244 }
245
246 m_initialised = true;
247 }
248
249 void
addSamplesToDecodeCache(float ** samples,sv_frame_t nframes)250 CodedAudioFileReader::addSamplesToDecodeCache(float **samples, sv_frame_t nframes)
251 {
252 QMutexLocker locker(&m_cacheMutex);
253
254 if (!m_initialised) return;
255
256 for (sv_frame_t i = 0; i < nframes; ++i) {
257
258 if (m_trimFromStart > 0) {
259 --m_trimFromStart;
260 continue;
261 }
262
263 for (int c = 0; c < m_channelCount; ++c) {
264
265 float sample = samples[c][i];
266 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
267
268 }
269
270 pushCacheWriteBufferMaybe(false);
271 }
272 }
273
274 void
addSamplesToDecodeCache(float * samples,sv_frame_t nframes)275 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, sv_frame_t nframes)
276 {
277 QMutexLocker locker(&m_cacheMutex);
278
279 if (!m_initialised) return;
280
281 for (sv_frame_t i = 0; i < nframes; ++i) {
282
283 if (m_trimFromStart > 0) {
284 --m_trimFromStart;
285 continue;
286 }
287
288 for (int c = 0; c < m_channelCount; ++c) {
289
290 float sample = samples[i * m_channelCount + c];
291
292 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
293 }
294
295 pushCacheWriteBufferMaybe(false);
296 }
297 }
298
299 void
addSamplesToDecodeCache(const floatvec_t & samples)300 CodedAudioFileReader::addSamplesToDecodeCache(const floatvec_t &samples)
301 {
302 QMutexLocker locker(&m_cacheMutex);
303
304 if (!m_initialised) return;
305
306 for (float sample: samples) {
307
308 if (m_trimFromStart > 0) {
309 --m_trimFromStart;
310 continue;
311 }
312
313 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
314
315 pushCacheWriteBufferMaybe(false);
316 }
317 }
318
319 void
finishDecodeCache()320 CodedAudioFileReader::finishDecodeCache()
321 {
322 QMutexLocker locker(&m_cacheMutex);
323
324 Profiler profiler("CodedAudioFileReader::finishDecodeCache");
325
326 if (!m_initialised) {
327 SVDEBUG << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << endl;
328 return;
329 }
330
331 pushCacheWriteBufferMaybe(true);
332
333 delete[] m_cacheWriteBuffer;
334 m_cacheWriteBuffer = nullptr;
335
336 delete[] m_resampleBuffer;
337 m_resampleBuffer = nullptr;
338
339 delete m_resampler;
340 m_resampler = nullptr;
341
342 if (m_cacheMode == CacheInTemporaryFile) {
343
344 sf_close(m_cacheFileWritePtr);
345 m_cacheFileWritePtr = nullptr;
346 if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
347
348 } else {
349 // I know, I know, we already allocated it...
350 StorageAdviser::notifyPlannedAllocation
351 (StorageAdviser::MemoryAllocation,
352 (m_data.size() * sizeof(float)) / 1024);
353 }
354
355 SVDEBUG << "CodedAudioFileReader: File decodes to " << m_fileFrameCount
356 << " frames" << endl;
357 if (m_fileFrameCount != m_frameCount) {
358 SVDEBUG << "CodedAudioFileReader: Resampled to " << m_frameCount
359 << " frames" << endl;
360 }
361 SVDEBUG << "CodedAudioFileReader: Signal abs max is " << m_max
362 << ", " << m_clippedCount
363 << " samples clipped, first non-zero frame is at "
364 << m_firstNonzero << ", last at " << m_lastNonzero << endl;
365 if (m_normalised) {
366 SVDEBUG << "CodedAudioFileReader: Normalising, gain is " << m_gain << endl;
367 }
368 }
369
370 void
pushCacheWriteBufferMaybe(bool final)371 CodedAudioFileReader::pushCacheWriteBufferMaybe(bool final)
372 {
373 if (final ||
374 (m_cacheWriteBufferIndex ==
375 m_cacheWriteBufferFrames * m_channelCount)) {
376
377 if (m_trimFromEnd > 0) {
378
379 sv_frame_t framesToPush =
380 (m_cacheWriteBufferIndex / m_channelCount) - m_trimFromEnd;
381
382 if (framesToPush <= 0 && !final) {
383 // This won't do, the buffer is full so we have to push
384 // something. Should have checked for this earlier
385 throw std::logic_error("Buffer full but nothing to push");
386 }
387
388 pushBuffer(m_cacheWriteBuffer, framesToPush, final);
389
390 m_cacheWriteBufferIndex -= framesToPush * m_channelCount;
391
392 for (sv_frame_t i = 0; i < m_cacheWriteBufferIndex; ++i) {
393 m_cacheWriteBuffer[i] =
394 m_cacheWriteBuffer[framesToPush * m_channelCount + i];
395 }
396
397 } else {
398
399 pushBuffer(m_cacheWriteBuffer,
400 m_cacheWriteBufferIndex / m_channelCount,
401 final);
402
403 m_cacheWriteBufferIndex = 0;
404 }
405
406 if (m_cacheFileReader) {
407 m_cacheFileReader->updateFrameCount();
408 }
409 }
410 }
411
412 sv_frame_t
pushBuffer(float * buffer,sv_frame_t sz,bool final)413 CodedAudioFileReader::pushBuffer(float *buffer, sv_frame_t sz, bool final)
414 {
415 m_fileFrameCount += sz;
416
417 double ratio = 1.0;
418 if (m_resampler && m_fileRate != 0) {
419 ratio = m_sampleRate / m_fileRate;
420 }
421
422 if (ratio != 1.0) {
423 pushBufferResampling(buffer, sz, ratio, final);
424 } else {
425 pushBufferNonResampling(buffer, sz);
426 }
427
428 return sz;
429 }
430
431 void
pushBufferNonResampling(float * buffer,sv_frame_t sz)432 CodedAudioFileReader::pushBufferNonResampling(float *buffer, sv_frame_t sz)
433 {
434 float clip = 1.0;
435 sv_frame_t count = sz * m_channelCount;
436
437 // statistics
438 for (sv_frame_t j = 0; j < sz; ++j) {
439 for (int c = 0; c < m_channelCount; ++c) {
440 sv_frame_t i = j * m_channelCount + c;
441 float v = buffer[i];
442 if (!m_normalised) {
443 if (v > clip) {
444 buffer[i] = clip;
445 ++m_clippedCount;
446 } else if (v < -clip) {
447 buffer[i] = -clip;
448 ++m_clippedCount;
449 }
450 }
451 v = fabsf(v);
452 if (v != 0.f) {
453 if (m_firstNonzero == 0) {
454 m_firstNonzero = m_frameCount;
455 }
456 m_lastNonzero = m_frameCount;
457 if (v > m_max) {
458 m_max = v;
459 }
460 }
461 }
462 ++m_frameCount;
463 }
464
465 if (m_max > 0.f) {
466 m_gain = 1.f / m_max; // used when normalising only
467 }
468
469 switch (m_cacheMode) {
470
471 case CacheInTemporaryFile:
472 if (sf_writef_float(m_cacheFileWritePtr, buffer, sz) < sz) {
473 sf_close(m_cacheFileWritePtr);
474 m_cacheFileWritePtr = nullptr;
475 throw InsufficientDiscSpace(TempDirectory::getInstance()->getPath());
476 }
477 break;
478
479 case CacheInMemory:
480 m_dataLock.lock();
481 try {
482 m_data.insert(m_data.end(), buffer, buffer + count);
483 } catch (const std::bad_alloc &e) {
484 m_data.clear();
485 SVCERR << "CodedAudioFileReader: Caught bad_alloc when trying to add " << count << " elements to buffer" << endl;
486 m_dataLock.unlock();
487 throw e;
488 }
489 m_dataLock.unlock();
490 break;
491 }
492 }
493
494 void
pushBufferResampling(float * buffer,sv_frame_t sz,double ratio,bool final)495 CodedAudioFileReader::pushBufferResampling(float *buffer, sv_frame_t sz,
496 double ratio, bool final)
497 {
498 // SVDEBUG << "pushBufferResampling: ratio = " << ratio << ", sz = " << sz << ", final = " << final << endl;
499
500 if (sz > 0) {
501
502 sv_frame_t out = m_resampler->resampleInterleaved
503 (m_resampleBuffer,
504 m_resampleBufferFrames,
505 buffer,
506 int(sz),
507 ratio,
508 false);
509
510 pushBufferNonResampling(m_resampleBuffer, out);
511 }
512
513 if (final) {
514
515 sv_frame_t padFrames = 1;
516 if (double(m_frameCount) / ratio < double(m_fileFrameCount)) {
517 padFrames = m_fileFrameCount - sv_frame_t(double(m_frameCount) / ratio) + 1;
518 }
519
520 sv_frame_t padSamples = padFrames * m_channelCount;
521
522 SVDEBUG << "CodedAudioFileReader::pushBufferResampling: frameCount = " << m_frameCount << ", equivFileFrames = " << double(m_frameCount) / ratio << ", m_fileFrameCount = " << m_fileFrameCount << ", padFrames = " << padFrames << ", padSamples = " << padSamples << endl;
523
524 float *padding = new float[padSamples];
525 for (sv_frame_t i = 0; i < padSamples; ++i) padding[i] = 0.f;
526
527 sv_frame_t out = m_resampler->resampleInterleaved
528 (m_resampleBuffer,
529 m_resampleBufferFrames,
530 padding,
531 int(padFrames),
532 ratio,
533 true);
534
535 SVDEBUG << "CodedAudioFileReader::pushBufferResampling: resampled padFrames to " << out << " frames" << endl;
536
537 sv_frame_t expected = sv_frame_t(round(double(m_fileFrameCount) * ratio));
538 if (m_frameCount + out > expected) {
539 out = expected - m_frameCount;
540 SVDEBUG << "CodedAudioFileReader::pushBufferResampling: clipping that to " << out << " to avoid producing more samples than desired" << endl;
541 }
542
543 pushBufferNonResampling(m_resampleBuffer, out);
544 delete[] padding;
545 }
546 }
547
548 floatvec_t
getInterleavedFrames(sv_frame_t start,sv_frame_t count) const549 CodedAudioFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const
550 {
551 // Lock is only required in CacheInMemory mode (the cache file
552 // reader is expected to be thread safe and manage its own
553 // locking)
554
555 if (!m_initialised) {
556 SVDEBUG << "CodedAudioFileReader::getInterleavedFrames: not initialised" << endl;
557 return {};
558 }
559
560 floatvec_t frames;
561
562 switch (m_cacheMode) {
563
564 case CacheInTemporaryFile:
565 if (m_cacheFileReader) {
566 frames = m_cacheFileReader->getInterleavedFrames(start, count);
567 }
568 break;
569
570 case CacheInMemory:
571 {
572 if (!isOK()) return {};
573 if (count == 0) return {};
574
575 sv_frame_t ix0 = start * m_channelCount;
576 sv_frame_t ix1 = ix0 + (count * m_channelCount);
577
578 // This lock used to be a QReadWriteLock, but it appears that
579 // its lock mechanism is significantly slower than QMutex so
580 // it's not a good idea in cases like this where we don't
581 // really have threads taking a long time to read concurrently
582 m_dataLock.lock();
583 sv_frame_t n = sv_frame_t(m_data.size());
584 if (ix0 > n) ix0 = n;
585 if (ix1 > n) ix1 = n;
586 frames = floatvec_t(m_data.begin() + ix0, m_data.begin() + ix1);
587 m_dataLock.unlock();
588 break;
589 }
590 }
591
592 if (m_normalised) {
593 for (auto &f: frames) f *= m_gain;
594 }
595
596 return frames;
597 }
598
599