annotate data/fileio/OggVorbisFileReader.cpp @ 661:a4faa1840384

* If a FileSource URL won't convert at all in strict mode, try again in tolerant mode (necessary for e.g. filenames with square brackets in them)
author Chris Cannam
date Tue, 19 Oct 2010 21:47:55 +0100
parents 773fc0e43feb
children 06f13a3b9e9e
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #ifdef HAVE_OGGZ
Chris@148 17 #ifdef HAVE_FISHSOUND
Chris@148 18
Chris@148 19 #include "OggVorbisFileReader.h"
Chris@357 20
Chris@392 21 #include "base/ProgressReporter.h"
Chris@148 22 #include "base/Profiler.h"
Chris@150 23 #include "system/System.h"
Chris@148 24
Chris@148 25 #include <sys/types.h>
Chris@148 26 #include <sys/stat.h>
Chris@148 27 #include <fcntl.h>
Chris@148 28 #include <cmath>
Chris@148 29
Chris@148 30 #include <QFileInfo>
Chris@148 31
Chris@524 32 //static int instances = 0;
Chris@148 33
Chris@317 34 OggVorbisFileReader::OggVorbisFileReader(FileSource source,
Chris@263 35 DecodeMode decodeMode,
Chris@297 36 CacheMode mode,
Chris@392 37 size_t targetRate,
Chris@392 38 ProgressReporter *reporter) :
Chris@297 39 CodedAudioFileReader(mode, targetRate),
Chris@316 40 m_source(source),
Chris@316 41 m_path(source.getLocalFilename()),
Chris@392 42 m_reporter(reporter),
Chris@148 43 m_fileSize(0),
Chris@148 44 m_bytesRead(0),
Chris@271 45 m_commentsRead(false),
Chris@263 46 m_cancelled(false),
Chris@265 47 m_completion(0),
Chris@263 48 m_decodeThread(0)
Chris@148 49 {
Chris@148 50 m_channelCount = 0;
Chris@297 51 m_fileRate = 0;
Chris@148 52
Chris@523 53 // std::cerr << "OggVorbisFileReader::OggVorbisFileReader(" << m_path.toLocal8Bit().data() << "): now have " << (++instances) << " instances" << std::endl;
Chris@148 54
Chris@148 55 Profiler profiler("OggVorbisFileReader::OggVorbisFileReader", true);
Chris@148 56
Chris@316 57 QFileInfo info(m_path);
Chris@148 58 m_fileSize = info.size();
Chris@148 59
Chris@316 60 if (!(m_oggz = oggz_open(m_path.toLocal8Bit().data(), OGGZ_READ))) {
Chris@316 61 m_error = QString("File %1 is not an OGG file.").arg(m_path);
Chris@148 62 return;
Chris@148 63 }
Chris@148 64
Chris@148 65 FishSoundInfo fsinfo;
Chris@148 66 m_fishSound = fish_sound_new(FISH_SOUND_DECODE, &fsinfo);
Chris@148 67
Chris@148 68 fish_sound_set_decoded_callback(m_fishSound, acceptFrames, this);
Chris@620 69 oggz_set_read_callback(m_oggz, -1, (OggzReadPacket)readPacket, this);
Chris@148 70
Chris@263 71 if (decodeMode == DecodeAtOnce) {
Chris@263 72
Chris@392 73 if (m_reporter) {
Chris@392 74 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@392 75 m_reporter->setMessage
Chris@392 76 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@327 77 }
Chris@148 78
Chris@263 79 while (oggz_read(m_oggz, 1024) > 0);
Chris@263 80
Chris@263 81 fish_sound_delete(m_fishSound);
Chris@263 82 m_fishSound = 0;
Chris@263 83 oggz_close(m_oggz);
Chris@263 84 m_oggz = 0;
Chris@148 85
Chris@263 86 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@398 87 endSerialised();
Chris@148 88
Chris@392 89 } else {
Chris@148 90
Chris@392 91 if (m_reporter) m_reporter->setProgress(100);
Chris@263 92
Chris@263 93 while (oggz_read(m_oggz, 1024) > 0 &&
Chris@386 94 (m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0));
Chris@263 95
Chris@263 96 if (m_channelCount > 0) {
Chris@263 97 m_decodeThread = new DecodeThread(this);
Chris@263 98 m_decodeThread->start();
Chris@263 99 }
Chris@148 100 }
Chris@148 101 }
Chris@148 102
Chris@148 103 OggVorbisFileReader::~OggVorbisFileReader()
Chris@148 104 {
Chris@524 105 // std::cerr << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path.toLocal8Bit().data() << "): now have " << (--instances) << " instances" << std::endl;
Chris@263 106 if (m_decodeThread) {
Chris@265 107 m_cancelled = true;
Chris@263 108 m_decodeThread->wait();
Chris@263 109 delete m_decodeThread;
Chris@263 110 }
Chris@148 111 }
Chris@148 112
Chris@263 113 void
Chris@392 114 OggVorbisFileReader::cancelled()
Chris@392 115 {
Chris@392 116 m_cancelled = true;
Chris@392 117 }
Chris@392 118
Chris@392 119 void
Chris@263 120 OggVorbisFileReader::DecodeThread::run()
Chris@263 121 {
Chris@297 122 if (m_reader->m_cacheMode == CacheInTemporaryFile) {
Chris@297 123 m_reader->m_completion = 1;
Chris@297 124 m_reader->startSerialised("OggVorbisFileReader::Decode");
Chris@297 125 }
Chris@297 126
Chris@263 127 while (oggz_read(m_reader->m_oggz, 1024) > 0);
Chris@263 128
Chris@263 129 fish_sound_delete(m_reader->m_fishSound);
Chris@263 130 m_reader->m_fishSound = 0;
Chris@263 131 oggz_close(m_reader->m_oggz);
Chris@263 132 m_reader->m_oggz = 0;
Chris@263 133
Chris@263 134 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@265 135 m_reader->m_completion = 100;
Chris@297 136
Chris@297 137 m_reader->endSerialised();
Chris@263 138 }
Chris@263 139
Chris@148 140 int
Chris@620 141 OggVorbisFileReader::readPacket(OGGZ *, ogg_packet *packet, long, void *data)
Chris@148 142 {
Chris@148 143 OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
Chris@148 144 FishSound *fs = reader->m_fishSound;
Chris@148 145
Chris@620 146 fish_sound_prepare_truncation(fs, packet->granulepos, packet->e_o_s);
Chris@620 147 fish_sound_decode(fs, packet->packet, packet->bytes);
Chris@148 148
Chris@620 149 reader->m_bytesRead += packet->bytes;
Chris@265 150
Chris@265 151 // The number of bytes read by this function is smaller than
Chris@265 152 // the file size because of the packet headers
Chris@357 153 int p = lrint(double(reader->m_bytesRead) * 114 /
Chris@357 154 double(reader->m_fileSize));
Chris@357 155 if (p > 99) p = 99;
Chris@357 156 reader->m_completion = p;
Chris@357 157 reader->progress(p);
Chris@357 158
Chris@392 159 if (reader->m_fileSize > 0 && reader->m_reporter) {
Chris@392 160 reader->m_reporter->setProgress(p);
Chris@265 161 }
Chris@148 162
Chris@148 163 if (reader->m_cancelled) return 1;
Chris@148 164 return 0;
Chris@148 165 }
Chris@148 166
Chris@148 167 int
Chris@148 168 OggVorbisFileReader::acceptFrames(FishSound *fs, float **frames, long nframes,
Chris@148 169 void *data)
Chris@148 170 {
Chris@148 171 OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
Chris@148 172
Chris@271 173 if (!reader->m_commentsRead) {
Chris@633 174 const FishSoundComment *comment;
Chris@633 175 comment = fish_sound_comment_first_byname(fs, "TITLE");
Chris@633 176 if (comment && comment->value) {
Chris@633 177 reader->m_title = QString::fromUtf8(comment->value);
Chris@333 178 }
Chris@633 179 comment = fish_sound_comment_first_byname(fs, "ARTIST");
Chris@633 180 if (comment && comment->value) {
Chris@633 181 reader->m_maker = QString::fromUtf8(comment->value);
Chris@633 182 }
Chris@633 183 comment = fish_sound_comment_first(fs);
Chris@633 184 while (comment) {
Chris@634 185 reader->m_tags[QString::fromUtf8(comment->name).toUpper()] =
Chris@633 186 QString::fromUtf8(comment->value);
Chris@633 187 comment = fish_sound_comment_next(fs, comment);
Chris@271 188 }
Chris@271 189 reader->m_commentsRead = true;
Chris@271 190 }
Chris@271 191
Chris@148 192 if (reader->m_channelCount == 0) {
Chris@148 193 FishSoundInfo fsinfo;
Chris@148 194 fish_sound_command(fs, FISH_SOUND_GET_INFO,
Chris@148 195 &fsinfo, sizeof(FishSoundInfo));
Chris@297 196 reader->m_fileRate = fsinfo.samplerate;
Chris@148 197 reader->m_channelCount = fsinfo.channels;
Chris@148 198 reader->initialiseDecodeCache();
Chris@148 199 }
Chris@148 200
Chris@148 201 if (nframes > 0) {
Chris@297 202 reader->addSamplesToDecodeCache(frames, nframes);
Chris@148 203 }
Chris@148 204
Chris@148 205 if (reader->m_cancelled) return 1;
Chris@148 206 return 0;
Chris@148 207 }
Chris@148 208
Chris@157 209 void
Chris@290 210 OggVorbisFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 211 {
Chris@157 212 extensions.insert("ogg");
Chris@571 213 extensions.insert("oga");
Chris@157 214 }
Chris@157 215
Chris@316 216 bool
Chris@316 217 OggVorbisFileReader::supportsExtension(QString extension)
Chris@316 218 {
Chris@316 219 std::set<QString> extensions;
Chris@316 220 getSupportedExtensions(extensions);
Chris@316 221 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 222 }
Chris@316 223
Chris@316 224 bool
Chris@316 225 OggVorbisFileReader::supportsContentType(QString type)
Chris@316 226 {
Chris@316 227 return (type == "application/ogg");
Chris@316 228 }
Chris@316 229
Chris@316 230 bool
Chris@317 231 OggVorbisFileReader::supports(FileSource &source)
Chris@316 232 {
Chris@316 233 return (supportsExtension(source.getExtension()) ||
Chris@316 234 supportsContentType(source.getContentType()));
Chris@316 235 }
Chris@316 236
Chris@148 237 #endif
Chris@148 238 #endif