megaopus commit: (1) add __STD_(LIMIT|FORMAT)_MACROS to command line flags for cc...
[ardour.git] / libs / ardour / coreaudiosource.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3     Written by Taybin Rutkin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <algorithm>
22 #define __STDC_FORMAT_MACROS
23 #include <inttypes.h>
24
25 #include "pbd/error.h"
26 #include "ardour/coreaudiosource.h"
27 #include "ardour/utils.h"
28
29 #include <appleutility/CAAudioFile.h>
30 #include <appleutility/CAStreamBasicDescription.h>
31
32 #include "i18n.h"
33
34 #include <AudioToolbox/AudioFormat.h>
35
36 using namespace std;
37 using namespace ARDOUR;
38 using namespace PBD;
39
40 CoreAudioSource::CoreAudioSource (Session& s, const XMLNode& node)
41         : Source (s, node)
42         , AudioFileSource (s, node)
43 {
44         init_cafile ();
45 }
46
47 CoreAudioSource::CoreAudioSource (Session& s, const string& path, int chn, Flag flags)
48         /* files created this way are never writable or removable */
49         : Source (s, DataType::AUDIO, path, Source::Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))),
50                 AudioFileSource (s, path,
51                         Source::Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
52 {
53         _channel = chn;
54         init_cafile ();
55 }
56
57 void
58 CoreAudioSource::init_cafile ()
59 {
60         /* note that we temporarily truncated _id at the colon */
61         try {
62                 af.Open(_path.c_str());
63
64                 CAStreamBasicDescription file_format (af.GetFileDataFormat());
65                 n_channels = file_format.NumberChannels();
66
67                 if (_channel >= n_channels) {
68                         error << string_compose("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number (%3)", n_channels, _channel, name()) << endmsg;
69                         throw failed_constructor();
70                 }
71
72                 _length = af.GetNumberFrames();
73
74                 CAStreamBasicDescription client_format (file_format);
75
76                 /* set canonial form (PCM, native float packed, 32 bit, with the correct number of channels
77                    and interleaved (since we plan to deinterleave ourselves)
78                 */
79
80                 client_format.SetCanonical(client_format.NumberChannels(), true);
81                 af.SetClientFormat (client_format);
82
83         } catch (CAXException& cax) {
84
85                 error << string_compose(_("CoreAudioSource: cannot open file \"%1\" for %2"),
86                                         _path, (writable() ? "read+write" : "reading")) << endmsg;
87                 throw failed_constructor ();
88         }
89 }
90
91 CoreAudioSource::~CoreAudioSource ()
92 {
93 }
94
95 int
96 CoreAudioSource::safe_read (Sample* dst, nframes_t start, nframes_t cnt, AudioBufferList& abl) const
97 {
98         nframes_t nread = 0;
99
100         while (nread < cnt) {
101
102                 try {
103                         af.Seek (start+nread);
104                 } catch (CAXException& cax) {
105                         error << string_compose("CoreAudioSource: %1 to %2 (%3)", cax.mOperation, start+nread, _name.substr (1)) << endmsg;
106                         return -1;
107                 }
108
109                 UInt32 new_cnt = cnt - nread;
110
111                 abl.mBuffers[0].mDataByteSize = new_cnt * n_channels * sizeof(Sample);
112                 abl.mBuffers[0].mData = dst + nread;
113
114                 try {
115                         af.Read (new_cnt, &abl);
116                 } catch (CAXException& cax) {
117                         error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name);
118                         return -1;
119                 }
120
121                 if (new_cnt == 0) {
122                         /* EOF */
123                         if (start+cnt == _length) {
124                                 /* we really did hit the end */
125                                 nread = cnt;
126                         }
127                         break;
128                 }
129
130                 nread += new_cnt;
131         }
132
133         if (nread < cnt) {
134                 return -1;
135         } else {
136                 return 0;
137         }
138 }
139
140
141 nframes_t
142 CoreAudioSource::read_unlocked (Sample *dst, sframes_t start, nframes_t cnt) const
143 {
144         nframes_t file_cnt;
145         AudioBufferList abl;
146
147         abl.mNumberBuffers = 1;
148         abl.mBuffers[0].mNumberChannels = n_channels;
149
150         if (start > _length) {
151
152                 /* read starts beyond end of data, just memset to zero */
153
154                 file_cnt = 0;
155
156         } else if (start + cnt > _length) {
157
158                 /* read ends beyond end of data, read some, memset the rest */
159
160                 file_cnt = _length - start;
161
162         } else {
163
164                 /* read is entirely within data */
165
166                 file_cnt = cnt;
167         }
168
169         if (file_cnt != cnt) {
170                 nframes_t delta = cnt - file_cnt;
171                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
172         }
173
174         if (file_cnt) {
175
176                 if (n_channels == 1) {
177                         if (safe_read (dst, start, file_cnt, abl) == 0) {
178                                 _read_data_count = cnt * sizeof (Sample);
179                                 return cnt;
180                         }
181                         return 0;
182                 }
183         }
184
185         Sample* interleave_buf = get_interleave_buffer (file_cnt * n_channels);
186
187         if (safe_read (interleave_buf, start, file_cnt, abl) != 0) {
188                 return 0;
189         }
190
191         _read_data_count = cnt * sizeof(float);
192
193         Sample *ptr = interleave_buf + _channel;
194
195         /* stride through the interleaved data */
196
197         for (uint32_t n = 0; n < file_cnt; ++n) {
198                 dst[n] = *ptr;
199                 ptr += n_channels;
200         }
201
202         return cnt;
203 }
204
205 float
206 CoreAudioSource::sample_rate() const
207 {
208         CAStreamBasicDescription client_asbd;
209
210         try {
211                 client_asbd = af.GetClientDataFormat ();
212         } catch (CAXException& cax) {
213                 error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name);
214                 return 0.0;
215         }
216
217         return client_asbd.mSampleRate;
218 }
219
220 int
221 CoreAudioSource::update_header (framepos_t when, struct tm&, time_t)
222 {
223         return 0;
224 }
225
226 int
227 CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg)
228 {
229         FSRef ref;
230         ExtAudioFileRef af = 0;
231         size_t size;
232         CFStringRef name;
233         int ret = -1;
234
235         if (FSPathMakeRef ((UInt8*)path.c_str(), &ref, 0) != noErr) {
236                 goto out;
237         }
238
239         if (ExtAudioFileOpen(&ref, &af) != noErr) {
240                 goto out;
241         }
242
243         AudioStreamBasicDescription absd;
244         memset(&absd, 0, sizeof(absd));
245         size = sizeof(AudioStreamBasicDescription);
246         if (ExtAudioFileGetProperty (af, kExtAudioFileProperty_FileDataFormat, &size, &absd) != noErr) {
247                 goto out;
248         }
249
250         _info.samplerate = absd.mSampleRate;
251         _info.channels   = absd.mChannelsPerFrame;
252
253         size = sizeof(_info.length);
254         if (ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &size, &_info.length) != noErr) {
255                 goto out;
256         }
257
258         size = sizeof(CFStringRef);
259         if (AudioFormatGetProperty(kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name) != noErr) {
260                 goto out;
261         }
262
263         _info.format_name = "";
264
265         if (absd.mFormatID == kAudioFormatLinearPCM) {
266                 if (absd.mFormatFlags & kAudioFormatFlagIsBigEndian) {
267                         _info.format_name += "big-endian";
268                 } else {
269                         _info.format_name += "little-endian";
270                 }
271
272                 char buf[32];
273                 snprintf (buf, sizeof (buf), " %" PRIu32 " bit", absd.mBitsPerChannel);
274                 _info.format_name += buf;
275                 _info.format_name += '\n';
276
277                 if (absd.mFormatFlags & kAudioFormatFlagIsFloat) {
278                         _info.format_name += "float";
279                 } else {
280                         if (absd.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
281                                 _info.format_name += "signed";
282                         } else {
283                                 _info.format_name += "unsigned";
284                         }
285                         /* integer is typical, do not show it */
286                 }
287
288                 if (_info.channels > 1) {
289                         if (absd.mFormatFlags & kAudioFormatFlagIsNonInterleaved) {
290                                 _info.format_name += " noninterleaved";
291                         }
292                         /* interleaved is the normal case, do not show it */
293                 }
294
295                 _info.format_name += ' ';
296         }
297
298         switch (absd.mFormatID) {
299         case kAudioFormatLinearPCM:
300                 _info.format_name += "PCM";
301                 break;
302
303         case kAudioFormatAC3:
304                 _info.format_name += "AC3";
305                 break;
306
307         case kAudioFormat60958AC3:
308                 _info.format_name += "60958 AC3";
309                 break;
310
311         case kAudioFormatMPEGLayer1:
312                 _info.format_name += "MPEG-1";
313                 break;
314
315         case kAudioFormatMPEGLayer2:
316                 _info.format_name += "MPEG-2";
317                 break;
318
319         case kAudioFormatMPEGLayer3:
320                 _info.format_name += "MPEG-3";
321                 break;
322
323         case kAudioFormatAppleIMA4:
324                 _info.format_name += "IMA-4";
325                 break;
326
327         case kAudioFormatMPEG4AAC:
328                 _info.format_name += "AAC";
329                 break;
330
331         case kAudioFormatMPEG4CELP:
332                 _info.format_name += "CELP";
333                 break;
334
335         case kAudioFormatMPEG4HVXC:
336                 _info.format_name += "HVXC";
337                 break;
338
339         case kAudioFormatMPEG4TwinVQ:
340                 _info.format_name += "TwinVQ";
341                 break;
342
343         /* these really shouldn't show up, but we should do something
344            somewhere else to make sure that doesn't happen. until
345            that is guaranteed, print something anyway.
346         */
347
348         case kAudioFormatTimeCode:
349                 _info.format_name += "timecode";
350                 break;
351
352         case kAudioFormatMIDIStream:
353                 _info.format_name += "MIDI";
354                 break;
355
356         case kAudioFormatParameterValueStream:
357                 _info.format_name += "parameter values";
358                 break;
359         }
360
361         // XXX it would be nice to find a way to get this information if it exists
362
363         _info.timecode = 0;
364         ret = 0;
365
366   out:
367         ExtAudioFileDispose (af);
368         return ret;
369
370 }
371
372 void
373 CoreAudioSource::set_path (const string& p)
374 {
375         FileSource::set_path (p);
376 }