svcore  1.9
CoreAudioFileReader.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006-2012 Chris Cannam and QMUL.
00008 
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014  */
00015 
00016 #ifdef HAVE_COREAUDIO
00017 
00018 #include "CoreAudioFileReader.h"
00019 #include "base/Profiler.h"
00020 #include "base/ProgressReporter.h"
00021 #include "system/System.h"
00022 
00023 #include <QFileInfo>
00024 
00025 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
00026 #include <AudioToolbox/AudioToolbox.h>
00027 #include <AudioToolbox/ExtendedAudioFile.h>
00028 #else
00029 #include "AudioToolbox.h"
00030 #include "ExtendedAudioFile.h"
00031 #endif
00032 
00033 class CoreAudioFileReader::D
00034 {
00035 public:
00036     D() : blockSize(1024), valid(false) { }
00037 
00038     ExtAudioFileRef file;
00039     AudioBufferList buffer;
00040     OSStatus err;
00041     AudioStreamBasicDescription asbd;
00042     int blockSize;
00043     bool valid;
00044 };
00045 
00046 static QString
00047 codestr(OSStatus err)
00048 {
00049     char text[5];
00050     UInt32 uerr = err;
00051     text[0] = (uerr >> 24) & 0xff;
00052     text[1] = (uerr >> 16) & 0xff;
00053     text[2] = (uerr >> 8) & 0xff;
00054     text[3] = (uerr) & 0xff;
00055     text[4] = '\0';
00056     return QString("%1 (%2)").arg(err).arg(QString::fromLocal8Bit(text));
00057 }
00058 
00059 CoreAudioFileReader::CoreAudioFileReader(FileSource source,
00060                                          DecodeMode decodeMode,
00061                                          CacheMode mode,
00062                                          int targetRate,
00063                                          bool normalised,
00064                                          ProgressReporter *reporter) :
00065     CodedAudioFileReader(mode, targetRate, normalised),
00066     m_source(source),
00067     m_path(source.getLocalFilename()),
00068     m_d(new D),
00069     m_reporter(reporter),
00070     m_cancelled(false),
00071     m_completion(0),
00072     m_decodeThread(0)
00073 {
00074     m_channelCount = 0;
00075     m_fileRate = 0;
00076 
00077     m_d->buffer.mBuffers[0].mData = 0;
00078 
00079     Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true);
00080 
00081     SVDEBUG << "CoreAudioFileReader: path is \"" << m_path << "\"" << endl;
00082 
00083     QByteArray ba = m_path.toLocal8Bit();
00084 
00085     CFURLRef url = CFURLCreateFromFileSystemRepresentation
00086         (kCFAllocatorDefault,
00087          (const UInt8 *)ba.data(),
00088          (CFIndex)ba.length(),
00089          false);
00090 
00092 
00093 //#if (MACOSX_DEPLOYMENT_TARGET <= 1040 && MAC_OS_X_VERSION_MIN_REQUIRED <= 1040)
00094 //    FSRef fsref;
00095 //    if (!CFURLGetFSRef(url, &fsref)) { // returns Boolean, not error code
00096 //        m_error = "CoreAudioReadStream: Error looking up FS ref (file not found?)";
00097 //        return;
00098 //    }
00099 //    m_d->err = ExtAudioFileOpen(&fsref, &m_d->file);
00100 //#else
00101     m_d->err = ExtAudioFileOpenURL(url, &m_d->file);
00102 //#endif
00103 
00104     CFRelease(url);
00105 
00106     if (m_d->err) { 
00107         m_error = "CoreAudioReadStream: Error opening file: code " + codestr(m_d->err);
00108         return;
00109     }
00110     if (!m_d->file) { 
00111         m_error = "CoreAudioReadStream: Failed to open file, but no error reported!";
00112         return;
00113     }
00114     
00115     UInt32 propsize = sizeof(AudioStreamBasicDescription);
00116     m_d->err = ExtAudioFileGetProperty
00117         (m_d->file, kExtAudioFileProperty_FileDataFormat, &propsize, &m_d->asbd);
00118     
00119     if (m_d->err) {
00120         m_error = "CoreAudioReadStream: Error in getting basic description: code " + codestr(m_d->err);
00121         ExtAudioFileDispose(m_d->file);
00122         return;
00123     }
00124         
00125     m_channelCount = m_d->asbd.mChannelsPerFrame;
00126     m_fileRate = m_d->asbd.mSampleRate;
00127 
00128     cerr << "CoreAudioReadStream: " << m_channelCount << " channels, " << m_fileRate << " Hz" << endl;
00129 
00130     m_d->asbd.mFormatID = kAudioFormatLinearPCM;
00131     m_d->asbd.mFormatFlags =
00132         kAudioFormatFlagIsFloat |
00133         kAudioFormatFlagIsPacked |
00134         kAudioFormatFlagsNativeEndian;
00135     m_d->asbd.mBitsPerChannel = sizeof(float) * 8;
00136     m_d->asbd.mBytesPerFrame = sizeof(float) * m_channelCount;
00137     m_d->asbd.mBytesPerPacket = sizeof(float) * m_channelCount;
00138     m_d->asbd.mFramesPerPacket = 1;
00139     m_d->asbd.mReserved = 0;
00140         
00141     m_d->err = ExtAudioFileSetProperty
00142         (m_d->file, kExtAudioFileProperty_ClientDataFormat, propsize, &m_d->asbd);
00143     
00144     if (m_d->err) {
00145         m_error = "CoreAudioReadStream: Error in setting client format: code " + codestr(m_d->err);
00146         ExtAudioFileDispose(m_d->file);
00147         return;
00148     }
00149 
00150     m_d->buffer.mNumberBuffers = 1;
00151     m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount;
00152     m_d->buffer.mBuffers[0].mDataByteSize = sizeof(float) * m_channelCount * m_d->blockSize;
00153     m_d->buffer.mBuffers[0].mData = new float[m_channelCount * m_d->blockSize];
00154 
00155     m_d->valid = true;
00156 
00157     initialiseDecodeCache();
00158 
00159     if (m_reporter) {
00160         connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
00161         m_reporter->setMessage
00162             (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
00163     }
00164 
00165     while (1) {
00166 
00167         UInt32 framesRead = m_d->blockSize;
00168         m_d->err = ExtAudioFileRead(m_d->file, &framesRead, &m_d->buffer);
00169 
00170         if (m_d->err) {
00171             m_error = QString("Error in CoreAudio decoding: code %1")
00172                 .arg(m_d->err);
00173             break;
00174         }
00175 
00177 
00178         //    cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << endl;
00179 
00180         // buffers are interleaved unless specified otherwise
00181         addSamplesToDecodeCache((float *)m_d->buffer.mBuffers[0].mData, framesRead);
00182 
00183         if (framesRead < m_d->blockSize) break;
00184     }
00185 
00186     finishDecodeCache();
00187     endSerialised();
00188 
00189     m_completion = 100;
00190 }
00191 
00192 
00193 CoreAudioFileReader::~CoreAudioFileReader()
00194 {
00195     cerr << "CoreAudioFileReader::~CoreAudioFileReader" << endl;
00196 
00197     if (m_d->valid) {
00198         ExtAudioFileDispose(m_d->file);
00199         delete[] m_d->buffer.mBuffers[0].mData;
00200     }
00201 
00202     delete m_d;
00203 }
00204 
00205 void
00206 CoreAudioFileReader::cancelled()
00207 {
00208   m_cancelled = true;
00209 }
00210 
00211 void
00212 CoreAudioFileReader::getSupportedExtensions(std::set<QString> &extensions)
00213 {
00214     extensions.insert("aiff");
00215     extensions.insert("aif");
00216     extensions.insert("au");
00217     extensions.insert("m4a");
00218     extensions.insert("m4b");
00219     extensions.insert("m4p");
00220     extensions.insert("mp3");
00221     extensions.insert("mp4");
00222     extensions.insert("wav");
00223 }
00224 
00225 bool
00226 CoreAudioFileReader::supportsExtension(QString extension)
00227 {
00228     std::set<QString> extensions;
00229     getSupportedExtensions(extensions);
00230     return (extensions.find(extension.toLower()) != extensions.end());
00231 }
00232 
00233 bool
00234 CoreAudioFileReader::supportsContentType(QString type)
00235 {
00236     return (type == "audio/x-aiff" ||
00237             type == "audio/x-wav" ||
00238             type == "audio/mpeg" ||
00239             type == "audio/basic" ||
00240             type == "audio/x-aac");
00241 }
00242 
00243 bool
00244 CoreAudioFileReader::supports(FileSource &source)
00245 {
00246     return (supportsExtension(source.getExtension()) ||
00247             supportsContentType(source.getContentType()));
00248 }
00249 
00250 #endif
00251