svcore
1.9
|
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