annotate data/fileio/CodedAudioFileReader.cpp @ 1283:2f468f43c02c 3.0-integration

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