annotate data/fileio/WavFileReader.cpp @ 823:f0558e69a074

Rename Resampling- to DecodingWavFileReader, and use it whenever we have an audio file that is not quickly seekable using libsndfile. Avoids very slow performance when analysing ogg files.
author Chris Cannam
date Wed, 17 Jul 2013 15:40:01 +0100
parents 459235dccff6
children e802e550a1f2
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@202 7 This file copyright 2006 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 "WavFileReader.h"
Chris@148 17
Chris@148 18 #include <iostream>
Chris@148 19
Chris@175 20 #include <QMutexLocker>
Chris@316 21 #include <QFileInfo>
Chris@175 22
Chris@317 23 WavFileReader::WavFileReader(FileSource source, bool fileUpdating) :
Chris@148 24 m_file(0),
Chris@316 25 m_source(source),
Chris@316 26 m_path(source.getLocalFilename()),
Chris@823 27 m_seekable(false),
Chris@148 28 m_buffer(0),
Chris@148 29 m_bufsiz(0),
Chris@148 30 m_lastStart(0),
Chris@176 31 m_lastCount(0),
Chris@176 32 m_updating(fileUpdating)
Chris@148 33 {
Chris@148 34 m_frameCount = 0;
Chris@148 35 m_channelCount = 0;
Chris@148 36 m_sampleRate = 0;
Chris@148 37
Chris@148 38 m_fileInfo.format = 0;
Chris@148 39 m_fileInfo.frames = 0;
Chris@290 40 m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo);
Chris@148 41
Chris@187 42 if (!m_file || (!fileUpdating && m_fileInfo.channels <= 0)) {
Chris@682 43 std::cerr << "WavFileReader::initialize: Failed to open file at \""
Chris@686 44 << m_path << "\" ("
Chris@148 45 << sf_strerror(m_file) << ")" << std::endl;
Chris@148 46
Chris@148 47 if (m_file) {
Chris@290 48 m_error = QString("Couldn't load audio file '%1':\n%2")
Chris@290 49 .arg(m_path).arg(sf_strerror(m_file));
Chris@148 50 } else {
Chris@290 51 m_error = QString("Failed to open audio file '%1'")
Chris@290 52 .arg(m_path);
Chris@148 53 }
Chris@148 54 return;
Chris@148 55 }
Chris@148 56
Chris@187 57 if (m_fileInfo.channels > 0) {
Chris@823 58
Chris@187 59 m_frameCount = m_fileInfo.frames;
Chris@187 60 m_channelCount = m_fileInfo.channels;
Chris@187 61 m_sampleRate = m_fileInfo.samplerate;
Chris@823 62
Chris@823 63 m_seekable = (m_fileInfo.seekable != 0);
Chris@823 64
Chris@823 65 // Our m_seekable reports whether a file is rapidly seekable,
Chris@823 66 // so things like Ogg don't qualify. We cautiously report
Chris@823 67 // every file type of "at least" the historical period of Ogg
Chris@823 68 // or FLAC as non-seekable.
Chris@823 69 int type = m_fileInfo.format & SF_FORMAT_TYPEMASK;
Chris@823 70 // std::cerr << "WavFileReader: format type is " << type << " (flac, ogg are " << SF_FORMAT_FLAC << ", " << SF_FORMAT_OGG << ")" << std::endl;
Chris@823 71 if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) {
Chris@823 72 // std::cerr << "WavFileReader: Recording as non-seekable" << std::endl;
Chris@823 73 m_seekable = false;
Chris@823 74 }
Chris@187 75 }
Chris@175 76
Chris@823 77 // std::cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", seekable " << m_seekable << std::endl;
Chris@175 78
Chris@148 79 }
Chris@148 80
Chris@148 81 WavFileReader::~WavFileReader()
Chris@148 82 {
Chris@148 83 if (m_file) sf_close(m_file);
Chris@564 84 delete[] m_buffer;
Chris@148 85 }
Chris@148 86
Chris@148 87 void
Chris@175 88 WavFileReader::updateFrameCount()
Chris@175 89 {
Chris@175 90 QMutexLocker locker(&m_mutex);
Chris@175 91
Chris@175 92 size_t prevCount = m_fileInfo.frames;
Chris@175 93
Chris@175 94 if (m_file) {
Chris@175 95 sf_close(m_file);
Chris@290 96 m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo);
Chris@175 97 if (!m_file || m_fileInfo.channels <= 0) {
Chris@686 98 std::cerr << "WavFileReader::updateFrameCount: Failed to open file at \"" << m_path << "\" ("
Chris@175 99 << sf_strerror(m_file) << ")" << std::endl;
Chris@175 100 }
Chris@175 101 }
Chris@175 102
Chris@690 103 // SVDEBUG << "WavFileReader::updateFrameCount: now " << m_fileInfo.frames << endl;
Chris@175 104
Chris@176 105 m_frameCount = m_fileInfo.frames;
Chris@176 106
Chris@187 107 if (m_channelCount == 0) {
Chris@187 108 m_channelCount = m_fileInfo.channels;
Chris@187 109 m_sampleRate = m_fileInfo.samplerate;
Chris@187 110 }
Chris@187 111
Chris@258 112 if (m_frameCount != prevCount) {
Chris@258 113 // std::cerr << "frameCountChanged" << std::endl;
Chris@258 114 emit frameCountChanged();
Chris@258 115 }
Chris@176 116 }
Chris@176 117
Chris@176 118 void
Chris@176 119 WavFileReader::updateDone()
Chris@176 120 {
Chris@176 121 updateFrameCount();
Chris@176 122 m_updating = false;
Chris@175 123 }
Chris@175 124
Chris@175 125 void
Chris@148 126 WavFileReader::getInterleavedFrames(size_t start, size_t count,
Chris@148 127 SampleBlock &results) const
Chris@148 128 {
Chris@175 129 if (count == 0) return;
Chris@148 130 results.clear();
Chris@377 131 results.reserve(count * m_fileInfo.channels);
Chris@175 132
Chris@175 133 QMutexLocker locker(&m_mutex);
Chris@175 134
Chris@175 135 if (!m_file || !m_channelCount) {
Chris@175 136 return;
Chris@175 137 }
Chris@148 138
Chris@148 139 if ((long)start >= m_fileInfo.frames) {
Chris@690 140 // SVDEBUG << "WavFileReader::getInterleavedFrames: " << start
Chris@687 141 // << " > " << m_fileInfo.frames << endl;
Chris@148 142 return;
Chris@148 143 }
Chris@148 144
Chris@148 145 if (long(start + count) > m_fileInfo.frames) {
Chris@148 146 count = m_fileInfo.frames - start;
Chris@148 147 }
Chris@148 148
Chris@148 149 sf_count_t readCount = 0;
Chris@148 150
Chris@148 151 if (start != m_lastStart || count != m_lastCount) {
Chris@148 152
Chris@148 153 if (sf_seek(m_file, start, SEEK_SET) < 0) {
Chris@175 154 // std::cerr << "sf_seek failed" << std::endl;
Chris@148 155 return;
Chris@148 156 }
Chris@148 157
Chris@148 158 if (count * m_fileInfo.channels > m_bufsiz) {
Chris@148 159 // std::cerr << "WavFileReader: Reallocating buffer for " << count
Chris@148 160 // << " frames, " << m_fileInfo.channels << " channels: "
Chris@148 161 // << m_bufsiz << " floats" << std::endl;
Chris@148 162 m_bufsiz = count * m_fileInfo.channels;
Chris@148 163 delete[] m_buffer;
Chris@148 164 m_buffer = new float[m_bufsiz];
Chris@148 165 }
Chris@148 166
Chris@148 167 if ((readCount = sf_readf_float(m_file, m_buffer, count)) < 0) {
Chris@175 168 // std::cerr << "sf_readf_float failed" << std::endl;
Chris@148 169 return;
Chris@148 170 }
Chris@148 171
Chris@148 172 m_lastStart = start;
Chris@148 173 m_lastCount = readCount;
Chris@148 174 }
Chris@148 175
Chris@148 176 for (size_t i = 0; i < count * m_fileInfo.channels; ++i) {
Chris@232 177 if (i >= m_bufsiz) {
Chris@232 178 std::cerr << "INTERNAL ERROR: WavFileReader::getInterleavedFrames: " << i << " >= " << m_bufsiz << std::endl;
Chris@232 179 }
Chris@148 180 results.push_back(m_buffer[i]);
Chris@148 181 }
Chris@148 182
Chris@148 183 return;
Chris@148 184 }
Chris@148 185
Chris@157 186 void
Chris@290 187 WavFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 188 {
Chris@157 189 int count;
Chris@157 190
Chris@157 191 if (sf_command(0, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(count))) {
Chris@157 192 extensions.insert("wav");
Chris@157 193 extensions.insert("aiff");
Chris@316 194 extensions.insert("aifc");
Chris@157 195 extensions.insert("aif");
Chris@157 196 return;
Chris@157 197 }
Chris@157 198
Chris@157 199 SF_FORMAT_INFO info;
Chris@157 200 for (int i = 0; i < count; ++i) {
Chris@157 201 info.format = i;
Chris@157 202 if (!sf_command(0, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) {
Chris@783 203 QString ext = QString(info.extension).toLower();
Chris@783 204 extensions.insert(ext);
Chris@783 205 if (ext == "oga") {
Chris@783 206 // libsndfile is awfully proper, it says it only
Chris@783 207 // supports .oga but lots of Ogg audio files in the
Chris@783 208 // wild are .ogg and it will accept that
Chris@783 209 extensions.insert("ogg");
Chris@783 210 }
Chris@157 211 }
Chris@157 212 }
Chris@157 213 }
Chris@316 214
Chris@316 215 bool
Chris@316 216 WavFileReader::supportsExtension(QString extension)
Chris@316 217 {
Chris@316 218 std::set<QString> extensions;
Chris@316 219 getSupportedExtensions(extensions);
Chris@316 220 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 221 }
Chris@316 222
Chris@316 223 bool
Chris@316 224 WavFileReader::supportsContentType(QString type)
Chris@316 225 {
Chris@316 226 return (type == "audio/x-wav" ||
Chris@316 227 type == "audio/x-aiff" ||
Chris@316 228 type == "audio/basic");
Chris@316 229 }
Chris@316 230
Chris@316 231 bool
Chris@317 232 WavFileReader::supports(FileSource &source)
Chris@316 233 {
Chris@316 234 return (supportsExtension(source.getExtension()) ||
Chris@316 235 supportsContentType(source.getContentType()));
Chris@316 236 }
Chris@316 237
Chris@316 238