annotate data/fileio/CodedAudioFileReader.cpp @ 1881:b504df98c3be

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