annotate data/fileio/CoreAudioFileReader.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents d03b3d956358
children 01a90c025eaa a1cd5abcb38b
rev   line source
luisf@665 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
luisf@665 2
luisf@665 3 /*
luisf@665 4 Sonic Visualiser
luisf@665 5 An audio file viewer and annotation editor.
luisf@665 6 Centre for Digital Music, Queen Mary, University of London.
Chris@748 7 This file copyright 2006-2012 Chris Cannam and QMUL.
luisf@665 8
luisf@665 9 This program is free software; you can redistribute it and/or
luisf@665 10 modify it under the terms of the GNU General Public License as
luisf@665 11 published by the Free Software Foundation; either version 2 of the
luisf@665 12 License, or (at your option) any later version. See the file
luisf@665 13 COPYING included with this distribution for more information.
luisf@665 14 */
luisf@665 15
luisf@665 16 #ifdef HAVE_COREAUDIO
luisf@665 17
luisf@665 18 #include "CoreAudioFileReader.h"
luisf@665 19 #include "base/Profiler.h"
luisf@665 20 #include "base/ProgressReporter.h"
luisf@665 21 #include "system/System.h"
luisf@665 22
luisf@665 23 #include <QFileInfo>
luisf@665 24
luisf@665 25 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
luisf@665 26 #include <AudioToolbox/AudioToolbox.h>
Chris@743 27 #include <AudioToolbox/ExtendedAudioFile.h>
luisf@665 28 #else
luisf@665 29 #include "AudioToolbox.h"
luisf@665 30 #include "ExtendedAudioFile.h"
luisf@665 31 #endif
luisf@665 32
luisf@665 33 class CoreAudioFileReader::D
luisf@665 34 {
luisf@665 35 public:
Chris@819 36 D() : blockSize(1024), valid(false) { }
luisf@665 37
Chris@743 38 ExtAudioFileRef file;
Chris@743 39 AudioBufferList buffer;
Chris@743 40 OSStatus err;
Chris@743 41 AudioStreamBasicDescription asbd;
Chris@743 42 int blockSize;
Chris@819 43 bool valid;
luisf@665 44 };
luisf@665 45
Chris@743 46 static QString
Chris@743 47 codestr(OSStatus err)
Chris@743 48 {
Chris@743 49 char text[5];
Chris@743 50 UInt32 uerr = err;
Chris@743 51 text[0] = (uerr >> 24) & 0xff;
Chris@743 52 text[1] = (uerr >> 16) & 0xff;
Chris@743 53 text[2] = (uerr >> 8) & 0xff;
Chris@743 54 text[3] = (uerr) & 0xff;
Chris@743 55 text[4] = '\0';
Chris@794 56 return QString("%1 (%2)").arg(err).arg(QString::fromLocal8Bit(text));
Chris@743 57 }
luisf@665 58
luisf@665 59 CoreAudioFileReader::CoreAudioFileReader(FileSource source,
Chris@743 60 DecodeMode decodeMode,
Chris@743 61 CacheMode mode,
Chris@929 62 int targetRate,
Chris@920 63 bool normalised,
Chris@743 64 ProgressReporter *reporter) :
Chris@920 65 CodedAudioFileReader(mode, targetRate, normalised),
luisf@665 66 m_source(source),
luisf@665 67 m_path(source.getLocalFilename()),
luisf@665 68 m_d(new D),
luisf@665 69 m_reporter(reporter),
luisf@665 70 m_cancelled(false),
luisf@665 71 m_completion(0),
luisf@665 72 m_decodeThread(0)
luisf@665 73 {
Chris@743 74 m_channelCount = 0;
Chris@743 75 m_fileRate = 0;
luisf@665 76
Chris@760 77 m_d->buffer.mBuffers[0].mData = 0;
Chris@760 78
Chris@743 79 Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true);
luisf@665 80
Chris@844 81 SVDEBUG << "CoreAudioFileReader: path is \"" << m_path << "\"" << endl;
luisf@665 82
Chris@743 83 QByteArray ba = m_path.toLocal8Bit();
luisf@665 84
Chris@743 85 CFURLRef url = CFURLCreateFromFileSystemRepresentation
Chris@743 86 (kCFAllocatorDefault,
Chris@743 87 (const UInt8 *)ba.data(),
Chris@743 88 (CFIndex)ba.length(),
Chris@743 89 false);
luisf@665 90
Chris@743 91 //!!! how do we find out if the file open fails because of DRM protection?
luisf@665 92
matthiasm@773 93 //#if (MACOSX_DEPLOYMENT_TARGET <= 1040 && MAC_OS_X_VERSION_MIN_REQUIRED <= 1040)
matthiasm@773 94 // FSRef fsref;
matthiasm@773 95 // if (!CFURLGetFSRef(url, &fsref)) { // returns Boolean, not error code
matthiasm@773 96 // m_error = "CoreAudioReadStream: Error looking up FS ref (file not found?)";
matthiasm@773 97 // return;
matthiasm@773 98 // }
matthiasm@773 99 // m_d->err = ExtAudioFileOpen(&fsref, &m_d->file);
matthiasm@773 100 //#else
Chris@743 101 m_d->err = ExtAudioFileOpenURL(url, &m_d->file);
matthiasm@773 102 //#endif
luisf@665 103
Chris@743 104 CFRelease(url);
luisf@665 105
Chris@743 106 if (m_d->err) {
Chris@743 107 m_error = "CoreAudioReadStream: Error opening file: code " + codestr(m_d->err);
Chris@743 108 return;
Chris@743 109 }
Chris@743 110 if (!m_d->file) {
Chris@743 111 m_error = "CoreAudioReadStream: Failed to open file, but no error reported!";
Chris@743 112 return;
Chris@743 113 }
Chris@743 114
Chris@743 115 UInt32 propsize = sizeof(AudioStreamBasicDescription);
Chris@743 116 m_d->err = ExtAudioFileGetProperty
Chris@743 117 (m_d->file, kExtAudioFileProperty_FileDataFormat, &propsize, &m_d->asbd);
Chris@743 118
Chris@743 119 if (m_d->err) {
Chris@743 120 m_error = "CoreAudioReadStream: Error in getting basic description: code " + codestr(m_d->err);
Chris@819 121 ExtAudioFileDispose(m_d->file);
Chris@743 122 return;
Chris@743 123 }
Chris@743 124
Chris@743 125 m_channelCount = m_d->asbd.mChannelsPerFrame;
Chris@755 126 m_fileRate = m_d->asbd.mSampleRate;
luisf@665 127
Chris@843 128 cerr << "CoreAudioReadStream: " << m_channelCount << " channels, " << m_fileRate << " Hz" << endl;
luisf@665 129
Chris@743 130 m_d->asbd.mFormatID = kAudioFormatLinearPCM;
Chris@743 131 m_d->asbd.mFormatFlags =
Chris@743 132 kAudioFormatFlagIsFloat |
Chris@743 133 kAudioFormatFlagIsPacked |
Chris@743 134 kAudioFormatFlagsNativeEndian;
Chris@743 135 m_d->asbd.mBitsPerChannel = sizeof(float) * 8;
Chris@743 136 m_d->asbd.mBytesPerFrame = sizeof(float) * m_channelCount;
Chris@743 137 m_d->asbd.mBytesPerPacket = sizeof(float) * m_channelCount;
Chris@743 138 m_d->asbd.mFramesPerPacket = 1;
Chris@743 139 m_d->asbd.mReserved = 0;
Chris@743 140
Chris@743 141 m_d->err = ExtAudioFileSetProperty
Chris@743 142 (m_d->file, kExtAudioFileProperty_ClientDataFormat, propsize, &m_d->asbd);
Chris@743 143
Chris@743 144 if (m_d->err) {
Chris@743 145 m_error = "CoreAudioReadStream: Error in setting client format: code " + codestr(m_d->err);
Chris@819 146 ExtAudioFileDispose(m_d->file);
Chris@743 147 return;
Chris@743 148 }
luisf@665 149
Chris@743 150 m_d->buffer.mNumberBuffers = 1;
Chris@743 151 m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount;
Chris@743 152 m_d->buffer.mBuffers[0].mDataByteSize = sizeof(float) * m_channelCount * m_d->blockSize;
Chris@743 153 m_d->buffer.mBuffers[0].mData = new float[m_channelCount * m_d->blockSize];
luisf@665 154
Chris@819 155 m_d->valid = true;
Chris@819 156
Chris@743 157 initialiseDecodeCache();
luisf@665 158
Chris@743 159 if (m_reporter) {
Chris@743 160 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
Chris@743 161 m_reporter->setMessage
Chris@743 162 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
Chris@743 163 }
luisf@665 164
Chris@743 165 while (1) {
luisf@665 166
Chris@743 167 UInt32 framesRead = m_d->blockSize;
Chris@743 168 m_d->err = ExtAudioFileRead(m_d->file, &framesRead, &m_d->buffer);
luisf@665 169
Chris@743 170 if (m_d->err) {
Chris@743 171 m_error = QString("Error in CoreAudio decoding: code %1")
Chris@743 172 .arg(m_d->err);
Chris@743 173 break;
Chris@743 174 }
luisf@665 175
Chris@743 176 //!!! progress?
luisf@665 177
Chris@843 178 // cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << endl;
luisf@665 179
Chris@743 180 // buffers are interleaved unless specified otherwise
Chris@743 181 addSamplesToDecodeCache((float *)m_d->buffer.mBuffers[0].mData, framesRead);
luisf@665 182
Chris@743 183 if (framesRead < m_d->blockSize) break;
Chris@743 184 }
luisf@665 185
Chris@743 186 finishDecodeCache();
Chris@743 187 endSerialised();
luisf@665 188
Chris@743 189 m_completion = 100;
luisf@665 190 }
luisf@665 191
luisf@665 192
luisf@665 193 CoreAudioFileReader::~CoreAudioFileReader()
luisf@665 194 {
Chris@843 195 cerr << "CoreAudioFileReader::~CoreAudioFileReader" << endl;
Chris@819 196
Chris@819 197 if (m_d->valid) {
Chris@819 198 ExtAudioFileDispose(m_d->file);
Chris@819 199 delete[] m_d->buffer.mBuffers[0].mData;
Chris@819 200 }
Chris@819 201
Chris@743 202 delete m_d;
luisf@665 203 }
luisf@665 204
luisf@665 205 void
luisf@665 206 CoreAudioFileReader::cancelled()
luisf@665 207 {
luisf@665 208 m_cancelled = true;
luisf@665 209 }
luisf@665 210
luisf@665 211 void
luisf@665 212 CoreAudioFileReader::getSupportedExtensions(std::set<QString> &extensions)
luisf@665 213 {
Chris@743 214 extensions.insert("aiff");
Chris@743 215 extensions.insert("aif");
Chris@743 216 extensions.insert("au");
Chris@743 217 extensions.insert("m4a");
Chris@743 218 extensions.insert("m4b");
Chris@743 219 extensions.insert("m4p");
Chris@743 220 extensions.insert("mp3");
Chris@743 221 extensions.insert("mp4");
Chris@743 222 extensions.insert("wav");
luisf@665 223 }
luisf@665 224
luisf@665 225 bool
luisf@665 226 CoreAudioFileReader::supportsExtension(QString extension)
luisf@665 227 {
Chris@743 228 std::set<QString> extensions;
Chris@743 229 getSupportedExtensions(extensions);
Chris@743 230 return (extensions.find(extension.toLower()) != extensions.end());
luisf@665 231 }
luisf@665 232
luisf@665 233 bool
luisf@665 234 CoreAudioFileReader::supportsContentType(QString type)
luisf@665 235 {
Chris@743 236 return (type == "audio/x-aiff" ||
Chris@743 237 type == "audio/x-wav" ||
Chris@743 238 type == "audio/mpeg" ||
Chris@743 239 type == "audio/basic" ||
Chris@749 240 type == "audio/x-aac");
luisf@665 241 }
luisf@665 242
luisf@665 243 bool
luisf@665 244 CoreAudioFileReader::supports(FileSource &source)
luisf@665 245 {
Chris@743 246 return (supportsExtension(source.getExtension()) ||
Chris@743 247 supportsContentType(source.getContentType()));
luisf@665 248 }
luisf@665 249
luisf@665 250 #endif
luisf@665 251