svcore  1.9
OggVorbisFileReader.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #ifdef HAVE_OGGZ
00017 #ifdef HAVE_FISHSOUND
00018 
00019 #include "OggVorbisFileReader.h"
00020 
00021 #include "base/ProgressReporter.h"
00022 #include "base/Profiler.h"
00023 #include "system/System.h"
00024 
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <fcntl.h>
00028 #include <cmath>
00029 
00030 #include <QFileInfo>
00031 
00032 //static int instances = 0;
00033 
00034 OggVorbisFileReader::OggVorbisFileReader(FileSource source,
00035                                          DecodeMode decodeMode,
00036                                          CacheMode mode,
00037                                          int targetRate,
00038                                          bool normalised,
00039                                          ProgressReporter *reporter) :
00040     CodedAudioFileReader(mode, targetRate, normalised),
00041     m_source(source),
00042     m_path(source.getLocalFilename()),
00043     m_reporter(reporter),
00044     m_fileSize(0),
00045     m_bytesRead(0),
00046     m_commentsRead(false),
00047     m_cancelled(false),
00048     m_completion(0),
00049     m_decodeThread(0)
00050 {
00051     m_channelCount = 0;
00052     m_fileRate = 0;
00053 
00054 //    SVDEBUG << "OggVorbisFileReader::OggVorbisFileReader(" << m_path << "): now have " << (++instances) << " instances" << endl;
00055 
00056     Profiler profiler("OggVorbisFileReader::OggVorbisFileReader", true);
00057 
00058     QFileInfo info(m_path);
00059     m_fileSize = info.size();
00060 
00061     if (!(m_oggz = oggz_open(m_path.toLocal8Bit().data(), OGGZ_READ))) {
00062         m_error = QString("File %1 is not an OGG file.").arg(m_path);
00063         return;
00064     }
00065 
00066     FishSoundInfo fsinfo;
00067     m_fishSound = fish_sound_new(FISH_SOUND_DECODE, &fsinfo);
00068 
00069     fish_sound_set_decoded_callback(m_fishSound, acceptFrames, this);
00070     oggz_set_read_callback(m_oggz, -1, (OggzReadPacket)readPacket, this);
00071 
00072     if (decodeMode == DecodeAtOnce) {
00073 
00074         if (m_reporter) {
00075             connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
00076             m_reporter->setMessage
00077                 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
00078         }
00079 
00080         while (oggz_read(m_oggz, 1024) > 0);
00081         
00082         fish_sound_delete(m_fishSound);
00083         m_fishSound = 0;
00084         oggz_close(m_oggz);
00085         m_oggz = 0;
00086 
00087         if (isDecodeCacheInitialised()) finishDecodeCache();
00088         endSerialised();
00089 
00090     } else {
00091 
00092         if (m_reporter) m_reporter->setProgress(100);
00093 
00094         while (oggz_read(m_oggz, 1024) > 0 &&
00095                (m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0));
00096 
00097         if (m_channelCount > 0) {
00098             m_decodeThread = new DecodeThread(this);
00099             m_decodeThread->start();
00100         }
00101     }
00102 }
00103 
00104 OggVorbisFileReader::~OggVorbisFileReader()
00105 {
00106 //    SVDEBUG << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path << "): now have " << (--instances) << " instances" << endl;
00107     if (m_decodeThread) {
00108         m_cancelled = true;
00109         m_decodeThread->wait();
00110         delete m_decodeThread;
00111     }
00112 }
00113 
00114 void
00115 OggVorbisFileReader::cancelled()
00116 {
00117     m_cancelled = true; 
00118 }
00119 
00120 void
00121 OggVorbisFileReader::DecodeThread::run()
00122 {
00123     if (m_reader->m_cacheMode == CacheInTemporaryFile) {
00124         m_reader->m_completion = 1;
00125         m_reader->startSerialised("OggVorbisFileReader::Decode");
00126     }
00127 
00128     while (oggz_read(m_reader->m_oggz, 1024) > 0);
00129         
00130     fish_sound_delete(m_reader->m_fishSound);
00131     m_reader->m_fishSound = 0;
00132     oggz_close(m_reader->m_oggz);
00133     m_reader->m_oggz = 0;
00134     
00135     if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
00136     m_reader->m_completion = 100;
00137 
00138     m_reader->endSerialised();
00139 } 
00140 
00141 int
00142 OggVorbisFileReader::readPacket(OGGZ *, ogg_packet *packet, long, void *data)
00143 {
00144     OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
00145     FishSound *fs = reader->m_fishSound;
00146 
00147     fish_sound_prepare_truncation(fs, packet->granulepos, packet->e_o_s);
00148     fish_sound_decode(fs, packet->packet, packet->bytes);
00149 
00150     reader->m_bytesRead += packet->bytes;
00151 
00152     // The number of bytes read by this function is smaller than
00153     // the file size because of the packet headers
00154     int p = lrint(double(reader->m_bytesRead) * 114 /
00155                   double(reader->m_fileSize));
00156     if (p > 99) p = 99;
00157     reader->m_completion = p;
00158     reader->progress(p);
00159 
00160     if (reader->m_fileSize > 0 && reader->m_reporter) {
00161         reader->m_reporter->setProgress(p);
00162     }
00163 
00164     if (reader->m_cancelled) return 1;
00165     return 0;
00166 }
00167 
00168 int
00169 OggVorbisFileReader::acceptFrames(FishSound *fs, float **frames, long nframes,
00170                                   void *data)
00171 {
00172     OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
00173 
00174     if (!reader->m_commentsRead) {
00175         const FishSoundComment *comment;
00176         comment = fish_sound_comment_first_byname(fs, "TITLE");
00177         if (comment && comment->value) {
00178             reader->m_title = QString::fromUtf8(comment->value);
00179         }
00180         comment = fish_sound_comment_first_byname(fs, "ARTIST");
00181         if (comment && comment->value) {
00182             reader->m_maker = QString::fromUtf8(comment->value);
00183         }
00184         comment = fish_sound_comment_first(fs);
00185         while (comment) {
00186             reader->m_tags[QString::fromUtf8(comment->name).toUpper()] =
00187                 QString::fromUtf8(comment->value);
00188             comment = fish_sound_comment_next(fs, comment);
00189         }
00190         reader->m_commentsRead = true;
00191     }
00192 
00193     if (reader->m_channelCount == 0) {
00194         FishSoundInfo fsinfo;
00195         fish_sound_command(fs, FISH_SOUND_GET_INFO,
00196                            &fsinfo, sizeof(FishSoundInfo));
00197         reader->m_fileRate = fsinfo.samplerate;
00198         reader->m_channelCount = fsinfo.channels;
00199         reader->initialiseDecodeCache();
00200     }
00201 
00202     if (nframes > 0) {
00203         reader->addSamplesToDecodeCache(frames, nframes);
00204     }
00205 
00206     if (reader->m_cancelled) return 1;
00207     return 0;
00208 }
00209 
00210 void
00211 OggVorbisFileReader::getSupportedExtensions(std::set<QString> &extensions)
00212 {
00213     extensions.insert("ogg");
00214     extensions.insert("oga");
00215 }
00216 
00217 bool
00218 OggVorbisFileReader::supportsExtension(QString extension)
00219 {
00220     std::set<QString> extensions;
00221     getSupportedExtensions(extensions);
00222     return (extensions.find(extension.toLower()) != extensions.end());
00223 }
00224 
00225 bool
00226 OggVorbisFileReader::supportsContentType(QString type)
00227 {
00228     return (type == "application/ogg");
00229 }
00230 
00231 bool
00232 OggVorbisFileReader::supports(FileSource &source)
00233 {
00234     return (supportsExtension(source.getExtension()) ||
00235             supportsContentType(source.getContentType()));
00236 }
00237 
00238 #endif
00239 #endif