annotate data/fileio/CodedAudioFileReader.cpp @ 1306:b325e91505b5 mp3-gapless

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