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 "OggVorbisFileReader.h"
Chris@386: #include "MP3FileReader.h"
Chris@386: #include "QuickTimeFileReader.h"
luisf@665: #include "CoreAudioFileReader.h"
Chris@386: 
Chris@386: #include <QString>
Chris@386: #include <QFileInfo>
Chris@386: #include <iostream>
Chris@386: 
Chris@386: QString
Chris@386: AudioFileReaderFactory::getKnownExtensions()
Chris@386: {
Chris@386:     std::set<QString> extensions;
Chris@386: 
Chris@386:     WavFileReader::getSupportedExtensions(extensions);
Chris@386: #ifdef HAVE_MAD
Chris@386:     MP3FileReader::getSupportedExtensions(extensions);
Chris@386: #endif
Chris@386: #ifdef HAVE_OGGZ
Chris@386: #ifdef HAVE_FISHSOUND
Chris@386:     OggVorbisFileReader::getSupportedExtensions(extensions);
Chris@386: #endif
Chris@386: #endif
Chris@386: #ifdef HAVE_QUICKTIME
Chris@386:     QuickTimeFileReader::getSupportedExtensions(extensions);
Chris@386: #endif
luisf@665: #ifdef HAVE_COREAUDIO
luisf@665:     CoreAudioFileReader::getSupportedExtensions(extensions);
luisf@665: #endif
Chris@386: 
Chris@386:     QString rv;
Chris@386:     for (std::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@386: AudioFileReader *
Chris@392: AudioFileReaderFactory::createReader(FileSource source, size_t targetRate,
Chris@392:                                      ProgressReporter *reporter)
Chris@386: {
Chris@392:     return create(source, targetRate, false, reporter);
Chris@386: }
Chris@386: 
Chris@386: AudioFileReader *
Chris@392: AudioFileReaderFactory::createThreadingReader(FileSource source, size_t targetRate,
Chris@392:                                               ProgressReporter *reporter)
Chris@386: {
Chris@392:     return create(source, targetRate, true, reporter);
Chris@386: }
Chris@386: 
Chris@386: AudioFileReader *
Chris@392: AudioFileReaderFactory::create(FileSource source, size_t targetRate, bool threading,
Chris@392:                                ProgressReporter *reporter)
Chris@386: {
Chris@386:     QString err;
Chris@386: 
Chris@742:     SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\"): Requested rate: " << targetRate << endl;
Chris@386: 
Chris@667:     if (!source.isOK()) {
Chris@843:         cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl;
Chris@667:         return 0;
Chris@667:     }
Chris@667: 
Chris@667:     if (!source.isAvailable()) {
Chris@690:         SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl;
Chris@386:         return 0;
Chris@386:     }
Chris@386: 
Chris@386:     AudioFileReader *reader = 0;
Chris@386: 
Chris@386:     // Try to construct a preferred reader based on the extension or
Chris@386:     // MIME type.
Chris@386: 
Chris@386:     if (WavFileReader::supports(source)) {
Chris@386: 
Chris@386:         reader = new WavFileReader(source);
Chris@386: 
Chris@823:         int fileRate = reader->getSampleRate();
Chris@386: 
Chris@823:         if (reader->isOK() &&
Chris@823:             (!reader->isQuicklySeekable() ||
Chris@823:              (targetRate != 0 && fileRate != targetRate))) {
Chris@823: 
Chris@823:             SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl;
Chris@386: 
Chris@386:             delete reader;
Chris@823:             reader = new DecodingWavFileReader
Chris@386:                 (source,
Chris@386:                  threading ?
Chris@823:                  DecodingWavFileReader::ResampleThreaded :
Chris@823:                  DecodingWavFileReader::ResampleAtOnce,
Chris@823:                  DecodingWavFileReader::CacheInTemporaryFile,
Chris@823:                  targetRate ? targetRate : fileRate,
Chris@392:                  reporter);
Chris@440:             if (!reader->isOK()) {
Chris@440:                 delete reader;
Chris@440:                 reader = 0;
Chris@440:             }
Chris@386:         }
Chris@386:     }
Chris@386:     
Chris@386: #ifdef HAVE_OGGZ
Chris@386: #ifdef HAVE_FISHSOUND
Chris@386:     if (!reader) {
Chris@386:         if (OggVorbisFileReader::supports(source)) {
Chris@386:             reader = new OggVorbisFileReader
Chris@386:                 (source,
Chris@386:                  threading ?
Chris@386:                  OggVorbisFileReader::DecodeThreaded :
Chris@386:                  OggVorbisFileReader::DecodeAtOnce,
Chris@386:                  OggVorbisFileReader::CacheInTemporaryFile,
Chris@392:                  targetRate,
Chris@392:                  reporter);
Chris@440:             if (!reader->isOK()) {
Chris@440:                 delete reader;
Chris@440:                 reader = 0;
Chris@440:             }
Chris@386:         }
Chris@386:     }
Chris@386: #endif
Chris@386: #endif
Chris@386: 
Chris@386: #ifdef HAVE_MAD
Chris@386:     if (!reader) {
Chris@386:         if (MP3FileReader::supports(source)) {
Chris@386:             reader = new MP3FileReader
Chris@386:                 (source,
Chris@386:                  threading ?
Chris@386:                  MP3FileReader::DecodeThreaded :
Chris@386:                  MP3FileReader::DecodeAtOnce,
Chris@386:                  MP3FileReader::CacheInTemporaryFile,
Chris@392:                  targetRate,
Chris@392:                  reporter);
Chris@440:             if (!reader->isOK()) {
Chris@440:                 delete reader;
Chris@440:                 reader = 0;
Chris@440:             }
Chris@386:         }
Chris@386:     }
Chris@386: #endif
Chris@386: 
Chris@386: #ifdef HAVE_QUICKTIME
Chris@386:     if (!reader) {
Chris@386:         if (QuickTimeFileReader::supports(source)) {
Chris@386:             reader = new QuickTimeFileReader
Chris@386:                 (source,
Chris@386:                  threading ?
Chris@386:                  QuickTimeFileReader::DecodeThreaded : 
Chris@386:                  QuickTimeFileReader::DecodeAtOnce,
Chris@386:                  QuickTimeFileReader::CacheInTemporaryFile,
Chris@392:                  targetRate,
Chris@392:                  reporter);
Chris@440:             if (!reader->isOK()) {
Chris@440:                 delete reader;
Chris@440:                 reader = 0;
Chris@440:             }
Chris@440:         }
Chris@440:     }
Chris@440: #endif
Chris@440: 
luisf@665: #ifdef HAVE_COREAUDIO
luisf@665:     if (!reader) {
luisf@665:         if (CoreAudioFileReader::supports(source)) {
luisf@665:             reader = new CoreAudioFileReader
luisf@665:                 (source,
luisf@665:                  threading ?
luisf@665:                  CoreAudioFileReader::DecodeThreaded :
luisf@665:                  CoreAudioFileReader::DecodeAtOnce,
luisf@665:                  CoreAudioFileReader::CacheInTemporaryFile,
luisf@665:                  targetRate,
luisf@665:                  reporter);
luisf@665:             if (!reader->isOK()) {
luisf@665:                 delete reader;
luisf@665:                 reader = 0;
luisf@665:             }
luisf@665:         }
luisf@665:     }
luisf@665: #endif
luisf@665: 
luisf@665: 
Chris@440:     // If none of the readers claimed to support this file extension,
Chris@440:     // perhaps the extension is missing or misleading.  Try again,
Chris@440:     // ignoring it.  We have to be confident that the reader won't
Chris@440:     // open just any old text file or whatever and pretend it's
Chris@440:     // succeeded
Chris@440: 
Chris@440:     if (!reader) {
Chris@440: 
Chris@440:         reader = new WavFileReader(source);
Chris@440: 
Chris@823:         int fileRate = reader->getSampleRate();
Chris@440: 
Chris@823:         if (reader->isOK() &&
Chris@823:             (!reader->isQuicklySeekable() ||
Chris@823:              (targetRate != 0 && fileRate != targetRate))) {
Chris@823: 
Chris@823:             SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl;
Chris@440: 
Chris@440:             delete reader;
Chris@823:             reader = new DecodingWavFileReader
Chris@440:                 (source,
Chris@440:                  threading ?
Chris@823:                  DecodingWavFileReader::ResampleThreaded :
Chris@823:                  DecodingWavFileReader::ResampleAtOnce,
Chris@823:                  DecodingWavFileReader::CacheInTemporaryFile,
Chris@823:                  targetRate ? targetRate : fileRate,
Chris@440:                  reporter);
Chris@440:         }
Chris@440: 
Chris@440:         if (!reader->isOK()) {
Chris@440:             delete reader;
Chris@440:             reader = 0;
Chris@440:         }
Chris@440:     }
Chris@440:     
Chris@440: #ifdef HAVE_OGGZ
Chris@440: #ifdef HAVE_FISHSOUND
Chris@440:     if (!reader) {
Chris@440:         reader = new OggVorbisFileReader
Chris@440:             (source,
Chris@440:              threading ?
Chris@440:              OggVorbisFileReader::DecodeThreaded :
Chris@440:              OggVorbisFileReader::DecodeAtOnce,
Chris@440:              OggVorbisFileReader::CacheInTemporaryFile,
Chris@440:              targetRate,
Chris@440:              reporter);
Chris@440: 
Chris@440:         if (!reader->isOK()) {
Chris@440:             delete reader;
Chris@440:             reader = 0;
Chris@440:         }
Chris@440:     }
Chris@440: #endif
Chris@440: #endif
Chris@440: 
Chris@440: #ifdef HAVE_MAD
Chris@440:     if (!reader) {
Chris@440:         reader = new MP3FileReader
Chris@440:             (source,
Chris@440:              threading ?
Chris@440:              MP3FileReader::DecodeThreaded :
Chris@440:              MP3FileReader::DecodeAtOnce,
Chris@440:              MP3FileReader::CacheInTemporaryFile,
Chris@440:              targetRate,
Chris@440:              reporter);
Chris@440: 
Chris@440:         if (!reader->isOK()) {
Chris@440:             delete reader;
Chris@440:             reader = 0;
Chris@440:         }
Chris@440:     }
Chris@440: #endif
Chris@440: 
Chris@440: #ifdef HAVE_QUICKTIME
Chris@440:     if (!reader) {
Chris@440:         reader = new QuickTimeFileReader
Chris@440:             (source,
Chris@440:              threading ?
Chris@440:              QuickTimeFileReader::DecodeThreaded : 
Chris@440:              QuickTimeFileReader::DecodeAtOnce,
Chris@440:              QuickTimeFileReader::CacheInTemporaryFile,
Chris@440:              targetRate,
Chris@440:              reporter);
Chris@440: 
Chris@440:         if (!reader->isOK()) {
Chris@440:             delete reader;
Chris@440:             reader = 0;
Chris@386:         }
Chris@386:     }
Chris@386: #endif
Chris@386: 
luisf@665: #ifdef HAVE_COREAUDIO
luisf@665:     if (!reader) {
luisf@665:         reader = new CoreAudioFileReader
luisf@665:             (source,
luisf@665:              threading ?
luisf@665:              CoreAudioFileReader::DecodeThreaded :
luisf@665:              CoreAudioFileReader::DecodeAtOnce,
luisf@665:              CoreAudioFileReader::CacheInTemporaryFile,
luisf@665:              targetRate,
luisf@665:              reporter);
luisf@665: 
luisf@665:         if (!reader->isOK()) {
luisf@665:             delete reader;
luisf@665:             reader = 0;
luisf@665:         }
luisf@665:     }
luisf@665: #endif
luisf@665: 
Chris@386:     if (reader) {
Chris@386:         if (reader->isOK()) {
Chris@756:             SVDEBUG << "AudioFileReaderFactory: Reader is OK" << endl;
Chris@386:             return reader;
Chris@386:         }
Chris@843:         cerr << "AudioFileReaderFactory: Preferred reader for "
Chris@844:                   << "url \"" << source.getLocation()
Chris@386:                   << "\" (content type \""
Chris@686:                   << source.getContentType() << "\") failed";
Chris@386: 
Chris@386:         if (reader->getError() != "") {
Chris@843:             cerr << ": \"" << reader->getError() << "\"";
Chris@386:         }
Chris@843:         cerr << endl;
Chris@386:         delete reader;
Chris@386:         reader = 0;
Chris@386:     }
Chris@386: 
Chris@843:     cerr << "AudioFileReaderFactory: No reader" << endl;
Chris@386:     return reader;
Chris@386: }
Chris@386: