annotate data/fileio/CodedAudioFileReader.cpp @ 360:ac300d385ab2

* Various fixes to object lifetime management, particularly in the spectrum layer and for notification of main model deletion. The main purpose of this is to improve the behaviour of the spectrum, but I think it may also help with #1840922 Various crashes in Layer Summary window.
author Chris Cannam
date Wed, 23 Jan 2008 15:43:27 +0000
parents 1d656dcda8ef
children f1ff248a793e
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@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@322 294 float max = 1.0;
Chris@322 295 size_t count = sz * m_channelCount;
Chris@318 296
Chris@297 297 if (m_resampler) {
Chris@297 298
Chris@297 299 float ratio = float(m_sampleRate) / float(m_fileRate);
Chris@297 300
Chris@297 301 if (ratio != 1.f) {
Chris@322 302
Chris@297 303 size_t out = m_resampler->resampleInterleaved
Chris@297 304 (buffer,
Chris@297 305 m_resampleBuffer,
Chris@297 306 sz,
Chris@297 307 ratio,
Chris@297 308 final);
Chris@297 309
Chris@297 310 buffer = m_resampleBuffer;
Chris@297 311 sz = out;
Chris@322 312 count = sz * m_channelCount;
Chris@297 313 }
Chris@322 314 }
Chris@318 315
Chris@322 316 for (size_t i = 0; i < count; ++i) {
Chris@322 317 if (buffer[i] > max) buffer[i] = max;
Chris@322 318 }
Chris@322 319 for (size_t i = 0; i < count; ++i) {
Chris@322 320 if (buffer[i] < -max) buffer[i] = -max;
Chris@297 321 }
Chris@297 322
Chris@297 323 m_frameCount += sz;
Chris@297 324
Chris@148 325 switch (m_cacheMode) {
Chris@148 326
Chris@148 327 case CacheInTemporaryFile:
Chris@297 328 //!!! check for return value! out of disk space, etc!
Chris@297 329 sf_writef_float(m_cacheFileWritePtr, buffer, sz);
Chris@148 330 break;
Chris@148 331
Chris@148 332 case CacheInMemory:
Chris@322 333 for (size_t s = 0; s < count; ++s) {
Chris@322 334 m_data.push_back(buffer[count]);
Chris@297 335 }
Chris@297 336 MUNLOCK_SAMPLEBLOCK(m_data);
Chris@148 337 break;
Chris@148 338 }
Chris@148 339 }
Chris@148 340
Chris@148 341 void
Chris@148 342 CodedAudioFileReader::getInterleavedFrames(size_t start, size_t count,
Chris@148 343 SampleBlock &frames) const
Chris@148 344 {
Chris@263 345 //!!! we want to ensure this doesn't require a lock -- at the
Chris@263 346 // moment it does need one, but it doesn't have one...
Chris@263 347
Chris@265 348 if (!m_initialised) {
Chris@265 349 std::cerr << "CodedAudioFileReader::getInterleavedFrames: not initialised" << std::endl;
Chris@265 350 return;
Chris@265 351 }
Chris@148 352
Chris@148 353 switch (m_cacheMode) {
Chris@148 354
Chris@148 355 case CacheInTemporaryFile:
Chris@148 356 if (m_cacheFileReader) {
Chris@148 357 m_cacheFileReader->getInterleavedFrames(start, count, frames);
Chris@148 358 }
Chris@148 359 break;
Chris@148 360
Chris@148 361 case CacheInMemory:
Chris@148 362 {
Chris@148 363 frames.clear();
Chris@148 364 if (!isOK()) return;
Chris@148 365 if (count == 0) return;
Chris@148 366
Chris@148 367 // slownessabounds
Chris@148 368
Chris@148 369 for (size_t i = start; i < start + count; ++i) {
Chris@148 370 for (size_t ch = 0; ch < m_channelCount; ++ch) {
Chris@148 371 size_t index = i * m_channelCount + ch;
Chris@148 372 if (index >= m_data.size()) return;
Chris@148 373 frames.push_back(m_data[index]);
Chris@148 374 }
Chris@148 375 }
Chris@148 376 }
Chris@148 377 }
Chris@148 378 }
Chris@148 379