luisf@665: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ luisf@665: luisf@665: /* luisf@665: Sonic Visualiser luisf@665: An audio file viewer and annotation editor. luisf@665: Centre for Digital Music, Queen Mary, University of London. luisf@665: This file copyright 2006-2007 Chris Cannam and QMUL. luisf@665: luisf@665: Based on QTAudioFile.cpp from SoundBite, copyright 2006 luisf@665: Chris Sutton and Mark Levy. luisf@665: luisf@665: This program is free software; you can redistribute it and/or luisf@665: modify it under the terms of the GNU General Public License as luisf@665: published by the Free Software Foundation; either version 2 of the luisf@665: License, or (at your option) any later version. See the file luisf@665: COPYING included with this distribution for more information. luisf@665: */ luisf@665: luisf@665: #ifdef HAVE_COREAUDIO luisf@665: luisf@665: #include "CoreAudioFileReader.h" luisf@665: #include "base/Profiler.h" luisf@665: #include "base/ProgressReporter.h" luisf@665: #include "system/System.h" luisf@665: luisf@665: #include luisf@665: luisf@665: luisf@665: // TODO: implement for windows luisf@665: #ifdef _WIN32 luisf@665: #include luisf@665: #include luisf@665: #else luisf@665: luisf@665: luisf@665: #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) luisf@665: #include luisf@665: #else luisf@665: #include "AudioToolbox.h" luisf@665: #include "ExtendedAudioFile.h" luisf@665: #endif luisf@665: luisf@665: #include "CAStreamBasicDescription.h" luisf@665: #include "CAXException.h" luisf@665: luisf@665: luisf@665: luisf@665: luisf@665: #endif luisf@665: luisf@665: class CoreAudioFileReader::D luisf@665: { luisf@665: public: luisf@665: D() : data(0), blockSize(1024) { } luisf@665: luisf@665: AudioBufferList buffer; luisf@665: float *data; luisf@665: OSErr err; luisf@665: AudioStreamBasicDescription asbd; luisf@665: // file reference luisf@665: luisf@665: ExtAudioFileRef *infile; luisf@665: size_t blockSize; luisf@665: }; luisf@665: luisf@665: luisf@665: CoreAudioFileReader::CoreAudioFileReader(FileSource source, luisf@665: DecodeMode decodeMode, luisf@665: CacheMode mode, luisf@665: size_t targetRate, luisf@665: ProgressReporter *reporter) : luisf@665: CodedAudioFileReader(mode, targetRate), luisf@665: m_source(source), luisf@665: m_path(source.getLocalFilename()), luisf@665: m_d(new D), luisf@665: m_reporter(reporter), luisf@665: m_cancelled(false), luisf@665: m_completion(0), luisf@665: m_decodeThread(0) luisf@665: { luisf@665: m_channelCount = 0; luisf@665: m_fileRate = 0; luisf@665: luisf@665: Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true); luisf@665: luisf@665: std::cerr << "CoreAudioFileReader: path is \"" << m_path.toStdString() << "\"" << std::endl; luisf@665: luisf@665: // TODO: check QT version luisf@665: luisf@665: Handle dataRef; luisf@665: OSType dataRefType; luisf@665: luisf@665: // CFStringRef URLString = CFStringCreateWithCString luisf@665: // (0, m_path.toLocal8Bit().data(), 0); luisf@665: luisf@665: // what are these used for??? luisf@665: // ExtAudioFileRef *infile, outfile; luisf@665: luisf@665: // Creates a new CFURL object for a file system entity using the native representation. luisf@665: CFURLRef url = CFURLCreateFromFileSystemRepresentation luisf@665: (kCFAllocatorDefault, luisf@665: (const UInt8 *)m_path.toLocal8Bit().data(), luisf@665: (CFIndex)m_path.length(), luisf@665: false); luisf@665: luisf@665: // first open the input file luisf@665: m_d->err = ExtAudioFileOpenURL (url, m_d->infile); luisf@665: luisf@665: if (m_d->err) { luisf@665: m_error = QString("Error opening Audio File for CoreAudio decoder: code %1").arg(m_d->err); luisf@665: return; luisf@665: } luisf@665: else { luisf@665: std::cerr << "CoreAudio: opened URL" << std::endl; luisf@665: } luisf@665: luisf@665: luisf@665: // Get the audio data format luisf@665: // the audio format gets stored in the &m_d->asbd luisf@665: UInt32 thePropertySize = sizeof(m_d->asbd); luisf@665: luisf@665: luisf@665: std::cerr << "CoreAudio: thePropertySize: " << thePropertySize << std::endl; luisf@665: luisf@665: m_d->err = ExtAudioFileGetProperty(*m_d->infile, kAudioFilePropertyDataFormat, &thePropertySize, &m_d->asbd); luisf@665: luisf@665: std::cerr << "CoreAudio: ExtAudioFileGetProperty res: " << &m_d->asbd << std::endl; luisf@665: luisf@665: // CAStreamBasicDescription clientFormat = (inputFormat.mFormatID == kAudioFormatLinearPCM ? inputFormat : outputFormat); luisf@665: // UInt32 size = sizeof(clientFormat); luisf@665: luisf@665: // TODO: test input file's DRM rights luisf@665: luisf@665: // are these already set? luisf@665: m_channelCount = m_d->asbd.mChannelsPerFrame; luisf@665: m_fileRate = m_d->asbd.mSampleRate; luisf@665: luisf@665: std::cerr << "CoreAudio: Format ID: " << m_d->asbd.mFormatID << std::endl; luisf@665: std::cerr << "CoreAudio: " << m_d->asbd.mChannelsPerFrame << " channels, " << m_fileRate << " kHz" << std::endl; luisf@665: luisf@665: luisf@665: luisf@665: m_d->asbd.mFormatFlags = luisf@665: kAudioFormatFlagIsFloat | luisf@665: kAudioFormatFlagIsPacked | luisf@665: kAudioFormatFlagsNativeEndian; luisf@665: m_d->asbd.mBitsPerChannel = sizeof(float) * 8; luisf@665: m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame; luisf@665: m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame; luisf@665: luisf@665: luisf@665: luisf@665: /* luisf@665: luisf@665: !!! what does this do exactly ????? luisf@665: luisf@665: m_d->err = MovieAudioExtractionSetProperty luisf@665: (m_d->extractionSessionRef, luisf@665: kQTPropertyClass_MovieAudioExtraction_Audio, luisf@665: kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, luisf@665: sizeof(m_d->asbd), luisf@665: &m_d->asbd); luisf@665: luisf@665: luisf@665: luisf@665: if (m_d->err) { luisf@665: m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err); luisf@665: m_channelCount = 0; luisf@665: return; luisf@665: } luisf@665: luisf@665: */ luisf@665: luisf@665: m_d->buffer.mNumberBuffers = 1; luisf@665: m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount; luisf@665: m_d->buffer.mBuffers[0].mDataByteSize = luisf@665: sizeof(float) * m_channelCount * m_d->blockSize; luisf@665: m_d->data = new float[m_channelCount * m_d->blockSize]; luisf@665: m_d->buffer.mBuffers[0].mData = m_d->data; luisf@665: luisf@665: initialiseDecodeCache(); luisf@665: luisf@665: // only decode at once for now luisf@665: // if (decodeMode == DecodeAtOnce) { luisf@665: luisf@665: if (m_reporter) { luisf@665: connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); luisf@665: m_reporter->setMessage luisf@665: (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); luisf@665: } luisf@665: luisf@665: while (1) { luisf@665: luisf@665: UInt32 framesRead = m_d->blockSize; luisf@665: luisf@665: m_d->err = ExtAudioFileRead (*m_d->infile, &framesRead, &m_d->buffer); luisf@665: luisf@665: if (m_d->err) { luisf@665: m_error = QString("Error in CoreAudio decoding: code %1") luisf@665: .arg(m_d->err); luisf@665: break; luisf@665: } luisf@665: luisf@665: //!!! progress? luisf@665: luisf@665: // std::cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << std::endl; luisf@665: luisf@665: // QuickTime buffers are interleaved unless specified otherwise luisf@665: addSamplesToDecodeCache(m_d->data, framesRead); luisf@665: luisf@665: if (framesRead < m_d->blockSize) break; luisf@665: } luisf@665: luisf@665: finishDecodeCache(); luisf@665: endSerialised(); luisf@665: luisf@665: /* luisf@665: TODO - close session luisf@665: m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef); luisf@665: if (m_d->err) { luisf@665: m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err); luisf@665: } luisf@665: */ luisf@665: m_completion = 100; luisf@665: luisf@665: luisf@665: luisf@665: // } else { luisf@665: // if (m_reporter) m_reporter->setProgress(100); luisf@665: // luisf@665: // if (m_channelCount > 0) { luisf@665: // m_decodeThread = new DecodeThread(this); luisf@665: // m_decodeThread->start(); luisf@665: // } luisf@665: // } luisf@665: luisf@665: std::cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error.toStdString() << "\"" << std::endl; luisf@665: } luisf@665: luisf@665: luisf@665: luisf@665: luisf@665: luisf@665: CoreAudioFileReader::~CoreAudioFileReader() luisf@665: { luisf@665: std::cerr << "CoreAudioFileReader::~CoreAudioFileReader" << std::endl; luisf@665: luisf@665: if (m_decodeThread) { luisf@665: m_cancelled = true; luisf@665: m_decodeThread->wait(); luisf@665: delete m_decodeThread; luisf@665: } luisf@665: luisf@665: // SetMovieActive(m_d->movie, FALSE); luisf@665: // DisposeMovie(m_d->movie); luisf@665: luisf@665: delete[] m_d->data; luisf@665: delete m_d; luisf@665: } luisf@665: luisf@665: void luisf@665: CoreAudioFileReader::cancelled() luisf@665: { luisf@665: m_cancelled = true; luisf@665: } luisf@665: luisf@665: /* luisf@665: void luisf@665: CoreAudioFileReader::DecodeThread::run() luisf@665: { luisf@665: if (m_reader->m_cacheMode == CacheInTemporaryFile) { luisf@665: m_reader->m_completion = 1; luisf@665: m_reader->startSerialised("QuickTimeFileReader::Decode"); luisf@665: } luisf@665: luisf@665: while (1) { luisf@665: luisf@665: UInt32 framesRead = m_reader->m_d->blockSize; luisf@665: UInt32 extractionFlags = 0; luisf@665: m_reader->m_d->err = MovieAudioExtractionFillBuffer luisf@665: (m_reader->m_d->extractionSessionRef, &framesRead, luisf@665: &m_reader->m_d->buffer, &extractionFlags); luisf@665: if (m_reader->m_d->err) { luisf@665: m_reader->m_error = QString("Error in QuickTime decoding: code %1") luisf@665: .arg(m_reader->m_d->err); luisf@665: break; luisf@665: } luisf@665: luisf@665: // QuickTime buffers are interleaved unless specified otherwise luisf@665: m_reader->addSamplesToDecodeCache(m_reader->m_d->data, framesRead); luisf@665: luisf@665: if (framesRead < m_reader->m_d->blockSize) break; luisf@665: } luisf@665: luisf@665: m_reader->finishDecodeCache(); luisf@665: luisf@665: m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef); luisf@665: if (m_reader->m_d->err) { luisf@665: m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err); luisf@665: } luisf@665: luisf@665: m_reader->m_completion = 100; luisf@665: m_reader->endSerialised(); luisf@665: } luisf@665: */ luisf@665: luisf@665: luisf@665: void luisf@665: CoreAudioFileReader::getSupportedExtensions(std::set &extensions) luisf@665: { luisf@665: extensions.insert("aiff"); luisf@665: extensions.insert("aif"); luisf@665: extensions.insert("au"); luisf@665: extensions.insert("avi"); luisf@665: extensions.insert("m4a"); luisf@665: extensions.insert("m4b"); luisf@665: extensions.insert("m4p"); luisf@665: extensions.insert("m4v"); luisf@665: extensions.insert("mov"); luisf@665: extensions.insert("mp3"); luisf@665: extensions.insert("mp4"); luisf@665: extensions.insert("wav"); luisf@665: } luisf@665: luisf@665: bool luisf@665: CoreAudioFileReader::supportsExtension(QString extension) luisf@665: { luisf@665: std::set extensions; luisf@665: getSupportedExtensions(extensions); luisf@665: return (extensions.find(extension.toLower()) != extensions.end()); luisf@665: } luisf@665: luisf@665: bool luisf@665: CoreAudioFileReader::supportsContentType(QString type) luisf@665: { luisf@665: return (type == "audio/x-aiff" || luisf@665: type == "audio/x-wav" || luisf@665: type == "audio/mpeg" || luisf@665: type == "audio/basic" || luisf@665: type == "audio/x-aac" || luisf@665: type == "video/mp4" || luisf@665: type == "video/quicktime"); luisf@665: } luisf@665: luisf@665: bool luisf@665: CoreAudioFileReader::supports(FileSource &source) luisf@665: { luisf@665: return (supportsExtension(source.getExtension()) || luisf@665: supportsContentType(source.getContentType())); luisf@665: } luisf@665: luisf@665: #endif luisf@665: