annotate data/fileio/AudioFileReaderFactory.cpp @ 1700:c1208b211d8c single-point

Ensure test fails rather than crashing if this reader doesn't get created
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 03 May 2019 15:02:09 +0100
parents dbd13eb7dad1
children
rev   line source
Chris@386 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@386 2
Chris@386 3 /*
Chris@386 4 Sonic Visualiser
Chris@386 5 An audio file viewer and annotation editor.
Chris@386 6 Centre for Digital Music, Queen Mary, University of London.
Chris@386 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@386 8
Chris@386 9 This program is free software; you can redistribute it and/or
Chris@386 10 modify it under the terms of the GNU General Public License as
Chris@386 11 published by the Free Software Foundation; either version 2 of the
Chris@386 12 License, or (at your option) any later version. See the file
Chris@386 13 COPYING included with this distribution for more information.
Chris@386 14 */
Chris@386 15
Chris@386 16 #include "AudioFileReaderFactory.h"
Chris@386 17
Chris@386 18 #include "WavFileReader.h"
Chris@823 19 #include "DecodingWavFileReader.h"
Chris@386 20 #include "MP3FileReader.h"
Chris@1583 21 #include "BQAFileReader.h"
Chris@1098 22 #include "AudioFileSizeEstimator.h"
Chris@1098 23
Chris@1098 24 #include "base/StorageAdviser.h"
Chris@386 25
Chris@386 26 #include <QString>
Chris@386 27 #include <QFileInfo>
Chris@386 28 #include <iostream>
Chris@386 29
Chris@1583 30 using namespace std;
Chris@1583 31
Chris@386 32 QString
Chris@386 33 AudioFileReaderFactory::getKnownExtensions()
Chris@386 34 {
Chris@1583 35 set<QString> extensions;
Chris@386 36
Chris@386 37 WavFileReader::getSupportedExtensions(extensions);
Chris@386 38 #ifdef HAVE_MAD
Chris@386 39 MP3FileReader::getSupportedExtensions(extensions);
Chris@386 40 #endif
Chris@1583 41 BQAFileReader::getSupportedExtensions(extensions);
Chris@386 42
Chris@386 43 QString rv;
Chris@1583 44 for (set<QString>::const_iterator i = extensions.begin();
Chris@386 45 i != extensions.end(); ++i) {
Chris@386 46 if (i != extensions.begin()) rv += " ";
Chris@386 47 rv += "*." + *i;
Chris@386 48 }
Chris@386 49
Chris@386 50 return rv;
Chris@386 51 }
Chris@386 52
Chris@1592 53 bool
Chris@1592 54 AudioFileReaderFactory::isSupported(FileSource source)
Chris@1592 55 {
Chris@1592 56 #ifdef HAVE_MAD
Chris@1592 57 if (MP3FileReader::supports(source)) {
Chris@1592 58 return true;
Chris@1592 59 }
Chris@1592 60 #endif
Chris@1592 61 if (WavFileReader::supports(source)) {
Chris@1592 62 return true;
Chris@1592 63 }
Chris@1592 64 if (BQAFileReader::supports(source)) {
Chris@1592 65 return true;
Chris@1592 66 }
Chris@1592 67 return false;
Chris@1592 68 }
Chris@1592 69
Chris@386 70 AudioFileReader *
Chris@1313 71 AudioFileReaderFactory::createReader(FileSource source,
Chris@1313 72 Parameters params,
Chris@392 73 ProgressReporter *reporter)
Chris@386 74 {
Chris@386 75 QString err;
Chris@386 76
Chris@1342 77 SVDEBUG << "AudioFileReaderFactory: url \"" << source.getLocation() << "\": requested rate: " << params.targetRate << (params.targetRate == 0 ? " (use source rate)" : "") << endl;
Chris@1342 78 SVDEBUG << "AudioFileReaderFactory: local filename \"" << source.getLocalFilename() << "\", content type \"" << source.getContentType() << "\"" << endl;
Chris@386 79
Chris@667 80 if (!source.isOK()) {
Chris@1698 81 SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl;
Chris@1582 82 return nullptr;
Chris@667 83 }
Chris@667 84
Chris@667 85 if (!source.isAvailable()) {
Chris@1698 86 SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl;
Chris@1582 87 return nullptr;
Chris@386 88 }
Chris@386 89
Chris@1582 90 AudioFileReader *reader = nullptr;
Chris@386 91
Chris@1313 92 sv_samplerate_t targetRate = params.targetRate;
Chris@1313 93 bool normalised = (params.normalisation == Normalisation::Peak);
Chris@1313 94
Chris@1098 95 sv_frame_t estimatedSamples =
Chris@1098 96 AudioFileSizeEstimator::estimate(source, targetRate);
Chris@1098 97
Chris@1097 98 CodedAudioFileReader::CacheMode cacheMode =
Chris@1097 99 CodedAudioFileReader::CacheInTemporaryFile;
Chris@1097 100
Chris@1098 101 if (estimatedSamples > 0) {
Chris@1098 102 size_t kb = (estimatedSamples * sizeof(float)) / 1024;
Chris@1342 103 SVDEBUG << "AudioFileReaderFactory: checking where to potentially cache "
Chris@1342 104 << kb << "K of sample data" << endl;
Chris@1098 105 StorageAdviser::Recommendation rec =
Chris@1098 106 StorageAdviser::recommend(StorageAdviser::SpeedCritical, kb, kb);
Chris@1277 107 if ((rec & StorageAdviser::UseMemory) ||
Chris@1277 108 (rec & StorageAdviser::PreferMemory)) {
Chris@1342 109 SVDEBUG << "AudioFileReaderFactory: cacheing (if at all) in memory" << endl;
Chris@1098 110 cacheMode = CodedAudioFileReader::CacheInMemory;
Chris@1342 111 } else {
Chris@1342 112 SVDEBUG << "AudioFileReaderFactory: cacheing (if at all) on disc" << endl;
Chris@1098 113 }
Chris@1098 114 }
Chris@1098 115
Chris@1097 116 CodedAudioFileReader::DecodeMode decodeMode =
Chris@1313 117 (params.threadingMode == ThreadingMode::Threaded ?
Chris@1097 118 CodedAudioFileReader::DecodeThreaded :
Chris@1097 119 CodedAudioFileReader::DecodeAtOnce);
Chris@386 120
Chris@1313 121 // We go through the set of supported readers at most twice: once
Chris@1313 122 // picking out only the readers that claim to support the given
Chris@1313 123 // file's extension or MIME type, and (if that fails) again
Chris@1313 124 // providing the file to every reader in turn regardless of
Chris@1313 125 // extension or type. (If none of the readers claim to support a
Chris@1313 126 // file, that may just mean its extension is missing or
Chris@1313 127 // misleading. We have to be confident that the reader won't open
Chris@1313 128 // just any old text file or whatever and pretend it's succeeded.)
Chris@1097 129
Chris@1313 130 for (int any = 0; any <= 1; ++any) {
Chris@386 131
Chris@1313 132 bool anyReader = (any > 0);
Chris@386 133
Chris@1342 134 if (!anyReader) {
Chris@1342 135 SVDEBUG << "AudioFileReaderFactory: Checking whether any reader officially handles this source" << endl;
Chris@1342 136 } else {
Chris@1342 137 SVDEBUG << "AudioFileReaderFactory: Source not officially handled by any reader, trying again with each reader in turn"
Chris@1342 138 << endl;
Chris@1342 139 }
Chris@1359 140
Chris@1583 141 #ifdef HAVE_MAD
Chris@1592 142 // Having said we'll try any reader on the second pass, we
Chris@1592 143 // actually don't want to try the mp3 reader for anything not
Chris@1592 144 // identified as an mp3 - it can't identify files by header,
Chris@1592 145 // it'll try to read any data and then fail with
Chris@1592 146 // synchronisation errors - causing misleading and potentially
Chris@1592 147 // alarming warning messages at the least
Chris@1592 148 if (!anyReader) {
Chris@1592 149 if (MP3FileReader::supports(source)) {
Chris@1359 150
Chris@1592 151 MP3FileReader::GaplessMode gapless =
Chris@1592 152 params.gaplessMode == GaplessMode::Gapless ?
Chris@1592 153 MP3FileReader::GaplessMode::Gapless :
Chris@1592 154 MP3FileReader::GaplessMode::Gappy;
Chris@1583 155
Chris@1592 156 reader = new MP3FileReader
Chris@1592 157 (source, decodeMode, cacheMode, gapless,
Chris@1592 158 targetRate, normalised, reporter);
Chris@1359 159
Chris@1592 160 if (reader->isOK()) {
Chris@1592 161 SVDEBUG << "AudioFileReaderFactory: MP3 file reader is OK, returning it" << endl;
Chris@1592 162 return reader;
Chris@1592 163 } else {
Chris@1592 164 delete reader;
Chris@1592 165 }
Chris@1359 166 }
Chris@1359 167 }
Chris@1359 168 #endif
Chris@1342 169
Chris@1313 170 if (anyReader || WavFileReader::supports(source)) {
Chris@386 171
Chris@1313 172 reader = new WavFileReader(source);
Chris@823 173
Chris@1313 174 sv_samplerate_t fileRate = reader->getSampleRate();
Chris@1313 175
Chris@1313 176 if (reader->isOK() &&
Chris@1313 177 (!reader->isQuicklySeekable() ||
Chris@1313 178 normalised ||
Chris@1313 179 (cacheMode == CodedAudioFileReader::CacheInMemory) ||
Chris@1313 180 (targetRate != 0 && fileRate != targetRate))) {
Chris@1313 181
Chris@1342 182 SVDEBUG << "AudioFileReaderFactory: WAV file reader rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl;
Chris@1161 183
Chris@1313 184 delete reader;
Chris@1313 185 reader = new DecodingWavFileReader
Chris@1313 186 (source,
Chris@1313 187 decodeMode, cacheMode,
Chris@1313 188 targetRate ? targetRate : fileRate,
Chris@1313 189 normalised,
Chris@1313 190 reporter);
Chris@1313 191 }
Chris@1313 192
Chris@1313 193 if (reader->isOK()) {
Chris@1342 194 SVDEBUG << "AudioFileReaderFactory: WAV file reader is OK, returning it" << endl;
Chris@1313 195 return reader;
Chris@1313 196 } else {
Chris@1313 197 delete reader;
Chris@1313 198 }
Chris@386 199 }
Chris@1592 200
Chris@1592 201 if (anyReader || BQAFileReader::supports(source)) {
Chris@386 202
Chris@1592 203 reader = new BQAFileReader
Chris@1592 204 (source, decodeMode, cacheMode,
Chris@1313 205 targetRate, normalised, reporter);
Chris@1313 206
Chris@1313 207 if (reader->isOK()) {
Chris@1592 208 SVDEBUG << "AudioFileReaderFactory: BQA reader is OK, returning it" << endl;
Chris@1313 209 return reader;
Chris@1313 210 } else {
Chris@1313 211 delete reader;
Chris@1313 212 }
Chris@1313 213 }
Chris@1097 214 }
Chris@1097 215
Chris@1698 216 SVDEBUG << "AudioFileReaderFactory::Failed to create a reader for "
Chris@1698 217 << "url \"" << source.getLocation()
Chris@1698 218 << "\" (local filename \"" << source.getLocalFilename()
Chris@1698 219 << "\", content type \""
Chris@1698 220 << source.getContentType() << "\")" << endl;
Chris@1313 221 return nullptr;
Chris@386 222 }
Chris@386 223