annotate data/fileio/CodedAudioFileReader.cpp @ 1288:5ef9b4d4bbdb 3.0-integration

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