annotate data/fileio/CodedAudioFileReader.cpp @ 271:822bd7fd526c

* Add support for reading mp3 and Ogg file title tags
author Chris Cannam
date Mon, 02 Jul 2007 13:53:38 +0000
parents 2268963dabd1
children 20028c634494
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@148 7 This file copyright 2006 Chris Cannam.
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@148 22
Chris@148 23 #include <iostream>
Chris@148 24 #include <QDir>
Chris@263 25 #include <QMutexLocker>
Chris@148 26
Chris@148 27 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode) :
Chris@148 28 m_cacheMode(cacheMode),
Chris@148 29 m_initialised(false),
Chris@148 30 m_cacheFileWritePtr(0),
Chris@148 31 m_cacheFileReader(0),
Chris@148 32 m_cacheWriteBuffer(0),
Chris@148 33 m_cacheWriteBufferIndex(0),
Chris@148 34 m_cacheWriteBufferSize(16384)
Chris@148 35 {
Chris@148 36 }
Chris@148 37
Chris@148 38 CodedAudioFileReader::~CodedAudioFileReader()
Chris@148 39 {
Chris@263 40 QMutexLocker locker(&m_cacheMutex);
Chris@263 41
Chris@148 42 if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
Chris@148 43 if (m_cacheFileReader) delete m_cacheFileReader;
Chris@148 44 if (m_cacheWriteBuffer) delete[] m_cacheWriteBuffer;
Chris@148 45
Chris@148 46 if (m_cacheFileName != "") {
Chris@148 47 if (!QFile(m_cacheFileName).remove()) {
Chris@148 48 std::cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName.toStdString() << "\"" << std::endl;
Chris@148 49 }
Chris@148 50 }
Chris@148 51 }
Chris@148 52
Chris@148 53 void
Chris@148 54 CodedAudioFileReader::initialiseDecodeCache()
Chris@148 55 {
Chris@263 56 QMutexLocker locker(&m_cacheMutex);
Chris@263 57
Chris@148 58 if (m_cacheMode == CacheInTemporaryFile) {
Chris@148 59
Chris@148 60 m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
Chris@148 61 m_cacheWriteBufferIndex = 0;
Chris@148 62
Chris@148 63 try {
Chris@148 64 QDir dir(TempDirectory::getInstance()->getPath());
Chris@148 65 m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
Chris@148 66 .arg((intptr_t)this));
Chris@148 67
Chris@148 68 SF_INFO fileInfo;
Chris@148 69 fileInfo.samplerate = m_sampleRate;
Chris@148 70 fileInfo.channels = m_channelCount;
Chris@148 71 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
Chris@148 72
Chris@148 73 m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
Chris@148 74 SFM_WRITE, &fileInfo);
Chris@148 75
Chris@265 76 if (m_cacheFileWritePtr) {
Chris@265 77
Chris@265 78 //!!! really want to do this now only if we're in a
Chris@265 79 //threaded mode -- creating the reader later if we're
Chris@265 80 //not threaded -- but we don't have access to that
Chris@265 81 //information here
Chris@265 82
Chris@265 83 m_cacheFileReader = new WavFileReader(m_cacheFileName);
Chris@265 84
Chris@265 85 if (!m_cacheFileReader->isOK()) {
Chris@265 86 std::cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl;
Chris@265 87 delete m_cacheFileReader;
Chris@265 88 m_cacheFileReader = 0;
Chris@265 89 m_cacheMode = CacheInMemory;
Chris@265 90 sf_close(m_cacheFileWritePtr);
Chris@265 91 }
Chris@265 92 } else {
Chris@148 93 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 94 m_cacheMode = CacheInMemory;
Chris@148 95 }
Chris@265 96
Chris@148 97 } catch (DirectoryCreationFailed f) {
Chris@148 98 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << std::endl;
Chris@148 99 m_cacheMode = CacheInMemory;
Chris@148 100 }
Chris@148 101 }
Chris@148 102
Chris@148 103 if (m_cacheMode == CacheInMemory) {
Chris@148 104 m_data.clear();
Chris@148 105 }
Chris@148 106
Chris@148 107 m_initialised = true;
Chris@148 108 }
Chris@148 109
Chris@148 110 void
Chris@148 111 CodedAudioFileReader::addSampleToDecodeCache(float sample)
Chris@148 112 {
Chris@263 113 QMutexLocker locker(&m_cacheMutex);
Chris@263 114
Chris@148 115 if (!m_initialised) return;
Chris@148 116
Chris@148 117 switch (m_cacheMode) {
Chris@148 118
Chris@148 119 case CacheInTemporaryFile:
Chris@148 120
Chris@148 121 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
Chris@148 122
Chris@148 123 if (m_cacheWriteBufferIndex ==
Chris@148 124 m_cacheWriteBufferSize * m_channelCount) {
Chris@148 125
Chris@148 126 //!!! check for return value! out of disk space, etc!
Chris@148 127 sf_writef_float(m_cacheFileWritePtr,
Chris@148 128 m_cacheWriteBuffer,
Chris@148 129 m_cacheWriteBufferSize);
Chris@148 130
Chris@148 131 m_cacheWriteBufferIndex = 0;
Chris@266 132 }
Chris@265 133
Chris@266 134 if (m_cacheWriteBufferIndex % 10240 == 0 &&
Chris@266 135 m_cacheFileReader) {
Chris@266 136 m_cacheFileReader->updateFrameCount();
Chris@148 137 }
Chris@148 138 break;
Chris@148 139
Chris@148 140 case CacheInMemory:
Chris@148 141 m_data.push_back(sample);
Chris@148 142 break;
Chris@148 143 }
Chris@148 144 }
Chris@148 145
Chris@148 146 void
Chris@148 147 CodedAudioFileReader::finishDecodeCache()
Chris@148 148 {
Chris@263 149 QMutexLocker locker(&m_cacheMutex);
Chris@263 150
Chris@192 151 Profiler profiler("CodedAudioFileReader::finishDecodeCache", true);
Chris@192 152
Chris@148 153 if (!m_initialised) {
Chris@148 154 std::cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << std::endl;
Chris@148 155 return;
Chris@148 156 }
Chris@148 157
Chris@148 158 switch (m_cacheMode) {
Chris@148 159
Chris@148 160 case CacheInTemporaryFile:
Chris@148 161
Chris@148 162 if (m_cacheWriteBufferIndex > 0) {
Chris@148 163 //!!! check for return value! out of disk space, etc!
Chris@148 164 sf_writef_float(m_cacheFileWritePtr,
Chris@148 165 m_cacheWriteBuffer,
Chris@148 166 m_cacheWriteBufferIndex / m_channelCount);
Chris@148 167 }
Chris@148 168
Chris@148 169 if (m_cacheWriteBuffer) {
Chris@148 170 delete[] m_cacheWriteBuffer;
Chris@148 171 m_cacheWriteBuffer = 0;
Chris@148 172 }
Chris@148 173
Chris@148 174 m_cacheWriteBufferIndex = 0;
Chris@148 175
Chris@148 176 sf_close(m_cacheFileWritePtr);
Chris@148 177 m_cacheFileWritePtr = 0;
Chris@148 178
Chris@265 179 m_cacheFileReader->updateFrameCount();
Chris@265 180 /*
Chris@148 181 m_cacheFileReader = new WavFileReader(m_cacheFileName);
Chris@148 182
Chris@148 183 if (!m_cacheFileReader->isOK()) {
Chris@148 184 std::cerr << "ERROR: CodedAudioFileReader::finishDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl;
Chris@148 185 delete m_cacheFileReader;
Chris@148 186 m_cacheFileReader = 0;
Chris@265 187 }*/
Chris@265 188
Chris@148 189 break;
Chris@148 190
Chris@148 191 case CacheInMemory:
Chris@148 192 // nothing to do
Chris@148 193 break;
Chris@148 194 }
Chris@148 195 }
Chris@148 196
Chris@148 197 void
Chris@148 198 CodedAudioFileReader::getInterleavedFrames(size_t start, size_t count,
Chris@148 199 SampleBlock &frames) const
Chris@148 200 {
Chris@263 201 //!!! we want to ensure this doesn't require a lock -- at the
Chris@263 202 // moment it does need one, but it doesn't have one...
Chris@263 203
Chris@265 204 if (!m_initialised) {
Chris@265 205 std::cerr << "CodedAudioFileReader::getInterleavedFrames: not initialised" << std::endl;
Chris@265 206 return;
Chris@265 207 }
Chris@148 208
Chris@148 209 switch (m_cacheMode) {
Chris@148 210
Chris@148 211 case CacheInTemporaryFile:
Chris@148 212 if (m_cacheFileReader) {
Chris@148 213 m_cacheFileReader->getInterleavedFrames(start, count, frames);
Chris@148 214 }
Chris@148 215 break;
Chris@148 216
Chris@148 217 case CacheInMemory:
Chris@148 218 {
Chris@148 219 frames.clear();
Chris@148 220 if (!isOK()) return;
Chris@148 221 if (count == 0) return;
Chris@148 222
Chris@148 223 // slownessabounds
Chris@148 224
Chris@148 225 for (size_t i = start; i < start + count; ++i) {
Chris@148 226 for (size_t ch = 0; ch < m_channelCount; ++ch) {
Chris@148 227 size_t index = i * m_channelCount + ch;
Chris@148 228 if (index >= m_data.size()) return;
Chris@148 229 frames.push_back(m_data[index]);
Chris@148 230 }
Chris@148 231 }
Chris@148 232 }
Chris@148 233 }
Chris@148 234 }
Chris@148 235