annotate data/fileio/WavFileReader.cpp @ 1248:58dd6a6fe414 piper

Update to use listargs variant of Piper stuff (so that the plugin winnowing feature from the penultimate commit actually works)
author Chris Cannam
date Thu, 03 Nov 2016 15:38:17 +0000
parents 6877f4200912
children d8d6d01505ed
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@1096 23 using namespace std;
Chris@1096 24
Chris@317 25 WavFileReader::WavFileReader(FileSource source, bool fileUpdating) :
Chris@148 26 m_file(0),
Chris@316 27 m_source(source),
Chris@316 28 m_path(source.getLocalFilename()),
Chris@823 29 m_seekable(false),
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@843 43 cerr << "WavFileReader::initialize: Failed to open file at \""
Chris@686 44 << m_path << "\" ("
Chris@843 45 << sf_strerror(m_file) << ")" << 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 int type = m_fileInfo.format & SF_FORMAT_TYPEMASK;
Chris@1162 66 int subtype = m_fileInfo.format & SF_FORMAT_SUBMASK;
Chris@1162 67
Chris@823 68 if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) {
Chris@1162 69 // Our m_seekable reports whether a file is rapidly
Chris@1162 70 // seekable, so things like Ogg don't qualify. We
Chris@1162 71 // cautiously report every file type of "at least" the
Chris@1162 72 // historical period of Ogg or FLAC as non-seekable.
Chris@823 73 m_seekable = false;
Chris@1162 74 } else if (type == SF_FORMAT_WAV && subtype <= SF_FORMAT_DOUBLE) {
Chris@1162 75 // libsndfile 1.0.26 has a bug (subsequently fixed in the
Chris@1162 76 // repo) that causes all files to be reported as
Chris@1162 77 // non-seekable. We know that certain common file types
Chris@1162 78 // are definitely seekable so, again cautiously, identify
Chris@1162 79 // and mark those (basically only non-adaptive WAVs).
Chris@1162 80 m_seekable = true;
Chris@823 81 }
Chris@187 82 }
Chris@175 83
Chris@1162 84 // cerr << "WavFileReader: Filename " << m_path << ", frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", format " << m_fileInfo.format << ", seekable " << m_fileInfo.seekable << " adjusted to " << m_seekable << endl;
Chris@148 85 }
Chris@148 86
Chris@148 87 WavFileReader::~WavFileReader()
Chris@148 88 {
Chris@148 89 if (m_file) sf_close(m_file);
Chris@148 90 }
Chris@148 91
Chris@148 92 void
Chris@175 93 WavFileReader::updateFrameCount()
Chris@175 94 {
Chris@175 95 QMutexLocker locker(&m_mutex);
Chris@175 96
Chris@1038 97 sv_frame_t prevCount = m_fileInfo.frames;
Chris@175 98
Chris@175 99 if (m_file) {
Chris@175 100 sf_close(m_file);
Chris@290 101 m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo);
Chris@175 102 if (!m_file || m_fileInfo.channels <= 0) {
Chris@843 103 cerr << "WavFileReader::updateFrameCount: Failed to open file at \"" << m_path << "\" ("
Chris@843 104 << sf_strerror(m_file) << ")" << endl;
Chris@175 105 }
Chris@175 106 }
Chris@175 107
Chris@690 108 // SVDEBUG << "WavFileReader::updateFrameCount: now " << m_fileInfo.frames << endl;
Chris@175 109
Chris@176 110 m_frameCount = m_fileInfo.frames;
Chris@176 111
Chris@187 112 if (m_channelCount == 0) {
Chris@187 113 m_channelCount = m_fileInfo.channels;
Chris@187 114 m_sampleRate = m_fileInfo.samplerate;
Chris@187 115 }
Chris@187 116
Chris@258 117 if (m_frameCount != prevCount) {
Chris@843 118 // cerr << "frameCountChanged" << endl;
Chris@258 119 emit frameCountChanged();
Chris@258 120 }
Chris@176 121 }
Chris@176 122
Chris@176 123 void
Chris@176 124 WavFileReader::updateDone()
Chris@176 125 {
Chris@176 126 updateFrameCount();
Chris@176 127 m_updating = false;
Chris@175 128 }
Chris@175 129
Chris@1096 130 vector<float>
Chris@1041 131 WavFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const
Chris@148 132 {
Chris@1096 133 if (count == 0) return {};
Chris@175 134
Chris@175 135 QMutexLocker locker(&m_mutex);
Chris@175 136
Chris@175 137 if (!m_file || !m_channelCount) {
Chris@1096 138 return {};
Chris@175 139 }
Chris@148 140
Chris@1040 141 if (start >= m_fileInfo.frames) {
Chris@690 142 // SVDEBUG << "WavFileReader::getInterleavedFrames: " << start
Chris@687 143 // << " > " << m_fileInfo.frames << endl;
Chris@1096 144 return {};
Chris@148 145 }
Chris@148 146
Chris@1040 147 if (start + count > m_fileInfo.frames) {
Chris@148 148 count = m_fileInfo.frames - start;
Chris@148 149 }
Chris@148 150
Chris@1096 151 // Because WaveFileModel::getSummaries() is called separately for
Chris@1096 152 // individual channels, it's quite common for us to be called
Chris@1096 153 // repeatedly for the same data. So this is worth cacheing.
Chris@1096 154 if (start == m_lastStart && count == m_lastCount) {
Chris@1096 155 return m_buffer;
Chris@1096 156 }
Chris@1096 157
Chris@1096 158 if (sf_seek(m_file, start, SEEK_SET) < 0) {
Chris@1096 159 return {};
Chris@148 160 }
Chris@148 161
Chris@1096 162 vector<float> data;
Chris@1096 163 sv_frame_t n = count * m_fileInfo.channels;
Chris@1096 164 data.resize(n);
Chris@1096 165
Chris@1096 166 m_lastStart = start;
Chris@1096 167 m_lastCount = count;
Chris@1096 168
Chris@1096 169 sf_count_t readCount = 0;
Chris@1096 170 if ((readCount = sf_readf_float(m_file, data.data(), count)) < 0) {
Chris@1096 171 return {};
Chris@1096 172 }
Chris@1096 173
Chris@1103 174 m_buffer = data;
Chris@1096 175 return data;
Chris@148 176 }
Chris@148 177
Chris@157 178 void
Chris@1096 179 WavFileReader::getSupportedExtensions(set<QString> &extensions)
Chris@157 180 {
Chris@157 181 int count;
Chris@157 182
Chris@157 183 if (sf_command(0, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof(count))) {
Chris@157 184 extensions.insert("wav");
Chris@157 185 extensions.insert("aiff");
Chris@316 186 extensions.insert("aifc");
Chris@157 187 extensions.insert("aif");
Chris@157 188 return;
Chris@157 189 }
Chris@157 190
Chris@157 191 SF_FORMAT_INFO info;
Chris@157 192 for (int i = 0; i < count; ++i) {
Chris@157 193 info.format = i;
Chris@157 194 if (!sf_command(0, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) {
Chris@783 195 QString ext = QString(info.extension).toLower();
Chris@783 196 extensions.insert(ext);
Chris@783 197 if (ext == "oga") {
Chris@783 198 // libsndfile is awfully proper, it says it only
Chris@783 199 // supports .oga but lots of Ogg audio files in the
Chris@783 200 // wild are .ogg and it will accept that
Chris@783 201 extensions.insert("ogg");
Chris@783 202 }
Chris@157 203 }
Chris@157 204 }
Chris@157 205 }
Chris@316 206
Chris@316 207 bool
Chris@316 208 WavFileReader::supportsExtension(QString extension)
Chris@316 209 {
Chris@1096 210 set<QString> extensions;
Chris@316 211 getSupportedExtensions(extensions);
Chris@316 212 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 213 }
Chris@316 214
Chris@316 215 bool
Chris@316 216 WavFileReader::supportsContentType(QString type)
Chris@316 217 {
Chris@316 218 return (type == "audio/x-wav" ||
Chris@316 219 type == "audio/x-aiff" ||
Chris@316 220 type == "audio/basic");
Chris@316 221 }
Chris@316 222
Chris@316 223 bool
Chris@317 224 WavFileReader::supports(FileSource &source)
Chris@316 225 {
Chris@316 226 return (supportsExtension(source.getExtension()) ||
Chris@316 227 supportsContentType(source.getContentType()));
Chris@316 228 }
Chris@316 229
Chris@316 230