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.
|
luisf@665
|
7 This file copyright 2006-2007 Chris Cannam and QMUL.
|
luisf@665
|
8
|
luisf@665
|
9 Based on QTAudioFile.cpp from SoundBite, copyright 2006
|
luisf@665
|
10 Chris Sutton and Mark Levy.
|
luisf@665
|
11
|
luisf@665
|
12 This program is free software; you can redistribute it and/or
|
luisf@665
|
13 modify it under the terms of the GNU General Public License as
|
luisf@665
|
14 published by the Free Software Foundation; either version 2 of the
|
luisf@665
|
15 License, or (at your option) any later version. See the file
|
luisf@665
|
16 COPYING included with this distribution for more information.
|
luisf@665
|
17 */
|
luisf@665
|
18
|
luisf@665
|
19 #ifdef HAVE_COREAUDIO
|
luisf@665
|
20
|
luisf@665
|
21 #include "CoreAudioFileReader.h"
|
luisf@665
|
22 #include "base/Profiler.h"
|
luisf@665
|
23 #include "base/ProgressReporter.h"
|
luisf@665
|
24 #include "system/System.h"
|
luisf@665
|
25
|
luisf@665
|
26 #include <QFileInfo>
|
luisf@665
|
27
|
luisf@665
|
28
|
luisf@665
|
29 // TODO: implement for windows
|
luisf@665
|
30 #ifdef _WIN32
|
luisf@665
|
31 #include <QTML.h>
|
luisf@665
|
32 #include <Movies.h>
|
luisf@665
|
33 #else
|
luisf@665
|
34
|
luisf@665
|
35
|
luisf@665
|
36 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
luisf@665
|
37 #include <AudioToolbox/AudioToolbox.h>
|
luisf@665
|
38 #else
|
luisf@665
|
39 #include "AudioToolbox.h"
|
luisf@665
|
40 #include "ExtendedAudioFile.h"
|
luisf@665
|
41 #endif
|
luisf@665
|
42
|
luisf@665
|
43 #include "CAStreamBasicDescription.h"
|
luisf@665
|
44 #include "CAXException.h"
|
luisf@665
|
45
|
luisf@665
|
46
|
luisf@665
|
47
|
luisf@665
|
48
|
luisf@665
|
49 #endif
|
luisf@665
|
50
|
luisf@665
|
51 class CoreAudioFileReader::D
|
luisf@665
|
52 {
|
luisf@665
|
53 public:
|
luisf@665
|
54 D() : data(0), blockSize(1024) { }
|
luisf@665
|
55
|
luisf@665
|
56 AudioBufferList buffer;
|
luisf@665
|
57 float *data;
|
luisf@665
|
58 OSErr err;
|
luisf@665
|
59 AudioStreamBasicDescription asbd;
|
luisf@665
|
60 // file reference
|
luisf@665
|
61
|
luisf@665
|
62 ExtAudioFileRef *infile;
|
luisf@665
|
63 size_t blockSize;
|
luisf@665
|
64 };
|
luisf@665
|
65
|
luisf@665
|
66
|
luisf@665
|
67 CoreAudioFileReader::CoreAudioFileReader(FileSource source,
|
luisf@665
|
68 DecodeMode decodeMode,
|
luisf@665
|
69 CacheMode mode,
|
luisf@665
|
70 size_t targetRate,
|
luisf@665
|
71 ProgressReporter *reporter) :
|
luisf@665
|
72 CodedAudioFileReader(mode, targetRate),
|
luisf@665
|
73 m_source(source),
|
luisf@665
|
74 m_path(source.getLocalFilename()),
|
luisf@665
|
75 m_d(new D),
|
luisf@665
|
76 m_reporter(reporter),
|
luisf@665
|
77 m_cancelled(false),
|
luisf@665
|
78 m_completion(0),
|
luisf@665
|
79 m_decodeThread(0)
|
luisf@665
|
80 {
|
luisf@665
|
81 m_channelCount = 0;
|
luisf@665
|
82 m_fileRate = 0;
|
luisf@665
|
83
|
luisf@665
|
84 Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true);
|
luisf@665
|
85
|
luisf@665
|
86 std::cerr << "CoreAudioFileReader: path is \"" << m_path.toStdString() << "\"" << std::endl;
|
luisf@665
|
87
|
luisf@665
|
88 // TODO: check QT version
|
luisf@665
|
89
|
luisf@665
|
90 Handle dataRef;
|
luisf@665
|
91 OSType dataRefType;
|
luisf@665
|
92
|
luisf@665
|
93 // CFStringRef URLString = CFStringCreateWithCString
|
luisf@665
|
94 // (0, m_path.toLocal8Bit().data(), 0);
|
luisf@665
|
95
|
luisf@665
|
96 // what are these used for???
|
luisf@665
|
97 // ExtAudioFileRef *infile, outfile;
|
luisf@665
|
98
|
luisf@665
|
99 // Creates a new CFURL object for a file system entity using the native representation.
|
luisf@665
|
100 CFURLRef url = CFURLCreateFromFileSystemRepresentation
|
luisf@665
|
101 (kCFAllocatorDefault,
|
luisf@665
|
102 (const UInt8 *)m_path.toLocal8Bit().data(),
|
luisf@665
|
103 (CFIndex)m_path.length(),
|
luisf@665
|
104 false);
|
luisf@665
|
105
|
luisf@665
|
106 // first open the input file
|
luisf@665
|
107 m_d->err = ExtAudioFileOpenURL (url, m_d->infile);
|
luisf@665
|
108
|
luisf@665
|
109 if (m_d->err) {
|
luisf@665
|
110 m_error = QString("Error opening Audio File for CoreAudio decoder: code %1").arg(m_d->err);
|
luisf@665
|
111 return;
|
luisf@665
|
112 }
|
luisf@665
|
113 else {
|
luisf@665
|
114 std::cerr << "CoreAudio: opened URL" << std::endl;
|
luisf@665
|
115 }
|
luisf@665
|
116
|
luisf@665
|
117
|
luisf@665
|
118 // Get the audio data format
|
luisf@665
|
119 // the audio format gets stored in the &m_d->asbd
|
luisf@665
|
120 UInt32 thePropertySize = sizeof(m_d->asbd);
|
luisf@665
|
121
|
luisf@665
|
122
|
luisf@665
|
123 std::cerr << "CoreAudio: thePropertySize: " << thePropertySize << std::endl;
|
luisf@665
|
124
|
luisf@665
|
125 m_d->err = ExtAudioFileGetProperty(*m_d->infile, kAudioFilePropertyDataFormat, &thePropertySize, &m_d->asbd);
|
luisf@665
|
126
|
luisf@665
|
127 std::cerr << "CoreAudio: ExtAudioFileGetProperty res: " << &m_d->asbd << std::endl;
|
luisf@665
|
128
|
luisf@665
|
129 // CAStreamBasicDescription clientFormat = (inputFormat.mFormatID == kAudioFormatLinearPCM ? inputFormat : outputFormat);
|
luisf@665
|
130 // UInt32 size = sizeof(clientFormat);
|
luisf@665
|
131
|
luisf@665
|
132 // TODO: test input file's DRM rights
|
luisf@665
|
133
|
luisf@665
|
134 // are these already set?
|
luisf@665
|
135 m_channelCount = m_d->asbd.mChannelsPerFrame;
|
luisf@665
|
136 m_fileRate = m_d->asbd.mSampleRate;
|
luisf@665
|
137
|
luisf@665
|
138 std::cerr << "CoreAudio: Format ID: " << m_d->asbd.mFormatID << std::endl;
|
luisf@665
|
139 std::cerr << "CoreAudio: " << m_d->asbd.mChannelsPerFrame << " channels, " << m_fileRate << " kHz" << std::endl;
|
luisf@665
|
140
|
luisf@665
|
141
|
luisf@665
|
142
|
luisf@665
|
143 m_d->asbd.mFormatFlags =
|
luisf@665
|
144 kAudioFormatFlagIsFloat |
|
luisf@665
|
145 kAudioFormatFlagIsPacked |
|
luisf@665
|
146 kAudioFormatFlagsNativeEndian;
|
luisf@665
|
147 m_d->asbd.mBitsPerChannel = sizeof(float) * 8;
|
luisf@665
|
148 m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame;
|
luisf@665
|
149 m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame;
|
luisf@665
|
150
|
luisf@665
|
151
|
luisf@665
|
152
|
luisf@665
|
153 /*
|
luisf@665
|
154
|
luisf@665
|
155 !!! what does this do exactly ?????
|
luisf@665
|
156
|
luisf@665
|
157 m_d->err = MovieAudioExtractionSetProperty
|
luisf@665
|
158 (m_d->extractionSessionRef,
|
luisf@665
|
159 kQTPropertyClass_MovieAudioExtraction_Audio,
|
luisf@665
|
160 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
|
luisf@665
|
161 sizeof(m_d->asbd),
|
luisf@665
|
162 &m_d->asbd);
|
luisf@665
|
163
|
luisf@665
|
164
|
luisf@665
|
165
|
luisf@665
|
166 if (m_d->err) {
|
luisf@665
|
167 m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err);
|
luisf@665
|
168 m_channelCount = 0;
|
luisf@665
|
169 return;
|
luisf@665
|
170 }
|
luisf@665
|
171
|
luisf@665
|
172 */
|
luisf@665
|
173
|
luisf@665
|
174 m_d->buffer.mNumberBuffers = 1;
|
luisf@665
|
175 m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount;
|
luisf@665
|
176 m_d->buffer.mBuffers[0].mDataByteSize =
|
luisf@665
|
177 sizeof(float) * m_channelCount * m_d->blockSize;
|
luisf@665
|
178 m_d->data = new float[m_channelCount * m_d->blockSize];
|
luisf@665
|
179 m_d->buffer.mBuffers[0].mData = m_d->data;
|
luisf@665
|
180
|
luisf@665
|
181 initialiseDecodeCache();
|
luisf@665
|
182
|
luisf@665
|
183 // only decode at once for now
|
luisf@665
|
184 // if (decodeMode == DecodeAtOnce) {
|
luisf@665
|
185
|
luisf@665
|
186 if (m_reporter) {
|
luisf@665
|
187 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
|
luisf@665
|
188 m_reporter->setMessage
|
luisf@665
|
189 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
|
luisf@665
|
190 }
|
luisf@665
|
191
|
luisf@665
|
192 while (1) {
|
luisf@665
|
193
|
luisf@665
|
194 UInt32 framesRead = m_d->blockSize;
|
luisf@665
|
195
|
luisf@665
|
196 m_d->err = ExtAudioFileRead (*m_d->infile, &framesRead, &m_d->buffer);
|
luisf@665
|
197
|
luisf@665
|
198 if (m_d->err) {
|
luisf@665
|
199 m_error = QString("Error in CoreAudio decoding: code %1")
|
luisf@665
|
200 .arg(m_d->err);
|
luisf@665
|
201 break;
|
luisf@665
|
202 }
|
luisf@665
|
203
|
luisf@665
|
204 //!!! progress?
|
luisf@665
|
205
|
luisf@665
|
206 // std::cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << std::endl;
|
luisf@665
|
207
|
luisf@665
|
208 // QuickTime buffers are interleaved unless specified otherwise
|
luisf@665
|
209 addSamplesToDecodeCache(m_d->data, framesRead);
|
luisf@665
|
210
|
luisf@665
|
211 if (framesRead < m_d->blockSize) break;
|
luisf@665
|
212 }
|
luisf@665
|
213
|
luisf@665
|
214 finishDecodeCache();
|
luisf@665
|
215 endSerialised();
|
luisf@665
|
216
|
luisf@665
|
217 /*
|
luisf@665
|
218 TODO - close session
|
luisf@665
|
219 m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef);
|
luisf@665
|
220 if (m_d->err) {
|
luisf@665
|
221 m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err);
|
luisf@665
|
222 }
|
luisf@665
|
223 */
|
luisf@665
|
224 m_completion = 100;
|
luisf@665
|
225
|
luisf@665
|
226
|
luisf@665
|
227
|
luisf@665
|
228 // } else {
|
luisf@665
|
229 // if (m_reporter) m_reporter->setProgress(100);
|
luisf@665
|
230 //
|
luisf@665
|
231 // if (m_channelCount > 0) {
|
luisf@665
|
232 // m_decodeThread = new DecodeThread(this);
|
luisf@665
|
233 // m_decodeThread->start();
|
luisf@665
|
234 // }
|
luisf@665
|
235 // }
|
luisf@665
|
236
|
luisf@665
|
237 std::cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error.toStdString() << "\"" << std::endl;
|
luisf@665
|
238 }
|
luisf@665
|
239
|
luisf@665
|
240
|
luisf@665
|
241
|
luisf@665
|
242
|
luisf@665
|
243
|
luisf@665
|
244 CoreAudioFileReader::~CoreAudioFileReader()
|
luisf@665
|
245 {
|
luisf@665
|
246 std::cerr << "CoreAudioFileReader::~CoreAudioFileReader" << std::endl;
|
luisf@665
|
247
|
luisf@665
|
248 if (m_decodeThread) {
|
luisf@665
|
249 m_cancelled = true;
|
luisf@665
|
250 m_decodeThread->wait();
|
luisf@665
|
251 delete m_decodeThread;
|
luisf@665
|
252 }
|
luisf@665
|
253
|
luisf@665
|
254 // SetMovieActive(m_d->movie, FALSE);
|
luisf@665
|
255 // DisposeMovie(m_d->movie);
|
luisf@665
|
256
|
luisf@665
|
257 delete[] m_d->data;
|
luisf@665
|
258 delete m_d;
|
luisf@665
|
259 }
|
luisf@665
|
260
|
luisf@665
|
261 void
|
luisf@665
|
262 CoreAudioFileReader::cancelled()
|
luisf@665
|
263 {
|
luisf@665
|
264 m_cancelled = true;
|
luisf@665
|
265 }
|
luisf@665
|
266
|
luisf@665
|
267 /*
|
luisf@665
|
268 void
|
luisf@665
|
269 CoreAudioFileReader::DecodeThread::run()
|
luisf@665
|
270 {
|
luisf@665
|
271 if (m_reader->m_cacheMode == CacheInTemporaryFile) {
|
luisf@665
|
272 m_reader->m_completion = 1;
|
luisf@665
|
273 m_reader->startSerialised("QuickTimeFileReader::Decode");
|
luisf@665
|
274 }
|
luisf@665
|
275
|
luisf@665
|
276 while (1) {
|
luisf@665
|
277
|
luisf@665
|
278 UInt32 framesRead = m_reader->m_d->blockSize;
|
luisf@665
|
279 UInt32 extractionFlags = 0;
|
luisf@665
|
280 m_reader->m_d->err = MovieAudioExtractionFillBuffer
|
luisf@665
|
281 (m_reader->m_d->extractionSessionRef, &framesRead,
|
luisf@665
|
282 &m_reader->m_d->buffer, &extractionFlags);
|
luisf@665
|
283 if (m_reader->m_d->err) {
|
luisf@665
|
284 m_reader->m_error = QString("Error in QuickTime decoding: code %1")
|
luisf@665
|
285 .arg(m_reader->m_d->err);
|
luisf@665
|
286 break;
|
luisf@665
|
287 }
|
luisf@665
|
288
|
luisf@665
|
289 // QuickTime buffers are interleaved unless specified otherwise
|
luisf@665
|
290 m_reader->addSamplesToDecodeCache(m_reader->m_d->data, framesRead);
|
luisf@665
|
291
|
luisf@665
|
292 if (framesRead < m_reader->m_d->blockSize) break;
|
luisf@665
|
293 }
|
luisf@665
|
294
|
luisf@665
|
295 m_reader->finishDecodeCache();
|
luisf@665
|
296
|
luisf@665
|
297 m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef);
|
luisf@665
|
298 if (m_reader->m_d->err) {
|
luisf@665
|
299 m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err);
|
luisf@665
|
300 }
|
luisf@665
|
301
|
luisf@665
|
302 m_reader->m_completion = 100;
|
luisf@665
|
303 m_reader->endSerialised();
|
luisf@665
|
304 }
|
luisf@665
|
305 */
|
luisf@665
|
306
|
luisf@665
|
307
|
luisf@665
|
308 void
|
luisf@665
|
309 CoreAudioFileReader::getSupportedExtensions(std::set<QString> &extensions)
|
luisf@665
|
310 {
|
luisf@665
|
311 extensions.insert("aiff");
|
luisf@665
|
312 extensions.insert("aif");
|
luisf@665
|
313 extensions.insert("au");
|
luisf@665
|
314 extensions.insert("avi");
|
luisf@665
|
315 extensions.insert("m4a");
|
luisf@665
|
316 extensions.insert("m4b");
|
luisf@665
|
317 extensions.insert("m4p");
|
luisf@665
|
318 extensions.insert("m4v");
|
luisf@665
|
319 extensions.insert("mov");
|
luisf@665
|
320 extensions.insert("mp3");
|
luisf@665
|
321 extensions.insert("mp4");
|
luisf@665
|
322 extensions.insert("wav");
|
luisf@665
|
323 }
|
luisf@665
|
324
|
luisf@665
|
325 bool
|
luisf@665
|
326 CoreAudioFileReader::supportsExtension(QString extension)
|
luisf@665
|
327 {
|
luisf@665
|
328 std::set<QString> extensions;
|
luisf@665
|
329 getSupportedExtensions(extensions);
|
luisf@665
|
330 return (extensions.find(extension.toLower()) != extensions.end());
|
luisf@665
|
331 }
|
luisf@665
|
332
|
luisf@665
|
333 bool
|
luisf@665
|
334 CoreAudioFileReader::supportsContentType(QString type)
|
luisf@665
|
335 {
|
luisf@665
|
336 return (type == "audio/x-aiff" ||
|
luisf@665
|
337 type == "audio/x-wav" ||
|
luisf@665
|
338 type == "audio/mpeg" ||
|
luisf@665
|
339 type == "audio/basic" ||
|
luisf@665
|
340 type == "audio/x-aac" ||
|
luisf@665
|
341 type == "video/mp4" ||
|
luisf@665
|
342 type == "video/quicktime");
|
luisf@665
|
343 }
|
luisf@665
|
344
|
luisf@665
|
345 bool
|
luisf@665
|
346 CoreAudioFileReader::supports(FileSource &source)
|
luisf@665
|
347 {
|
luisf@665
|
348 return (supportsExtension(source.getExtension()) ||
|
luisf@665
|
349 supportsContentType(source.getContentType()));
|
luisf@665
|
350 }
|
luisf@665
|
351
|
luisf@665
|
352 #endif
|
luisf@665
|
353
|