summaryrefslogtreecommitdiff
path: root/libs/ardour/sndfilesource.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour/sndfilesource.cc')
-rw-r--r--libs/ardour/sndfilesource.cc476
1 files changed, 413 insertions, 63 deletions
diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc
index 87f7faf423..6929385141 100644
--- a/libs/ardour/sndfilesource.cc
+++ b/libs/ardour/sndfilesource.cc
@@ -18,47 +18,203 @@
$Id$
*/
+#include <cerrno>
+#include <climits>
+
+#include <pwd.h>
+#include <sys/utsname.h>
+
+#include <glibmm/miscutils.h>
+
#include <ardour/sndfilesource.h>
#include "i18n.h"
+using namespace std;
using namespace ARDOUR;
SndFileSource::SndFileSource (const XMLNode& node)
- : ExternalSource (node)
+ : AudioFileSource (node)
{
- init (_name, true);
- SourceCreated (this); /* EMIT SIGNAL */
+ init (_name);
+
+ if (open()) {
+ throw failed_constructor ();
+ }
+
+ if (_build_peakfiles) {
+ if (initialize_peakfile (false, _path)) {
+ sf_close (sf);
+ sf = 0;
+ throw failed_constructor ();
+ }
+ }
+
+ AudioSourceCreated (this); /* EMIT SIGNAL */
}
-SndFileSource::SndFileSource (const string& idstr, bool build_peak)
- : ExternalSource(idstr, build_peak)
+SndFileSource::SndFileSource (string idstr, Flag flags)
+ : AudioFileSource (idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
{
- init (idstr, build_peak);
+ init (idstr);
- if (build_peak) {
- SourceCreated (this); /* EMIT SIGNAL */
+ if (open()) {
+ throw failed_constructor ();
}
+
+ if (_build_peakfiles) {
+ if (initialize_peakfile (false, _path)) {
+ sf_close (sf);
+ sf = 0;
+ throw failed_constructor ();
+ }
+ }
+
+
+ AudioSourceCreated (this); /* EMIT SIGNAL */
+}
+
+SndFileSource::SndFileSource (string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags)
+ : AudioFileSource(idstr, flags, sfmt, hf)
+{
+ int fmt = 0;
+
+ init (idstr);
+
+ cerr << "creating " << idstr << " hf = " << hf << endl;
+
+ switch (hf) {
+ case CAF:
+ fmt = SF_FORMAT_CAF;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ case AIFF:
+ fmt = SF_FORMAT_AIFF;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ case BWF:
+ fmt = SF_FORMAT_WAV;
+ _flags = Flag (_flags | Broadcast);
+ break;
+
+ case WAVE:
+ fmt = SF_FORMAT_WAV;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ case WAVE64:
+ fmt = SF_FORMAT_W64;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ default:
+ fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
+ /*NOTREACHED*/
+ break;
+
+ }
+
+ switch (sfmt) {
+ case FormatFloat:
+ fmt |= SF_FORMAT_FLOAT;
+ break;
+
+ case FormatInt24:
+ fmt |= SF_FORMAT_PCM_24;
+ break;
+ }
+
+ _info.channels = 1;
+ _info.samplerate = rate;
+ _info.format = fmt;
+
+ if (open()) {
+ throw failed_constructor();
+ }
+
+ if (writable() && (_flags & Broadcast)) {
+
+ _broadcast_info = new SF_BROADCAST_INFO;
+ memset (_broadcast_info, 0, sizeof (*_broadcast_info));
+
+ snprintf (_broadcast_info->description, sizeof (_broadcast_info->description), "BWF %s", _name.c_str());
+
+ struct utsname utsinfo;
+
+ if (uname (&utsinfo)) {
+ error << string_compose(_("FileSource: cannot get host information for BWF header (%1)"), strerror(errno)) << endmsg;
+ return;
+ }
+
+ snprintf (_broadcast_info->originator, sizeof (_broadcast_info->originator), "ardour:%s:%s:%s:%s:%s)",
+ Glib::get_real_name().c_str(),
+ utsinfo.nodename,
+ utsinfo.sysname,
+ utsinfo.release,
+ utsinfo.version);
+
+ _broadcast_info->version = 1;
+
+ /* XXX do something about this field */
+
+ snprintf (_broadcast_info->umid, sizeof (_broadcast_info->umid), "%s", "fnord");
+
+ /* coding history is added by libsndfile */
+
+ if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (_broadcast_info)) != SF_TRUE) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"), _path, errbuf) << endmsg;
+ _flags = Flag (_flags & ~Broadcast);
+ delete _broadcast_info;
+ _broadcast_info = 0;
+ }
+ }
+
+ if (_build_peakfiles) {
+ if (initialize_peakfile (false, _path)) {
+ sf_close (sf);
+ sf = 0;
+ throw failed_constructor ();
+ }
+ }
+
+ /* since SndFileSource's constructed with this constructor can be writable, make sure we update if the header info changes */
+
+ if (writable()) {
+ HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioFileSource::handle_header_position_change));
+ }
+
+ if (_build_peakfiles) {
+ if (initialize_peakfile (false, _path)) {
+ sf_close (sf);
+ sf = 0;
+ throw failed_constructor ();
+ }
+ }
+
+ AudioSourceCreated (this); /* EMIT SIGNAL */
}
void
-SndFileSource::init (const string& idstr, bool build_peak)
+SndFileSource::init (const string& idstr)
{
string::size_type pos;
string file;
- tmpbuf = 0;
- tmpbufsize = 0;
+ interleave_buf = 0;
+ interleave_bufsize = 0;
sf = 0;
-
- _name = idstr;
+ _broadcast_info = 0;
if ((pos = idstr.find_last_of (':')) == string::npos) {
channel = 0;
- file = idstr;
+ _name = Glib::path_get_basename (idstr);
} else {
channel = atoi (idstr.substr (pos+1).c_str());
- file = idstr.substr (0, pos);
+ _name = Glib::path_get_basename (idstr.substr (0, pos));
}
/* although libsndfile says we don't need to set this,
@@ -66,46 +222,56 @@ SndFileSource::init (const string& idstr, bool build_peak)
*/
memset (&_info, 0, sizeof(_info));
+}
- /* note that we temporarily truncated _id at the colon */
-
- if ((sf = sf_open (file.c_str(), SFM_READ, &_info)) == 0) {
+int
+SndFileSource::open ()
+{
+ if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
char errbuf[256];
sf_error_str (0, errbuf, sizeof (errbuf) - 1);
- error << string_compose(_("SndFileSource: cannot open file \"%1\" (%2)"), file, errbuf) << endmsg;
- throw failed_constructor();
+ error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
+ _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
+ return -1;
}
if (channel >= _info.channels) {
error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, channel) << endmsg;
sf_close (sf);
sf = 0;
- throw failed_constructor();
+ return -1;
}
_length = _info.frames;
- _path = file;
- if (build_peak) {
- if (initialize_peakfile (false, _path)) {
- sf_close (sf);
- sf = 0;
- throw failed_constructor ();
- }
+ if (writable()) {
+ sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
+ }
+
+ return 0;
+}
+
+void
+SndFileSource::close ()
+{
+ if (sf) {
+ sf_close (sf);
+ sf = 0;
}
}
SndFileSource::~SndFileSource ()
-
{
GoingAway (this); /* EMIT SIGNAL */
- if (sf) {
- sf_close (sf);
+ close ();
+
+ if (interleave_buf) {
+ delete [] interleave_buf;
}
- if (tmpbuf) {
- delete [] tmpbuf;
+ if (_broadcast_info) {
+ delete [] _broadcast_info;
}
}
@@ -116,49 +282,73 @@ SndFileSource::sample_rate () const
}
jack_nframes_t
-SndFileSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
+SndFileSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
{
int32_t nread;
float *ptr;
uint32_t real_cnt;
+ jack_nframes_t file_cnt;
- if (sf_seek (sf, (off_t) start, SEEK_SET) < 0) {
- char errbuf[256];
- sf_error_str (0, errbuf, sizeof (errbuf) - 1);
- error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
- return 0;
- }
+ if (start > _length) {
- if (_info.channels == 1) {
- jack_nframes_t ret = sf_read_float (sf, dst, cnt);
- _read_data_count = cnt * sizeof(float);
- return ret;
- }
+ /* read starts beyond end of data, just memset to zero */
+
+ file_cnt = 0;
- real_cnt = cnt * _info.channels;
+ } else if (start + cnt > _length) {
+
+ /* read ends beyond end of data, read some, memset the rest */
+
+ file_cnt = _length - start;
- {
- Glib::Mutex::Lock lm (_tmpbuf_lock);
+ } else {
- if (tmpbufsize < real_cnt) {
-
- if (tmpbuf) {
- delete [] tmpbuf;
- }
- tmpbufsize = real_cnt;
- tmpbuf = new float[tmpbufsize];
+ /* read is entirely within data */
+
+ file_cnt = cnt;
+ }
+
+ if (file_cnt) {
+
+ if (sf_seek (sf, (off_t) start, SEEK_SET) < 0) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
+ return 0;
}
- nread = sf_read_float (sf, tmpbuf, real_cnt);
- ptr = tmpbuf + channel;
- nread /= _info.channels;
-
- /* stride through the interleaved data */
+ if (_info.channels == 1) {
+ jack_nframes_t ret = sf_read_float (sf, dst, file_cnt);
+ _read_data_count = cnt * sizeof(float);
+ return ret;
+ }
+ }
+
+ if (file_cnt != cnt) {
+ jack_nframes_t delta = cnt - file_cnt;
+ memset (dst+file_cnt, 0, sizeof (Sample) * delta);
+ }
+
+ real_cnt = cnt * _info.channels;
+
+ if (interleave_bufsize < real_cnt) {
- for (int32_t n = 0; n < nread; ++n) {
- dst[n] = *ptr;
- ptr += _info.channels;
+ if (interleave_buf) {
+ delete [] interleave_buf;
}
+ interleave_bufsize = real_cnt;
+ interleave_buf = new float[interleave_bufsize];
+ }
+
+ nread = sf_read_float (sf, interleave_buf, real_cnt);
+ ptr = interleave_buf + channel;
+ nread /= _info.channels;
+
+ /* stride through the interleaved data */
+
+ for (int32_t n = 0; n < nread; ++n) {
+ dst[n] = *ptr;
+ ptr += _info.channels;
}
_read_data_count = cnt * sizeof(float);
@@ -166,3 +356,163 @@ SndFileSource::read (Sample *dst, jack_nframes_t start, jack_nframes_t cnt, char
return nread;
}
+jack_nframes_t
+SndFileSource::write_unlocked (Sample *data, jack_nframes_t cnt, char * workbuf)
+{
+ if (!writable()) {
+ return 0;
+ }
+
+ if (_info.channels != 1) {
+ fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
+ /*NOTREACHED*/
+ return 0;
+ }
+
+ jack_nframes_t oldlen;
+ int32_t frame_pos = _length;
+
+ if (write_float (data, frame_pos, cnt) != cnt) {
+ return 0;
+ }
+
+ oldlen = _length;
+ update_length (oldlen, cnt);
+
+ if (_build_peakfiles) {
+ PeakBuildRecord *pbr = 0;
+
+ if (pending_peak_builds.size()) {
+ pbr = pending_peak_builds.back();
+ }
+
+ if (pbr && pbr->frame + pbr->cnt == oldlen) {
+
+ /* the last PBR extended to the start of the current write,
+ so just extend it again.
+ */
+
+ pbr->cnt += cnt;
+ } else {
+ pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt));
+ }
+
+ _peaks_built = false;
+ }
+
+
+ if (_build_peakfiles) {
+ queue_for_peaks (*this);
+ }
+
+ _write_data_count = cnt;
+
+ return cnt;
+}
+
+int
+SndFileSource::update_header (jack_nframes_t when, struct tm& now, time_t tnow)
+{
+ /* allow derived classes to override how this is done */
+
+ set_timeline_position (when);
+
+ if (_flags & Broadcast) {
+ /* this will flush the header implicitly */
+ return setup_broadcast_info (when, now, tnow);
+ } else {
+ return flush_header ();
+ }
+}
+
+int
+SndFileSource::flush_header ()
+{
+ return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
+}
+
+int
+SndFileSource::setup_broadcast_info (jack_nframes_t when, struct tm& now, time_t tnow)
+{
+ /* random code is 9 digits */
+
+ int random_code = random() % 999999999;
+
+ snprintf (_broadcast_info->originator_reference, sizeof (_broadcast_info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
+ bwf_country_code,
+ bwf_organization_code,
+ bwf_serial_number,
+ now.tm_hour,
+ now.tm_min,
+ now.tm_sec,
+ random_code);
+
+ snprintf (_broadcast_info->origination_date, sizeof (_broadcast_info->origination_date), "%4d-%02d-%02d",
+ 1900 + now.tm_year,
+ now.tm_mon,
+ now.tm_mday);
+
+ snprintf (_broadcast_info->origination_time, sizeof (_broadcast_info->origination_time), "%02d-%02d-%02d",
+ now.tm_hour,
+ now.tm_min,
+ now.tm_sec);
+
+ /* now update header position taking header offset into account */
+
+ set_header_timeline_position ();
+
+ /* note that libsndfile flushes the header to disk when resetting the broadcast info */
+
+ if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
+ error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
+ _flags = Flag (_flags & ~Broadcast);
+ delete _broadcast_info;
+ _broadcast_info = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+SndFileSource::set_header_timeline_position ()
+{
+ uint64_t pos;
+
+ _broadcast_info->time_reference_high = 0;
+
+ if (header_position_negative) {
+
+ if (ULONG_LONG_MAX - header_position_offset < timeline_position) {
+ pos = ULONG_LONG_MAX; // impossible
+ } else {
+ pos = timeline_position + header_position_offset;
+ }
+
+ } else {
+
+ if (timeline_position < header_position_offset) {
+ pos = 0;
+ } else {
+ pos = timeline_position - header_position_offset;
+ }
+ }
+
+ _broadcast_info->time_reference_high = (pos >> 32);
+ _broadcast_info->time_reference_low = (pos & 0xffffffff);
+}
+
+jack_nframes_t
+SndFileSource::write_float (Sample* data, jack_nframes_t frame_pos, jack_nframes_t cnt)
+{
+ if (sf_seek (sf, frame_pos, SEEK_SET) != frame_pos) {
+ error << string_compose (_("%1: cannot seek to %2"), _path, frame_pos) << endmsg;
+ return 0;
+ }
+
+ if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
+ return 0;
+ }
+
+ return cnt;
+}