Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/soundsourcecoreaudio.cpp

Go to the documentation of this file.
00001 
00007 /***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include <QtDebug>
00017 #include <QUrl>
00018 #include <taglib/mpegfile.h>
00019 #include <taglib/mp4file.h>
00020 
00021 #include "soundsourcecoreaudio.h"
00022 
00023 SoundSourceCoreAudio::SoundSourceCoreAudio(QString filename)
00024     : Mixxx::SoundSource(filename)
00025     , m_file(filename)
00026     , m_samples(0)
00027     , m_headerFrames(0)
00028 {
00029 }
00030 
00031 SoundSourceCoreAudio::~SoundSourceCoreAudio() {
00032         ExtAudioFileDispose(m_audioFile);
00033 
00034 }
00035 
00036 // soundsource overrides
00037 int SoundSourceCoreAudio::open() {
00038     //m_file.open(QIODevice::ReadOnly);
00039 
00040     //Open the audio file.
00041     OSStatus err;
00042 
00043         //QUrl blah(m_qFilename);
00044     QString qurlStr = m_qFilename;//blah.toString();
00045     qDebug() << qurlStr;
00046 
00048     CFStringRef urlStr = CFStringCreateWithCharacters(0,
00049                                 reinterpret_cast<const UniChar *>(
00050                 qurlStr.unicode()), qurlStr.size());
00051     CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, urlStr, kCFURLPOSIXPathStyle, false);
00052     err = ExtAudioFileOpenURL(urlRef, &m_audioFile);
00053     CFRelease(urlStr);
00054     CFRelease(urlRef);
00055 
00064         if (err != noErr)
00065         {
00066                 qDebug() << "SSCA: Error opening file.";
00067                 return ERR;
00068         }
00069 
00070     // get the input file format
00071     CAStreamBasicDescription inputFormat;
00072     UInt32 size = sizeof(inputFormat);
00073     m_inputFormat = inputFormat;
00074     err = ExtAudioFileGetProperty(m_audioFile, kExtAudioFileProperty_FileDataFormat, &size, &inputFormat);
00075         if (err != noErr)
00076         {
00077                 qDebug() << "SSCA: Error getting file format";
00078                 return ERR;
00079         }
00080 
00081     //Debugging:
00082     //printf ("Source File format: "); inputFormat.Print();
00083     //printf ("Dest File format: "); outputFormat.Print();
00084 
00085 
00086         // create the output format
00087         CAStreamBasicDescription outputFormat;
00088     bzero(&outputFormat, sizeof(AudioStreamBasicDescription));
00089         outputFormat.mFormatID = kAudioFormatLinearPCM;
00090         outputFormat.mSampleRate = inputFormat.mSampleRate;
00091         outputFormat.mChannelsPerFrame = 2;
00092         outputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger;
00093 
00094         /*
00095         switch(inputFormat.mBitsPerChannel) {
00096                 case 16:
00097                         outputFormat.mFormatFlags =  kAppleLosslessFormatFlag_16BitSourceData;
00098                         break;
00099                 case 20:
00100                         outputFormat.mFormatFlags =  kAppleLosslessFormatFlag_20BitSourceData;
00101                         break;
00102                 case 24:
00103                         outputFormat.mFormatFlags =  kAppleLosslessFormatFlag_24BitSourceData;
00104                         break;
00105                 case 32:
00106                         outputFormat.mFormatFlags =  kAppleLosslessFormatFlag_32BitSourceData;
00107                         break;
00108         }*/
00109 
00110     // get and set the client format - it should be lpcm
00111     CAStreamBasicDescription clientFormat = (inputFormat.mFormatID == kAudioFormatLinearPCM ? inputFormat : outputFormat);
00112         clientFormat.mBytesPerPacket = 4;
00113         clientFormat.mFramesPerPacket = 1;
00114         clientFormat.mBytesPerFrame = 4;
00115         clientFormat.mChannelsPerFrame = 2;
00116         clientFormat.mBitsPerChannel = 16;
00117         clientFormat.mReserved = 0;
00118         m_clientFormat = clientFormat;
00119     size = sizeof(clientFormat);
00120 
00121     err = ExtAudioFileSetProperty(m_audioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
00122         if (err != noErr)
00123         {
00124                 qDebug() << "SSCA: Error setting file property";
00125                 return ERR;
00126         }
00127 
00128         //Set m_iChannels and m_samples;
00129         m_iChannels = clientFormat.NumberChannels();
00130 
00131         //get the total length in frames of the audio file - copypasta: http://discussions.apple.com/thread.jspa?threadID=2364583&tstart=47
00132         UInt32          dataSize;
00133         SInt64          totalFrameCount;
00134         dataSize        = sizeof(totalFrameCount); //XXX: This looks sketchy to me - Albert
00135         err                     = ExtAudioFileGetProperty(m_audioFile, kExtAudioFileProperty_FileLengthFrames, &dataSize, &totalFrameCount);
00136         if (err != noErr)
00137         {
00138                 qDebug() << "SSCA: Error getting number of frames";
00139                 return ERR;
00140         }
00141 
00142       //
00143       // WORKAROUND for bug in ExtFileAudio
00144       //
00145 
00146       AudioConverterRef acRef;
00147       UInt32 acrsize=sizeof(AudioConverterRef);
00148       err = ExtAudioFileGetProperty(m_audioFile, kExtAudioFileProperty_AudioConverter, &acrsize, &acRef);
00149       //_ThrowExceptionIfErr(@"kExtAudioFileProperty_AudioConverter", err);
00150 
00151       AudioConverterPrimeInfo primeInfo;
00152       UInt32 piSize=sizeof(AudioConverterPrimeInfo);
00153       memset(&primeInfo, 0, piSize);
00154       err = AudioConverterGetProperty(acRef, kAudioConverterPrimeInfo, &piSize, &primeInfo);
00155       if(err != kAudioConverterErr_PropertyNotSupported) // Only if decompressing
00156       {
00157          //_ThrowExceptionIfErr(@"kAudioConverterPrimeInfo", err);
00158 
00159          m_headerFrames=primeInfo.leadingFrames;
00160       }
00161 
00162         m_samples = (totalFrameCount/*-m_headerFrames*/)*m_iChannels;
00163         m_iDuration = m_samples / (inputFormat.mSampleRate * m_iChannels);
00164         m_iSampleRate = inputFormat.mSampleRate;
00165         qDebug() << m_samples << totalFrameCount << m_iChannels;
00166 
00167         //Seek to position 0, which forces us to skip over all the header frames.
00168         //This makes sure we're ready to just let the Analyser rip and it'll
00169         //get the number of samples it expects (ie. no header frames).
00170         seek(0);
00171 
00172     return OK;
00173 }
00174 
00175 long SoundSourceCoreAudio::seek(long filepos) {
00176     // important division here, filepos is in audio samples (i.e. shorts)
00177     // but libflac expects a number in time samples. I _think_ this should
00178     // be hard-coded at two because *2 is the assumption the caller makes
00179     // -- bkgood
00180     OSStatus err = noErr;
00181     SInt64 segmentStart = filepos / 2;
00182 
00183       err = ExtAudioFileSeek(m_audioFile, (SInt64)segmentStart+m_headerFrames);
00184       //_ThrowExceptionIfErr(@"ExtAudioFileSeek", err);
00185         //qDebug() << "SSCA: Seeking to" << segmentStart;
00186 
00187         //err = ExtAudioFileSeek(m_audioFile, filepos / 2);
00188         if (err != noErr)
00189         {
00190                 qDebug() << "SSCA: Error seeking to" << filepos;// << GetMacOSStatusErrorString(err) << GetMacOSStatusCommentString(err);
00191         }
00192     return filepos;
00193 }
00194 
00195 unsigned int SoundSourceCoreAudio::read(unsigned long size, const SAMPLE *destination) {
00196     //if (!m_decoder) return 0;
00197     OSStatus err;
00198     SAMPLE *destBuffer(const_cast<SAMPLE*>(destination));
00199     unsigned int samplesWritten = 0;
00200     unsigned int i = 0;
00201     UInt32 numFrames = 0;//(size / 2); /// m_inputFormat.mBytesPerFrame);
00202     unsigned int totalFramesToRead = size/2;
00203     unsigned int numFramesRead = 0;
00204     unsigned int numFramesToRead = totalFramesToRead;
00205 
00206     while (numFramesRead < totalFramesToRead) { //FIXME: Hardcoded 2
00207         numFramesToRead = totalFramesToRead - numFramesRead;
00208 
00209                 AudioBufferList fillBufList;
00210                 fillBufList.mNumberBuffers = 1; //Decode a single track?
00211                 fillBufList.mBuffers[0].mNumberChannels = m_inputFormat.mChannelsPerFrame;
00212                 fillBufList.mBuffers[0].mDataByteSize = math_min(1024, numFramesToRead*4);//numFramesToRead*sizeof(*destBuffer); // 2 = num bytes per SAMPLE
00213                 fillBufList.mBuffers[0].mData = (void*)(&destBuffer[numFramesRead*2]);
00214 
00215                         // client format is always linear PCM - so here we determine how many frames of lpcm
00216                         // we can read/write given our buffer size
00217                 numFrames = numFramesToRead; //This silly variable acts as both a parameter and return value.
00218                 err = ExtAudioFileRead (m_audioFile, &numFrames, &fillBufList);
00219                 //The actual number of frames read also comes back in numFrames.
00220                 //(It's both a parameter to a function and a return value. wat apple?)
00221                 //XThrowIfError (err, "ExtAudioFileRead");
00222                 if (!numFrames) {
00223                                 // this is our termination condition
00224                         break;
00225                 }
00226                 numFramesRead += numFrames;
00227     }
00228     return numFramesRead*2;
00229 }
00230 
00231 inline unsigned long SoundSourceCoreAudio::length() {
00232     return m_samples;
00233 }
00234 
00235 int SoundSourceCoreAudio::parseHeader() {
00236     if (getFilename().endsWith(".m4a"))
00237         setType("m4a");
00238     else if (getFilename().endsWith(".mp3"))
00239         setType("mp3");
00240     else if (getFilename().endsWith(".mp2"))
00241         setType("mp2");
00242 
00243     bool result = false;
00244 
00245     if (getType() == "m4a") {
00246         // No need for toLocal8Bit on Windows since CoreAudio is OS X only.
00247         TagLib::MP4::File f(getFilename().toUtf8().constData());
00248         result = processTaglibFile(f);
00249         TagLib::MP4::Tag* tag = f.tag();
00250         if (tag) {
00251             processMP4Tag(tag);
00252         }
00253     } else if (getType() == "mp3") {
00254         // No need for toLocal8Bit on Windows since CoreAudio is OS X only.
00255         TagLib::MPEG::File f(getFilename().toUtf8().constData());
00256 
00257         // Takes care of all the default metadata
00258         result = processTaglibFile(f);
00259 
00260         // Now look for MP3 specific metadata (e.g. BPM)
00261         TagLib::ID3v2::Tag* id3v2 = f.ID3v2Tag();
00262         if (id3v2) {
00263             processID3v2Tag(id3v2);
00264         }
00265 
00266         TagLib::APE::Tag *ape = f.APETag();
00267         if (ape) {
00268             processAPETag(ape);
00269         }
00270     } else if (getType() == "mp2") {
00271         //TODO: MP2 metadata. Does anyone use mp2 files anymore?
00272         //      Feels like 1995 again...
00273     }
00274 
00275 
00276     if (result)
00277         return OK;
00278     return ERR;
00279 }
00280 
00281 
00282 // static
00283 QList<QString> SoundSourceCoreAudio::supportedFileExtensions() {
00284     QList<QString> list;
00285     list.push_back("m4a");
00286     list.push_back("mp3");
00287     list.push_back("mp2");
00288     //Can add mp3, mp2, ac3, and others here if you want.
00289     //See:
00290     //  http://developer.apple.com/library/mac/documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.html#//apple_ref/doc/c_ref/AudioFileTypeID
00291 
00292     //XXX: ... but make sure you implement handling for any new format in ParseHeader!!!!!! -- asantoni
00293     return list;
00294 }
00295 
00296 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines