svcore  1.9
CodedAudioFileReader.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006-2007 Chris Cannam and QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "CodedAudioFileReader.h"
00017 
00018 #include "WavFileReader.h"
00019 #include "base/TempDirectory.h"
00020 #include "base/Exceptions.h"
00021 #include "base/Profiler.h"
00022 #include "base/Serialiser.h"
00023 #include "base/Resampler.h"
00024 
00025 #include <stdint.h>
00026 #include <iostream>
00027 #include <QDir>
00028 #include <QMutexLocker>
00029 
00030 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode,
00031                                            int targetRate,
00032                                            bool normalised) :
00033     m_cacheMode(cacheMode),
00034     m_initialised(false),
00035     m_serialiser(0),
00036     m_fileRate(0),
00037     m_cacheFileWritePtr(0),
00038     m_cacheFileReader(0),
00039     m_cacheWriteBuffer(0),
00040     m_cacheWriteBufferIndex(0),
00041     m_cacheWriteBufferSize(16384),
00042     m_resampler(0),
00043     m_resampleBuffer(0),
00044     m_fileFrameCount(0),
00045     m_normalised(normalised),
00046     m_max(0.f),
00047     m_gain(1.f)
00048 {
00049     SVDEBUG << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << ", normalised = " << normalised << endl;
00050 
00051     m_frameCount = 0;
00052     m_sampleRate = targetRate;
00053 }
00054 
00055 CodedAudioFileReader::~CodedAudioFileReader()
00056 {
00057     QMutexLocker locker(&m_cacheMutex);
00058 
00059     endSerialised();
00060 
00061     if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
00062 
00063     SVDEBUG << "CodedAudioFileReader::~CodedAudioFileReader: deleting cache file reader" << endl;
00064 
00065     delete m_cacheFileReader;
00066     delete[] m_cacheWriteBuffer;
00067 
00068     if (m_cacheFileName != "") {
00069         if (!QFile(m_cacheFileName).remove()) {
00070             cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName << "\"" << endl;
00071         }
00072     }
00073 
00074     delete m_resampler;
00075     delete[] m_resampleBuffer;
00076 }
00077 
00078 void
00079 CodedAudioFileReader::startSerialised(QString id)
00080 {
00081     SVDEBUG << "CodedAudioFileReader::startSerialised(" << id << ")" << endl;
00082 
00083     delete m_serialiser;
00084     m_serialiser = new Serialiser(id);
00085 }
00086 
00087 void
00088 CodedAudioFileReader::endSerialised()
00089 {
00090     SVDEBUG << "CodedAudioFileReader(" << this << ")::endSerialised: id = " << (m_serialiser ? m_serialiser->getId() : "(none)") << endl;
00091 
00092     delete m_serialiser;
00093     m_serialiser = 0;
00094 }
00095 
00096 void
00097 CodedAudioFileReader::initialiseDecodeCache()
00098 {
00099     QMutexLocker locker(&m_cacheMutex);
00100 
00101     SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: file rate = " << m_fileRate << endl;
00102 
00103     if (m_fileRate == 0) {
00104         cerr << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << endl;
00105         throw FileOperationFailed("(coded file)", "File sample rate unknown (bug in subclass implementation?)");
00106     }
00107     if (m_sampleRate == 0) {
00108         m_sampleRate = m_fileRate;
00109         SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: rate (from file) = " << m_fileRate << endl;
00110     }
00111     if (m_fileRate != m_sampleRate) {
00112         SVDEBUG << "CodedAudioFileReader: resampling " << m_fileRate << " -> " <<  m_sampleRate << endl;
00113         m_resampler = new Resampler(Resampler::FastestTolerable,
00114                                     m_channelCount,
00115                                     m_cacheWriteBufferSize);
00116         float ratio = float(m_sampleRate) / float(m_fileRate);
00117         m_resampleBuffer = new float
00118             [lrintf(ceilf(m_cacheWriteBufferSize * m_channelCount * ratio + 1))];
00119     }
00120 
00121     m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
00122     m_cacheWriteBufferIndex = 0;
00123 
00124     if (m_cacheMode == CacheInTemporaryFile) {
00125 
00126         try {
00127             QDir dir(TempDirectory::getInstance()->getPath());
00128             m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
00129                                            .arg((intptr_t)this));
00130 
00131             SF_INFO fileInfo;
00132             fileInfo.samplerate = m_sampleRate;
00133             fileInfo.channels = m_channelCount;
00134 
00135             // No point in writing 24-bit or float; generally this
00136             // class is used for decoding files that have come from a
00137             // 16 bit source or that decode to only 16 bits anyway.
00138             fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
00139     
00140             m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
00141                                           SFM_WRITE, &fileInfo);
00142 
00143             if (m_cacheFileWritePtr) {
00144 
00145                 // Ideally we would do this now only if we were in a
00146                 // threaded mode -- creating the reader later if we're
00147                 // not threaded -- but we don't have access to that
00148                 // information here
00149 
00150                 m_cacheFileReader = new WavFileReader(m_cacheFileName);
00151 
00152                 if (!m_cacheFileReader->isOK()) {
00153                     cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError() << endl;
00154                     delete m_cacheFileReader;
00155                     m_cacheFileReader = 0;
00156                     m_cacheMode = CacheInMemory;
00157                     sf_close(m_cacheFileWritePtr);
00158                 }
00159 
00160             } else {
00161                 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" << endl;
00162                 m_cacheMode = CacheInMemory;
00163             }
00164 
00165         } catch (DirectoryCreationFailed f) {
00166             cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << endl;
00167             m_cacheMode = CacheInMemory;
00168         }
00169     }
00170 
00171     if (m_cacheMode == CacheInMemory) {
00172         m_data.clear();
00173     }
00174 
00175     m_initialised = true;
00176 }
00177 
00178 void
00179 CodedAudioFileReader::addSamplesToDecodeCache(float **samples, int nframes)
00180 {
00181     QMutexLocker locker(&m_cacheMutex);
00182 
00183     if (!m_initialised) return;
00184 
00185     for (int i = 0; i < nframes; ++i) {
00186         
00187         for (int c = 0; c < m_channelCount; ++c) {
00188 
00189             float sample = samples[c][i];
00190         
00191             m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
00192 
00193             if (m_cacheWriteBufferIndex ==
00194                 m_cacheWriteBufferSize * m_channelCount) {
00195 
00196                 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
00197                 m_cacheWriteBufferIndex = 0;
00198             }
00199 
00200             if (m_cacheWriteBufferIndex % 10240 == 0 &&
00201                 m_cacheFileReader) {
00202                 m_cacheFileReader->updateFrameCount();
00203             }
00204         }
00205     }
00206 }
00207 
00208 void
00209 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, int nframes)
00210 {
00211     QMutexLocker locker(&m_cacheMutex);
00212 
00213     if (!m_initialised) return;
00214 
00215     for (int i = 0; i < nframes; ++i) {
00216         
00217         for (int c = 0; c < m_channelCount; ++c) {
00218 
00219             float sample = samples[i * m_channelCount + c];
00220         
00221             m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
00222 
00223             if (m_cacheWriteBufferIndex ==
00224                 m_cacheWriteBufferSize * m_channelCount) {
00225 
00226                 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
00227                 m_cacheWriteBufferIndex = 0;
00228             }
00229 
00230             if (m_cacheWriteBufferIndex % 10240 == 0 &&
00231                 m_cacheFileReader) {
00232                 m_cacheFileReader->updateFrameCount();
00233             }
00234         }
00235     }
00236 }
00237 
00238 void
00239 CodedAudioFileReader::addSamplesToDecodeCache(const SampleBlock &samples)
00240 {
00241     QMutexLocker locker(&m_cacheMutex);
00242 
00243     if (!m_initialised) return;
00244 
00245     for (int i = 0; i < (int)samples.size(); ++i) {
00246 
00247         float sample = samples[i];
00248         
00249         m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
00250 
00251         if (m_cacheWriteBufferIndex ==
00252             m_cacheWriteBufferSize * m_channelCount) {
00253 
00254             pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
00255             m_cacheWriteBufferIndex = 0;
00256         }
00257 
00258         if (m_cacheWriteBufferIndex % 10240 == 0 &&
00259             m_cacheFileReader) {
00260             m_cacheFileReader->updateFrameCount();
00261         }
00262     }
00263 }
00264 
00265 void
00266 CodedAudioFileReader::finishDecodeCache()
00267 {
00268     QMutexLocker locker(&m_cacheMutex);
00269 
00270     Profiler profiler("CodedAudioFileReader::finishDecodeCache", true);
00271 
00272     if (!m_initialised) {
00273         cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << endl;
00274         return;
00275     }
00276 
00277     pushBuffer(m_cacheWriteBuffer,
00278                m_cacheWriteBufferIndex / m_channelCount,
00279                true);
00280 
00281     delete[] m_cacheWriteBuffer;
00282     m_cacheWriteBuffer = 0;
00283 
00284     delete[] m_resampleBuffer;
00285     m_resampleBuffer = 0;
00286 
00287     delete m_resampler;
00288     m_resampler = 0;
00289 
00290     if (m_cacheMode == CacheInTemporaryFile) {
00291         sf_close(m_cacheFileWritePtr);
00292         m_cacheFileWritePtr = 0;
00293         if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
00294     }
00295 }
00296 
00297 void
00298 CodedAudioFileReader::pushBuffer(float *buffer, int sz, bool final)
00299 {
00300     m_fileFrameCount += sz;
00301 
00302     float ratio = 1.f;
00303     if (m_resampler && m_fileRate != 0) {
00304         ratio = float(m_sampleRate) / float(m_fileRate);
00305     }
00306         
00307     if (ratio != 1.f) {
00308         pushBufferResampling(buffer, sz, ratio, final);
00309     } else {
00310         pushBufferNonResampling(buffer, sz);
00311     }
00312 }
00313 
00314 void
00315 CodedAudioFileReader::pushBufferNonResampling(float *buffer, int sz)
00316 {
00317     float clip = 1.0;
00318     int count = sz * m_channelCount;
00319 
00320     if (m_normalised) {
00321         for (int i = 0; i < count; ++i) {
00322             float v = fabsf(buffer[i]);
00323             if (v > m_max) {
00324                 m_max = v;
00325                 m_gain = 1.f / m_max;
00326             }
00327         }
00328     } else {
00329         for (int i = 0; i < count; ++i) {
00330             if (buffer[i] >  clip) buffer[i] =  clip;
00331         }
00332         for (int i = 0; i < count; ++i) {
00333             if (buffer[i] < -clip) buffer[i] = -clip;
00334         }
00335     }
00336 
00337     m_frameCount += sz;
00338 
00339     switch (m_cacheMode) {
00340 
00341     case CacheInTemporaryFile:
00342         if (sf_writef_float(m_cacheFileWritePtr, buffer, sz) < (int)sz) {
00343             sf_close(m_cacheFileWritePtr);
00344             m_cacheFileWritePtr = 0;
00345             throw InsufficientDiscSpace(TempDirectory::getInstance()->getPath());
00346         }
00347         break;
00348 
00349     case CacheInMemory:
00350         m_dataLock.lockForWrite();
00351         for (int s = 0; s < count; ++s) {
00352             m_data.push_back(buffer[s]);
00353         }
00354         MUNLOCK_SAMPLEBLOCK(m_data);
00355         m_dataLock.unlock();
00356         break;
00357     }
00358 }
00359 
00360 void
00361 CodedAudioFileReader::pushBufferResampling(float *buffer, int sz,
00362                                            float ratio, bool final)
00363 {
00364     SVDEBUG << "pushBufferResampling: ratio = " << ratio << ", sz = " << sz << ", final = " << final << endl;
00365 
00366     if (sz > 0) {
00367 
00368         int out = m_resampler->resampleInterleaved
00369             (buffer,
00370              m_resampleBuffer,
00371              sz,
00372              ratio,
00373              false);
00374 
00375         pushBufferNonResampling(m_resampleBuffer, out);
00376     }
00377 
00378     if (final) {
00379 
00380         int padFrames = 1;
00381         if (m_frameCount / ratio < m_fileFrameCount) {
00382             padFrames = m_fileFrameCount - (m_frameCount / ratio) + 1;
00383         }
00384 
00385         int padSamples = padFrames * m_channelCount;
00386 
00387         SVDEBUG << "frameCount = " << m_frameCount << ", equivFileFrames = " << m_frameCount / ratio << ", m_fileFrameCount = " << m_fileFrameCount << ", padFrames= " << padFrames << ", padSamples = " << padSamples << endl;
00388 
00389         float *padding = new float[padSamples];
00390         for (int i = 0; i < padSamples; ++i) padding[i] = 0.f;
00391 
00392         int out = m_resampler->resampleInterleaved
00393             (padding,
00394              m_resampleBuffer,
00395              padFrames,
00396              ratio,
00397              true);
00398 
00399         if (int(m_frameCount + out) > int(m_fileFrameCount * ratio)) {
00400             out = int(m_fileFrameCount * ratio) - int(m_frameCount);
00401         }
00402 
00403         pushBufferNonResampling(m_resampleBuffer, out);
00404         delete[] padding;
00405     }
00406 }
00407 
00408 void
00409 CodedAudioFileReader::getInterleavedFrames(int start, int count,
00410                                            SampleBlock &frames) const
00411 {
00412     // Lock is only required in CacheInMemory mode (the cache file
00413     // reader is expected to be thread safe and manage its own
00414     // locking)
00415 
00416     if (!m_initialised) {
00417         SVDEBUG << "CodedAudioFileReader::getInterleavedFrames: not initialised" << endl;
00418         return;
00419     }
00420 
00421     switch (m_cacheMode) {
00422 
00423     case CacheInTemporaryFile:
00424         if (m_cacheFileReader) {
00425             m_cacheFileReader->getInterleavedFrames(start, count, frames);
00426         }
00427         break;
00428 
00429     case CacheInMemory:
00430     {
00431         frames.clear();
00432         if (!isOK()) return;
00433         if (count == 0) return;
00434         frames.reserve(count * m_channelCount);
00435 
00436         int idx = start * m_channelCount;
00437         int i = 0;
00438 
00439         m_dataLock.lockForRead();
00440         while (i < count * m_channelCount && idx < (int)m_data.size()) {
00441             frames.push_back(m_data[idx]);
00442             ++idx;
00443         }
00444         m_dataLock.unlock();
00445     }
00446     }
00447 
00448     if (m_normalised) {
00449         for (int i = 0; i < (int)(count * m_channelCount); ++i) {
00450             frames[i] *= m_gain;
00451         }
00452     }
00453 }
00454