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 <QString>
Chris@386: #include <QFileInfo>
Chris@386: #include <iostream>
Chris@386: 
Chris@1583: using namespace std;
Chris@1583: 
Chris@386: QString
Chris@386: AudioFileReaderFactory::getKnownExtensions()
Chris@386: {
Chris@1583:     set<QString> 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<QString>::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: