annotate data/fileio/OggVorbisFileReader.cpp @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents be49bf95d4a5
children bd7c46636bd0
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 <sys/mman.h>
Chris@148 28 #include <fcntl.h>
Chris@148 29 #include <cmath>
Chris@148 30
Chris@148 31 #include <QFileInfo>
Chris@148 32
Chris@148 33 static int instances = 0;
Chris@148 34
Chris@317 35 OggVorbisFileReader::OggVorbisFileReader(FileSource source,
Chris@263 36 DecodeMode decodeMode,
Chris@297 37 CacheMode mode,
Chris@392 38 size_t targetRate,
Chris@392 39 ProgressReporter *reporter) :
Chris@297 40 CodedAudioFileReader(mode, targetRate),
Chris@316 41 m_source(source),
Chris@316 42 m_path(source.getLocalFilename()),
Chris@392 43 m_reporter(reporter),
Chris@148 44 m_fileSize(0),
Chris@148 45 m_bytesRead(0),
Chris@271 46 m_commentsRead(false),
Chris@263 47 m_cancelled(false),
Chris@265 48 m_completion(0),
Chris@263 49 m_decodeThread(0)
Chris@148 50 {
Chris@148 51 m_channelCount = 0;
Chris@297 52 m_fileRate = 0;
Chris@148 53
Chris@316 54 std::cerr << "OggVorbisFileReader::OggVorbisFileReader(" << m_path.toLocal8Bit().data() << "): now have " << (++instances) << " instances" << std::endl;
Chris@148 55
Chris@148 56 Profiler profiler("OggVorbisFileReader::OggVorbisFileReader", true);
Chris@148 57
Chris@316 58 QFileInfo info(m_path);
Chris@148 59 m_fileSize = info.size();
Chris@148 60
Chris@316 61 if (!(m_oggz = oggz_open(m_path.toLocal8Bit().data(), OGGZ_READ))) {
Chris@316 62 m_error = QString("File %1 is not an OGG file.").arg(m_path);
Chris@148 63 return;
Chris@148 64 }
Chris@148 65
Chris@148 66 FishSoundInfo fsinfo;
Chris@148 67 m_fishSound = fish_sound_new(FISH_SOUND_DECODE, &fsinfo);
Chris@148 68
Chris@148 69 fish_sound_set_decoded_callback(m_fishSound, acceptFrames, this);
Chris@263 70 oggz_set_read_callback(m_oggz, -1, readPacket, this);
Chris@148 71
Chris@263 72 if (decodeMode == DecodeAtOnce) {
Chris@263 73
Chris@392 74 if (m_reporter) {
Chris@392 75 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@392 76 m_reporter->setMessage
Chris@392 77 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@327 78 }
Chris@148 79
Chris@263 80 while (oggz_read(m_oggz, 1024) > 0);
Chris@263 81
Chris@263 82 fish_sound_delete(m_fishSound);
Chris@263 83 m_fishSound = 0;
Chris@263 84 oggz_close(m_oggz);
Chris@263 85 m_oggz = 0;
Chris@148 86
Chris@263 87 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@398 88 endSerialised();
Chris@148 89
Chris@392 90 } else {
Chris@148 91
Chris@392 92 if (m_reporter) m_reporter->setProgress(100);
Chris@263 93
Chris@263 94 while (oggz_read(m_oggz, 1024) > 0 &&
Chris@386 95 (m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0));
Chris@263 96
Chris@263 97 if (m_channelCount > 0) {
Chris@263 98 m_decodeThread = new DecodeThread(this);
Chris@263 99 m_decodeThread->start();
Chris@263 100 }
Chris@148 101 }
Chris@148 102 }
Chris@148 103
Chris@148 104 OggVorbisFileReader::~OggVorbisFileReader()
Chris@148 105 {
Chris@290 106 std::cerr << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path.toLocal8Bit().data() << "): now have " << (--instances) << " instances" << std::endl;
Chris@263 107 if (m_decodeThread) {
Chris@265 108 m_cancelled = true;
Chris@263 109 m_decodeThread->wait();
Chris@263 110 delete m_decodeThread;
Chris@263 111 }
Chris@148 112 }
Chris@148 113
Chris@263 114 void
Chris@392 115 OggVorbisFileReader::cancelled()
Chris@392 116 {
Chris@392 117 m_cancelled = true;
Chris@392 118 }
Chris@392 119
Chris@392 120 void
Chris@263 121 OggVorbisFileReader::DecodeThread::run()
Chris@263 122 {
Chris@297 123 if (m_reader->m_cacheMode == CacheInTemporaryFile) {
Chris@297 124 m_reader->m_completion = 1;
Chris@297 125 m_reader->startSerialised("OggVorbisFileReader::Decode");
Chris@297 126 }
Chris@297 127
Chris@263 128 while (oggz_read(m_reader->m_oggz, 1024) > 0);
Chris@263 129
Chris@263 130 fish_sound_delete(m_reader->m_fishSound);
Chris@263 131 m_reader->m_fishSound = 0;
Chris@263 132 oggz_close(m_reader->m_oggz);
Chris@263 133 m_reader->m_oggz = 0;
Chris@263 134
Chris@263 135 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@265 136 m_reader->m_completion = 100;
Chris@297 137
Chris@297 138 m_reader->endSerialised();
Chris@263 139 }
Chris@263 140
Chris@148 141 int
Chris@148 142 OggVorbisFileReader::readPacket(OGGZ *, ogg_packet *packet, long, void *data)
Chris@148 143 {
Chris@148 144 OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
Chris@148 145 FishSound *fs = reader->m_fishSound;
Chris@148 146
Chris@148 147 fish_sound_prepare_truncation(fs, packet->granulepos, packet->e_o_s);
Chris@148 148 fish_sound_decode(fs, packet->packet, packet->bytes);
Chris@148 149
Chris@148 150 reader->m_bytesRead += packet->bytes;
Chris@265 151
Chris@265 152 // The number of bytes read by this function is smaller than
Chris@265 153 // the file size because of the packet headers
Chris@357 154 int p = lrint(double(reader->m_bytesRead) * 114 /
Chris@357 155 double(reader->m_fileSize));
Chris@357 156 if (p > 99) p = 99;
Chris@357 157 reader->m_completion = p;
Chris@357 158 reader->progress(p);
Chris@357 159
Chris@392 160 if (reader->m_fileSize > 0 && reader->m_reporter) {
Chris@392 161 reader->m_reporter->setProgress(p);
Chris@265 162 }
Chris@148 163
Chris@148 164 if (reader->m_cancelled) return 1;
Chris@148 165 return 0;
Chris@148 166 }
Chris@148 167
Chris@148 168 int
Chris@148 169 OggVorbisFileReader::acceptFrames(FishSound *fs, float **frames, long nframes,
Chris@148 170 void *data)
Chris@148 171 {
Chris@148 172 OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
Chris@148 173
Chris@271 174 if (!reader->m_commentsRead) {
Chris@333 175 {
Chris@333 176 const FishSoundComment *comment = fish_sound_comment_first_byname
Chris@333 177 (fs, "TITLE");
Chris@333 178 if (comment && comment->value) {
Chris@333 179 reader->m_title = QString::fromUtf8(comment->value);
Chris@333 180 }
Chris@333 181 }
Chris@333 182 {
Chris@333 183 const FishSoundComment *comment = fish_sound_comment_first_byname
Chris@333 184 (fs, "ARTIST");
Chris@333 185 if (comment && comment->value) {
Chris@333 186 reader->m_maker = QString::fromUtf8(comment->value);
Chris@333 187 }
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@157 213 }
Chris@157 214
Chris@316 215 bool
Chris@316 216 OggVorbisFileReader::supportsExtension(QString extension)
Chris@316 217 {
Chris@316 218 std::set<QString> extensions;
Chris@316 219 getSupportedExtensions(extensions);
Chris@316 220 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 221 }
Chris@316 222
Chris@316 223 bool
Chris@316 224 OggVorbisFileReader::supportsContentType(QString type)
Chris@316 225 {
Chris@316 226 return (type == "application/ogg");
Chris@316 227 }
Chris@316 228
Chris@316 229 bool
Chris@317 230 OggVorbisFileReader::supports(FileSource &source)
Chris@316 231 {
Chris@316 232 return (supportsExtension(source.getExtension()) ||
Chris@316 233 supportsContentType(source.getContentType()));
Chris@316 234 }
Chris@316 235
Chris@148 236 #endif
Chris@148 237 #endif