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