annotate data/fileio/OggVorbisFileReader.cpp @ 1412:b7a9edee85e0 scale-ticks

Change loop to something that feels more correct, though it makes no difference to the tests here. More tests, one failing.
author Chris Cannam
date Thu, 04 May 2017 08:32:41 +0100
parents ce08318aad83
children 70e172e6cc59
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@1040 37 sv_samplerate_t targetRate,
Chris@920 38 bool normalised,
Chris@392 39 ProgressReporter *reporter) :
Chris@920 40 CodedAudioFileReader(mode, targetRate, normalised),
Chris@316 41 m_source(source),
Chris@316 42 m_path(source.getLocalFilename()),
Chris@1359 43 m_qfile(0),
Chris@1359 44 m_ffile(0),
Chris@1359 45 m_oggz(0),
Chris@1359 46 m_fishSound(0),
Chris@392 47 m_reporter(reporter),
Chris@148 48 m_fileSize(0),
Chris@148 49 m_bytesRead(0),
Chris@271 50 m_commentsRead(false),
Chris@263 51 m_cancelled(false),
Chris@265 52 m_completion(0),
Chris@263 53 m_decodeThread(0)
Chris@148 54 {
Chris@1279 55 SVDEBUG << "OggVorbisFileReader: local path: \"" << m_path
Chris@1279 56 << "\", decode mode: " << decodeMode << " ("
Chris@1279 57 << (decodeMode == DecodeAtOnce ? "DecodeAtOnce" : "DecodeThreaded")
Chris@1279 58 << ")" << endl;
Chris@1279 59
Chris@148 60 m_channelCount = 0;
Chris@297 61 m_fileRate = 0;
Chris@148 62
Chris@845 63 // SVDEBUG << "OggVorbisFileReader::OggVorbisFileReader(" << m_path << "): now have " << (++instances) << " instances" << endl;
Chris@148 64
Chris@1295 65 Profiler profiler("OggVorbisFileReader::OggVorbisFileReader");
Chris@148 66
Chris@1359 67 // These shenanigans are to avoid using oggz_open(..) with a local
Chris@1359 68 // codepage on Windows (make sure proper filename encoding is used)
Chris@1359 69
Chris@1359 70 m_qfile = new QFile(m_path);
Chris@1359 71 if (!m_qfile->open(QIODevice::ReadOnly)) {
Chris@1359 72 m_error = QString("Failed to open file %1 for reading.").arg(m_path);
Chris@1359 73 SVDEBUG << "OggVorbisFileReader: " << m_error << endl;
Chris@1359 74 delete m_qfile;
Chris@1359 75 m_qfile = 0;
Chris@1359 76 return;
Chris@1359 77 }
Chris@1359 78
Chris@1359 79 m_fileSize = m_qfile->size();
Chris@148 80
Chris@1381 81 m_ffile = fdopen(dup(m_qfile->handle()), "rb");
Chris@1359 82 if (!m_ffile) {
Chris@1359 83 m_error = QString("Failed to open file pointer for file %1").arg(m_path);
Chris@1359 84 SVDEBUG << "OggVorbisFileReader: " << m_error << endl;
Chris@1359 85 delete m_qfile;
Chris@1359 86 m_qfile = 0;
Chris@1359 87 return;
Chris@1359 88 }
Chris@1359 89
Chris@1359 90 if (!(m_oggz = oggz_open_stdio(m_ffile, OGGZ_READ))) {
Chris@1343 91 m_error = QString("File %1 is not an OGG file.").arg(m_path);
Chris@1359 92 fclose(m_ffile);
Chris@1359 93 m_ffile = 0;
Chris@1359 94 delete m_qfile;
Chris@1359 95 m_qfile = 0;
Chris@1343 96 return;
Chris@148 97 }
Chris@148 98
Chris@148 99 FishSoundInfo fsinfo;
Chris@148 100 m_fishSound = fish_sound_new(FISH_SOUND_DECODE, &fsinfo);
Chris@148 101
Chris@148 102 fish_sound_set_decoded_callback(m_fishSound, acceptFrames, this);
Chris@620 103 oggz_set_read_callback(m_oggz, -1, (OggzReadPacket)readPacket, this);
Chris@148 104
Chris@263 105 if (decodeMode == DecodeAtOnce) {
Chris@263 106
Chris@392 107 if (m_reporter) {
Chris@392 108 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@392 109 m_reporter->setMessage
Chris@392 110 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@327 111 }
Chris@148 112
Chris@263 113 while (oggz_read(m_oggz, 1024) > 0);
Chris@263 114
Chris@263 115 fish_sound_delete(m_fishSound);
Chris@263 116 m_fishSound = 0;
Chris@263 117 oggz_close(m_oggz);
Chris@263 118 m_oggz = 0;
Chris@148 119
Chris@263 120 if (isDecodeCacheInitialised()) finishDecodeCache();
Chris@398 121 endSerialised();
Chris@148 122
Chris@392 123 } else {
Chris@148 124
Chris@392 125 if (m_reporter) m_reporter->setProgress(100);
Chris@263 126
Chris@263 127 while (oggz_read(m_oggz, 1024) > 0 &&
Chris@386 128 (m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0));
Chris@263 129
Chris@263 130 if (m_channelCount > 0) {
Chris@263 131 m_decodeThread = new DecodeThread(this);
Chris@263 132 m_decodeThread->start();
Chris@263 133 }
Chris@148 134 }
Chris@148 135 }
Chris@148 136
Chris@148 137 OggVorbisFileReader::~OggVorbisFileReader()
Chris@148 138 {
Chris@845 139 // SVDEBUG << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path << "): now have " << (--instances) << " instances" << endl;
Chris@263 140 if (m_decodeThread) {
Chris@265 141 m_cancelled = true;
Chris@263 142 m_decodeThread->wait();
Chris@263 143 delete m_decodeThread;
Chris@263 144 }
Chris@1359 145 if (m_qfile) {
Chris@1359 146 // don't fclose m_ffile; oggz_close did that
Chris@1359 147 delete m_qfile;
Chris@1359 148 m_qfile = 0;
Chris@1359 149 }
Chris@148 150 }
Chris@148 151
Chris@263 152 void
Chris@392 153 OggVorbisFileReader::cancelled()
Chris@392 154 {
Chris@392 155 m_cancelled = true;
Chris@392 156 }
Chris@392 157
Chris@392 158 void
Chris@263 159 OggVorbisFileReader::DecodeThread::run()
Chris@263 160 {
Chris@297 161 if (m_reader->m_cacheMode == CacheInTemporaryFile) {
Chris@297 162 m_reader->m_completion = 1;
Chris@297 163 m_reader->startSerialised("OggVorbisFileReader::Decode");
Chris@297 164 }
Chris@297 165
Chris@263 166 while (oggz_read(m_reader->m_oggz, 1024) > 0);
Chris@263 167
Chris@263 168 fish_sound_delete(m_reader->m_fishSound);
Chris@263 169 m_reader->m_fishSound = 0;
Chris@1359 170
Chris@263 171 oggz_close(m_reader->m_oggz);
Chris@263 172 m_reader->m_oggz = 0;
Chris@1359 173
Chris@1359 174 // don't fclose m_ffile; oggz_close did that
Chris@1359 175
Chris@1359 176 delete m_reader->m_qfile;
Chris@1359 177 m_reader->m_qfile = 0;
Chris@263 178
Chris@263 179 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
Chris@265 180 m_reader->m_completion = 100;
Chris@297 181
Chris@297 182 m_reader->endSerialised();
Chris@263 183 }
Chris@263 184
Chris@148 185 int
Chris@620 186 OggVorbisFileReader::readPacket(OGGZ *, ogg_packet *packet, long, void *data)
Chris@148 187 {
Chris@148 188 OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
Chris@148 189 FishSound *fs = reader->m_fishSound;
Chris@148 190
Chris@1043 191 fish_sound_prepare_truncation(fs, packet->granulepos, int(packet->e_o_s));
Chris@620 192 fish_sound_decode(fs, packet->packet, packet->bytes);
Chris@148 193
Chris@620 194 reader->m_bytesRead += packet->bytes;
Chris@265 195
Chris@265 196 // The number of bytes read by this function is smaller than
Chris@265 197 // the file size because of the packet headers
Chris@1043 198 int p = int(lrint(double(reader->m_bytesRead) * 114 /
Chris@1043 199 double(reader->m_fileSize)));
Chris@357 200 if (p > 99) p = 99;
Chris@357 201 reader->m_completion = p;
Chris@357 202 reader->progress(p);
Chris@357 203
Chris@392 204 if (reader->m_fileSize > 0 && reader->m_reporter) {
Chris@392 205 reader->m_reporter->setProgress(p);
Chris@265 206 }
Chris@148 207
Chris@148 208 if (reader->m_cancelled) return 1;
Chris@148 209 return 0;
Chris@148 210 }
Chris@148 211
Chris@148 212 int
Chris@148 213 OggVorbisFileReader::acceptFrames(FishSound *fs, float **frames, long nframes,
Chris@1343 214 void *data)
Chris@148 215 {
Chris@148 216 OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
Chris@148 217
Chris@271 218 if (!reader->m_commentsRead) {
Chris@633 219 const FishSoundComment *comment;
Chris@1029 220 comment = fish_sound_comment_first_byname(fs, (char *)"TITLE");
Chris@633 221 if (comment && comment->value) {
Chris@633 222 reader->m_title = QString::fromUtf8(comment->value);
Chris@333 223 }
Chris@1029 224 comment = fish_sound_comment_first_byname(fs, (char *)"ARTIST");
Chris@633 225 if (comment && comment->value) {
Chris@633 226 reader->m_maker = QString::fromUtf8(comment->value);
Chris@633 227 }
Chris@633 228 comment = fish_sound_comment_first(fs);
Chris@633 229 while (comment) {
Chris@634 230 reader->m_tags[QString::fromUtf8(comment->name).toUpper()] =
Chris@633 231 QString::fromUtf8(comment->value);
Chris@633 232 comment = fish_sound_comment_next(fs, comment);
Chris@271 233 }
Chris@271 234 reader->m_commentsRead = true;
Chris@271 235 }
Chris@271 236
Chris@148 237 if (reader->m_channelCount == 0) {
Chris@1343 238 FishSoundInfo fsinfo;
Chris@1343 239 fish_sound_command(fs, FISH_SOUND_GET_INFO,
Chris@1343 240 &fsinfo, sizeof(FishSoundInfo));
Chris@1343 241 reader->m_fileRate = fsinfo.samplerate;
Chris@1343 242 reader->m_channelCount = fsinfo.channels;
Chris@148 243 reader->initialiseDecodeCache();
Chris@148 244 }
Chris@148 245
Chris@148 246 if (nframes > 0) {
Chris@297 247 reader->addSamplesToDecodeCache(frames, nframes);
Chris@148 248 }
Chris@148 249
Chris@148 250 if (reader->m_cancelled) return 1;
Chris@148 251 return 0;
Chris@148 252 }
Chris@148 253
Chris@157 254 void
Chris@290 255 OggVorbisFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 256 {
Chris@157 257 extensions.insert("ogg");
Chris@571 258 extensions.insert("oga");
Chris@157 259 }
Chris@157 260
Chris@316 261 bool
Chris@316 262 OggVorbisFileReader::supportsExtension(QString extension)
Chris@316 263 {
Chris@316 264 std::set<QString> extensions;
Chris@316 265 getSupportedExtensions(extensions);
Chris@316 266 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 267 }
Chris@316 268
Chris@316 269 bool
Chris@316 270 OggVorbisFileReader::supportsContentType(QString type)
Chris@316 271 {
Chris@316 272 return (type == "application/ogg");
Chris@316 273 }
Chris@316 274
Chris@316 275 bool
Chris@317 276 OggVorbisFileReader::supports(FileSource &source)
Chris@316 277 {
Chris@316 278 return (supportsExtension(source.getExtension()) ||
Chris@316 279 supportsContentType(source.getContentType()));
Chris@316 280 }
Chris@316 281
Chris@148 282 #endif
Chris@148 283 #endif