annotate data/fileio/OggVorbisFileReader.cpp @ 537:3cc4b7cd2aa5

* Merge from one-fftdataserver-per-fftmodel branch. This bit of reworking (which is not described very accurately by the title of the branch) turns the MatrixFile object into something that either reads or writes, but not both, and separates the FFT file cache reader and writer implementations separately. This allows the FFT data server to have a single thread owning writers and one reader per "customer" thread, and for all locking to be vastly simplified and concentrated in the data server alone (because none of the classes it makes use of is used in more than one thread at a time). The result is faster and more trustworthy code.
author Chris Cannam
date Tue, 27 Jan 2009 13:25:10 +0000
parents 12608139f6bc
children dfc4dd561bb6
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@263 69 oggz_set_read_callback(m_oggz, -1, 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@148 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@148 146 fish_sound_prepare_truncation(fs, packet->granulepos, packet->e_o_s);
Chris@148 147 fish_sound_decode(fs, packet->packet, packet->bytes);
Chris@148 148
Chris@148 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@333 174 {
Chris@333 175 const FishSoundComment *comment = fish_sound_comment_first_byname
Chris@333 176 (fs, "TITLE");
Chris@333 177 if (comment && comment->value) {
Chris@333 178 reader->m_title = QString::fromUtf8(comment->value);
Chris@333 179 }
Chris@333 180 }
Chris@333 181 {
Chris@333 182 const FishSoundComment *comment = fish_sound_comment_first_byname
Chris@333 183 (fs, "ARTIST");
Chris@333 184 if (comment && comment->value) {
Chris@333 185 reader->m_maker = QString::fromUtf8(comment->value);
Chris@333 186 }
Chris@271 187 }
Chris@271 188 reader->m_commentsRead = true;
Chris@271 189 }
Chris@271 190
Chris@148 191 if (reader->m_channelCount == 0) {
Chris@148 192 FishSoundInfo fsinfo;
Chris@148 193 fish_sound_command(fs, FISH_SOUND_GET_INFO,
Chris@148 194 &fsinfo, sizeof(FishSoundInfo));
Chris@297 195 reader->m_fileRate = fsinfo.samplerate;
Chris@148 196 reader->m_channelCount = fsinfo.channels;
Chris@148 197 reader->initialiseDecodeCache();
Chris@148 198 }
Chris@148 199
Chris@148 200 if (nframes > 0) {
Chris@297 201 reader->addSamplesToDecodeCache(frames, nframes);
Chris@148 202 }
Chris@148 203
Chris@148 204 if (reader->m_cancelled) return 1;
Chris@148 205 return 0;
Chris@148 206 }
Chris@148 207
Chris@157 208 void
Chris@290 209 OggVorbisFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 210 {
Chris@157 211 extensions.insert("ogg");
Chris@157 212 }
Chris@157 213
Chris@316 214 bool
Chris@316 215 OggVorbisFileReader::supportsExtension(QString extension)
Chris@316 216 {
Chris@316 217 std::set<QString> extensions;
Chris@316 218 getSupportedExtensions(extensions);
Chris@316 219 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 220 }
Chris@316 221
Chris@316 222 bool
Chris@316 223 OggVorbisFileReader::supportsContentType(QString type)
Chris@316 224 {
Chris@316 225 return (type == "application/ogg");
Chris@316 226 }
Chris@316 227
Chris@316 228 bool
Chris@317 229 OggVorbisFileReader::supports(FileSource &source)
Chris@316 230 {
Chris@316 231 return (supportsExtension(source.getExtension()) ||
Chris@316 232 supportsContentType(source.getContentType()));
Chris@316 233 }
Chris@316 234
Chris@148 235 #endif
Chris@148 236 #endif