comparison data/fileio/CoreAudioFileReader.cpp @ 743:964a20b5a747 coreaudio_tests

Overhaul CoreAudioFileReader -- it now at least superficially appears to work
author Chris Cannam
date Mon, 02 Jul 2012 09:39:55 +0100
parents 029dd9e5cc29
children c8ab5f63360d
comparison
equal deleted inserted replaced
742:c10cb8782576 743:964a20b5a747
23 #include "base/ProgressReporter.h" 23 #include "base/ProgressReporter.h"
24 #include "system/System.h" 24 #include "system/System.h"
25 25
26 #include <QFileInfo> 26 #include <QFileInfo>
27 27
28
29 // TODO: implement for windows
30 #ifdef _WIN32
31 #include <QTML.h>
32 #include <Movies.h>
33 #else
34
35
36 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) 28 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
37 #include <AudioToolbox/AudioToolbox.h> 29 #include <AudioToolbox/AudioToolbox.h>
30 #include <AudioToolbox/ExtendedAudioFile.h>
38 #else 31 #else
39 #include "AudioToolbox.h" 32 #include "AudioToolbox.h"
40 #include "ExtendedAudioFile.h" 33 #include "ExtendedAudioFile.h"
41 #endif 34 #endif
42 35
43 #include "CAStreamBasicDescription.h"
44 #include "CAXException.h"
45
46
47
48
49 #endif
50
51 class CoreAudioFileReader::D 36 class CoreAudioFileReader::D
52 { 37 {
53 public: 38 public:
54 D() : data(0), blockSize(1024) { } 39 D() : blockSize(1024) { }
55 40
56 AudioBufferList buffer; 41 ExtAudioFileRef file;
57 float *data; 42 AudioBufferList buffer;
58 OSErr err; 43 OSStatus err;
59 AudioStreamBasicDescription asbd; 44 AudioStreamBasicDescription asbd;
60 // file reference 45 int blockSize;
61
62 ExtAudioFileRef *infile;
63 size_t blockSize;
64 }; 46 };
65 47
48 static QString
49 codestr(OSStatus err)
50 {
51 char text[5];
52 UInt32 uerr = err;
53 text[0] = (uerr >> 24) & 0xff;
54 text[1] = (uerr >> 16) & 0xff;
55 text[2] = (uerr >> 8) & 0xff;
56 text[3] = (uerr) & 0xff;
57 text[4] = '\0';
58 return QString("%1 (%2)").arg(err).arg(QString::fromAscii(text));
59 }
66 60
67 CoreAudioFileReader::CoreAudioFileReader(FileSource source, 61 CoreAudioFileReader::CoreAudioFileReader(FileSource source,
68 DecodeMode decodeMode, 62 DecodeMode decodeMode,
69 CacheMode mode, 63 CacheMode mode,
70 size_t targetRate, 64 size_t targetRate,
71 ProgressReporter *reporter) : 65 ProgressReporter *reporter) :
72 CodedAudioFileReader(mode, targetRate), 66 CodedAudioFileReader(mode, targetRate),
73 m_source(source), 67 m_source(source),
74 m_path(source.getLocalFilename()), 68 m_path(source.getLocalFilename()),
75 m_d(new D), 69 m_d(new D),
76 m_reporter(reporter), 70 m_reporter(reporter),
77 m_cancelled(false), 71 m_cancelled(false),
78 m_completion(0), 72 m_completion(0),
79 m_decodeThread(0) 73 m_decodeThread(0)
80 { 74 {
81 m_channelCount = 0; 75 m_channelCount = 0;
82 m_fileRate = 0; 76 m_fileRate = 0;
83 77
84 Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true); 78 Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true);
85 79
86 std::cerr << "CoreAudioFileReader: path is \"" << m_path.toStdString() << "\"" << std::endl; 80 std::cerr << "CoreAudioFileReader: path is \"" << m_path.toStdString() << "\"" << std::endl;
87 81
88 // TODO: check QT version 82 QByteArray ba = m_path.toLocal8Bit();
89 83
90 Handle dataRef; 84 CFURLRef url = CFURLCreateFromFileSystemRepresentation
91 OSType dataRefType; 85 (kCFAllocatorDefault,
92 86 (const UInt8 *)ba.data(),
93 // CFStringRef URLString = CFStringCreateWithCString 87 (CFIndex)ba.length(),
94 // (0, m_path.toLocal8Bit().data(), 0); 88 false);
95 89
96 // what are these used for??? 90 //!!! how do we find out if the file open fails because of DRM protection?
97 // ExtAudioFileRef *infile, outfile; 91
98 92 #if (MACOSX_DEPLOYMENT_TARGET <= 1040 && MAC_OS_X_VERSION_MIN_REQUIRED <= 1040)
99 // Creates a new CFURL object for a file system entity using the native representation. 93 FSRef fsref;
100 CFURLRef url = CFURLCreateFromFileSystemRepresentation 94 if (!CFURLGetFSRef(url, &fsref)) { // returns Boolean, not error code
101 (kCFAllocatorDefault, 95 m_error = "CoreAudioReadStream: Error looking up FS ref (file not found?)";
102 (const UInt8 *)m_path.toLocal8Bit().data(), 96 return;
103 (CFIndex)m_path.length(), 97 }
104 false); 98 m_d->err = ExtAudioFileOpen(&fsref, &m_d->file);
105 99 #else
106 // first open the input file 100 m_d->err = ExtAudioFileOpenURL(url, &m_d->file);
107 m_d->err = ExtAudioFileOpenURL (url, m_d->infile); 101 #endif
108 102
109 if (m_d->err) { 103 CFRelease(url);
110 m_error = QString("Error opening Audio File for CoreAudio decoder: code %1").arg(m_d->err); 104
111 return; 105 if (m_d->err) {
112 } 106 m_error = "CoreAudioReadStream: Error opening file: code " + codestr(m_d->err);
113 else { 107 return;
114 std::cerr << "CoreAudio: opened URL" << std::endl; 108 }
115 } 109 if (!m_d->file) {
116 110 m_error = "CoreAudioReadStream: Failed to open file, but no error reported!";
117 111 return;
118 // Get the audio data format 112 }
119 // the audio format gets stored in the &m_d->asbd 113
120 UInt32 thePropertySize = sizeof(m_d->asbd); 114 UInt32 propsize = sizeof(AudioStreamBasicDescription);
121 115 m_d->err = ExtAudioFileGetProperty
122 116 (m_d->file, kExtAudioFileProperty_FileDataFormat, &propsize, &m_d->asbd);
123 std::cerr << "CoreAudio: thePropertySize: " << thePropertySize << std::endl; 117
124 118 if (m_d->err) {
125 m_d->err = ExtAudioFileGetProperty(*m_d->infile, kAudioFilePropertyDataFormat, &thePropertySize, &m_d->asbd); 119 m_error = "CoreAudioReadStream: Error in getting basic description: code " + codestr(m_d->err);
126 120 return;
127 std::cerr << "CoreAudio: ExtAudioFileGetProperty res: " << &m_d->asbd << std::endl; 121 }
128 122
129 // CAStreamBasicDescription clientFormat = (inputFormat.mFormatID == kAudioFormatLinearPCM ? inputFormat : outputFormat); 123 m_channelCount = m_d->asbd.mChannelsPerFrame;
130 // UInt32 size = sizeof(clientFormat); 124 m_sampleRate = m_d->asbd.mSampleRate;
131 125
132 // TODO: test input file's DRM rights 126 std::cerr << "CoreAudioReadStream: " << m_channelCount << " channels, " << m_sampleRate << " Hz" << std::endl;
133 127
134 // are these already set? 128 m_d->asbd.mSampleRate = getSampleRate();
135 m_channelCount = m_d->asbd.mChannelsPerFrame; 129
136 m_fileRate = m_d->asbd.mSampleRate; 130 m_d->asbd.mFormatID = kAudioFormatLinearPCM;
137 131 m_d->asbd.mFormatFlags =
138 std::cerr << "CoreAudio: Format ID: " << m_d->asbd.mFormatID << std::endl; 132 kAudioFormatFlagIsFloat |
139 std::cerr << "CoreAudio: " << m_d->asbd.mChannelsPerFrame << " channels, " << m_fileRate << " kHz" << std::endl; 133 kAudioFormatFlagIsPacked |
140 134 kAudioFormatFlagsNativeEndian;
141 135 m_d->asbd.mBitsPerChannel = sizeof(float) * 8;
142 136 m_d->asbd.mBytesPerFrame = sizeof(float) * m_channelCount;
143 m_d->asbd.mFormatFlags = 137 m_d->asbd.mBytesPerPacket = sizeof(float) * m_channelCount;
144 kAudioFormatFlagIsFloat | 138 m_d->asbd.mFramesPerPacket = 1;
145 kAudioFormatFlagIsPacked | 139 m_d->asbd.mReserved = 0;
146 kAudioFormatFlagsNativeEndian; 140
147 m_d->asbd.mBitsPerChannel = sizeof(float) * 8; 141 m_d->err = ExtAudioFileSetProperty
148 m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame; 142 (m_d->file, kExtAudioFileProperty_ClientDataFormat, propsize, &m_d->asbd);
149 m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame; 143
150 144 if (m_d->err) {
151 145 m_error = "CoreAudioReadStream: Error in setting client format: code " + codestr(m_d->err);
152 146 return;
153 /* 147 }
154 148
155 !!! what does this do exactly ????? 149 m_d->buffer.mNumberBuffers = 1;
156 150 m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount;
157 m_d->err = MovieAudioExtractionSetProperty 151 m_d->buffer.mBuffers[0].mDataByteSize = sizeof(float) * m_channelCount * m_d->blockSize;
158 (m_d->extractionSessionRef, 152 m_d->buffer.mBuffers[0].mData = new float[m_channelCount * m_d->blockSize];
159 kQTPropertyClass_MovieAudioExtraction_Audio, 153
160 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, 154 initialiseDecodeCache();
161 sizeof(m_d->asbd), 155
162 &m_d->asbd); 156 if (m_reporter) {
163 157 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
164 158 m_reporter->setMessage
165 159 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
166 if (m_d->err) { 160 }
167 m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err); 161
168 m_channelCount = 0; 162 while (1) {
169 return; 163
170 } 164 UInt32 framesRead = m_d->blockSize;
171 165 m_d->err = ExtAudioFileRead(m_d->file, &framesRead, &m_d->buffer);
172 */ 166
173 167 if (m_d->err) {
174 m_d->buffer.mNumberBuffers = 1; 168 m_error = QString("Error in CoreAudio decoding: code %1")
175 m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount; 169 .arg(m_d->err);
176 m_d->buffer.mBuffers[0].mDataByteSize = 170 break;
177 sizeof(float) * m_channelCount * m_d->blockSize; 171 }
178 m_d->data = new float[m_channelCount * m_d->blockSize]; 172
179 m_d->buffer.mBuffers[0].mData = m_d->data; 173 //!!! progress?
180 174
181 initialiseDecodeCache(); 175 // std::cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << std::endl;
182 176
183 // only decode at once for now 177 // buffers are interleaved unless specified otherwise
184 // if (decodeMode == DecodeAtOnce) { 178 addSamplesToDecodeCache((float *)m_d->buffer.mBuffers[0].mData, framesRead);
185 179
186 if (m_reporter) { 180 if (framesRead < m_d->blockSize) break;
187 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); 181 }
188 m_reporter->setMessage 182
189 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); 183 finishDecodeCache();
190 } 184 endSerialised();
191 185
192 while (1) { 186 m_completion = 100;
193 187
194 UInt32 framesRead = m_d->blockSize; 188 ExtAudioFileDispose(m_d->file);
195 189 }
196 m_d->err = ExtAudioFileRead (*m_d->infile, &framesRead, &m_d->buffer);
197
198 if (m_d->err) {
199 m_error = QString("Error in CoreAudio decoding: code %1")
200 .arg(m_d->err);
201 break;
202 }
203
204 //!!! progress?
205
206 // std::cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << std::endl;
207
208 // QuickTime buffers are interleaved unless specified otherwise
209 addSamplesToDecodeCache(m_d->data, framesRead);
210
211 if (framesRead < m_d->blockSize) break;
212 }
213
214 finishDecodeCache();
215 endSerialised();
216
217 /*
218 TODO - close session
219 m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef);
220 if (m_d->err) {
221 m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err);
222 }
223 */
224 m_completion = 100;
225
226
227
228 // } else {
229 // if (m_reporter) m_reporter->setProgress(100);
230 //
231 // if (m_channelCount > 0) {
232 // m_decodeThread = new DecodeThread(this);
233 // m_decodeThread->start();
234 // }
235 // }
236
237 std::cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error.toStdString() << "\"" << std::endl;
238 }
239
240
241
242 190
243 191
244 CoreAudioFileReader::~CoreAudioFileReader() 192 CoreAudioFileReader::~CoreAudioFileReader()
245 { 193 {
246 std::cerr << "CoreAudioFileReader::~CoreAudioFileReader" << std::endl; 194 std::cerr << "CoreAudioFileReader::~CoreAudioFileReader" << std::endl;
247 195 delete[] m_d->buffer.mBuffers[0].mData;
248 if (m_decodeThread) { 196 delete m_d;
249 m_cancelled = true;
250 m_decodeThread->wait();
251 delete m_decodeThread;
252 }
253
254 // SetMovieActive(m_d->movie, FALSE);
255 // DisposeMovie(m_d->movie);
256
257 delete[] m_d->data;
258 delete m_d;
259 } 197 }
260 198
261 void 199 void
262 CoreAudioFileReader::cancelled() 200 CoreAudioFileReader::cancelled()
263 { 201 {
306 244
307 245
308 void 246 void
309 CoreAudioFileReader::getSupportedExtensions(std::set<QString> &extensions) 247 CoreAudioFileReader::getSupportedExtensions(std::set<QString> &extensions)
310 { 248 {
311 extensions.insert("aiff"); 249 extensions.insert("aiff");
312 extensions.insert("aif"); 250 extensions.insert("aif");
313 extensions.insert("au"); 251 extensions.insert("au");
314 extensions.insert("avi"); 252 extensions.insert("avi");
315 extensions.insert("m4a"); 253 extensions.insert("m4a");
316 extensions.insert("m4b"); 254 extensions.insert("m4b");
317 extensions.insert("m4p"); 255 extensions.insert("m4p");
318 extensions.insert("m4v"); 256 extensions.insert("m4v");
319 extensions.insert("mov"); 257 extensions.insert("mov");
320 extensions.insert("mp3"); 258 extensions.insert("mp3");
321 extensions.insert("mp4"); 259 extensions.insert("mp4");
322 extensions.insert("wav"); 260 extensions.insert("wav");
323 } 261 }
324 262
325 bool 263 bool
326 CoreAudioFileReader::supportsExtension(QString extension) 264 CoreAudioFileReader::supportsExtension(QString extension)
327 { 265 {
328 std::set<QString> extensions; 266 std::set<QString> extensions;
329 getSupportedExtensions(extensions); 267 getSupportedExtensions(extensions);
330 return (extensions.find(extension.toLower()) != extensions.end()); 268 return (extensions.find(extension.toLower()) != extensions.end());
331 } 269 }
332 270
333 bool 271 bool
334 CoreAudioFileReader::supportsContentType(QString type) 272 CoreAudioFileReader::supportsContentType(QString type)
335 { 273 {
336 return (type == "audio/x-aiff" || 274 return (type == "audio/x-aiff" ||
337 type == "audio/x-wav" || 275 type == "audio/x-wav" ||
338 type == "audio/mpeg" || 276 type == "audio/mpeg" ||
339 type == "audio/basic" || 277 type == "audio/basic" ||
340 type == "audio/x-aac" || 278 type == "audio/x-aac" ||
341 type == "video/mp4" || 279 type == "video/mp4" ||
342 type == "video/quicktime"); 280 type == "video/quicktime");
343 } 281 }
344 282
345 bool 283 bool
346 CoreAudioFileReader::supports(FileSource &source) 284 CoreAudioFileReader::supports(FileSource &source)
347 { 285 {
348 return (supportsExtension(source.getExtension()) || 286 return (supportsExtension(source.getExtension()) ||
349 supportsContentType(source.getContentType())); 287 supportsContentType(source.getContentType()));
350 } 288 }
351 289
352 #endif 290 #endif
353 291