X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fcoreaudiosource.cc;h=90ab07b86d5df7ac1f5104b7cfea6b44ef9f8131;hb=cf52d6e4b40111eb04b244ec054055a4ec15dbe0;hp=d2a9a8c8e650f9df0d1c89748b583b90301480eb;hpb=d752986314eb37151983393c1d62efefe503e47c;p=ardour.git diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc index d2a9a8c8e6..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,210 +18,218 @@ */ -#include -#include +#include +#include -#include "i18n.h" +#include "pbd/error.h" +#include "ardour/coreaudiosource.h" +#include "ardour/utils.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; -CoreAudioSource::CoreAudioSource (const XMLNode& node) - : AudioFileSource (node) +/** Create a new CoreAudioSource using session state, which implies that the + * file must already exist. + */ +CoreAudioSource::CoreAudioSource (Session& s, const XMLNode& node) + : Source (s, node) + , AudioFileSource (s, node) { - init (_name); - - SourceCreated (this); /* EMIT SIGNAL */ + init_cafile (); + + assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS)); + existence_check (); } -CoreAudioSource::CoreAudioSource (const string& idstr, Flag flags) - : AudioFileSource(idstr, flags) +/** 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) + : Source (s, DataType::AUDIO, path, Source::Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))), + AudioFileSource (s, path, + Source::Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) { - init (idstr); + _channel = chn; + init_cafile (); - SourceCreated (this); /* EMIT SIGNAL */ + assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS)); + existence_check (); } -void -CoreAudioSource::init (const string& idstr) +void +CoreAudioSource::init_cafile () { - string::size_type pos; - string file; + /* note that we temporarily truncated _id at the colon */ + try { + af.Open(_path.c_str()); - tmpbuf = 0; - tmpbufsize = 0; - af = 0; - OSStatus err = noErr; + CAStreamBasicDescription file_format (af.GetFileDataFormat()); + n_channels = file_format.NumberChannels(); - _name = idstr; + 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(); + } - if ((pos = idstr.find_last_of (':')) == string::npos) { - channel = 0; - file = idstr; - } else { - channel = atoi (idstr.substr (pos+1).c_str()); - file = idstr.substr (0, pos); - } + _length = af.GetNumberFrames(); - /* note that we temporarily truncated _id at the colon */ - FSRef fsr; - err = FSPathMakeRef ((UInt8*)file.c_str(), &fsr, 0); - if (err != noErr) { - error << string_compose (_("Could not make reference to file: %1"), name()) << endmsg; - throw failed_constructor(); - } - - err = ExtAudioFileOpen (&fsr, &af); - if (err != noErr) { - error << string_compose (_("Could not open file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } - - AudioStreamBasicDescription file_asbd; - memset(&file_asbd, 0, sizeof(AudioStreamBasicDescription)); - size_t asbd_size = sizeof(AudioStreamBasicDescription); - err = ExtAudioFileGetProperty(af, - kExtAudioFileProperty_FileDataFormat, &asbd_size, &file_asbd); - if (err != noErr) { - error << string_compose (_("Could not get file data format for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } - n_channels = file_asbd.mChannelsPerFrame; - - cerr << "number of channels: " << n_channels << endl; - - if (channel >= n_channels) { - error << string_compose(_("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number"), n_channels, channel) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } - - int64_t ca_frames; - size_t prop_size = sizeof(int64_t); - - err = ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &prop_size, &ca_frames); - if (err != noErr) { - error << string_compose (_("Could not get file length for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } - - _length = ca_frames; - _path = file; - - AudioStreamBasicDescription client_asbd; - memset(&client_asbd, 0, sizeof(AudioStreamBasicDescription)); - client_asbd.mSampleRate = file_asbd.mSampleRate; - client_asbd.mFormatID = kAudioFormatLinearPCM; - client_asbd.mFormatFlags = kLinearPCMFormatFlagIsFloat; - client_asbd.mBytesPerPacket = file_asbd.mChannelsPerFrame * 4; - client_asbd.mFramesPerPacket = 1; - client_asbd.mBytesPerFrame = client_asbd.mBytesPerPacket; - client_asbd.mChannelsPerFrame = file_asbd.mChannelsPerFrame; - client_asbd.mBitsPerChannel = 32; - - err = ExtAudioFileSetProperty (af, kExtAudioFileProperty_ClientDataFormat, asbd_size, &client_asbd); - if (err != noErr) { - error << string_compose (_("Could not set client data format for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); + 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: cannot open file \"%1\" for %2"), + _path, (writable() ? "read+write" : "reading")) << endmsg; throw failed_constructor (); } - - if (_build_peakfiles) { - if (initialize_peakfile (false, file)) { - error << string_compose(_("initialize peakfile failed for file %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor (); - } - } } CoreAudioSource::~CoreAudioSource () { - GoingAway (this); /* EMIT SIGNAL */ - - if (af) { - ExtAudioFileDispose (af); - } +} - if (tmpbuf) { - delete [] tmpbuf; - } +void +CoreAudioSource::close () +{ + af.Close (); } -jack_nframes_t -CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const +int +CoreAudioSource::safe_read (Sample* dst, framepos_t start, framecnt_t cnt, AudioBufferList& abl) const { - OSStatus err = noErr; + framecnt_t nread = 0; + + 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; + + 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; + } - err = ExtAudioFileSeek(af, start); - if (err != noErr) { - error << string_compose(_("CoreAudioSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), err) << endmsg; + nread += new_cnt; + } + + 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; - abl.mBuffers[0].mDataByteSize = cnt * sizeof(Sample); - abl.mBuffers[0].mData = dst; - if (n_channels == 1) { - err = ExtAudioFileRead(af, (UInt32*) &cnt, &abl); - _read_data_count = cnt * sizeof(float); - return cnt; + 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 */ + + file_cnt = _length - start; + + } else { + + /* read is entirely within data */ + + file_cnt = cnt; } - uint32_t real_cnt = cnt * n_channels; + if (file_cnt != cnt) { + frameoffset_t delta = cnt - file_cnt; + memset (dst+file_cnt, 0, sizeof (Sample) * delta); + } - { - Glib::Mutex::Lock lm (_tmpbuf_lock); - - if (tmpbufsize < real_cnt) { - - if (tmpbuf) { - delete [] tmpbuf; + 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 = real_cnt * sizeof(Sample); - abl.mBuffers[0].mData = tmpbuf; - - err = ExtAudioFileRead(af, (UInt32*) &real_cnt, &abl); - 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; - } + Sample* interleave_buf = get_interleave_buffer (file_cnt * n_channels); + + if (safe_read (interleave_buf, start, file_cnt, abl) != 0) { + return 0; } - _read_data_count = cnt * sizeof(float); - - return real_cnt; + 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; + } + + return cnt; } float CoreAudioSource::sample_rate() const { - AudioStreamBasicDescription client_asbd; - memset(&client_asbd, 0, sizeof(AudioStreamBasicDescription)); - - OSStatus err = noErr; - size_t asbd_size = sizeof(AudioStreamBasicDescription); + CAStreamBasicDescription client_asbd; - err = ExtAudioFileSetProperty (af, kExtAudioFileProperty_ClientDataFormat, asbd_size, &client_asbd); - if (err != noErr) { - error << string_compose(_("Could not detect samplerate for: %1"), name()) << endmsg; + try { + client_asbd = af.GetClientDataFormat (); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); return 0.0; } @@ -228,8 +237,171 @@ CoreAudioSource::sample_rate() const } int -CoreAudioSource::update_header (jack_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&) +{ +#ifdef COREAUDIO105 + FSRef ref; +#endif + ExtAudioFileRef af = 0; + 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; + + size = sizeof(_info.length); + 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 = ""; + + 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: + ExtAudioFileDispose (af); + return ret; + +} + +void +CoreAudioSource::set_path (const string& p) +{ + FileSource::set_path (p); +}