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