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