svcore  1.9
CodedAudioFileReader.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006-2007 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "CodedAudioFileReader.h"
17 
18 #include "WavFileReader.h"
19 #include "base/TempDirectory.h"
20 #include "base/Exceptions.h"
21 #include "base/Profiler.h"
22 #include "base/Serialiser.h"
23 #include "base/Resampler.h"
24 
25 #include <stdint.h>
26 #include <iostream>
27 #include <QDir>
28 #include <QMutexLocker>
29 
31  int targetRate,
32  bool normalised) :
33  m_cacheMode(cacheMode),
34  m_initialised(false),
35  m_serialiser(0),
36  m_fileRate(0),
37  m_cacheFileWritePtr(0),
38  m_cacheFileReader(0),
39  m_cacheWriteBuffer(0),
40  m_cacheWriteBufferIndex(0),
41  m_cacheWriteBufferSize(16384),
42  m_resampler(0),
43  m_resampleBuffer(0),
44  m_fileFrameCount(0),
45  m_normalised(normalised),
46  m_max(0.f),
47  m_gain(1.f)
48 {
49  SVDEBUG << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << ", normalised = " << normalised << endl;
50 
51  m_frameCount = 0;
52  m_sampleRate = targetRate;
53 }
54 
56 {
57  QMutexLocker locker(&m_cacheMutex);
58 
59  endSerialised();
60 
62 
63  SVDEBUG << "CodedAudioFileReader::~CodedAudioFileReader: deleting cache file reader" << endl;
64 
65  delete m_cacheFileReader;
66  delete[] m_cacheWriteBuffer;
67 
68  if (m_cacheFileName != "") {
69  if (!QFile(m_cacheFileName).remove()) {
70  cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName << "\"" << endl;
71  }
72  }
73 
74  delete m_resampler;
75  delete[] m_resampleBuffer;
76 }
77 
78 void
80 {
81  SVDEBUG << "CodedAudioFileReader::startSerialised(" << id << ")" << endl;
82 
83  delete m_serialiser;
84  m_serialiser = new Serialiser(id);
85 }
86 
87 void
89 {
90  SVDEBUG << "CodedAudioFileReader(" << this << ")::endSerialised: id = " << (m_serialiser ? m_serialiser->getId() : "(none)") << endl;
91 
92  delete m_serialiser;
93  m_serialiser = 0;
94 }
95 
96 void
98 {
99  QMutexLocker locker(&m_cacheMutex);
100 
101  SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: file rate = " << m_fileRate << endl;
102 
103  if (m_fileRate == 0) {
104  cerr << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << endl;
105  throw FileOperationFailed("(coded file)", "File sample rate unknown (bug in subclass implementation?)");
106  }
107  if (m_sampleRate == 0) {
109  SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: rate (from file) = " << m_fileRate << endl;
110  }
111  if (m_fileRate != m_sampleRate) {
112  SVDEBUG << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << endl;
116  float ratio = float(m_sampleRate) / float(m_fileRate);
117  m_resampleBuffer = new float
118  [lrintf(ceilf(m_cacheWriteBufferSize * m_channelCount * ratio + 1))];
119  }
120 
123 
125 
126  try {
127  QDir dir(TempDirectory::getInstance()->getPath());
128  m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
129  .arg((intptr_t)this));
130 
131  SF_INFO fileInfo;
132  fileInfo.samplerate = m_sampleRate;
133  fileInfo.channels = m_channelCount;
134 
135  // No point in writing 24-bit or float; generally this
136  // class is used for decoding files that have come from a
137  // 16 bit source or that decode to only 16 bits anyway.
138  fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
139 
140  m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
141  SFM_WRITE, &fileInfo);
142 
143  if (m_cacheFileWritePtr) {
144 
145  // Ideally we would do this now only if we were in a
146  // threaded mode -- creating the reader later if we're
147  // not threaded -- but we don't have access to that
148  // information here
149 
151 
152  if (!m_cacheFileReader->isOK()) {
153  cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError() << endl;
154  delete m_cacheFileReader;
155  m_cacheFileReader = 0;
157  sf_close(m_cacheFileWritePtr);
158  }
159 
160  } else {
161  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;
163  }
164 
165  } catch (DirectoryCreationFailed f) {
166  cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << endl;
168  }
169  }
170 
171  if (m_cacheMode == CacheInMemory) {
172  m_data.clear();
173  }
174 
175  m_initialised = true;
176 }
177 
178 void
180 {
181  QMutexLocker locker(&m_cacheMutex);
182 
183  if (!m_initialised) return;
184 
185  for (int i = 0; i < nframes; ++i) {
186 
187  for (int c = 0; c < m_channelCount; ++c) {
188 
189  float sample = samples[c][i];
190 
192 
195 
198  }
199 
200  if (m_cacheWriteBufferIndex % 10240 == 0 &&
203  }
204  }
205  }
206 }
207 
208 void
210 {
211  QMutexLocker locker(&m_cacheMutex);
212 
213  if (!m_initialised) return;
214 
215  for (int i = 0; i < nframes; ++i) {
216 
217  for (int c = 0; c < m_channelCount; ++c) {
218 
219  float sample = samples[i * m_channelCount + c];
220 
222 
225 
228  }
229 
230  if (m_cacheWriteBufferIndex % 10240 == 0 &&
233  }
234  }
235  }
236 }
237 
238 void
240 {
241  QMutexLocker locker(&m_cacheMutex);
242 
243  if (!m_initialised) return;
244 
245  for (int i = 0; i < (int)samples.size(); ++i) {
246 
247  float sample = samples[i];
248 
250 
253 
256  }
257 
258  if (m_cacheWriteBufferIndex % 10240 == 0 &&
261  }
262  }
263 }
264 
265 void
267 {
268  QMutexLocker locker(&m_cacheMutex);
269 
270  Profiler profiler("CodedAudioFileReader::finishDecodeCache", true);
271 
272  if (!m_initialised) {
273  cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << endl;
274  return;
275  }
276 
279  true);
280 
281  delete[] m_cacheWriteBuffer;
282  m_cacheWriteBuffer = 0;
283 
284  delete[] m_resampleBuffer;
285  m_resampleBuffer = 0;
286 
287  delete m_resampler;
288  m_resampler = 0;
289 
291  sf_close(m_cacheFileWritePtr);
294  }
295 }
296 
297 void
298 CodedAudioFileReader::pushBuffer(float *buffer, int sz, bool final)
299 {
300  m_fileFrameCount += sz;
301 
302  float ratio = 1.f;
303  if (m_resampler && m_fileRate != 0) {
304  ratio = float(m_sampleRate) / float(m_fileRate);
305  }
306 
307  if (ratio != 1.f) {
308  pushBufferResampling(buffer, sz, ratio, final);
309  } else {
310  pushBufferNonResampling(buffer, sz);
311  }
312 }
313 
314 void
316 {
317  float clip = 1.0;
318  int count = sz * m_channelCount;
319 
320  if (m_normalised) {
321  for (int i = 0; i < count; ++i) {
322  float v = fabsf(buffer[i]);
323  if (v > m_max) {
324  m_max = v;
325  m_gain = 1.f / m_max;
326  }
327  }
328  } else {
329  for (int i = 0; i < count; ++i) {
330  if (buffer[i] > clip) buffer[i] = clip;
331  }
332  for (int i = 0; i < count; ++i) {
333  if (buffer[i] < -clip) buffer[i] = -clip;
334  }
335  }
336 
337  m_frameCount += sz;
338 
339  switch (m_cacheMode) {
340 
342  if (sf_writef_float(m_cacheFileWritePtr, buffer, sz) < (int)sz) {
343  sf_close(m_cacheFileWritePtr);
346  }
347  break;
348 
349  case CacheInMemory:
350  m_dataLock.lockForWrite();
351  for (int s = 0; s < count; ++s) {
352  m_data.push_back(buffer[s]);
353  }
355  m_dataLock.unlock();
356  break;
357  }
358 }
359 
360 void
362  float ratio, bool final)
363 {
364  SVDEBUG << "pushBufferResampling: ratio = " << ratio << ", sz = " << sz << ", final = " << final << endl;
365 
366  if (sz > 0) {
367 
369  (buffer,
371  sz,
372  ratio,
373  false);
374 
376  }
377 
378  if (final) {
379 
380  int padFrames = 1;
381  if (m_frameCount / ratio < m_fileFrameCount) {
382  padFrames = m_fileFrameCount - (m_frameCount / ratio) + 1;
383  }
384 
385  int padSamples = padFrames * m_channelCount;
386 
387  SVDEBUG << "frameCount = " << m_frameCount << ", equivFileFrames = " << m_frameCount / ratio << ", m_fileFrameCount = " << m_fileFrameCount << ", padFrames= " << padFrames << ", padSamples = " << padSamples << endl;
388 
389  float *padding = new float[padSamples];
390  for (int i = 0; i < padSamples; ++i) padding[i] = 0.f;
391 
393  (padding,
395  padFrames,
396  ratio,
397  true);
398 
399  if (int(m_frameCount + out) > int(m_fileFrameCount * ratio)) {
400  out = int(m_fileFrameCount * ratio) - int(m_frameCount);
401  }
402 
404  delete[] padding;
405  }
406 }
407 
408 void
410  SampleBlock &frames) const
411 {
412  // Lock is only required in CacheInMemory mode (the cache file
413  // reader is expected to be thread safe and manage its own
414  // locking)
415 
416  if (!m_initialised) {
417  SVDEBUG << "CodedAudioFileReader::getInterleavedFrames: not initialised" << endl;
418  return;
419  }
420 
421  switch (m_cacheMode) {
422 
424  if (m_cacheFileReader) {
425  m_cacheFileReader->getInterleavedFrames(start, count, frames);
426  }
427  break;
428 
429  case CacheInMemory:
430  {
431  frames.clear();
432  if (!isOK()) return;
433  if (count == 0) return;
434  frames.reserve(count * m_channelCount);
435 
436  int idx = start * m_channelCount;
437  int i = 0;
438 
439  m_dataLock.lockForRead();
440  while (i < count * m_channelCount && idx < (int)m_data.size()) {
441  frames.push_back(m_data[idx]);
442  ++idx;
443  }
444  m_dataLock.unlock();
445  }
446  }
447 
448  if (m_normalised) {
449  for (int i = 0; i < (int)(count * m_channelCount); ++i) {
450  frames[i] *= m_gain;
451  }
452  }
453 }
454 
int resampleInterleaved(float *in, float *out, int incount, float ratio, bool final=false)
Definition: Resampler.cpp:184
std::vector< float > SampleBlock
QString getId() const
Definition: Serialiser.h:30
Reader for audio files using libsndfile.
Definition: WavFileReader.h:36
void pushBufferResampling(float *interleaved, int sz, float ratio, bool final)
bool isOK() const
void addSamplesToDecodeCache(float **samples, int nframes)
CodedAudioFileReader(CacheMode cacheMode, int targetRate, bool normalised)
static TempDirectory * getInstance()
void pushBufferNonResampling(float *interleaved, int sz)
#define MUNLOCK_SAMPLEBLOCK(a)
Definition: System.h:81
void pushBuffer(float *interleaved, int sz, bool final)
virtual void getInterleavedFrames(int start, int count, SampleBlock &frames) const
Return interleaved samples for count frames from index start.
WavFileReader * m_cacheFileReader
#define SVDEBUG
Definition: Debug.h:42
virtual void getInterleavedFrames(int start, int count, SampleBlock &frames) const
Must be safe to call from multiple threads with different arguments on the same object at the same ti...
void updateFrameCount()
virtual QString getError() const
Definition: WavFileReader.h:43
void startSerialised(QString id)
Profile point instance class.
Definition: Profiler.h:86