annotate data/fileio/CodedAudioFileReader.cpp @ 1773:fadd9f8aaa27

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