X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsndfilesource.cc;h=7c4859aa55a06a1bee9d8569365e62102ffd8013;hb=badc087263990ecf360792c10e4d9f2d60828d43;hp=17e45c11d762998a76bdc5b34ceec3726ead8e7e;hpb=45d35d0732e18501762dc7c9bc87b519c2590447;p=ardour.git diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index 17e45c11d7..7c4859aa55 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include @@ -26,9 +25,9 @@ #include #include - +#include #include - +#include #include #include "i18n.h" @@ -36,6 +35,7 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +using Glib::ustring; gain_t* SndFileSource::out_coefficient = 0; gain_t* SndFileSource::in_coefficient = 0; @@ -45,33 +45,50 @@ const AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSou AudioFileSource::RemovableIfEmpty| AudioFileSource::CanRename); +struct SizedSampleBuffer { + nframes_t size; + Sample* buf; + + SizedSampleBuffer (nframes_t sz) : size (sz) { + buf = new Sample[size]; + } + + ~SizedSampleBuffer() { + delete [] buf; + } +}; + +Glib::StaticPrivate thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT; + SndFileSource::SndFileSource (Session& s, const XMLNode& node) : AudioFileSource (s, node) { - init (_name); + init (); if (open()) { throw failed_constructor (); } } -SndFileSource::SndFileSource (Session& s, string idstr, Flag flags) - /* files created this way are never writable or removable */ - : AudioFileSource (s, idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) +SndFileSource::SndFileSource (Session& s, ustring path, int chn, Flag flags) + /* files created this way are never writable or removable */ + : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) { - init (idstr); + _channel = chn; + + init (); if (open()) { throw failed_constructor (); } } -SndFileSource::SndFileSource (Session& s, string idstr, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags) - : AudioFileSource (s, idstr, flags, sfmt, hf) +SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags) + : AudioFileSource (s, path, flags, sfmt, hf) { int fmt = 0; - init (idstr); + init (); /* this constructor is used to construct new files, not open existing ones. @@ -120,6 +137,10 @@ SndFileSource::SndFileSource (Session& s, string idstr, SampleFormat sfmt, Heade case FormatInt24: fmt |= SF_FORMAT_PCM_24; break; + + case FormatInt16: + fmt |= SF_FORMAT_PCM_16; + break; } _info.channels = 1; @@ -174,30 +195,19 @@ SndFileSource::SndFileSource (Session& s, string idstr, SampleFormat sfmt, Heade } void -SndFileSource::init (string idstr) +SndFileSource::init () { - string::size_type pos; - string file; + ustring file; - interleave_buf = 0; - interleave_bufsize = 0; + // lets try to keep the object initalizations here at the top + xfade_buf = 0; sf = 0; _broadcast_info = 0; - string tmp_name; - - if ((pos = idstr.find_last_of (':')) == string::npos) { - channel = 0; - tmp_name = idstr; - } else { - channel = atoi (idstr.substr (pos+1).c_str()); - tmp_name = idstr.substr (0, pos); - } - if (is_embedded()) { - _name = tmp_name; + _name = _path; } else { - _name = Glib::path_get_basename (tmp_name); + _name = Glib::path_get_basename (_path); } /* although libsndfile says we don't need to set this, @@ -210,10 +220,10 @@ SndFileSource::init (string idstr) _capture_end = false; file_pos = 0; - if (destructive()) { + if (destructive()) { xfade_buf = new Sample[xfade_frames]; timeline_position = header_position_offset; - } + } AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change)); } @@ -229,8 +239,8 @@ SndFileSource::open () 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; + 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; return -1; @@ -241,27 +251,14 @@ SndFileSource::open () _broadcast_info = new SF_BROADCAST_INFO; memset (_broadcast_info, 0, sizeof (*_broadcast_info)); - /* lookup broadcast info */ - - if (sf_command (sf, SFC_GET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) { - - /* if the file has data but no broadcast info, then clearly, there is no broadcast info */ + bool timecode_info_exists; - if (_length) { - delete _broadcast_info; - _broadcast_info = 0; - _flags = Flag (_flags & ~Broadcast); - } - - set_timeline_position (header_position_offset); - - } else { - - /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits - of the time reference. - */ + set_timeline_position (get_timecode_info (sf, _broadcast_info, timecode_info_exists)); - set_timeline_position ( _broadcast_info->time_reference_low ); + if (!timecode_info_exists) { + delete _broadcast_info; + _broadcast_info = 0; + _flags = Flag (_flags & ~Broadcast); } if (writable()) { @@ -288,10 +285,6 @@ SndFileSource::~SndFileSource () touch_peakfile (); } - if (interleave_buf) { - delete [] interleave_buf; - } - if (_broadcast_info) { delete _broadcast_info; } @@ -357,17 +350,10 @@ SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const real_cnt = cnt * _info.channels; - if (interleave_bufsize < real_cnt) { - - if (interleave_buf) { - delete [] interleave_buf; - } - interleave_bufsize = real_cnt; - interleave_buf = new float[interleave_bufsize]; - } + Sample* interleave_buf = get_interleave_buffer (real_cnt); nread = sf_read_float (sf, interleave_buf, real_cnt); - ptr = interleave_buf + channel; + ptr = interleave_buf + _channel; nread /= _info.channels; /* stride through the interleaved data */ @@ -396,6 +382,7 @@ nframes_t SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) { if (!writable()) { + warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg; return 0; } @@ -407,7 +394,7 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) nframes_t oldlen; int32_t frame_pos = _length; - + if (write_float (data, frame_pos, cnt) != cnt) { return 0; } @@ -416,29 +403,7 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) 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 (shared_from_this ()); + compute_and_write_peaks (data, frame_pos, cnt, false, true); } _write_data_count = cnt; @@ -452,6 +417,7 @@ SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt) nframes_t old_file_pos; if (!writable()) { + warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg; return 0; } @@ -527,32 +493,12 @@ SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt) old_file_pos = file_pos; update_length (file_pos, cnt); - file_pos += cnt; if (_build_peakfiles) { - PeakBuildRecord *pbr = 0; - - if (pending_peak_builds.size()) { - pbr = pending_peak_builds.back(); - } - - if (pbr && pbr->frame + pbr->cnt == old_file_pos) { - - /* 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 (old_file_pos, cnt)); - } - - _peaks_built = false; + compute_and_write_peaks (data, file_pos, cnt, false, true); } - if (_build_peakfiles) { - queue_for_peaks (shared_from_this ()); - } + file_pos += cnt; return cnt; } @@ -575,6 +521,7 @@ int SndFileSource::flush_header () { if (!writable() || (sf == 0)) { + warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg; return -1; } return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE); @@ -584,6 +531,7 @@ int SndFileSource::setup_broadcast_info (nframes_t when, struct tm& now, time_t tnow) { if (!writable()) { + warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg; return -1; } @@ -636,8 +584,8 @@ SndFileSource::set_header_timeline_position () return; } - _broadcast_info->time_reference_high = (timeline_position >> 32); - _broadcast_info->time_reference_low = (timeline_position & 0xffffffff); + _broadcast_info->time_reference_high = (timeline_position >> 32); + _broadcast_info->time_reference_low = (timeline_position & 0xffffffff); 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; @@ -645,9 +593,6 @@ SndFileSource::set_header_timeline_position () delete _broadcast_info; _broadcast_info = 0; } - - - } nframes_t @@ -678,11 +623,15 @@ SndFileSource::set_destructive (bool yn) { if (yn) { _flags = Flag (_flags | Destructive); + if (!xfade_buf) { + xfade_buf = new Sample[xfade_frames]; + } clear_capture_marks (); timeline_position = header_position_offset; } else { _flags = Flag (_flags & ~Destructive); timeline_position = 0; + /* leave xfade buf alone in case we need it again later */ } return true; @@ -803,7 +752,7 @@ SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in) } } - } else if (xfade) { + } else if (xfade < xfade_frames) { gain_t in[xfade]; gain_t out[xfade]; @@ -815,6 +764,11 @@ SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in) for (nframes_t n = 0; n < xfade; ++n) { xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]); } + + } else if (xfade) { + + /* long xfade length, has to be computed across several calls */ + } if (xfade) { @@ -837,7 +791,11 @@ SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in) nframes_t SndFileSource::last_capture_start_frame () const { - return capture_start_frame; + if (destructive()) { + return capture_start_frame; + } else { + return 0; + } } void @@ -863,8 +821,6 @@ SndFileSource::setup_standard_crossfades (nframes_t rate) xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate); - cerr << "based on " << Config->get_destructive_xfade_msecs() << " msecs, xfade_frames = " << xfade_frames << endl; - if (out_coefficient) { delete [] out_coefficient; } @@ -880,7 +836,7 @@ SndFileSource::setup_standard_crossfades (nframes_t rate) } void -SndFileSource::set_timeline_position (nframes_t pos) +SndFileSource::set_timeline_position (int64_t pos) { // destructive track timeline postion does not change // except at instantion or when header_position_offset @@ -890,3 +846,81 @@ SndFileSource::set_timeline_position (nframes_t pos) AudioFileSource::set_timeline_position (pos); } } + +int +SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg) +{ + SNDFILE *sf; + SF_INFO sf_info; + SF_BROADCAST_INFO binfo; + bool timecode_exists; + + sf_info.format = 0; // libsndfile says to clear this before sf_open(). + + if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) { + char errbuf[256]; + error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1); + return false; + } + + info.samplerate = sf_info.samplerate; + info.channels = sf_info.channels; + info.length = sf_info.frames; + info.format_name = string_compose("Format: %1, %2", + sndfile_major_format(sf_info.format), + sndfile_minor_format(sf_info.format)); + + memset (&binfo, 0, sizeof (binfo)); + info.timecode = get_timecode_info (sf, &binfo, timecode_exists); + + if (!timecode_exists) { + info.timecode = 0; + } + + sf_close (sf); + + return true; +} + +int64_t +SndFileSource::get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists) +{ + if (sf_command (sf, SFC_GET_BROADCAST_INFO, binfo, sizeof (*binfo)) != SF_TRUE) { + exists = false; + return (header_position_offset); + } + + /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits + of the time reference. + */ + + exists = true; + int64_t ret = (uint32_t) binfo->time_reference_high; + ret <<= 32; + ret |= (uint32_t) binfo->time_reference_low; + return ret; +} + +bool +SndFileSource::one_of_several_channels () const +{ + return _info.channels > 1; +} + +Sample* +SndFileSource::get_interleave_buffer (nframes_t size) +{ + SizedSampleBuffer* ssb; + + if ((ssb = thread_interleave_buffer.get()) == 0) { + ssb = new SizedSampleBuffer (size); + thread_interleave_buffer.set (ssb); + } + + if (ssb->size < size) { + ssb = new SizedSampleBuffer (size); + thread_interleave_buffer.set (ssb); + } + + return ssb->buf; +}