annotate data/fileio/CodedAudioFileReader.cpp @ 795:dc20458f6f85 qt5

Don't need to check for Dataquay, and in fact we can pick up the wrong version if we do. Just assume it is available (building in e.g. sv subdir configuration)
author Chris Cannam
date Tue, 07 May 2013 15:41:58 +0100
parents a43acbe3988f
children e802e550a1f2
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@148 24
Chris@723 25 #include <stdint.h>
Chris@148 26 #include <iostream>
Chris@148 27 #include <QDir>
Chris@263 28 #include <QMutexLocker>
Chris@148 29
Chris@297 30 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode,
Chris@297 31 size_t targetRate) :
Chris@148 32 m_cacheMode(cacheMode),
Chris@148 33 m_initialised(false),
Chris@297 34 m_serialiser(0),
Chris@297 35 m_fileRate(0),
Chris@148 36 m_cacheFileWritePtr(0),
Chris@148 37 m_cacheFileReader(0),
Chris@148 38 m_cacheWriteBuffer(0),
Chris@148 39 m_cacheWriteBufferIndex(0),
Chris@297 40 m_cacheWriteBufferSize(16384),
Chris@297 41 m_resampler(0),
Chris@757 42 m_resampleBuffer(0),
Chris@757 43 m_fileFrameCount(0)
Chris@148 44 {
Chris@742 45 SVDEBUG << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << endl;
Chris@297 46
Chris@297 47 m_frameCount = 0;
Chris@297 48 m_sampleRate = targetRate;
Chris@148 49 }
Chris@148 50
Chris@148 51 CodedAudioFileReader::~CodedAudioFileReader()
Chris@148 52 {
Chris@263 53 QMutexLocker locker(&m_cacheMutex);
Chris@263 54
Chris@297 55 endSerialised();
Chris@297 56
Chris@148 57 if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
Chris@297 58
Chris@742 59 SVDEBUG << "CodedAudioFileReader::~CodedAudioFileReader: deleting cache file reader" << endl;
Chris@532 60
Chris@297 61 delete m_cacheFileReader;
Chris@297 62 delete[] m_cacheWriteBuffer;
Chris@148 63
Chris@148 64 if (m_cacheFileName != "") {
Chris@290 65 if (!QFile(m_cacheFileName).remove()) {
Chris@686 66 std::cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName << "\"" << std::endl;
Chris@148 67 }
Chris@148 68 }
Chris@297 69
Chris@297 70 delete m_resampler;
Chris@297 71 delete[] m_resampleBuffer;
Chris@297 72 }
Chris@297 73
Chris@297 74 void
Chris@297 75 CodedAudioFileReader::startSerialised(QString id)
Chris@297 76 {
Chris@742 77 SVDEBUG << "CodedAudioFileReader::startSerialised(" << id << ")" << endl;
Chris@297 78
Chris@297 79 delete m_serialiser;
Chris@297 80 m_serialiser = new Serialiser(id);
Chris@297 81 }
Chris@297 82
Chris@297 83 void
Chris@297 84 CodedAudioFileReader::endSerialised()
Chris@297 85 {
Chris@756 86 SVDEBUG << "CodedAudioFileReader(" << this << ")::endSerialised: id = " << (m_serialiser ? m_serialiser->getId().toStdString() : "(none)") << endl;
Chris@297 87
Chris@297 88 delete m_serialiser;
Chris@297 89 m_serialiser = 0;
Chris@148 90 }
Chris@148 91
Chris@148 92 void
Chris@148 93 CodedAudioFileReader::initialiseDecodeCache()
Chris@148 94 {
Chris@263 95 QMutexLocker locker(&m_cacheMutex);
Chris@263 96
Chris@742 97 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: file rate = " << m_fileRate << endl;
Chris@297 98
Chris@297 99 if (m_fileRate == 0) {
Chris@754 100 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << std::endl;
Chris@754 101 throw FileOperationFailed("(coded file)", "File sample rate unknown (bug in subclass implementation?)");
Chris@297 102 }
Chris@297 103 if (m_sampleRate == 0) {
Chris@297 104 m_sampleRate = m_fileRate;
Chris@690 105 SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: rate (from file) = " << m_fileRate << endl;
Chris@297 106 }
Chris@297 107 if (m_fileRate != m_sampleRate) {
Chris@757 108 SVDEBUG << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << endl;
Chris@297 109 m_resampler = new Resampler(Resampler::FastestTolerable,
Chris@297 110 m_channelCount,
Chris@297 111 m_cacheWriteBufferSize);
Chris@297 112 float ratio = float(m_sampleRate) / float(m_fileRate);
Chris@297 113 m_resampleBuffer = new float
Chris@398 114 [lrintf(ceilf(m_cacheWriteBufferSize * m_channelCount * ratio + 1))];
Chris@297 115 }
Chris@297 116
Chris@297 117 m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
Chris@297 118 m_cacheWriteBufferIndex = 0;
Chris@297 119
Chris@148 120 if (m_cacheMode == CacheInTemporaryFile) {
Chris@148 121
Chris@148 122 try {
Chris@148 123 QDir dir(TempDirectory::getInstance()->getPath());
Chris@148 124 m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
Chris@290 125 .arg((intptr_t)this));
Chris@148 126
Chris@148 127 SF_INFO fileInfo;
Chris@148 128 fileInfo.samplerate = m_sampleRate;
Chris@148 129 fileInfo.channels = m_channelCount;
Chris@297 130
Chris@297 131 // No point in writing 24-bit or float; generally this
Chris@297 132 // class is used for decoding files that have come from a
Chris@297 133 // 16 bit source or that decode to only 16 bits anyway.
Chris@297 134 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
Chris@148 135
Chris@290 136 m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
Chris@148 137 SFM_WRITE, &fileInfo);
Chris@148 138
Chris@265 139 if (m_cacheFileWritePtr) {
Chris@265 140
Chris@297 141 // Ideally we would do this now only if we were in a
Chris@297 142 // threaded mode -- creating the reader later if we're
Chris@297 143 // not threaded -- but we don't have access to that
Chris@297 144 // information here
Chris@265 145
Chris@265 146 m_cacheFileReader = new WavFileReader(m_cacheFileName);
Chris@265 147
Chris@265 148 if (!m_cacheFileReader->isOK()) {
Chris@686 149 std::cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError() << std::endl;
Chris@265 150 delete m_cacheFileReader;
Chris@265 151 m_cacheFileReader = 0;
Chris@265 152 m_cacheMode = CacheInMemory;
Chris@265 153 sf_close(m_cacheFileWritePtr);
Chris@265 154 }
Chris@297 155
Chris@265 156 } else {
Chris@686 157 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to open cache file \"" << m_cacheFileName << "\" (" << m_channelCount << " channels, sample rate " << m_sampleRate << " for writing, falling back to in-memory cache" << std::endl;
Chris@148 158 m_cacheMode = CacheInMemory;
Chris@148 159 }
Chris@265 160
Chris@148 161 } catch (DirectoryCreationFailed f) {
Chris@148 162 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << std::endl;
Chris@148 163 m_cacheMode = CacheInMemory;
Chris@148 164 }
Chris@148 165 }
Chris@148 166
Chris@148 167 if (m_cacheMode == CacheInMemory) {
Chris@148 168 m_data.clear();
Chris@148 169 }
Chris@148 170
Chris@148 171 m_initialised = true;
Chris@148 172 }
Chris@148 173
Chris@148 174 void
Chris@297 175 CodedAudioFileReader::addSamplesToDecodeCache(float **samples, size_t nframes)
Chris@148 176 {
Chris@263 177 QMutexLocker locker(&m_cacheMutex);
Chris@263 178
Chris@148 179 if (!m_initialised) return;
Chris@148 180
Chris@297 181 for (size_t i = 0; i < nframes; ++i) {
Chris@297 182
Chris@297 183 for (size_t c = 0; c < m_channelCount; ++c) {
Chris@148 184
Chris@297 185 float sample = samples[c][i];
Chris@297 186
Chris@297 187 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 188
Chris@297 189 if (m_cacheWriteBufferIndex ==
Chris@297 190 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 191
Chris@297 192 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 193 m_cacheWriteBufferIndex = 0;
Chris@297 194 }
Chris@297 195
Chris@297 196 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 197 m_cacheFileReader) {
Chris@297 198 m_cacheFileReader->updateFrameCount();
Chris@297 199 }
Chris@297 200 }
Chris@297 201 }
Chris@297 202 }
Chris@297 203
Chris@297 204 void
Chris@297 205 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, size_t nframes)
Chris@297 206 {
Chris@297 207 QMutexLocker locker(&m_cacheMutex);
Chris@297 208
Chris@297 209 if (!m_initialised) return;
Chris@297 210
Chris@297 211 for (size_t i = 0; i < nframes; ++i) {
Chris@297 212
Chris@297 213 for (size_t c = 0; c < m_channelCount; ++c) {
Chris@297 214
Chris@297 215 float sample = samples[i * m_channelCount + c];
Chris@297 216
Chris@297 217 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@297 218
Chris@297 219 if (m_cacheWriteBufferIndex ==
Chris@297 220 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 221
Chris@297 222 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 223 m_cacheWriteBufferIndex = 0;
Chris@297 224 }
Chris@297 225
Chris@297 226 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 227 m_cacheFileReader) {
Chris@297 228 m_cacheFileReader->updateFrameCount();
Chris@297 229 }
Chris@297 230 }
Chris@297 231 }
Chris@297 232 }
Chris@297 233
Chris@297 234 void
Chris@297 235 CodedAudioFileReader::addSamplesToDecodeCache(const SampleBlock &samples)
Chris@297 236 {
Chris@297 237 QMutexLocker locker(&m_cacheMutex);
Chris@297 238
Chris@297 239 if (!m_initialised) return;
Chris@297 240
Chris@297 241 for (size_t i = 0; i < samples.size(); ++i) {
Chris@297 242
Chris@297 243 float sample = samples[i];
Chris@297 244
Chris@148 245 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 246
Chris@148 247 if (m_cacheWriteBufferIndex ==
Chris@148 248 m_cacheWriteBufferSize * m_channelCount) {
Chris@148 249
Chris@297 250 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@148 251 m_cacheWriteBufferIndex = 0;
Chris@266 252 }
Chris@265 253
Chris@266 254 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@266 255 m_cacheFileReader) {
Chris@266 256 m_cacheFileReader->updateFrameCount();
Chris@148 257 }
Chris@148 258 }
Chris@148 259 }
Chris@148 260
Chris@148 261 void
Chris@148 262 CodedAudioFileReader::finishDecodeCache()
Chris@148 263 {
Chris@263 264 QMutexLocker locker(&m_cacheMutex);
Chris@263 265
Chris@192 266 Profiler profiler("CodedAudioFileReader::finishDecodeCache", true);
Chris@192 267
Chris@148 268 if (!m_initialised) {
Chris@148 269 std::cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << std::endl;
Chris@148 270 return;
Chris@148 271 }
Chris@148 272
Chris@759 273 // if (m_cacheWriteBufferIndex > 0) {
Chris@297 274 pushBuffer(m_cacheWriteBuffer,
Chris@297 275 m_cacheWriteBufferIndex / m_channelCount,
Chris@297 276 true);
Chris@759 277 // }
Chris@297 278
Chris@297 279 delete[] m_cacheWriteBuffer;
Chris@297 280 m_cacheWriteBuffer = 0;
Chris@297 281
Chris@297 282 delete[] m_resampleBuffer;
Chris@297 283 m_resampleBuffer = 0;
Chris@297 284
Chris@297 285 delete m_resampler;
Chris@297 286 m_resampler = 0;
Chris@297 287
Chris@297 288 if (m_cacheMode == CacheInTemporaryFile) {
Chris@297 289 sf_close(m_cacheFileWritePtr);
Chris@297 290 m_cacheFileWritePtr = 0;
Chris@297 291 if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
Chris@297 292 }
Chris@297 293 }
Chris@297 294
Chris@297 295 void
Chris@297 296 CodedAudioFileReader::pushBuffer(float *buffer, size_t sz, bool final)
Chris@297 297 {
Chris@757 298 m_fileFrameCount += sz;
Chris@757 299
Chris@757 300 float ratio = 1.f;
Chris@758 301 if (m_resampler && m_fileRate != 0) {
Chris@758 302 ratio = float(m_sampleRate) / float(m_fileRate);
Chris@758 303 }
Chris@758 304
Chris@758 305 if (ratio != 1.f) {
Chris@758 306 pushBufferResampling(buffer, sz, ratio, final);
Chris@758 307 } else {
Chris@758 308 pushBufferNonResampling(buffer, sz);
Chris@758 309 }
Chris@758 310 }
Chris@757 311
Chris@758 312 void
Chris@758 313 CodedAudioFileReader::pushBufferNonResampling(float *buffer, size_t sz)
Chris@758 314 {
Chris@758 315 float max = 1.0;
Chris@758 316 size_t count = sz * m_channelCount;
Chris@318 317
Chris@322 318 for (size_t i = 0; i < count; ++i) {
Chris@322 319 if (buffer[i] > max) buffer[i] = max;
Chris@322 320 }
Chris@322 321 for (size_t i = 0; i < count; ++i) {
Chris@322 322 if (buffer[i] < -max) buffer[i] = -max;
Chris@297 323 }
Chris@297 324
Chris@297 325 m_frameCount += sz;
Chris@297 326
Chris@148 327 switch (m_cacheMode) {
Chris@148 328
Chris@148 329 case CacheInTemporaryFile:
Chris@544 330 if (sf_writef_float(m_cacheFileWritePtr, buffer, sz) < sz) {
Chris@544 331 sf_close(m_cacheFileWritePtr);
Chris@544 332 m_cacheFileWritePtr = 0;
Chris@544 333 throw InsufficientDiscSpace(TempDirectory::getInstance()->getPath());
Chris@544 334 }
Chris@148 335 break;
Chris@148 336
Chris@148 337 case CacheInMemory:
Chris@543 338 m_dataLock.lockForWrite();
Chris@322 339 for (size_t s = 0; s < count; ++s) {
Chris@543 340 m_data.push_back(buffer[s]);
Chris@297 341 }
Chris@297 342 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@543 343 m_dataLock.unlock();
Chris@148 344 break;
Chris@148 345 }
Chris@758 346 }
Chris@757 347
Chris@758 348 void
Chris@758 349 CodedAudioFileReader::pushBufferResampling(float *buffer, size_t sz,
Chris@758 350 float ratio, bool final)
Chris@758 351 {
Chris@759 352 SVDEBUG << "pushBufferResampling: ratio = " << ratio << ", sz = " << sz << ", final = " << final << endl;
Chris@757 353
Chris@759 354 if (sz > 0) {
Chris@759 355
Chris@759 356 size_t out = m_resampler->resampleInterleaved
Chris@759 357 (buffer,
Chris@759 358 m_resampleBuffer,
Chris@759 359 sz,
Chris@759 360 ratio,
Chris@759 361 false);
Chris@759 362
Chris@759 363 pushBufferNonResampling(m_resampleBuffer, out);
Chris@759 364 }
Chris@757 365
Chris@758 366 if (final) {
Chris@758 367
Chris@758 368 size_t padFrames = 1;
Chris@758 369 if (m_frameCount / ratio < m_fileFrameCount) {
Chris@758 370 padFrames = m_fileFrameCount - (m_frameCount / ratio) + 1;
Chris@757 371 }
Chris@758 372
Chris@758 373 size_t padSamples = padFrames * m_channelCount;
Chris@758 374
Chris@759 375 SVDEBUG << "frameCount = " << m_frameCount << ", equivFileFrames = " << m_frameCount / ratio << ", m_fileFrameCount = " << m_fileFrameCount << ", padFrames= " << padFrames << ", padSamples = " << padSamples << endl;
Chris@758 376
Chris@758 377 float *padding = new float[padSamples];
Chris@758 378 for (int i = 0; i < padSamples; ++i) padding[i] = 0.f;
Chris@758 379
Chris@759 380 size_t out = m_resampler->resampleInterleaved
Chris@758 381 (padding,
Chris@758 382 m_resampleBuffer,
Chris@758 383 padFrames,
Chris@758 384 ratio,
Chris@758 385 true);
Chris@758 386
Chris@759 387 if (m_frameCount + out > int(m_fileFrameCount * ratio)) {
Chris@759 388 out = int(m_fileFrameCount * ratio) - m_frameCount;
Chris@759 389 }
Chris@759 390
Chris@758 391 pushBufferNonResampling(m_resampleBuffer, out);
Chris@758 392 delete[] padding;
Chris@757 393 }
Chris@148 394 }
Chris@148 395
Chris@148 396 void
Chris@148 397 CodedAudioFileReader::getInterleavedFrames(size_t start, size_t count,
Chris@148 398 SampleBlock &frames) const
Chris@148 399 {
Chris@543 400 // Lock is only required in CacheInMemory mode (the cache file
Chris@543 401 // reader is expected to be thread safe and manage its own
Chris@543 402 // locking)
Chris@263 403
Chris@265 404 if (!m_initialised) {
Chris@690 405 SVDEBUG << "CodedAudioFileReader::getInterleavedFrames: not initialised" << endl;
Chris@265 406 return;
Chris@265 407 }
Chris@148 408
Chris@148 409 switch (m_cacheMode) {
Chris@148 410
Chris@148 411 case CacheInTemporaryFile:
Chris@148 412 if (m_cacheFileReader) {
Chris@148 413 m_cacheFileReader->getInterleavedFrames(start, count, frames);
Chris@148 414 }
Chris@148 415 break;
Chris@148 416
Chris@148 417 case CacheInMemory:
Chris@148 418 {
Chris@148 419 frames.clear();
Chris@148 420 if (!isOK()) return;
Chris@148 421 if (count == 0) return;
Chris@543 422 frames.reserve(count * m_channelCount);
Chris@148 423
Chris@543 424 size_t idx = start * m_channelCount;
Chris@543 425 size_t i = 0;
Chris@148 426
Chris@543 427 m_dataLock.lockForRead();
Chris@543 428 while (i < count * m_channelCount && idx < m_data.size()) {
Chris@543 429 frames.push_back(m_data[idx]);
Chris@543 430 ++idx;
Chris@148 431 }
Chris@543 432 m_dataLock.unlock();
Chris@148 433 }
Chris@148 434 }
Chris@148 435 }
Chris@148 436