Chris@386: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@386: Chris@386: /* Chris@386: Sonic Visualiser Chris@386: An audio file viewer and annotation editor. Chris@386: Centre for Digital Music, Queen Mary, University of London. Chris@386: This file copyright 2006 Chris Cannam and QMUL. Chris@386: Chris@386: This program is free software; you can redistribute it and/or Chris@386: modify it under the terms of the GNU General Public License as Chris@386: published by the Free Software Foundation; either version 2 of the Chris@386: License, or (at your option) any later version. See the file Chris@386: COPYING included with this distribution for more information. Chris@386: */ Chris@386: Chris@386: #include "AudioFileReaderFactory.h" Chris@386: Chris@386: #include "WavFileReader.h" Chris@823: #include "DecodingWavFileReader.h" Chris@386: #include "MP3FileReader.h" Chris@1583: #include "BQAFileReader.h" Chris@1098: #include "AudioFileSizeEstimator.h" Chris@1098: Chris@1098: #include "base/StorageAdviser.h" Chris@386: Chris@386: #include Chris@386: #include Chris@386: #include Chris@386: Chris@1583: using namespace std; Chris@1583: Chris@386: QString Chris@386: AudioFileReaderFactory::getKnownExtensions() Chris@386: { Chris@1583: set extensions; Chris@386: Chris@386: WavFileReader::getSupportedExtensions(extensions); Chris@386: #ifdef HAVE_MAD Chris@386: MP3FileReader::getSupportedExtensions(extensions); Chris@386: #endif Chris@1583: BQAFileReader::getSupportedExtensions(extensions); Chris@386: Chris@386: QString rv; Chris@1583: for (set::const_iterator i = extensions.begin(); Chris@386: i != extensions.end(); ++i) { Chris@386: if (i != extensions.begin()) rv += " "; Chris@386: rv += "*." + *i; Chris@386: } Chris@386: Chris@386: return rv; Chris@386: } Chris@386: Chris@1592: bool Chris@1592: AudioFileReaderFactory::isSupported(FileSource source) Chris@1592: { Chris@1592: #ifdef HAVE_MAD Chris@1592: if (MP3FileReader::supports(source)) { Chris@1592: return true; Chris@1592: } Chris@1592: #endif Chris@1592: if (WavFileReader::supports(source)) { Chris@1592: return true; Chris@1592: } Chris@1592: if (BQAFileReader::supports(source)) { Chris@1592: return true; Chris@1592: } Chris@1592: return false; Chris@1592: } Chris@1592: Chris@386: AudioFileReader * Chris@1313: AudioFileReaderFactory::createReader(FileSource source, Chris@1313: Parameters params, Chris@392: ProgressReporter *reporter) Chris@386: { Chris@386: QString err; Chris@386: Chris@1342: SVDEBUG << "AudioFileReaderFactory: url \"" << source.getLocation() << "\": requested rate: " << params.targetRate << (params.targetRate == 0 ? " (use source rate)" : "") << endl; Chris@1342: SVDEBUG << "AudioFileReaderFactory: local filename \"" << source.getLocalFilename() << "\", content type \"" << source.getContentType() << "\"" << endl; Chris@386: Chris@667: if (!source.isOK()) { Chris@1698: SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl; Chris@1582: return nullptr; Chris@667: } Chris@667: Chris@667: if (!source.isAvailable()) { Chris@1698: SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; Chris@1582: return nullptr; Chris@386: } Chris@386: Chris@1582: AudioFileReader *reader = nullptr; Chris@386: Chris@1313: sv_samplerate_t targetRate = params.targetRate; Chris@1313: bool normalised = (params.normalisation == Normalisation::Peak); Chris@1313: Chris@1098: sv_frame_t estimatedSamples = Chris@1098: AudioFileSizeEstimator::estimate(source, targetRate); Chris@1098: Chris@1097: CodedAudioFileReader::CacheMode cacheMode = Chris@1097: CodedAudioFileReader::CacheInTemporaryFile; Chris@1097: Chris@1098: if (estimatedSamples > 0) { Chris@1098: size_t kb = (estimatedSamples * sizeof(float)) / 1024; Chris@1342: SVDEBUG << "AudioFileReaderFactory: checking where to potentially cache " Chris@1342: << kb << "K of sample data" << endl; Chris@1098: StorageAdviser::Recommendation rec = Chris@1098: StorageAdviser::recommend(StorageAdviser::SpeedCritical, kb, kb); Chris@1277: if ((rec & StorageAdviser::UseMemory) || Chris@1277: (rec & StorageAdviser::PreferMemory)) { Chris@1342: SVDEBUG << "AudioFileReaderFactory: cacheing (if at all) in memory" << endl; Chris@1098: cacheMode = CodedAudioFileReader::CacheInMemory; Chris@1342: } else { Chris@1342: SVDEBUG << "AudioFileReaderFactory: cacheing (if at all) on disc" << endl; Chris@1098: } Chris@1098: } Chris@1098: Chris@1097: CodedAudioFileReader::DecodeMode decodeMode = Chris@1313: (params.threadingMode == ThreadingMode::Threaded ? Chris@1097: CodedAudioFileReader::DecodeThreaded : Chris@1097: CodedAudioFileReader::DecodeAtOnce); Chris@386: Chris@1313: // We go through the set of supported readers at most twice: once Chris@1313: // picking out only the readers that claim to support the given Chris@1313: // file's extension or MIME type, and (if that fails) again Chris@1313: // providing the file to every reader in turn regardless of Chris@1313: // extension or type. (If none of the readers claim to support a Chris@1313: // file, that may just mean its extension is missing or Chris@1313: // misleading. We have to be confident that the reader won't open Chris@1313: // just any old text file or whatever and pretend it's succeeded.) Chris@1097: Chris@1313: for (int any = 0; any <= 1; ++any) { Chris@386: Chris@1313: bool anyReader = (any > 0); Chris@386: Chris@1342: if (!anyReader) { Chris@1342: SVDEBUG << "AudioFileReaderFactory: Checking whether any reader officially handles this source" << endl; Chris@1342: } else { Chris@1342: SVDEBUG << "AudioFileReaderFactory: Source not officially handled by any reader, trying again with each reader in turn" Chris@1342: << endl; Chris@1342: } Chris@1359: Chris@1583: #ifdef HAVE_MAD Chris@1592: // Having said we'll try any reader on the second pass, we Chris@1592: // actually don't want to try the mp3 reader for anything not Chris@1592: // identified as an mp3 - it can't identify files by header, Chris@1592: // it'll try to read any data and then fail with Chris@1592: // synchronisation errors - causing misleading and potentially Chris@1592: // alarming warning messages at the least Chris@1592: if (!anyReader) { Chris@1592: if (MP3FileReader::supports(source)) { Chris@1359: Chris@1592: MP3FileReader::GaplessMode gapless = Chris@1592: params.gaplessMode == GaplessMode::Gapless ? Chris@1592: MP3FileReader::GaplessMode::Gapless : Chris@1592: MP3FileReader::GaplessMode::Gappy; Chris@1583: Chris@1592: reader = new MP3FileReader Chris@1592: (source, decodeMode, cacheMode, gapless, Chris@1592: targetRate, normalised, reporter); Chris@1359: Chris@1592: if (reader->isOK()) { Chris@1592: SVDEBUG << "AudioFileReaderFactory: MP3 file reader is OK, returning it" << endl; Chris@1592: return reader; Chris@1592: } else { Chris@1592: delete reader; Chris@1592: } Chris@1359: } Chris@1359: } Chris@1359: #endif Chris@1342: Chris@1313: if (anyReader || WavFileReader::supports(source)) { Chris@386: Chris@1313: reader = new WavFileReader(source); Chris@823: Chris@1313: sv_samplerate_t fileRate = reader->getSampleRate(); Chris@1313: Chris@1313: if (reader->isOK() && Chris@1313: (!reader->isQuicklySeekable() || Chris@1313: normalised || Chris@1313: (cacheMode == CodedAudioFileReader::CacheInMemory) || Chris@1313: (targetRate != 0 && fileRate != targetRate))) { Chris@1313: Chris@1342: SVDEBUG << "AudioFileReaderFactory: WAV file reader rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; Chris@1161: Chris@1313: delete reader; Chris@1313: reader = new DecodingWavFileReader Chris@1313: (source, Chris@1313: decodeMode, cacheMode, Chris@1313: targetRate ? targetRate : fileRate, Chris@1313: normalised, Chris@1313: reporter); Chris@1313: } Chris@1313: Chris@1313: if (reader->isOK()) { Chris@1342: SVDEBUG << "AudioFileReaderFactory: WAV file reader is OK, returning it" << endl; Chris@1313: return reader; Chris@1313: } else { Chris@1313: delete reader; Chris@1313: } Chris@386: } Chris@1592: Chris@1592: if (anyReader || BQAFileReader::supports(source)) { Chris@386: Chris@1592: reader = new BQAFileReader Chris@1592: (source, decodeMode, cacheMode, Chris@1313: targetRate, normalised, reporter); Chris@1313: Chris@1313: if (reader->isOK()) { Chris@1592: SVDEBUG << "AudioFileReaderFactory: BQA reader is OK, returning it" << endl; Chris@1313: return reader; Chris@1313: } else { Chris@1313: delete reader; Chris@1313: } Chris@1313: } Chris@1097: } Chris@1097: Chris@1698: SVDEBUG << "AudioFileReaderFactory::Failed to create a reader for " Chris@1698: << "url \"" << source.getLocation() Chris@1698: << "\" (local filename \"" << source.getLocalFilename() Chris@1698: << "\", content type \"" Chris@1698: << source.getContentType() << "\")" << endl; Chris@1313: return nullptr; Chris@386: } Chris@386: