ardour
coreaudiosource.cc
Go to the documentation of this file.
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 
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 
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
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 
104 {
105 }
106 
107 void
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 
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
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
237 {
238  return 0;
239 }
240 
241 int
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 {
391 }
framecnt_t _length
Definition: audiosource.h:127
T const & val() const
Definition: properties.h:96
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
int update_header(framepos_t when, struct tm &, time_t)
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
void set_path(const std::string &p)
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
#define _(Text)
Definition: i18n.h:11
int64_t framecnt_t
Definition: types.h:76
float Sample
Definition: types.h:54
CoreAudioSource(ARDOUR::Session &, const XMLNode &)
Definition: amp.h:29
std::string _path
Definition: file_source.h:107
int64_t framepos_t
Definition: types.h:66
int64_t frameoffset_t
Definition: types.h:71
PBD::Property< std::string > _name
static int get_soundfile_info(string path, SoundFileInfo &_info, string &error_msg)
framecnt_t read_unlocked(Sample *dst, framepos_t start, framecnt_t cnt) const
static Sample * get_interleave_buffer(framecnt_t size)
int safe_read(Sample *, framepos_t start, framecnt_t cnt, AudioBufferList &) const
Writable
Definition: selectable.h:36
Definition: xml++.h:95
virtual uint32_t n_channels() const
Definition: audiosource.h:51
std::string name() const
Definition: debug.h:30
virtual void set_path(const std::string &)
Definition: file_source.cc:545
bool writable() const
Definition: source.cc:305
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208