X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fcoreaudiosource.cc;h=90ab07b86d5df7ac1f5104b7cfea6b44ef9f8131;hb=cf52d6e4b40111eb04b244ec054055a4ec15dbe0;hp=703225e502eaa953eee5c5d40a9afe9f828c1796;hpb=959a7909c1adca430a63f783fd16687242a7be3d;p=ardour.git diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc index 703225e502..90ab07b86d 100644 --- a/libs/ardour/coreaudiosource.cc +++ b/libs/ardour/coreaudiosource.cc @@ -1,5 +1,6 @@ /* - Copyright (C) 2006 Paul Davis + Copyright (C) 2006 Paul Davis + Written by Taybin Rutkin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,50 +18,68 @@ */ -#include -#include -#include +#include +#include -#include -#include +#include "pbd/error.h" +#include "ardour/coreaudiosource.h" +#include "ardour/utils.h" -#include "i18n.h" +#ifdef COREAUDIO105 +#include "CAAudioFile.h" +#else +#include "CAExtAudioFile.h" +#endif +#include "CAStreamBasicDescription.h" + +#include + +#include "pbd/i18n.h" #include +using namespace std; using namespace ARDOUR; using namespace PBD; +/** Create a new CoreAudioSource using session state, which implies that the + * file must already exist. + */ CoreAudioSource::CoreAudioSource (Session& s, const XMLNode& node) - : AudioFileSource (s, node) + : Source (s, node) + , AudioFileSource (s, node) { - init (); + init_cafile (); + + assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS)); + existence_check (); } +/** Create a new CoreAudioSource from an existing file. Sources created with this + * method are never writable or removable. + */ CoreAudioSource::CoreAudioSource (Session& s, const string& path, int chn, Flag flags) - /* files created this way are never writable or removable */ - : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) + : Source (s, DataType::AUDIO, path, Source::Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))), + AudioFileSource (s, path, + Source::Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) { _channel = chn; - init (); + init_cafile (); + + assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS)); + existence_check (); } -void -CoreAudioSource::init () +void +CoreAudioSource::init_cafile () { - tmpbuf = 0; - tmpbufsize = 0; - - cerr << "CoreAudioSource::init() " << name() << endl; - /* note that we temporarily truncated _id at the colon */ try { af.Open(_path.c_str()); - CAStreamBasicDescription file_asbd (af.GetFileDataFormat()); - n_channels = file_asbd.NumberChannels(); - cerr << "number of channels: " << n_channels << endl; - + CAStreamBasicDescription file_format (af.GetFileDataFormat()); + n_channels = file_format.NumberChannels(); + if (_channel >= n_channels) { error << string_compose("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number (%3)", n_channels, _channel, name()) << endmsg; throw failed_constructor(); @@ -68,92 +87,138 @@ CoreAudioSource::init () _length = af.GetNumberFrames(); - CAStreamBasicDescription client_asbd(file_asbd); - client_asbd.SetCanonical(client_asbd.NumberChannels(), false); - af.SetClientFormat (client_asbd); + CAStreamBasicDescription client_format (file_format); + + /* set canonial form (PCM, native float packed, 32 bit, with the correct number of channels + and interleaved (since we plan to deinterleave ourselves) + */ + + client_format.SetCanonical(client_format.NumberChannels(), true); + af.SetClientFormat (client_format); + } catch (CAXException& cax) { - error << string_compose ("CoreAudioSource: %1 (%2)", cax.mOperation, name()) << endmsg; + + error << string_compose(_("CoreAudioSource: cannot open file \"%1\" for %2"), + _path, (writable() ? "read+write" : "reading")) << endmsg; throw failed_constructor (); } } CoreAudioSource::~CoreAudioSource () { - cerr << "CoreAudioSource::~CoreAudioSource() " << name() << endl; - GoingAway (); /* EMIT SIGNAL */ +} - if (tmpbuf) { - delete [] tmpbuf; - } - - cerr << "deletion done" << endl; +void +CoreAudioSource::close () +{ + af.Close (); } -nframes_t -CoreAudioSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const +int +CoreAudioSource::safe_read (Sample* dst, framepos_t start, framecnt_t cnt, AudioBufferList& abl) const { - try { - af.Seek (start); - } catch (CAXException& cax) { - error << string_compose("CoreAudioSource: %1 to %2 (%3)", cax.mOperation, start, _name.substr (1)) << endmsg; - return 0; - } + framecnt_t nread = 0; - AudioBufferList abl; - abl.mNumberBuffers = 1; - abl.mBuffers[0].mNumberChannels = n_channels; + while (nread < cnt) { + + try { + af.Seek (start+nread); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 to %2 (%3)", cax.mOperation, start+nread, _name.val().substr (1)) << endmsg; + return -1; + } + + UInt32 new_cnt = cnt - nread; + + abl.mBuffers[0].mDataByteSize = new_cnt * n_channels * sizeof(Sample); + abl.mBuffers[0].mData = dst + nread; - UInt32 new_cnt = cnt; - if (n_channels == 1) { - abl.mBuffers[0].mDataByteSize = cnt * sizeof(Sample); - abl.mBuffers[0].mData = dst; try { af.Read (new_cnt, &abl); } catch (CAXException& cax) { error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); + return -1; + } + + if (new_cnt == 0) { + /* EOF */ + if (start+cnt == _length) { + /* we really did hit the end */ + nread = cnt; + } + break; } - _read_data_count = new_cnt * sizeof(float); - return new_cnt; + + nread += new_cnt; } - UInt32 real_cnt = cnt * n_channels; + if (nread < cnt) { + return -1; + } else { + return 0; + } +} + + +framecnt_t +CoreAudioSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const +{ + framecnt_t file_cnt; + AudioBufferList abl; + + abl.mNumberBuffers = 1; + abl.mBuffers[0].mNumberChannels = n_channels; + + if (start > _length) { + + /* read starts beyond end of data, just memset to zero */ + + file_cnt = 0; + + } else if (start + cnt > _length) { + + /* read ends beyond end of data, read some, memset the rest */ - { - Glib::Mutex::Lock lm (_tmpbuf_lock); - - if (tmpbufsize < real_cnt) { - - if (tmpbuf) { - delete [] tmpbuf; + file_cnt = _length - start; + + } else { + + /* read is entirely within data */ + + file_cnt = cnt; + } + + if (file_cnt != cnt) { + frameoffset_t delta = cnt - file_cnt; + memset (dst+file_cnt, 0, sizeof (Sample) * delta); + } + + if (file_cnt) { + + if (n_channels == 1) { + if (safe_read (dst, start, file_cnt, abl) == 0) { + return cnt; } - tmpbufsize = real_cnt; - tmpbuf = new float[tmpbufsize]; + return 0; } + } - abl.mBuffers[0].mDataByteSize = tmpbufsize * sizeof(Sample); - abl.mBuffers[0].mData = tmpbuf; + Sample* interleave_buf = get_interleave_buffer (file_cnt * n_channels); - cerr << "channel: " << _channel << endl; - - try { - af.Read (real_cnt, &abl); - } catch (CAXException& cax) { - error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); - } - float *ptr = tmpbuf + _channel; - real_cnt /= n_channels; - - /* stride through the interleaved data */ - - for (uint32_t n = 0; n < real_cnt; ++n) { - dst[n] = *ptr; - ptr += n_channels; - } + if (safe_read (interleave_buf, start, file_cnt, abl) != 0) { + return 0; + } + + Sample *ptr = interleave_buf + _channel; + + /* stride through the interleaved data */ + + for (framecnt_t n = 0; n < file_cnt; ++n) { + dst[n] = *ptr; + ptr += n_channels; } - _read_data_count = cnt * sizeof(float); - - return real_cnt; + return cnt; } float @@ -172,35 +237,47 @@ CoreAudioSource::sample_rate() const } int -CoreAudioSource::update_header (nframes_t when, struct tm&, time_t) +CoreAudioSource::update_header (framepos_t, struct tm&, time_t) { return 0; } int -CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg) +CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string&) { - FSRef ref; +#ifdef COREAUDIO105 + FSRef ref; +#endif ExtAudioFileRef af = 0; - size_t size; + UInt32 size; CFStringRef name; int ret = -1; +#ifdef COREAUDIO105 if (FSPathMakeRef ((UInt8*)path.c_str(), &ref, 0) != noErr) { goto out; } - + if (ExtAudioFileOpen(&ref, &af) != noErr) { goto out; } - +#else + CFURLRef url = CFURLCreateFromFileSystemRepresentation (kCFAllocatorDefault, (const UInt8*)path.c_str (), strlen (path.c_str ()), false); + OSStatus res = ExtAudioFileOpenURL(url, &af); + if (url) CFRelease (url); + + if (res != noErr) { + goto out; + } +#endif + AudioStreamBasicDescription absd; memset(&absd, 0, sizeof(absd)); size = sizeof(AudioStreamBasicDescription); if (ExtAudioFileGetProperty (af, kExtAudioFileProperty_FileDataFormat, &size, &absd) != noErr) { goto out; } - + _info.samplerate = absd.mSampleRate; _info.channels = absd.mChannelsPerFrame; @@ -208,21 +285,123 @@ CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string& if (ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &size, &_info.length) != noErr) { goto out; } - + size = sizeof(CFStringRef); if (AudioFormatGetProperty(kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name) != noErr) { goto out; } - _info.format_name = CFStringRefToStdString(name); + _info.format_name = ""; + + if (absd.mFormatID == kAudioFormatLinearPCM) { + if (absd.mFormatFlags & kAudioFormatFlagIsBigEndian) { + _info.format_name += "big-endian"; + } else { + _info.format_name += "little-endian"; + } + + char buf[32]; + snprintf (buf, sizeof (buf), " %" PRIu32 " bit", absd.mBitsPerChannel); + _info.format_name += buf; + _info.format_name += '\n'; + + if (absd.mFormatFlags & kAudioFormatFlagIsFloat) { + _info.format_name += "float"; + } else { + if (absd.mFormatFlags & kAudioFormatFlagIsSignedInteger) { + _info.format_name += "signed"; + } else { + _info.format_name += "unsigned"; + } + /* integer is typical, do not show it */ + } + + if (_info.channels > 1) { + if (absd.mFormatFlags & kAudioFormatFlagIsNonInterleaved) { + _info.format_name += " noninterleaved"; + } + /* interleaved is the normal case, do not show it */ + } + + _info.format_name += ' '; + } + + switch (absd.mFormatID) { + case kAudioFormatLinearPCM: + _info.format_name += "PCM"; + break; + + case kAudioFormatAC3: + _info.format_name += "AC3"; + break; + + case kAudioFormat60958AC3: + _info.format_name += "60958 AC3"; + break; + + case kAudioFormatMPEGLayer1: + _info.format_name += "MPEG-1"; + break; + + case kAudioFormatMPEGLayer2: + _info.format_name += "MPEG-2"; + break; + + case kAudioFormatMPEGLayer3: + _info.format_name += "MPEG-3"; + break; + + case kAudioFormatAppleIMA4: + _info.format_name += "IMA-4"; + break; + + case kAudioFormatMPEG4AAC: + _info.format_name += "AAC"; + break; + + case kAudioFormatMPEG4CELP: + _info.format_name += "CELP"; + break; + + case kAudioFormatMPEG4HVXC: + _info.format_name += "HVXC"; + break; + + case kAudioFormatMPEG4TwinVQ: + _info.format_name += "TwinVQ"; + break; + + /* these really shouldn't show up, but we should do something + somewhere else to make sure that doesn't happen. until + that is guaranteed, print something anyway. + */ + + case kAudioFormatTimeCode: + _info.format_name += "timecode"; + break; + + case kAudioFormatMIDIStream: + _info.format_name += "MIDI"; + break; + + case kAudioFormatParameterValueStream: + _info.format_name += "parameter values"; + break; + } // XXX it would be nice to find a way to get this information if it exists _info.timecode = 0; ret = 0; - - out: + +out: ExtAudioFileDispose (af); return ret; - + +} + +void +CoreAudioSource::set_path (const string& p) +{ + FileSource::set_path (p); }