annotate data/fileio/WavFileReader.cpp @ 1078:ce82bcdc95d0

Fail upfront if the file is going to be too large. We expect the caller to split up large data sets into several MatrixFiles
author Chris Cannam
date Wed, 10 Jun 2015 13:10:26 +0100
parents 48e4ffa9fb48
children 4d9816ba0ebe 1517d4c60e88
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_lastStart(0),
Chris@176 29 m_lastCount(0),
Chris@176 30 m_updating(fileUpdating)
Chris@148 31 {
Chris@148 32 m_frameCount = 0;
Chris@148 33 m_channelCount = 0;
Chris@148 34 m_sampleRate = 0;
Chris@148 35
Chris@148 36 m_fileInfo.format = 0;
Chris@148 37 m_fileInfo.frames = 0;
Chris@290 38 m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo);
Chris@148 39
Chris@187 40 if (!m_file || (!fileUpdating && m_fileInfo.channels <= 0)) {
Chris@843 41 cerr << "WavFileReader::initialize: Failed to open file at \""
Chris@686 42 << m_path << "\" ("
Chris@843 43 << sf_strerror(m_file) << ")" << endl;
Chris@148 44
Chris@148 45 if (m_file) {
Chris@290 46 m_error = QString("Couldn't load audio file '%1':\n%2")
Chris@290 47 .arg(m_path).arg(sf_strerror(m_file));
Chris@148 48 } else {
Chris@290 49 m_error = QString("Failed to open audio file '%1'")
Chris@290 50 .arg(m_path);
Chris@148 51 }
Chris@148 52 return;
Chris@148 53 }
Chris@148 54
Chris@187 55 if (m_fileInfo.channels > 0) {
Chris@823 56
Chris@187 57 m_frameCount = m_fileInfo.frames;
Chris@187 58 m_channelCount = m_fileInfo.channels;
Chris@187 59 m_sampleRate = m_fileInfo.samplerate;
Chris@823 60
Chris@823 61 m_seekable = (m_fileInfo.seekable != 0);
Chris@823 62
Chris@823 63 // Our m_seekable reports whether a file is rapidly seekable,
Chris@823 64 // so things like Ogg don't qualify. We cautiously report
Chris@823 65 // every file type of "at least" the historical period of Ogg
Chris@823 66 // or FLAC as non-seekable.
Chris@823 67 int type = m_fileInfo.format & SF_FORMAT_TYPEMASK;
Chris@843 68 // cerr << "WavFileReader: format type is " << type << " (flac, ogg are " << SF_FORMAT_FLAC << ", " << SF_FORMAT_OGG << ")" << endl;
Chris@823 69 if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) {
Chris@843 70 // cerr << "WavFileReader: Recording as non-seekable" << endl;
Chris@823 71 m_seekable = false;
Chris@823 72 }
Chris@187 73 }
Chris@175 74
Chris@843 75 // cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", seekable " << m_seekable << endl;
Chris@175 76
Chris@148 77 }
Chris@148 78
Chris@148 79 WavFileReader::~WavFileReader()
Chris@148 80 {
Chris@148 81 if (m_file) sf_close(m_file);
Chris@148 82 }
Chris@148 83
Chris@148 84 void
Chris@175 85 WavFileReader::updateFrameCount()
Chris@175 86 {
Chris@175 87 QMutexLocker locker(&m_mutex);
Chris@175 88
Chris@1038 89 sv_frame_t prevCount = m_fileInfo.frames;
Chris@175 90
Chris@175 91 if (m_file) {
Chris@175 92 sf_close(m_file);
Chris@290 93 m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo);
Chris@175 94 if (!m_file || m_fileInfo.channels <= 0) {
Chris@843 95 cerr << "WavFileReader::updateFrameCount: Failed to open file at \"" << m_path << "\" ("
Chris@843 96 << sf_strerror(m_file) << ")" << endl;
Chris@175 97 }
Chris@175 98 }
Chris@175 99
Chris@690 100 // SVDEBUG << "WavFileReader::updateFrameCount: now " << m_fileInfo.frames << endl;
Chris@175 101
Chris@176 102 m_frameCount = m_fileInfo.frames;
Chris@176 103
Chris@187 104 if (m_channelCount == 0) {
Chris@187 105 m_channelCount = m_fileInfo.channels;
Chris@187 106 m_sampleRate = m_fileInfo.samplerate;
Chris@187 107 }
Chris@187 108
Chris@258 109 if (m_frameCount != prevCount) {
Chris@843 110 // cerr << "frameCountChanged" << endl;
Chris@258 111 emit frameCountChanged();
Chris@258 112 }
Chris@176 113 }
Chris@176 114
Chris@176 115 void
Chris@176 116 WavFileReader::updateDone()
Chris@176 117 {
Chris@176 118 updateFrameCount();
Chris@176 119 m_updating = false;
Chris@175 120 }
Chris@175 121
Chris@1041 122 SampleBlock
Chris@1041 123 WavFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const
Chris@148 124 {
Chris@1041 125 if (count == 0) return SampleBlock();
Chris@175 126
Chris@175 127 QMutexLocker locker(&m_mutex);
Chris@175 128
Chris@175 129 if (!m_file || !m_channelCount) {
Chris@1041 130 return SampleBlock();
Chris@175 131 }
Chris@148 132
Chris@1040 133 if (start >= m_fileInfo.frames) {
Chris@690 134 // SVDEBUG << "WavFileReader::getInterleavedFrames: " << start
Chris@687 135 // << " > " << m_fileInfo.frames << endl;
Chris@1041 136 return SampleBlock();
Chris@148 137 }
Chris@148 138
Chris@1040 139 if (start + count > m_fileInfo.frames) {
Chris@148 140 count = m_fileInfo.frames - start;
Chris@148 141 }
Chris@148 142
Chris@148 143 if (start != m_lastStart || count != m_lastCount) {
Chris@148 144
Chris@148 145 if (sf_seek(m_file, start, SEEK_SET) < 0) {
Chris@1041 146 return SampleBlock();
Chris@148 147 }
Chris@1041 148
Chris@1041 149 sv_frame_t n = count * m_fileInfo.channels;
Chris@1041 150 m_buffer.resize(n);
Chris@148 151
Chris@1053 152 sf_count_t readCount = 0;
Chris@1053 153
Chris@1041 154 if ((readCount = sf_readf_float(m_file, m_buffer.data(), count)) < 0) {
Chris@1041 155 return SampleBlock();
Chris@148 156 }
Chris@148 157
Chris@1053 158 m_buffer.resize(readCount * m_fileInfo.channels);
Chris@1053 159
Chris@148 160 m_lastStart = start;
Chris@148 161 m_lastCount = readCount;
Chris@148 162 }
Chris@148 163
Chris@1041 164 return m_buffer;
Chris@148 165 }
Chris@148 166
Chris@157 167 void
Chris@290 168 WavFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 169 {
Chris@157 170 int count;
Chris@157 171
Chris@157 172 if (sf_command(0, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(count))) {
Chris@157 173 extensions.insert("wav");
Chris@157 174 extensions.insert("aiff");
Chris@316 175 extensions.insert("aifc");
Chris@157 176 extensions.insert("aif");
Chris@157 177 return;
Chris@157 178 }
Chris@157 179
Chris@157 180 SF_FORMAT_INFO info;
Chris@157 181 for (int i = 0; i < count; ++i) {
Chris@157 182 info.format = i;
Chris@157 183 if (!sf_command(0, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) {
Chris@783 184 QString ext = QString(info.extension).toLower();
Chris@783 185 extensions.insert(ext);
Chris@783 186 if (ext == "oga") {
Chris@783 187 // libsndfile is awfully proper, it says it only
Chris@783 188 // supports .oga but lots of Ogg audio files in the
Chris@783 189 // wild are .ogg and it will accept that
Chris@783 190 extensions.insert("ogg");
Chris@783 191 }
Chris@157 192 }
Chris@157 193 }
Chris@157 194 }
Chris@316 195
Chris@316 196 bool
Chris@316 197 WavFileReader::supportsExtension(QString extension)
Chris@316 198 {
Chris@316 199 std::set<QString> extensions;
Chris@316 200 getSupportedExtensions(extensions);
Chris@316 201 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 202 }
Chris@316 203
Chris@316 204 bool
Chris@316 205 WavFileReader::supportsContentType(QString type)
Chris@316 206 {
Chris@316 207 return (type == "audio/x-wav" ||
Chris@316 208 type == "audio/x-aiff" ||
Chris@316 209 type == "audio/basic");
Chris@316 210 }
Chris@316 211
Chris@316 212 bool
Chris@317 213 WavFileReader::supports(FileSource &source)
Chris@316 214 {
Chris@316 215 return (supportsExtension(source.getExtension()) ||
Chris@316 216 supportsContentType(source.getContentType()));
Chris@316 217 }
Chris@316 218
Chris@316 219