annotate data/fileio/CodedAudioFileReader.cpp @ 490:c3fb8258e34d

* Make it possible to import an entire session from an RDF document. However, at the moment the timings of events appear to be constrained by how far the audio decoder has got through its audio file at the time the event is queried -- need to investigate.
author Chris Cannam
date Fri, 21 Nov 2008 18:03:14 +0000
parents d095214ffbaf
children 76f6971c8433
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@148 25 #include <iostream>
Chris@148 26 #include <QDir>
Chris@263 27 #include <QMutexLocker>
Chris@148 28
Chris@297 29 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode,
Chris@297 30 size_t targetRate) :
Chris@148 31 m_cacheMode(cacheMode),
Chris@148 32 m_initialised(false),
Chris@297 33 m_serialiser(0),
Chris@297 34 m_fileRate(0),
Chris@148 35 m_cacheFileWritePtr(0),
Chris@148 36 m_cacheFileReader(0),
Chris@148 37 m_cacheWriteBuffer(0),
Chris@148 38 m_cacheWriteBufferIndex(0),
Chris@297 39 m_cacheWriteBufferSize(16384),
Chris@297 40 m_resampler(0),
Chris@297 41 m_resampleBuffer(0)
Chris@148 42 {
Chris@327 43 // std::cerr << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << std::endl;
Chris@297 44
Chris@297 45 m_frameCount = 0;
Chris@297 46 m_sampleRate = targetRate;
Chris@148 47 }
Chris@148 48
Chris@148 49 CodedAudioFileReader::~CodedAudioFileReader()
Chris@148 50 {
Chris@263 51 QMutexLocker locker(&m_cacheMutex);
Chris@263 52
Chris@297 53 endSerialised();
Chris@297 54
Chris@148 55 if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
Chris@297 56
Chris@297 57 delete m_cacheFileReader;
Chris@297 58 delete[] m_cacheWriteBuffer;
Chris@148 59
Chris@148 60 if (m_cacheFileName != "") {
Chris@290 61 if (!QFile(m_cacheFileName).remove()) {
Chris@290 62 std::cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName.toStdString() << "\"" << std::endl;
Chris@148 63 }
Chris@148 64 }
Chris@297 65
Chris@297 66 delete m_resampler;
Chris@297 67 delete[] m_resampleBuffer;
Chris@297 68 }
Chris@297 69
Chris@297 70 void
Chris@297 71 CodedAudioFileReader::startSerialised(QString id)
Chris@297 72 {
Chris@297 73 // std::cerr << "CodedAudioFileReader::startSerialised(" << id.toStdString() << ")" << std::endl;
Chris@297 74
Chris@297 75 delete m_serialiser;
Chris@297 76 m_serialiser = new Serialiser(id);
Chris@297 77 }
Chris@297 78
Chris@297 79 void
Chris@297 80 CodedAudioFileReader::endSerialised()
Chris@297 81 {
Chris@406 82 // std::cerr << "CodedAudioFileReader(" << this << ")::endSerialised: id = " << (m_serialiser ? m_serialiser->getId().toStdString() : "(none)") << std::endl;
Chris@297 83
Chris@297 84 delete m_serialiser;
Chris@297 85 m_serialiser = 0;
Chris@148 86 }
Chris@148 87
Chris@148 88 void
Chris@148 89 CodedAudioFileReader::initialiseDecodeCache()
Chris@148 90 {
Chris@263 91 QMutexLocker locker(&m_cacheMutex);
Chris@263 92
Chris@297 93 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: file rate = " << m_fileRate << std::endl;
Chris@297 94
Chris@297 95 if (m_fileRate == 0) {
Chris@297 96 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << std::endl;
Chris@297 97 m_fileRate = 48000; // got to have something
Chris@297 98 }
Chris@297 99 if (m_sampleRate == 0) {
Chris@297 100 m_sampleRate = m_fileRate;
Chris@386 101 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: rate (from file) = " << m_fileRate << std::endl;
Chris@297 102 }
Chris@297 103 if (m_fileRate != m_sampleRate) {
Chris@297 104 std::cerr << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << std::endl;
Chris@297 105 m_resampler = new Resampler(Resampler::FastestTolerable,
Chris@297 106 m_channelCount,
Chris@297 107 m_cacheWriteBufferSize);
Chris@297 108 float ratio = float(m_sampleRate) / float(m_fileRate);
Chris@297 109 m_resampleBuffer = new float
Chris@398 110 [lrintf(ceilf(m_cacheWriteBufferSize * m_channelCount * ratio + 1))];
Chris@297 111 }
Chris@297 112
Chris@297 113 m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
Chris@297 114 m_cacheWriteBufferIndex = 0;
Chris@297 115
Chris@148 116 if (m_cacheMode == CacheInTemporaryFile) {
Chris@148 117
Chris@148 118 try {
Chris@148 119 QDir dir(TempDirectory::getInstance()->getPath());
Chris@148 120 m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
Chris@290 121 .arg((intptr_t)this));
Chris@148 122
Chris@148 123 SF_INFO fileInfo;
Chris@148 124 fileInfo.samplerate = m_sampleRate;
Chris@148 125 fileInfo.channels = m_channelCount;
Chris@297 126
Chris@297 127 // No point in writing 24-bit or float; generally this
Chris@297 128 // class is used for decoding files that have come from a
Chris@297 129 // 16 bit source or that decode to only 16 bits anyway.
Chris@297 130 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
Chris@148 131
Chris@290 132 m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
Chris@148 133 SFM_WRITE, &fileInfo);
Chris@148 134
Chris@265 135 if (m_cacheFileWritePtr) {
Chris@265 136
Chris@297 137 // Ideally we would do this now only if we were in a
Chris@297 138 // threaded mode -- creating the reader later if we're
Chris@297 139 // not threaded -- but we don't have access to that
Chris@297 140 // information here
Chris@265 141
Chris@265 142 m_cacheFileReader = new WavFileReader(m_cacheFileName);
Chris@265 143
Chris@265 144 if (!m_cacheFileReader->isOK()) {
Chris@290 145 std::cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl;
Chris@265 146 delete m_cacheFileReader;
Chris@265 147 m_cacheFileReader = 0;
Chris@265 148 m_cacheMode = CacheInMemory;
Chris@265 149 sf_close(m_cacheFileWritePtr);
Chris@265 150 }
Chris@297 151
Chris@265 152 } else {
Chris@290 153 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to open cache file \"" << m_cacheFileName.toStdString() << "\" (" << m_channelCount << " channels, sample rate " << m_sampleRate << " for writing, falling back to in-memory cache" << std::endl;
Chris@148 154 m_cacheMode = CacheInMemory;
Chris@148 155 }
Chris@265 156
Chris@148 157 } catch (DirectoryCreationFailed f) {
Chris@148 158 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << std::endl;
Chris@148 159 m_cacheMode = CacheInMemory;
Chris@148 160 }
Chris@148 161 }
Chris@148 162
Chris@148 163 if (m_cacheMode == CacheInMemory) {
Chris@148 164 m_data.clear();
Chris@148 165 }
Chris@148 166
Chris@148 167 m_initialised = true;
Chris@148 168 }
Chris@148 169
Chris@148 170 void
Chris@297 171 CodedAudioFileReader::addSamplesToDecodeCache(float **samples, size_t nframes)
Chris@148 172 {
Chris@263 173 QMutexLocker locker(&m_cacheMutex);
Chris@263 174
Chris@148 175 if (!m_initialised) return;
Chris@148 176
Chris@297 177 for (size_t i = 0; i < nframes; ++i) {
Chris@297 178
Chris@297 179 for (size_t c = 0; c < m_channelCount; ++c) {
Chris@148 180
Chris@297 181 float sample = samples[c][i];
Chris@297 182
Chris@297 183 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 184
Chris@297 185 if (m_cacheWriteBufferIndex ==
Chris@297 186 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 187
Chris@297 188 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 189 m_cacheWriteBufferIndex = 0;
Chris@297 190 }
Chris@297 191
Chris@297 192 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 193 m_cacheFileReader) {
Chris@297 194 m_cacheFileReader->updateFrameCount();
Chris@297 195 }
Chris@297 196 }
Chris@297 197 }
Chris@297 198 }
Chris@297 199
Chris@297 200 void
Chris@297 201 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, size_t nframes)
Chris@297 202 {
Chris@297 203 QMutexLocker locker(&m_cacheMutex);
Chris@297 204
Chris@297 205 if (!m_initialised) return;
Chris@297 206
Chris@297 207 for (size_t i = 0; i < nframes; ++i) {
Chris@297 208
Chris@297 209 for (size_t c = 0; c < m_channelCount; ++c) {
Chris@297 210
Chris@297 211 float sample = samples[i * m_channelCount + c];
Chris@297 212
Chris@297 213 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@297 214
Chris@297 215 if (m_cacheWriteBufferIndex ==
Chris@297 216 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 217
Chris@297 218 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 219 m_cacheWriteBufferIndex = 0;
Chris@297 220 }
Chris@297 221
Chris@297 222 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 223 m_cacheFileReader) {
Chris@297 224 m_cacheFileReader->updateFrameCount();
Chris@297 225 }
Chris@297 226 }
Chris@297 227 }
Chris@297 228 }
Chris@297 229
Chris@297 230 void
Chris@297 231 CodedAudioFileReader::addSamplesToDecodeCache(const SampleBlock &samples)
Chris@297 232 {
Chris@297 233 QMutexLocker locker(&m_cacheMutex);
Chris@297 234
Chris@297 235 if (!m_initialised) return;
Chris@297 236
Chris@297 237 for (size_t i = 0; i < samples.size(); ++i) {
Chris@297 238
Chris@297 239 float sample = samples[i];
Chris@297 240
Chris@148 241 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 242
Chris@148 243 if (m_cacheWriteBufferIndex ==
Chris@148 244 m_cacheWriteBufferSize * m_channelCount) {
Chris@148 245
Chris@297 246 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@148 247 m_cacheWriteBufferIndex = 0;
Chris@266 248 }
Chris@265 249
Chris@266 250 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@266 251 m_cacheFileReader) {
Chris@266 252 m_cacheFileReader->updateFrameCount();
Chris@148 253 }
Chris@148 254 }
Chris@148 255 }
Chris@148 256
Chris@148 257 void
Chris@148 258 CodedAudioFileReader::finishDecodeCache()
Chris@148 259 {
Chris@263 260 QMutexLocker locker(&m_cacheMutex);
Chris@263 261
Chris@192 262 Profiler profiler("CodedAudioFileReader::finishDecodeCache", true);
Chris@192 263
Chris@148 264 if (!m_initialised) {
Chris@148 265 std::cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << std::endl;
Chris@148 266 return;
Chris@148 267 }
Chris@148 268
Chris@297 269 if (m_cacheWriteBufferIndex > 0) {
Chris@297 270 //!!! check for return value! out of disk space, etc!
Chris@297 271 pushBuffer(m_cacheWriteBuffer,
Chris@297 272 m_cacheWriteBufferIndex / m_channelCount,
Chris@297 273 true);
Chris@297 274 }
Chris@297 275
Chris@297 276 delete[] m_cacheWriteBuffer;
Chris@297 277 m_cacheWriteBuffer = 0;
Chris@297 278
Chris@297 279 delete[] m_resampleBuffer;
Chris@297 280 m_resampleBuffer = 0;
Chris@297 281
Chris@297 282 delete m_resampler;
Chris@297 283 m_resampler = 0;
Chris@297 284
Chris@297 285 if (m_cacheMode == CacheInTemporaryFile) {
Chris@297 286 sf_close(m_cacheFileWritePtr);
Chris@297 287 m_cacheFileWritePtr = 0;
Chris@297 288 if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
Chris@297 289 }
Chris@297 290 }
Chris@297 291
Chris@297 292 void
Chris@297 293 CodedAudioFileReader::pushBuffer(float *buffer, size_t sz, bool final)
Chris@297 294 {
Chris@322 295 float max = 1.0;
Chris@322 296 size_t count = sz * m_channelCount;
Chris@318 297
Chris@375 298 if (m_resampler && m_fileRate != 0) {
Chris@297 299
Chris@297 300 float ratio = float(m_sampleRate) / float(m_fileRate);
Chris@297 301
Chris@297 302 if (ratio != 1.f) {
Chris@322 303
Chris@297 304 size_t out = m_resampler->resampleInterleaved
Chris@297 305 (buffer,
Chris@297 306 m_resampleBuffer,
Chris@297 307 sz,
Chris@297 308 ratio,
Chris@297 309 final);
Chris@297 310
Chris@297 311 buffer = m_resampleBuffer;
Chris@297 312 sz = out;
Chris@322 313 count = sz * m_channelCount;
Chris@297 314 }
Chris@322 315 }
Chris@318 316
Chris@322 317 for (size_t i = 0; i < count; ++i) {
Chris@322 318 if (buffer[i] > max) buffer[i] = max;
Chris@322 319 }
Chris@322 320 for (size_t i = 0; i < count; ++i) {
Chris@322 321 if (buffer[i] < -max) buffer[i] = -max;
Chris@297 322 }
Chris@297 323
Chris@297 324 m_frameCount += sz;
Chris@297 325
Chris@148 326 switch (m_cacheMode) {
Chris@148 327
Chris@148 328 case CacheInTemporaryFile:
Chris@297 329 //!!! check for return value! out of disk space, etc!
Chris@297 330 sf_writef_float(m_cacheFileWritePtr, buffer, sz);
Chris@148 331 break;
Chris@148 332
Chris@148 333 case CacheInMemory:
Chris@322 334 for (size_t s = 0; s < count; ++s) {
Chris@322 335 m_data.push_back(buffer[count]);
Chris@297 336 }
Chris@297 337 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 338 break;
Chris@148 339 }
Chris@148 340 }
Chris@148 341
Chris@148 342 void
Chris@148 343 CodedAudioFileReader::getInterleavedFrames(size_t start, size_t count,
Chris@148 344 SampleBlock &frames) const
Chris@148 345 {
Chris@263 346 //!!! we want to ensure this doesn't require a lock -- at the
Chris@263 347 // moment it does need one, but it doesn't have one...
Chris@263 348
Chris@265 349 if (!m_initialised) {
Chris@265 350 std::cerr << "CodedAudioFileReader::getInterleavedFrames: not initialised" << std::endl;
Chris@265 351 return;
Chris@265 352 }
Chris@148 353
Chris@148 354 switch (m_cacheMode) {
Chris@148 355
Chris@148 356 case CacheInTemporaryFile:
Chris@148 357 if (m_cacheFileReader) {
Chris@148 358 m_cacheFileReader->getInterleavedFrames(start, count, frames);
Chris@148 359 }
Chris@148 360 break;
Chris@148 361
Chris@148 362 case CacheInMemory:
Chris@148 363 {
Chris@148 364 frames.clear();
Chris@148 365 if (!isOK()) return;
Chris@148 366 if (count == 0) return;
Chris@148 367
Chris@148 368 // slownessabounds
Chris@148 369
Chris@148 370 for (size_t i = start; i < start + count; ++i) {
Chris@148 371 for (size_t ch = 0; ch < m_channelCount; ++ch) {
Chris@148 372 size_t index = i * m_channelCount + ch;
Chris@148 373 if (index >= m_data.size()) return;
Chris@148 374 frames.push_back(m_data[index]);
Chris@148 375 }
Chris@148 376 }
Chris@148 377 }
Chris@148 378 }
Chris@148 379 }
Chris@148 380