# HG changeset patch # User Chris Cannam # Date 1186411079 0 # Node ID 9c447d66427555b3910295dd60ed49904d14e9d5 # Parent daf89d31f45c26abb5318cd558159d7d13aeecc4 * Add QuickTime file reader class -- totally untested, shouldn't even compile diff -r daf89d31f45c -r 9c447d664275 data/data.pro --- a/data/data.pro Fri Jul 13 15:54:17 2007 +0000 +++ b/data/data.pro Mon Aug 06 14:37:59 2007 +0000 @@ -1,6 +1,6 @@ TEMPLATE = lib -SV_UNIT_PACKAGES = fftw3f sndfile mad id3tag oggz fishsound +SV_UNIT_PACKAGES = fftw3f sndfile mad quicktime id3tag oggz fishsound load(../sv.prf) CONFIG += sv staticlib qt thread warn_on stl rtti exceptions @@ -33,6 +33,7 @@ fileio/MIDIFileReader.h \ fileio/MP3FileReader.h \ fileio/OggVorbisFileReader.h \ + fileio/QuickTimeFileReader.h \ fileio/RemoteFile.h \ fileio/WavFileReader.h \ fileio/WavFileWriter.h \ @@ -69,6 +70,7 @@ fileio/MIDIFileReader.cpp \ fileio/MP3FileReader.cpp \ fileio/OggVorbisFileReader.cpp \ + fileio/QuickTimeFileReader.cpp \ fileio/RemoteFile.cpp \ fileio/WavFileReader.cpp \ fileio/WavFileWriter.cpp \ diff -r daf89d31f45c -r 9c447d664275 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Fri Jul 13 15:54:17 2007 +0000 +++ b/data/fileio/AudioFileReaderFactory.cpp Mon Aug 06 14:37:59 2007 +0000 @@ -18,6 +18,7 @@ #include "WavFileReader.h" #include "OggVorbisFileReader.h" #include "MP3FileReader.h" +#include "QuickTimeFileReader.h" #include #include @@ -37,6 +38,9 @@ OggVorbisFileReader::getSupportedExtensions(extensions); #endif #endif +#ifdef HAVE_QUICKTIME + QuickTimeFileReader::getSupportedExtensions(extensions); +#endif QString rv; for (std::set::const_iterator i = extensions.begin(); @@ -67,18 +71,6 @@ reader = new WavFileReader(path); } -#ifdef HAVE_MAD - if (!reader) { - extensions.clear(); - MP3FileReader::getSupportedExtensions(extensions); - if (extensions.find(ext) != extensions.end()) { - reader = new MP3FileReader - (path, - MP3FileReader::DecodeThreaded, - MP3FileReader::CacheInTemporaryFile); - } - } -#endif #ifdef HAVE_OGGZ #ifdef HAVE_FISHSOUND if (!reader) { @@ -94,6 +86,32 @@ #endif #endif +#ifdef HAVE_MAD + if (!reader) { + extensions.clear(); + MP3FileReader::getSupportedExtensions(extensions); + if (extensions.find(ext) != extensions.end()) { + reader = new MP3FileReader + (path, + MP3FileReader::DecodeThreaded, + MP3FileReader::CacheInTemporaryFile); + } + } +#endif + +#ifdef HAVE_QUICKTIME + if (!reader) { + extensions.clear(); + QuickTimeFileReader::getSupportedExtensions(extensions); + if (extensions.find(ext) != extensions.end()) { + reader = new QuickTimeFileReader + (path, + QuickTimeFileReader::DecodeThreaded, + QuickTimeFileReader::CacheInTemporaryFile); + } + } +#endif + if (reader) { if (reader->isOK()) return reader; if (reader->getError() != "") { @@ -154,6 +172,22 @@ delete reader; #endif +#ifdef HAVE_QUICKTIME + reader = new QuickTimeFileReader + (path, + QuickTimeFileReader::DecodeThreaded, + QuickTimeFileReader::CacheInTemporaryFile); + if (reader->isOK()) return reader; + if (reader->getError() != "") { + std::cerr << "AudioFileReaderFactory: QuickTime file reader error: \"" + << reader->getError().toStdString() << "\"" << std::endl; + } else { + std::cerr << "AudioFileReaderFactory: QuickTime file reader failed" + << std::endl; + } + delete reader; +#endif + return 0; } diff -r daf89d31f45c -r 9c447d664275 data/fileio/OggVorbisFileReader.cpp --- a/data/fileio/OggVorbisFileReader.cpp Fri Jul 13 15:54:17 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Mon Aug 06 14:37:59 2007 +0000 @@ -83,10 +83,8 @@ if (isDecodeCacheInitialised()) finishDecodeCache(); - if (decodeMode == DecodeAtOnce) { - delete m_progress; - m_progress = 0; - } + delete m_progress; + m_progress = 0; } else { diff -r daf89d31f45c -r 9c447d664275 data/fileio/QuickTimeFileReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/QuickTimeFileReader.cpp Mon Aug 06 14:37:59 2007 +0000 @@ -0,0 +1,328 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + Based on QTAudioFile.cpp from SoundBite, copyright 2006 + Chris Sutton and Mark Levy. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifdef HAVE_QUICKTIME + +#include "QuickTimeFileReader.h" +#include "base/Profiler.h" +#include "system/System.h" + +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#endif + +class QuickTimeFileReader::D +{ +public: + D() : data(0), blockSize(1024) { } + + MovieAudioExtractionRef extractionSessionRef; + AudioBufferList buffer; + float *data; + OSErr err; + AudioStreamBasicDescription asbd; + Movie movie; + size_t blockSize; +}; + + +QuickTimeFileReader::QuickTimeFileReader(QString path, + DecodeMode decodeMode, + CacheMode mode) : + CodedAudioFileReader(mode), + m_path(path), + m_d(new D), + m_progress(0), + m_cancelled(false), + m_completion(0), + m_decodeThread(0) +{ + m_frameCount = 0; + m_channelCount = 0; + m_sampleRate = 0; + + Profiler profiler("QuickTimeFileReader::QuickTimeFileReader", true); + + long QTversion; + +#ifdef WIN32 + InitializeQTML(0); // FIXME should check QT version +#else + m_d->err = Gestalt(gestaltQuickTime,&QTversion); + if ((m_d->err != noErr) || (QTversion < 0x07000000)) { + m_error = QString("Failed to find compatible version of QuickTime (version 7 or above required)"); + return; + } +#endif + + EnterMovies(); + + Handle dataRef; + OSType dataRefType; + + CFStringRef URLString = CFStringCreateWithCString + (0, m_path.toLocal8Bit().data(), 0); + + m_d->err = QTNewDataReferenceFromURLCFString + (URLString, 0, &dataRef, &dataRefType); + + if (m_d->err) { + m_error = QString("Error creating data reference for QuickTime decoder: code %1").arg(m_d->err); + return; + } + + short fileID = movieInDataForkResID; + short flags = 0; + m_d->err = NewMovieFromDataRef + (&m_d->movie, flags, &fileID, dataRef, dataRefType); + + DisposeHandle(dataRef); + if (m_d->err) { + m_error = QString("Error creating new movie for QuickTime decoder: code %1").arg(m_d->err); + return; + } + + Boolean isProtected = 0; + Track aTrack = GetMovieIndTrackType + (m_d->movie, 1, SoundMediaType, + movieTrackMediaType | movieTrackEnabledOnly); + + if (aTrack) { + Media aMedia = GetTrackMedia(aTrack); // get the track media + if (aMedia) { + MediaHandler mh = GetMediaHandler(aMedia); // get the media handler we can query + if (mh) { + m_d->err = QTGetComponentProperty(mh, + kQTPropertyClass_DRM, + kQTDRMPropertyID_IsProtected, + sizeof(Boolean), &isProtected,nil); + } else { + m_d->err = 1; + } + } else { + m_d->err = 1; + } + } else { + m_d->err = 1; + } + + if (m_d->err && m_d->err != kQTPropertyNotSupportedErr) { + m_error = QString("Error checking for DRM in QuickTime decoder: code %1").arg(m_d->err); + return; + } else if (!m_d->err && isProtected) { + m_error = QString("File is protected with DRM"); + return; + } else if (m_d->err == kQTPropertyNotSupportedErr && !isProtected) { + std::cerr << "QuickTime: File is not protected with DRM" << std::endl; + } + + if (m_d->movie) { + SetMovieActive(m_d->movie, TRUE); + m_d->err = GetMoviesError(); + if (m_d->err) { + m_error = QString("Error in QuickTime decoder activation: code %1").arg(m_d->err); + return; + } + } + + m_d->err = MovieAudioExtractionBegin + (m_d->movie, 0, &m_d->extractionSessionRef); + if (m_d->err) { + m_error = QString("Error in QuickTime decoder extraction init: code %1").arg(m_d->err); + return; + } + /* + AudioChannelLayout monoLayout = {0}; + monoLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; + m_d->err = MovieAudioExtractionSetProperty(m_d->extractionSessionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + sizeof(monoLayout), &monoLayout); + if (m_d->err) { + m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err); + return; + } + */ + + m_d->err = MovieAudioExtractionGetProperty + (m_d->extractionSessionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof(m_d->asbd), + &m_d->asbd, + nil); + + if (m_d->err) { + m_error = QString("Error in QuickTime decoder property get: code %1").arg(m_d->err); + return; + } + + m_channelCount = m_d->asbd.mChannelsPerFrame; + m_sampleRate = m_d->asbd.mSampleRate; + + std::cerr << "QuickTime: " << m_channelCount << " channels, " << m_sampleRate << " kHz" << std::endl; + + m_d->asbd.mFormatFlags = + kAudioFormatFlagIsFloat | + kAudioFormatFlagIsPacked | + kAudioFormatFlagsNativeEndian; + m_d->asbd.mBitsPerChannel = sizeof(float) * 8; + m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame; + m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame; + + m_d->err = MovieAudioExtractionSetProperty + (m_d->extractionSessionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof(m_d->asbd), + &m_d->asbd); + + if (m_d->err) { + m_error = QString("Error in QuickTime decoder ASBD set: code %1").arg(m_d->err); + return; + } + + m_d->buffer.mNumberBuffers = 1; + m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount; + m_d->buffer.mBuffers[0].mDataByteSize = + sizeof(float) * m_channelCount * m_d->blockSize; + m_d->data = new float[m_d->blockSize]; + m_d->buffer[0].mData = m_d->data; + + initialiseDecodeCache(); + + if (decodeMode == DecodeAtOnce) { + + m_progress = new QProgressDialog + (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + + while (1) { + + UInt32 framesRead = 0; + UInt32 extractionFlags = 0; + m_d->err = MovieAudioExtractionFillBuffer + (m_d->extractionSessionRef, &framesRead, &m_d->buffer, + &extractionFlags); + if (m_d->err) { + m_error = QString("Error in QuickTime decoding: code %1") + .arg(m_d->err); + break; + } + + // QuickTime buffers are interleaved unless specified otherwise + for (UInt32 i = 0; i < framesRead * m_channelCount; ++i) { + addSampleToDecodeCache(m_d->data[i]); + } + + if (framesRead < m_d->blockSize) break; + } + + finishDecodeCache(); + + m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef); + if (m_d->err) { + m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err); + } + + m_completion = 100; + + delete m_progress; + m_progress = 0; + + } else { + if (m_channelCount > 0) { + m_decodeThread = new DecodeThread(this); + m_decodeThread->start(); + } + } +} + +QuickTimeFileReader::~QuickTimeFileReader() +{ + if (m_decodeThread) { + m_cancelled = true; + m_decodeThread->wait(); + delete m_decodeThread; + } + + SetMovieActive(m_d->movie); + DisposeMovie(m_d->movie); + + delete[] m_d->data; + delete m_d; +} + +void +QuickTimeFileReader::DecodeThread::run() +{ + while (1) { + + UInt32 framesRead = 0; + UInt32 extractionFlags = 0; + m_reader->m_d->err = MovieAudioExtractionFillBuffer + (m_reader->m_d->extractionSessionRef, &framesRead, + &m_reader->m_d->buffer, &extractionFlags); + if (m_reader->m_d->err) { + m_error = QString("Error in QuickTime decoding: code %1") + .arg(m_reader->m_d->err); + break; + } + + // QuickTime buffers are interleaved unless specified otherwise + for (UInt32 i = 0; i < framesRead * m_channelCount; ++i) { + addSampleToDecodeCache(m_reader->m_d->data[i]); + } + + if (framesRead < m_reader->m_d->blockSize) break; + } + + m_reader->finishDecodeCache(); + + m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef); + if (m_reader->m_d->err) { + m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err); + } + + m_reader->m_completion = 100; +} + +void +QuickTimeFileReader::getSupportedExtensions(std::set &extensions) +{ + extensions.insert("aiff"); + extensions.insert("au"); + extensions.insert("avi"); + extensions.insert("m4a"); + extensions.insert("m4b"); + extensions.insert("m4p"); + extensions.insert("m4v"); + extensions.insert("mov"); + extensions.insert("mp3"); + extensions.insert("mp4"); + extensions.insert("wav"); +} + +#endif diff -r daf89d31f45c -r 9c447d664275 data/fileio/QuickTimeFileReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/QuickTimeFileReader.h Mon Aug 06 14:37:59 2007 +0000 @@ -0,0 +1,81 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + Based in part on QTAudioFile.h from SoundBite, copyright 2006 + Chris Sutton and Mark Levy. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _QUICKTIME_FILE_READER_H_ +#define _QUICKTIME_FILE_READER_H_ + +#ifdef HAVE_QUICKTIME + +#include "CodedAudioFileReader.h" + +#include "base/Thread.h" + +#include + +class QProgressDialog; + +class QuickTimeFileReader : public CodedAudioFileReader +{ +public: + enum DecodeMode { + DecodeAtOnce, // decode the file on construction, with progress dialog + DecodeThreaded // decode in a background thread after construction + }; + + QuickTimeFileReader(QString path, DecodeMode decodeMode, + CacheMode cacheMode); + virtual ~QuickTimeFileReader(); + + virtual QString getError() const { return m_error; } + virtual QString getTitle() const { return m_title; } + + static void getSupportedExtensions(std::set &extensions); + + virtual int getDecodeCompletion() const { return m_completion; } + + virtual bool isUpdating() const { + return m_decodeThread && m_decodeThread->isRunning(); + } + +protected: + QString m_path; + QString m_error; + QString m_title; + + class D; + D *m_d; + + bool m_cancelled; + int m_completion; + + class DecodeThread : public Thread + { + public: + DecodeThread(QuickTimeFileReader *reader) : m_reader(reader) { } + virtual void run(); + + protected: + QuickTimeFileReader *m_reader; + }; + + DecodeThread *m_decodeThread; +}; + +#endif + +#endif