changeset 281:9c447d664275

* Add QuickTime file reader class -- totally untested, shouldn't even compile
author Chris Cannam
date Mon, 06 Aug 2007 14:37:59 +0000
parents daf89d31f45c
children e2fdcf9d35c5
files data/data.pro data/fileio/AudioFileReaderFactory.cpp data/fileio/OggVorbisFileReader.cpp data/fileio/QuickTimeFileReader.cpp data/fileio/QuickTimeFileReader.h
diffstat 5 files changed, 460 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- 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 \
--- 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 <QString>
 #include <QFileInfo>
@@ -37,6 +38,9 @@
     OggVorbisFileReader::getSupportedExtensions(extensions);
 #endif
 #endif
+#ifdef HAVE_QUICKTIME
+    QuickTimeFileReader::getSupportedExtensions(extensions);
+#endif
 
     QString rv;
     for (std::set<QString>::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;
 }
 
--- 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 {
 
--- /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 <QApplication>
+#include <QFileInfo>
+#include <QProgressDialog>
+
+#ifdef WIN32
+#include <QTML.h>
+#include <Movies.h>
+#else
+#include <QuickTime/QuickTime.h>
+#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<QString> &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
--- /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 <set>
+
+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<QString> &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