annotate data/fileio/CodedAudioFileReader.cpp @ 1346:75ad55315db4 3.0-integration

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