annotate data/fileio/CodedAudioFileReader.cpp @ 1305:9f9f55a8af92 mp3-gapless

Add gapless flag to MP3FileReader, and implement trimming the delay samples from the start (padding is not yet trimmed from end)
author Chris Cannam
date Tue, 29 Nov 2016 11:35:56 +0000
parents 5cc969b236b0
children b325e91505b5
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
Chris@297 245 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 246
Chris@297 247 if (m_cacheWriteBufferIndex ==
Chris@297 248 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 249
Chris@297 250 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 251 m_cacheWriteBufferIndex = 0;
Chris@297 252 }
Chris@297 253
Chris@297 254 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 255 m_cacheFileReader) {
Chris@297 256 m_cacheFileReader->updateFrameCount();
Chris@297 257 }
Chris@297 258 }
Chris@297 259 }
Chris@297 260 }
Chris@297 261
Chris@297 262 void
Chris@1038 263 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, sv_frame_t nframes)
Chris@297 264 {
Chris@297 265 QMutexLocker locker(&m_cacheMutex);
Chris@297 266
Chris@297 267 if (!m_initialised) return;
Chris@297 268
Chris@1038 269 for (sv_frame_t i = 0; i < nframes; ++i) {
Chris@1305 270
Chris@1305 271 if (m_trimFromStart > 0) {
Chris@1305 272 --m_trimFromStart;
Chris@1305 273 continue;
Chris@1305 274 }
Chris@297 275
Chris@929 276 for (int c = 0; c < m_channelCount; ++c) {
Chris@297 277
Chris@297 278 float sample = samples[i * m_channelCount + c];
Chris@297 279
Chris@297 280 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@297 281
Chris@297 282 if (m_cacheWriteBufferIndex ==
Chris@297 283 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 284
Chris@297 285 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 286 m_cacheWriteBufferIndex = 0;
Chris@297 287 }
Chris@297 288
Chris@297 289 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 290 m_cacheFileReader) {
Chris@297 291 m_cacheFileReader->updateFrameCount();
Chris@297 292 }
Chris@297 293 }
Chris@297 294 }
Chris@297 295 }
Chris@297 296
Chris@297 297 void
Chris@1096 298 CodedAudioFileReader::addSamplesToDecodeCache(const vector<float> &samples)
Chris@297 299 {
Chris@297 300 QMutexLocker locker(&m_cacheMutex);
Chris@297 301
Chris@297 302 if (!m_initialised) return;
Chris@297 303
Chris@1038 304 for (float sample: samples) {
Chris@1305 305
Chris@1305 306 if (m_trimFromStart > 0) {
Chris@1305 307 --m_trimFromStart;
Chris@1305 308 continue;
Chris@1305 309 }
Chris@297 310
Chris@148 311 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 312
Chris@148 313 if (m_cacheWriteBufferIndex ==
Chris@148 314 m_cacheWriteBufferSize * m_channelCount) {
Chris@148 315
Chris@297 316 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@148 317 m_cacheWriteBufferIndex = 0;
Chris@266 318 }
Chris@265 319
Chris@266 320 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@266 321 m_cacheFileReader) {
Chris@266 322 m_cacheFileReader->updateFrameCount();
Chris@148 323 }
Chris@148 324 }
Chris@148 325 }
Chris@148 326
Chris@148 327 void
Chris@148 328 CodedAudioFileReader::finishDecodeCache()
Chris@148 329 {
Chris@263 330 QMutexLocker locker(&m_cacheMutex);
Chris@263 331
Chris@1295 332 Profiler profiler("CodedAudioFileReader::finishDecodeCache");
Chris@192 333
Chris@148 334 if (!m_initialised) {
Chris@1279 335 SVDEBUG << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << endl;
Chris@148 336 return;
Chris@148 337 }
Chris@148 338
Chris@920 339 pushBuffer(m_cacheWriteBuffer,
Chris@920 340 m_cacheWriteBufferIndex / m_channelCount,
Chris@920 341 true);
Chris@297 342
Chris@297 343 delete[] m_cacheWriteBuffer;
Chris@297 344 m_cacheWriteBuffer = 0;
Chris@297 345
Chris@297 346 delete[] m_resampleBuffer;
Chris@297 347 m_resampleBuffer = 0;
Chris@297 348
Chris@297 349 delete m_resampler;
Chris@297 350 m_resampler = 0;
Chris@297 351
Chris@297 352 if (m_cacheMode == CacheInTemporaryFile) {
Chris@1098 353
Chris@297 354 sf_close(m_cacheFileWritePtr);
Chris@297 355 m_cacheFileWritePtr = 0;
Chris@297 356 if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
Chris@1098 357
Chris@1098 358 } else {
Chris@1098 359 // I know, I know, we already allocated it...
Chris@1098 360 StorageAdviser::notifyPlannedAllocation
Chris@1098 361 (StorageAdviser::MemoryAllocation,
Chris@1098 362 (m_data.size() * sizeof(float)) / 1024);
Chris@297 363 }
Chris@1285 364
Chris@1285 365 SVDEBUG << "CodedAudioFileReader: File decodes to " << m_fileFrameCount
Chris@1285 366 << " frames" << endl;
Chris@1285 367 if (m_fileFrameCount != m_frameCount) {
Chris@1285 368 SVDEBUG << "CodedAudioFileReader: Resampled to " << m_frameCount
Chris@1285 369 << " frames" << endl;
Chris@1285 370 }
Chris@1285 371 SVDEBUG << "CodedAudioFileReader: Signal abs max is " << m_max
Chris@1285 372 << ", " << m_clippedCount
Chris@1285 373 << " samples clipped, first non-zero frame is at "
Chris@1286 374 << m_firstNonzero << ", last at " << m_lastNonzero << endl;
Chris@1285 375 if (m_normalised) {
Chris@1285 376 SVDEBUG << "CodedAudioFileReader: Normalising, gain is " << m_gain << endl;
Chris@1285 377 }
Chris@297 378 }
Chris@297 379
Chris@297 380 void
Chris@1038 381 CodedAudioFileReader::pushBuffer(float *buffer, sv_frame_t sz, bool final)
Chris@297 382 {
Chris@757 383 m_fileFrameCount += sz;
Chris@757 384
Chris@1040 385 double ratio = 1.0;
Chris@758 386 if (m_resampler && m_fileRate != 0) {
Chris@1040 387 ratio = m_sampleRate / m_fileRate;
Chris@758 388 }
Chris@758 389
Chris@1040 390 if (ratio != 1.0) {
Chris@758 391 pushBufferResampling(buffer, sz, ratio, final);
Chris@758 392 } else {
Chris@758 393 pushBufferNonResampling(buffer, sz);
Chris@758 394 }
Chris@758 395 }
Chris@757 396
Chris@758 397 void
Chris@1038 398 CodedAudioFileReader::pushBufferNonResampling(float *buffer, sv_frame_t sz)
Chris@758 399 {
Chris@920 400 float clip = 1.0;
Chris@1038 401 sv_frame_t count = sz * m_channelCount;
Chris@318 402
Chris@1305 403 // statistics
Chris@1286 404 for (sv_frame_t j = 0; j < sz; ++j) {
Chris@1286 405 for (int c = 0; c < m_channelCount; ++c) {
Chris@1286 406 sv_frame_t i = j * m_channelCount + c;
Chris@1286 407 float v = buffer[i];
Chris@1286 408 if (!m_normalised) {
Chris@1286 409 if (v > clip) {
Chris@1286 410 buffer[i] = clip;
Chris@1286 411 ++m_clippedCount;
Chris@1286 412 } else if (v < -clip) {
Chris@1286 413 buffer[i] = -clip;
Chris@1286 414 ++m_clippedCount;
Chris@1286 415 }
Chris@1285 416 }
Chris@1286 417 v = fabsf(v);
Chris@1286 418 if (v != 0.f) {
Chris@1286 419 if (m_firstNonzero == 0) {
Chris@1286 420 m_firstNonzero = m_frameCount;
Chris@1286 421 }
Chris@1286 422 m_lastNonzero = m_frameCount;
Chris@1286 423 if (v > m_max) {
Chris@1286 424 m_max = v;
Chris@1286 425 }
Chris@920 426 }
Chris@920 427 }
Chris@1286 428 ++m_frameCount;
Chris@297 429 }
Chris@297 430
Chris@1286 431 if (m_max > 0.f) {
Chris@1286 432 m_gain = 1.f / m_max; // used when normalising only
Chris@1286 433 }
Chris@297 434
Chris@148 435 switch (m_cacheMode) {
Chris@148 436
Chris@148 437 case CacheInTemporaryFile:
Chris@1038 438 if (sf_writef_float(m_cacheFileWritePtr, buffer, sz) < sz) {
Chris@544 439 sf_close(m_cacheFileWritePtr);
Chris@544 440 m_cacheFileWritePtr = 0;
Chris@544 441 throw InsufficientDiscSpace(TempDirectory::getInstance()->getPath());
Chris@544 442 }
Chris@148 443 break;
Chris@148 444
Chris@148 445 case CacheInMemory:
Chris@1100 446 m_dataLock.lock();
Chris@1285 447 /*
Chris@1285 448 if (m_data.size() < 5120) {
Chris@1285 449 for (int i = 0; i < count && i < 5120; ++i) {
Chris@1285 450 if (i % 8 == 0) cerr << i << ": ";
Chris@1285 451 cerr << buffer[i] << " ";
Chris@1285 452 if (i % 8 == 7) cerr << endl;
Chris@1285 453 }
Chris@1285 454 }
Chris@1285 455 cerr << endl;
Chris@1285 456 */
Chris@1096 457 m_data.insert(m_data.end(), buffer, buffer + count);
Chris@543 458 m_dataLock.unlock();
Chris@148 459 break;
Chris@148 460 }
Chris@758 461 }
Chris@757 462
Chris@758 463 void
Chris@1038 464 CodedAudioFileReader::pushBufferResampling(float *buffer, sv_frame_t sz,
Chris@1038 465 double ratio, bool final)
Chris@758 466 {
Chris@759 467 SVDEBUG << "pushBufferResampling: ratio = " << ratio << ", sz = " << sz << ", final = " << final << endl;
Chris@757 468
Chris@759 469 if (sz > 0) {
Chris@759 470
Chris@1038 471 sv_frame_t out = m_resampler->resampleInterleaved
Chris@759 472 (buffer,
Chris@759 473 m_resampleBuffer,
Chris@759 474 sz,
Chris@759 475 ratio,
Chris@759 476 false);
Chris@759 477
Chris@759 478 pushBufferNonResampling(m_resampleBuffer, out);
Chris@759 479 }
Chris@757 480
Chris@758 481 if (final) {
Chris@758 482
Chris@1038 483 sv_frame_t padFrames = 1;
Chris@1038 484 if (double(m_frameCount) / ratio < double(m_fileFrameCount)) {
Chris@1038 485 padFrames = m_fileFrameCount - sv_frame_t(double(m_frameCount) / ratio) + 1;
Chris@757 486 }
Chris@758 487
Chris@1038 488 sv_frame_t padSamples = padFrames * m_channelCount;
Chris@758 489
Chris@1038 490 SVDEBUG << "frameCount = " << m_frameCount << ", equivFileFrames = " << double(m_frameCount) / ratio << ", m_fileFrameCount = " << m_fileFrameCount << ", padFrames= " << padFrames << ", padSamples = " << padSamples << endl;
Chris@758 491
Chris@758 492 float *padding = new float[padSamples];
Chris@1038 493 for (sv_frame_t i = 0; i < padSamples; ++i) padding[i] = 0.f;
Chris@758 494
Chris@1038 495 sv_frame_t out = m_resampler->resampleInterleaved
Chris@758 496 (padding,
Chris@758 497 m_resampleBuffer,
Chris@758 498 padFrames,
Chris@758 499 ratio,
Chris@758 500 true);
Chris@758 501
Chris@1038 502 if (m_frameCount + out > sv_frame_t(double(m_fileFrameCount) * ratio)) {
Chris@1038 503 out = sv_frame_t(double(m_fileFrameCount) * ratio) - m_frameCount;
Chris@759 504 }
Chris@759 505
Chris@758 506 pushBufferNonResampling(m_resampleBuffer, out);
Chris@758 507 delete[] padding;
Chris@757 508 }
Chris@148 509 }
Chris@148 510
Chris@1096 511 vector<float>
Chris@1041 512 CodedAudioFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const
Chris@148 513 {
Chris@543 514 // Lock is only required in CacheInMemory mode (the cache file
Chris@543 515 // reader is expected to be thread safe and manage its own
Chris@543 516 // locking)
Chris@263 517
Chris@265 518 if (!m_initialised) {
Chris@690 519 SVDEBUG << "CodedAudioFileReader::getInterleavedFrames: not initialised" << endl;
Chris@1096 520 return {};
Chris@265 521 }
Chris@148 522
Chris@1096 523 vector<float> frames;
Chris@1041 524
Chris@148 525 switch (m_cacheMode) {
Chris@148 526
Chris@148 527 case CacheInTemporaryFile:
Chris@148 528 if (m_cacheFileReader) {
Chris@1041 529 frames = m_cacheFileReader->getInterleavedFrames(start, count);
Chris@148 530 }
Chris@148 531 break;
Chris@148 532
Chris@148 533 case CacheInMemory:
Chris@148 534 {
Chris@1096 535 if (!isOK()) return {};
Chris@1096 536 if (count == 0) return {};
Chris@148 537
Chris@1100 538 sv_frame_t ix0 = start * m_channelCount;
Chris@1100 539 sv_frame_t ix1 = ix0 + (count * m_channelCount);
Chris@148 540
Chris@1100 541 // This lock used to be a QReadWriteLock, but it appears that
Chris@1100 542 // its lock mechanism is significantly slower than QMutex so
Chris@1100 543 // it's not a good idea in cases like this where we don't
Chris@1100 544 // really have threads taking a long time to read concurrently
Chris@1100 545 m_dataLock.lock();
Chris@1100 546 sv_frame_t n = sv_frame_t(m_data.size());
Chris@1282 547 if (ix0 > n) ix0 = n;
Chris@1100 548 if (ix1 > n) ix1 = n;
Chris@1100 549 frames = vector<float>(m_data.begin() + ix0, m_data.begin() + ix1);
Chris@543 550 m_dataLock.unlock();
Chris@1282 551 break;
Chris@148 552 }
Chris@148 553 }
Chris@920 554
Chris@920 555 if (m_normalised) {
Chris@1052 556 for (auto &f: frames) f *= m_gain;
Chris@920 557 }
Chris@1041 558
Chris@1041 559 return frames;
Chris@148 560 }
Chris@148 561