annotate data/fileio/CodedAudioFileReader.cpp @ 316:3a6725f285d6

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents 4fc6f49436b3
children 7a4bd2c8585c
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@297 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@297 82 // std::cerr << "CodedAudioFileReader::endSerialised" << 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@297 101 }
Chris@297 102 if (m_fileRate != m_sampleRate) {
Chris@297 103 std::cerr << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << std::endl;
Chris@297 104 m_resampler = new Resampler(Resampler::FastestTolerable,
Chris@297 105 m_channelCount,
Chris@297 106 m_cacheWriteBufferSize);
Chris@297 107 float ratio = float(m_sampleRate) / float(m_fileRate);
Chris@297 108 m_resampleBuffer = new float
Chris@297 109 [lrintf(ceilf(m_cacheWriteBufferSize * m_channelCount * ratio))];
Chris@297 110 }
Chris@297 111
Chris@297 112 m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
Chris@297 113 m_cacheWriteBufferIndex = 0;
Chris@297 114
Chris@148 115 if (m_cacheMode == CacheInTemporaryFile) {
Chris@148 116
Chris@148 117 try {
Chris@148 118 QDir dir(TempDirectory::getInstance()->getPath());
Chris@148 119 m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
Chris@290 120 .arg((intptr_t)this));
Chris@148 121
Chris@148 122 SF_INFO fileInfo;
Chris@148 123 fileInfo.samplerate = m_sampleRate;
Chris@148 124 fileInfo.channels = m_channelCount;
Chris@297 125
Chris@297 126 // No point in writing 24-bit or float; generally this
Chris@297 127 // class is used for decoding files that have come from a
Chris@297 128 // 16 bit source or that decode to only 16 bits anyway.
Chris@297 129 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
Chris@148 130
Chris@290 131 m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
Chris@148 132 SFM_WRITE, &fileInfo);
Chris@148 133
Chris@265 134 if (m_cacheFileWritePtr) {
Chris@265 135
Chris@297 136 // Ideally we would do this now only if we were in a
Chris@297 137 // threaded mode -- creating the reader later if we're
Chris@297 138 // not threaded -- but we don't have access to that
Chris@297 139 // information here
Chris@265 140
Chris@265 141 m_cacheFileReader = new WavFileReader(m_cacheFileName);
Chris@265 142
Chris@265 143 if (!m_cacheFileReader->isOK()) {
Chris@290 144 std::cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl;
Chris@265 145 delete m_cacheFileReader;
Chris@265 146 m_cacheFileReader = 0;
Chris@265 147 m_cacheMode = CacheInMemory;
Chris@265 148 sf_close(m_cacheFileWritePtr);
Chris@265 149 }
Chris@297 150
Chris@265 151 } else {
Chris@290 152 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 153 m_cacheMode = CacheInMemory;
Chris@148 154 }
Chris@265 155
Chris@148 156 } catch (DirectoryCreationFailed f) {
Chris@148 157 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << std::endl;
Chris@148 158 m_cacheMode = CacheInMemory;
Chris@148 159 }
Chris@148 160 }
Chris@148 161
Chris@148 162 if (m_cacheMode == CacheInMemory) {
Chris@148 163 m_data.clear();
Chris@148 164 }
Chris@148 165
Chris@148 166 m_initialised = true;
Chris@148 167 }
Chris@148 168
Chris@148 169 void
Chris@297 170 CodedAudioFileReader::addSamplesToDecodeCache(float **samples, size_t nframes)
Chris@148 171 {
Chris@263 172 QMutexLocker locker(&m_cacheMutex);
Chris@263 173
Chris@148 174 if (!m_initialised) return;
Chris@148 175
Chris@297 176 for (size_t i = 0; i < nframes; ++i) {
Chris@297 177
Chris@297 178 for (size_t c = 0; c < m_channelCount; ++c) {
Chris@148 179
Chris@297 180 float sample = samples[c][i];
Chris@297 181
Chris@297 182 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 183
Chris@297 184 if (m_cacheWriteBufferIndex ==
Chris@297 185 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 186
Chris@297 187 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 188 m_cacheWriteBufferIndex = 0;
Chris@297 189 }
Chris@297 190
Chris@297 191 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 192 m_cacheFileReader) {
Chris@297 193 m_cacheFileReader->updateFrameCount();
Chris@297 194 }
Chris@297 195 }
Chris@297 196 }
Chris@297 197 }
Chris@297 198
Chris@297 199 void
Chris@297 200 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, size_t nframes)
Chris@297 201 {
Chris@297 202 QMutexLocker locker(&m_cacheMutex);
Chris@297 203
Chris@297 204 if (!m_initialised) return;
Chris@297 205
Chris@297 206 for (size_t i = 0; i < nframes; ++i) {
Chris@297 207
Chris@297 208 for (size_t c = 0; c < m_channelCount; ++c) {
Chris@297 209
Chris@297 210 float sample = samples[i * m_channelCount + c];
Chris@297 211
Chris@297 212 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@297 213
Chris@297 214 if (m_cacheWriteBufferIndex ==
Chris@297 215 m_cacheWriteBufferSize * m_channelCount) {
Chris@297 216
Chris@297 217 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@297 218 m_cacheWriteBufferIndex = 0;
Chris@297 219 }
Chris@297 220
Chris@297 221 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@297 222 m_cacheFileReader) {
Chris@297 223 m_cacheFileReader->updateFrameCount();
Chris@297 224 }
Chris@297 225 }
Chris@297 226 }
Chris@297 227 }
Chris@297 228
Chris@297 229 void
Chris@297 230 CodedAudioFileReader::addSamplesToDecodeCache(const SampleBlock &samples)
Chris@297 231 {
Chris@297 232 QMutexLocker locker(&m_cacheMutex);
Chris@297 233
Chris@297 234 if (!m_initialised) return;
Chris@297 235
Chris@297 236 for (size_t i = 0; i < samples.size(); ++i) {
Chris@297 237
Chris@297 238 float sample = samples[i];
Chris@297 239
Chris@148 240 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 241
Chris@148 242 if (m_cacheWriteBufferIndex ==
Chris@148 243 m_cacheWriteBufferSize * m_channelCount) {
Chris@148 244
Chris@297 245 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
Chris@148 246 m_cacheWriteBufferIndex = 0;
Chris@266 247 }
Chris@265 248
Chris@266 249 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@266 250 m_cacheFileReader) {
Chris@266 251 m_cacheFileReader->updateFrameCount();
Chris@148 252 }
Chris@148 253 }
Chris@148 254 }
Chris@148 255
Chris@148 256 void
Chris@148 257 CodedAudioFileReader::finishDecodeCache()
Chris@148 258 {
Chris@263 259 QMutexLocker locker(&m_cacheMutex);
Chris@263 260
Chris@192 261 Profiler profiler("CodedAudioFileReader::finishDecodeCache", true);
Chris@192 262
Chris@148 263 if (!m_initialised) {
Chris@148 264 std::cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << std::endl;
Chris@148 265 return;
Chris@148 266 }
Chris@148 267
Chris@297 268 if (m_cacheWriteBufferIndex > 0) {
Chris@297 269 //!!! check for return value! out of disk space, etc!
Chris@297 270 pushBuffer(m_cacheWriteBuffer,
Chris@297 271 m_cacheWriteBufferIndex / m_channelCount,
Chris@297 272 true);
Chris@297 273 }
Chris@297 274
Chris@297 275 delete[] m_cacheWriteBuffer;
Chris@297 276 m_cacheWriteBuffer = 0;
Chris@297 277
Chris@297 278 delete[] m_resampleBuffer;
Chris@297 279 m_resampleBuffer = 0;
Chris@297 280
Chris@297 281 delete m_resampler;
Chris@297 282 m_resampler = 0;
Chris@297 283
Chris@297 284 if (m_cacheMode == CacheInTemporaryFile) {
Chris@297 285 sf_close(m_cacheFileWritePtr);
Chris@297 286 m_cacheFileWritePtr = 0;
Chris@297 287 if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
Chris@297 288 }
Chris@297 289 }
Chris@297 290
Chris@297 291 void
Chris@297 292 CodedAudioFileReader::pushBuffer(float *buffer, size_t sz, bool final)
Chris@297 293 {
Chris@297 294 if (m_resampler) {
Chris@297 295
Chris@297 296 float ratio = float(m_sampleRate) / float(m_fileRate);
Chris@297 297
Chris@297 298 if (ratio != 1.f) {
Chris@297 299 size_t out = m_resampler->resampleInterleaved
Chris@297 300 (buffer,
Chris@297 301 m_resampleBuffer,
Chris@297 302 sz,
Chris@297 303 ratio,
Chris@297 304 final);
Chris@297 305
Chris@304 306 for (size_t i = 0; i < out; ++i) {
Chris@304 307 if (m_resampleBuffer[i] > 1.f) m_resampleBuffer[i] = 1.f;
Chris@304 308 if (m_resampleBuffer[i] < -1.f) m_resampleBuffer[i] = -1.f;
Chris@304 309 }
Chris@304 310
Chris@297 311 buffer = m_resampleBuffer;
Chris@297 312 sz = out;
Chris@297 313 }
Chris@297 314 }
Chris@297 315
Chris@297 316 m_frameCount += sz;
Chris@297 317
Chris@148 318 switch (m_cacheMode) {
Chris@148 319
Chris@148 320 case CacheInTemporaryFile:
Chris@297 321 //!!! check for return value! out of disk space, etc!
Chris@297 322 sf_writef_float(m_cacheFileWritePtr, buffer, sz);
Chris@148 323 break;
Chris@148 324
Chris@148 325 case CacheInMemory:
Chris@297 326 for (size_t s = 0; s < sz; ++s) {
Chris@297 327 m_data.push_back(buffer[sz]);
Chris@297 328 }
Chris@297 329 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 330 break;
Chris@148 331 }
Chris@148 332 }
Chris@148 333
Chris@148 334 void
Chris@148 335 CodedAudioFileReader::getInterleavedFrames(size_t start, size_t count,
Chris@148 336 SampleBlock &frames) const
Chris@148 337 {
Chris@263 338 //!!! we want to ensure this doesn't require a lock -- at the
Chris@263 339 // moment it does need one, but it doesn't have one...
Chris@263 340
Chris@265 341 if (!m_initialised) {
Chris@265 342 std::cerr << "CodedAudioFileReader::getInterleavedFrames: not initialised" << std::endl;
Chris@265 343 return;
Chris@265 344 }
Chris@148 345
Chris@148 346 switch (m_cacheMode) {
Chris@148 347
Chris@148 348 case CacheInTemporaryFile:
Chris@148 349 if (m_cacheFileReader) {
Chris@148 350 m_cacheFileReader->getInterleavedFrames(start, count, frames);
Chris@148 351 }
Chris@148 352 break;
Chris@148 353
Chris@148 354 case CacheInMemory:
Chris@148 355 {
Chris@148 356 frames.clear();
Chris@148 357 if (!isOK()) return;
Chris@148 358 if (count == 0) return;
Chris@148 359
Chris@148 360 // slownessabounds
Chris@148 361
Chris@148 362 for (size_t i = start; i < start + count; ++i) {
Chris@148 363 for (size_t ch = 0; ch < m_channelCount; ++ch) {
Chris@148 364 size_t index = i * m_channelCount + ch;
Chris@148 365 if (index >= m_data.size()) return;
Chris@148 366 frames.push_back(m_data[index]);
Chris@148 367 }
Chris@148 368 }
Chris@148 369 }
Chris@148 370 }
Chris@148 371 }
Chris@148 372