annotate data/fileio/CodedAudioFileReader.cpp @ 1310:aa1b1fc2d018 mp3-gapless

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