annotate data/fileio/OggVorbisFileReader.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents d03b3d956358
children 1ecd533a9977
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@929 37 int 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@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@845 54 // SVDEBUG << "OggVorbisFileReader::OggVorbisFileReader(" << m_path << "): now have " << (++instances) << " instances" << 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@620 70 oggz_set_read_callback(m_oggz, -1, (OggzReadPacket)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@845 106 // SVDEBUG << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path << "): now have " << (--instances) << " instances" << 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@620 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@620 147 fish_sound_prepare_truncation(fs, packet->granulepos, packet->e_o_s);
Chris@620 148 fish_sound_decode(fs, packet->packet, packet->bytes);
Chris@148 149
Chris@620 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@633 175 const FishSoundComment *comment;
Chris@633 176 comment = fish_sound_comment_first_byname(fs, "TITLE");
Chris@633 177 if (comment && comment->value) {
Chris@633 178 reader->m_title = QString::fromUtf8(comment->value);
Chris@333 179 }
Chris@633 180 comment = fish_sound_comment_first_byname(fs, "ARTIST");
Chris@633 181 if (comment && comment->value) {
Chris@633 182 reader->m_maker = QString::fromUtf8(comment->value);
Chris@633 183 }
Chris@633 184 comment = fish_sound_comment_first(fs);
Chris@633 185 while (comment) {
Chris@634 186 reader->m_tags[QString::fromUtf8(comment->name).toUpper()] =
Chris@633 187 QString::fromUtf8(comment->value);
Chris@633 188 comment = fish_sound_comment_next(fs, comment);
Chris@271 189 }
Chris@271 190 reader->m_commentsRead = true;
Chris@271 191 }
Chris@271 192
Chris@148 193 if (reader->m_channelCount == 0) {
Chris@148 194 FishSoundInfo fsinfo;
Chris@148 195 fish_sound_command(fs, FISH_SOUND_GET_INFO,
Chris@148 196 &fsinfo, sizeof(FishSoundInfo));
Chris@297 197 reader->m_fileRate = fsinfo.samplerate;
Chris@148 198 reader->m_channelCount = fsinfo.channels;
Chris@148 199 reader->initialiseDecodeCache();
Chris@148 200 }
Chris@148 201
Chris@148 202 if (nframes > 0) {
Chris@297 203 reader->addSamplesToDecodeCache(frames, nframes);
Chris@148 204 }
Chris@148 205
Chris@148 206 if (reader->m_cancelled) return 1;
Chris@148 207 return 0;
Chris@148 208 }
Chris@148 209
Chris@157 210 void
Chris@290 211 OggVorbisFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@157 212 {
Chris@157 213 extensions.insert("ogg");
Chris@571 214 extensions.insert("oga");
Chris@157 215 }
Chris@157 216
Chris@316 217 bool
Chris@316 218 OggVorbisFileReader::supportsExtension(QString extension)
Chris@316 219 {
Chris@316 220 std::set<QString> extensions;
Chris@316 221 getSupportedExtensions(extensions);
Chris@316 222 return (extensions.find(extension.toLower()) != extensions.end());
Chris@316 223 }
Chris@316 224
Chris@316 225 bool
Chris@316 226 OggVorbisFileReader::supportsContentType(QString type)
Chris@316 227 {
Chris@316 228 return (type == "application/ogg");
Chris@316 229 }
Chris@316 230
Chris@316 231 bool
Chris@317 232 OggVorbisFileReader::supports(FileSource &source)
Chris@316 233 {
Chris@316 234 return (supportsExtension(source.getExtension()) ||
Chris@316 235 supportsContentType(source.getContentType()));
Chris@316 236 }
Chris@316 237
Chris@148 238 #endif
Chris@148 239 #endif