Mercurial > hg > svcore
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 |