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