summaryrefslogtreecommitdiff
path: root/libs/ardour/audiosource.cc
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2008-06-02 21:41:35 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2008-06-02 21:41:35 +0000
commit449aab3c465bbbf66d221fac3d7ea559f1720357 (patch)
tree6843cc40c88250a132acac701271f1504cd2df04 /libs/ardour/audiosource.cc
parent9c0d7d72d70082a54f823cd44c0ccda5da64bb6f (diff)
rollback to 3428, before the mysterious removal of libs/* at 3431/3432
git-svn-id: svn://localhost/ardour2/branches/3.0@3435 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour/audiosource.cc')
-rw-r--r--libs/ardour/audiosource.cc932
1 files changed, 932 insertions, 0 deletions
diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc
new file mode 100644
index 0000000000..01dea08d3e
--- /dev/null
+++ b/libs/ardour/audiosource.cc
@@ -0,0 +1,932 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <float.h>
+#include <utime.h>
+#include <cerrno>
+#include <ctime>
+#include <cmath>
+#include <iomanip>
+#include <fstream>
+#include <algorithm>
+#include <vector>
+
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+
+#include <pbd/xml++.h>
+#include <pbd/pthread_utils.h>
+
+#include <ardour/audiosource.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/session.h>
+#include <ardour/transient_detector.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using Glib::ustring;
+
+bool AudioSource::_build_missing_peakfiles = false;
+bool AudioSource::_build_peakfiles = false;
+
+#define _FPP 256
+
+AudioSource::AudioSource (Session& s, ustring name)
+ : Source (s, name, DataType::AUDIO)
+{
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ peakfile = -1;
+ _read_data_count = 0;
+ _write_data_count = 0;
+ peak_leftover_cnt = 0;
+ peak_leftover_size = 0;
+ peak_leftovers = 0;
+}
+
+AudioSource::AudioSource (Session& s, const XMLNode& node)
+ : Source (s, node)
+{
+
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ peakfile = -1;
+ _read_data_count = 0;
+ _write_data_count = 0;
+ peak_leftover_cnt = 0;
+ peak_leftover_size = 0;
+ peak_leftovers = 0;
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+}
+
+AudioSource::~AudioSource ()
+{
+ /* shouldn't happen but make sure we don't leak file descriptors anyway */
+
+ if (peak_leftover_cnt) {
+ cerr << "AudioSource destroyed with leftover peak data pending" << endl;
+ }
+
+ if (peakfile >= 0) {
+ ::close (peakfile);
+ }
+
+ if (peak_leftovers) {
+ delete [] peak_leftovers;
+ }
+}
+
+XMLNode&
+AudioSource::get_state ()
+{
+ XMLNode& node (Source::get_state());
+
+ if (_captured_for.length()) {
+ node.add_property ("captured-for", _captured_for);
+ }
+
+ return node;
+}
+
+int
+AudioSource::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+
+ Source::set_state (node);
+
+ if ((prop = node.property ("captured-for")) != 0) {
+ _captured_for = prop->value();
+ }
+
+ return 0;
+}
+
+/***********************************************************************
+ PEAK FILE STUFF
+ ***********************************************************************/
+
+bool
+AudioSource::peaks_ready (sigc::slot<void> the_slot, sigc::connection& conn) const
+{
+ bool ret;
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+
+ /* check to see if the peak data is ready. if not
+ connect the slot while still holding the lock.
+ */
+
+ if (!(ret = _peaks_built)) {
+ conn = PeaksReady.connect (the_slot);
+ }
+
+ return ret;
+}
+
+void
+AudioSource::touch_peakfile ()
+{
+ struct stat statbuf;
+
+ if (stat (peakpath.c_str(), &statbuf) != 0 || statbuf.st_size == 0) {
+ return;
+ }
+
+ struct utimbuf tbuf;
+
+ tbuf.actime = statbuf.st_atime;
+ tbuf.modtime = time ((time_t) 0);
+
+ utime (peakpath.c_str(), &tbuf);
+}
+
+int
+AudioSource::rename_peakfile (ustring newpath)
+{
+ /* caller must hold _lock */
+
+ ustring oldpath = peakpath;
+
+ if (access (oldpath.c_str(), F_OK) == 0) {
+ if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
+ error << string_compose (_("cannot rename peakfile for %1 from %2 to %3 (%4)"), _name, oldpath, newpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+ }
+
+ peakpath = newpath;
+
+ return 0;
+}
+
+int
+AudioSource::initialize_peakfile (bool newfile, ustring audio_path)
+{
+ struct stat statbuf;
+
+ peakpath = peak_path (audio_path);
+
+ /* if the peak file should be there, but isn't .... */
+
+ if (!newfile && !Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
+ peakpath = find_broken_peakfile (peakpath, audio_path);
+ }
+
+ if (stat (peakpath.c_str(), &statbuf)) {
+ if (errno != ENOENT) {
+ /* it exists in the peaks dir, but there is some kind of error */
+
+ error << string_compose(_("AudioSource: cannot stat peakfile \"%1\""), peakpath) << endmsg;
+ return -1;
+ }
+
+ /* peakfile does not exist */
+
+ _peaks_built = false;
+
+ } else {
+
+ /* we found it in the peaks dir, so check it out */
+
+ if (statbuf.st_size == 0 || (statbuf.st_size < ((length() / _FPP) * sizeof (PeakData)))) {
+ // empty
+ _peaks_built = false;
+ } else {
+ // Check if the audio file has changed since the peakfile was built.
+ struct stat stat_file;
+ int err = stat (audio_path.c_str(), &stat_file);
+
+ if (err) {
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ } else {
+
+ /* allow 6 seconds slop on checking peak vs. file times because of various
+ disk action "races"
+ */
+
+ if (stat_file.st_mtime > statbuf.st_mtime && (stat_file.st_mtime - statbuf.st_mtime > 6)) {
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ } else {
+ _peaks_built = true;
+ _peak_byte_max = statbuf.st_size;
+ }
+ }
+ }
+ }
+
+ if (!newfile && !_peaks_built && _build_missing_peakfiles && _build_peakfiles) {
+ build_peaks_from_scratch ();
+ }
+
+ return 0;
+}
+
+nframes_t
+AudioSource::read (Sample *dst, nframes_t start, nframes_t cnt) const
+{
+ Glib::Mutex::Lock lm (_lock);
+ return read_unlocked (dst, start, cnt);
+}
+
+nframes_t
+AudioSource::write (Sample *dst, nframes_t cnt)
+{
+ Glib::Mutex::Lock lm (_lock);
+ return write_unlocked (dst, cnt);
+}
+
+int
+AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_visual_peak) const
+{
+ return read_peaks_with_fpp (peaks, npeaks, start, cnt, samples_per_visual_peak, _FPP);
+}
+
+int
+AudioSource::read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt,
+ double samples_per_visual_peak, nframes_t samples_per_file_peak) const
+{
+ Glib::Mutex::Lock lm (_lock);
+ double scale;
+ double expected_peaks;
+ PeakData::PeakDatum xmax;
+ PeakData::PeakDatum xmin;
+ int32_t to_read;
+ uint32_t nread;
+ nframes_t zero_fill = 0;
+ int ret = -1;
+ PeakData* staging = 0;
+ Sample* raw_staging = 0;
+ int _peakfile = -1;
+
+ expected_peaks = (cnt / (double) samples_per_file_peak);
+ scale = npeaks/expected_peaks;
+
+#undef DEBUG_READ_PEAKS
+#ifdef DEBUG_READ_PEAKS
+ cerr << "======>RP: npeaks = " << npeaks
+ << " start = " << start
+ << " cnt = " << cnt
+ << " len = " << _length
+ << " samples_per_visual_peak =" << samples_per_visual_peak
+ << " expected was " << expected_peaks << " ... scale = " << scale
+ << " PD ptr = " << peaks
+ <<endl;
+
+#endif
+
+ /* fix for near-end-of-file conditions */
+
+ if (cnt > _length - start) {
+ // cerr << "too close to end @ " << _length << " given " << start << " + " << cnt << endl;
+ cnt = _length - start;
+ nframes_t old = npeaks;
+ npeaks = min ((nframes_t) floor (cnt / samples_per_visual_peak), npeaks);
+ zero_fill = old - npeaks;
+ }
+
+ // cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl;
+
+ if (npeaks == cnt) {
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "RAW DATA\n";
+#endif
+ /* no scaling at all, just get the sample data and duplicate it for
+ both max and min peak values.
+ */
+
+ Sample* raw_staging = new Sample[cnt];
+
+ if (read_unlocked (raw_staging, start, cnt) != cnt) {
+ error << _("cannot read sample data for unscaled peak computation") << endmsg;
+ return -1;
+ }
+
+ for (nframes_t i = 0; i < npeaks; ++i) {
+ peaks[i].max = raw_staging[i];
+ peaks[i].min = raw_staging[i];
+ }
+
+ delete [] raw_staging;
+ return 0;
+ }
+
+ if (scale == 1.0) {
+
+ off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData);
+
+ /* open, read, close */
+
+ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) {
+ error << string_compose(_("AudioSource: cannot open peakpath (a) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "DIRECT PEAKS\n";
+#endif
+
+ nread = ::pread (_peakfile, peaks, sizeof (PeakData)* npeaks, first_peak_byte);
+ close (_peakfile);
+
+ if (nread != sizeof (PeakData) * npeaks) {
+ cerr << "AudioSource["
+ << _name
+ << "]: cannot read peaks from peakfile! (read only "
+ << nread
+ << " not "
+ << npeaks
+ << "at sample "
+ << start
+ << " = byte "
+ << first_peak_byte
+ << ')'
+ << endl;
+ return -1;
+ }
+
+ if (zero_fill) {
+ memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
+ }
+
+ return 0;
+ }
+
+
+ nframes_t tnp;
+
+ if (scale < 1.0) {
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "DOWNSAMPLE\n";
+#endif
+ /* the caller wants:
+
+ - more frames-per-peak (lower resolution) than the peakfile, or to put it another way,
+ - less peaks than the peakfile holds for the same range
+
+ So, read a block into a staging area, and then downsample from there.
+
+ to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks
+ */
+
+ const uint32_t chunksize = (uint32_t) min (expected_peaks, 65536.0);
+
+ staging = new PeakData[chunksize];
+
+ /* compute the rounded up frame position */
+
+ nframes_t current_frame = start;
+ nframes_t current_stored_peak = (nframes_t) ceil (current_frame / (double) samples_per_file_peak);
+ uint32_t next_visual_peak = (uint32_t) ceil (current_frame / samples_per_visual_peak);
+ double next_visual_peak_frame = next_visual_peak * samples_per_visual_peak;
+ uint32_t stored_peak_before_next_visual_peak = (nframes_t) next_visual_peak_frame / samples_per_file_peak;
+ uint32_t nvisual_peaks = 0;
+ uint32_t stored_peaks_read = 0;
+ uint32_t i = 0;
+
+ /* handle the case where the initial visual peak is on a pixel boundary */
+
+ current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak);
+
+ /* open ... close during out: handling */
+
+ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) {
+ error << string_compose(_("AudioSource: cannot open peakpath (b) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ return 0;
+ }
+
+ while (nvisual_peaks < npeaks) {
+
+ if (i == stored_peaks_read) {
+
+ uint32_t start_byte = current_stored_peak * sizeof(PeakData);
+ tnp = min ((_length/samples_per_file_peak - current_stored_peak), (nframes_t) expected_peaks);
+ to_read = min (chunksize, tnp);
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "read " << sizeof (PeakData) * to_read << " from peakfile @ " << start_byte << endl;
+#endif
+
+ if ((nread = ::pread (_peakfile, staging, sizeof (PeakData) * to_read, start_byte))
+ != sizeof (PeakData) * to_read) {
+
+ off_t fend = lseek (_peakfile, 0, SEEK_END);
+
+ cerr << "AudioSource["
+ << _name
+ << "]: cannot read peak data from peakfile ("
+ << (nread / sizeof(PeakData))
+ << " peaks instead of "
+ << to_read
+ << ") ("
+ << strerror (errno)
+ << ')'
+ << " at start_byte = " << start_byte
+ << " _length = " << _length << " versus len = " << fend
+ << " expected maxpeaks = " << (_length - current_frame)/samples_per_file_peak
+ << " npeaks was " << npeaks
+ << endl;
+ goto out;
+ }
+
+ i = 0;
+ stored_peaks_read = nread / sizeof(PeakData);
+ }
+
+ xmax = -1.0;
+ xmin = 1.0;
+
+ while ((i < stored_peaks_read) && (current_stored_peak <= stored_peak_before_next_visual_peak)) {
+
+ xmax = max (xmax, staging[i].max);
+ xmin = min (xmin, staging[i].min);
+ ++i;
+ ++current_stored_peak;
+ --expected_peaks;
+ }
+
+ peaks[nvisual_peaks].max = xmax;
+ peaks[nvisual_peaks].min = xmin;
+ ++nvisual_peaks;
+ ++next_visual_peak;
+
+ //next_visual_peak_frame = min ((next_visual_peak * samples_per_visual_peak), (next_visual_peak_frame+samples_per_visual_peak) );
+ next_visual_peak_frame = min ((double) start+cnt, (next_visual_peak_frame+samples_per_visual_peak) );
+ stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
+ }
+
+ if (zero_fill) {
+ memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
+ }
+
+ ret = 0;
+
+ } else {
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "UPSAMPLE\n";
+#endif
+ /* the caller wants
+
+ - less frames-per-peak (more resolution)
+ - more peaks than stored in the Peakfile
+
+ So, fetch data from the raw source, and generate peak
+ data on the fly.
+ */
+
+ nframes_t frames_read = 0;
+ nframes_t current_frame = start;
+ nframes_t i = 0;
+ nframes_t nvisual_peaks = 0;
+ nframes_t chunksize = (nframes_t) min (cnt, (nframes_t) 4096);
+ raw_staging = new Sample[chunksize];
+
+ nframes_t frame_pos = start;
+ double pixel_pos = floor (frame_pos / samples_per_visual_peak);
+ double next_pixel_pos = ceil (frame_pos / samples_per_visual_peak);
+ double pixels_per_frame = 1.0 / samples_per_visual_peak;
+
+ xmin = 1.0;
+ xmax = -1.0;
+
+ while (nvisual_peaks < npeaks) {
+
+ if (i == frames_read) {
+
+ to_read = min (chunksize, (_length - current_frame));
+
+ if (to_read == 0) {
+ /* XXX ARGH .. out by one error ... need to figure out why this happens
+ and fix it rather than do this band-aid move.
+ */
+ zero_fill = npeaks - nvisual_peaks;
+ break;
+ }
+
+ if ((frames_read = read_unlocked (raw_staging, current_frame, to_read)) == 0) {
+ error << string_compose(_("AudioSource[%1]: peak read - cannot read %2 samples at offset %3 of %4 (%5)"),
+ _name, to_read, current_frame, _length, strerror (errno))
+ << endmsg;
+ goto out;
+ }
+
+ i = 0;
+ }
+
+ xmax = max (xmax, raw_staging[i]);
+ xmin = min (xmin, raw_staging[i]);
+ ++i;
+ ++current_frame;
+ pixel_pos += pixels_per_frame;
+
+ if (pixel_pos >= next_pixel_pos) {
+
+ peaks[nvisual_peaks].max = xmax;
+ peaks[nvisual_peaks].min = xmin;
+ ++nvisual_peaks;
+ xmin = 1.0;
+ xmax = -1.0;
+
+ next_pixel_pos = ceil (pixel_pos + 0.5);
+ }
+ }
+
+ if (zero_fill) {
+ memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
+ }
+
+ ret = 0;
+ }
+
+ out:
+ if (_peakfile >= 0) {
+ close (_peakfile);
+ }
+
+ if (staging) {
+ delete [] staging;
+ }
+
+ if (raw_staging) {
+ delete [] raw_staging;
+ }
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "RP DONE\n";
+#endif
+
+ return ret;
+}
+
+#undef DEBUG_PEAK_BUILD
+
+int
+AudioSource::build_peaks_from_scratch ()
+{
+ nframes_t current_frame;
+ nframes_t cnt;
+ Sample* buf = 0;
+ nframes_t frames_read;
+ nframes_t frames_to_read;
+ const nframes_t bufsize = 65536; // 256kB per disk read for mono data is about ideal
+
+ int ret = -1;
+
+ {
+ /* hold lock while building peaks */
+
+ Glib::Mutex::Lock lp (_lock);
+
+ if (prepare_for_peakfile_writes ()) {
+ goto out;
+ }
+
+ current_frame = 0;
+ cnt = _length;
+ _peaks_built = false;
+ buf = new Sample[bufsize];
+
+ while (cnt) {
+
+ frames_to_read = min (bufsize, cnt);
+
+ if ((frames_read = read_unlocked (buf, current_frame, frames_to_read)) != frames_to_read) {
+ error << string_compose(_("%1: could not write read raw data for peak computation (%2)"), _name, strerror (errno)) << endmsg;
+ done_with_peakfile_writes (false);
+ goto out;
+ }
+
+ if (compute_and_write_peaks (buf, current_frame, frames_read, true, false, _FPP)) {
+ break;
+ }
+
+ current_frame += frames_read;
+ cnt -= frames_read;
+ }
+
+ if (cnt == 0) {
+ /* success */
+ truncate_peakfile();
+ }
+
+ done_with_peakfile_writes ((cnt == 0));
+ }
+
+ {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+
+ if (_peaks_built) {
+ PeaksReady (); /* EMIT SIGNAL */
+ ret = 0;
+ }
+ }
+
+ out:
+ if (ret) {
+ unlink (peakpath.c_str());
+ }
+
+ if (buf) {
+ delete [] buf;
+ }
+
+ return ret;
+}
+
+int
+AudioSource::prepare_for_peakfile_writes ()
+{
+ if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) {
+ error << string_compose(_("AudioSource: cannot open peakpath (c) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+ return 0;
+}
+
+void
+AudioSource::done_with_peakfile_writes (bool done)
+{
+ if (peak_leftover_cnt) {
+ compute_and_write_peaks (0, 0, 0, true, false, _FPP);
+ }
+
+ if (done) {
+ _peaks_built = true;
+ }
+
+ if (peakfile >= 0) {
+ close (peakfile);
+ peakfile = -1;
+ }
+}
+
+int
+AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready)
+{
+ return compute_and_write_peaks (buf, first_frame, cnt, force, intermediate_peaks_ready, _FPP);
+}
+
+int
+AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force,
+ bool intermediate_peaks_ready, nframes_t fpp)
+{
+ Sample* buf2 = 0;
+ nframes_t to_do;
+ uint32_t peaks_computed;
+ PeakData* peakbuf = 0;
+ int ret = -1;
+ nframes_t current_frame;
+ nframes_t frames_done;
+ const size_t blocksize = (128 * 1024);
+ off_t first_peak_byte;
+
+ if (peakfile < 0) {
+ prepare_for_peakfile_writes ();
+ }
+
+ restart:
+ if (peak_leftover_cnt) {
+
+ if (first_frame != peak_leftover_frame + peak_leftover_cnt) {
+
+ /* uh-oh, ::seek() since the last ::compute_and_write_peaks(),
+ and we have leftovers. flush a single peak (since the leftovers
+ never represent more than that, and restart.
+ */
+
+ PeakData x;
+
+ x.min = peak_leftovers[0];
+ x.max = peak_leftovers[0];
+
+ off_t byte = (peak_leftover_frame / fpp) * sizeof (PeakData);
+
+ if (::pwrite (peakfile, &x, sizeof (PeakData), byte) != sizeof (PeakData)) {
+ error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ _peak_byte_max = max (_peak_byte_max, (off_t) (byte + sizeof(PeakData)));
+
+ {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+ PeakRangeReady (peak_leftover_frame, peak_leftover_cnt); /* EMIT SIGNAL */
+ if (intermediate_peaks_ready) {
+ PeaksReady (); /* EMIT SIGNAL */
+ }
+ }
+
+ /* left overs are done */
+
+ peak_leftover_cnt = 0;
+ goto restart;
+ }
+
+ /* else ... had leftovers, but they immediately preceed the new data, so just
+ merge them and compute.
+ */
+
+ /* make a new contiguous buffer containing leftovers and the new stuff */
+
+ to_do = cnt + peak_leftover_cnt;
+ buf2 = new Sample[to_do];
+
+ /* the remnants */
+ memcpy (buf2, peak_leftovers, peak_leftover_cnt * sizeof (Sample));
+
+ /* the new stuff */
+ memcpy (buf2+peak_leftover_cnt, buf, cnt * sizeof (Sample));
+
+ /* no more leftovers */
+ peak_leftover_cnt = 0;
+
+ /* use the temporary buffer */
+ buf = buf2;
+
+ /* make sure that when we write into the peakfile, we startup where we left off */
+
+ first_frame = peak_leftover_frame;
+
+ } else {
+ to_do = cnt;
+ }
+
+ peakbuf = new PeakData[(to_do/fpp)+1];
+ peaks_computed = 0;
+ current_frame = first_frame;
+ frames_done = 0;
+
+ while (to_do) {
+
+ /* if some frames were passed in (i.e. we're not flushing leftovers)
+ and there are less than fpp to do, save them till
+ next time
+ */
+
+ if (force && (to_do < fpp)) {
+ /* keep the left overs around for next time */
+
+ if (peak_leftover_size < to_do) {
+ delete [] peak_leftovers;
+ peak_leftovers = new Sample[to_do];
+ peak_leftover_size = to_do;
+ }
+ memcpy (peak_leftovers, buf, to_do * sizeof (Sample));
+ peak_leftover_cnt = to_do;
+ peak_leftover_frame = current_frame;
+
+ /* done for now */
+
+ break;
+ }
+
+ nframes_t this_time = min (fpp, to_do);
+
+ peakbuf[peaks_computed].max = buf[0];
+ peakbuf[peaks_computed].min = buf[0];
+
+ ARDOUR::find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max);
+
+ peaks_computed++;
+ buf += this_time;
+ to_do -= this_time;
+ frames_done += this_time;
+ current_frame += this_time;
+ }
+
+ first_peak_byte = (first_frame / fpp) * sizeof (PeakData);
+
+ if (can_truncate_peaks()) {
+
+ /* on some filesystems (ext3, at least) this helps to reduce fragmentation of
+ the peakfiles. its not guaranteed to do so, and even on ext3 (as of december 2006)
+ it does not cause single-extent allocation even for peakfiles of
+ less than BLOCKSIZE bytes. only call ftruncate if we'll make the file larger.
+ */
+
+ off_t endpos = lseek (peakfile, 0, SEEK_END);
+ off_t target_length = blocksize * ((first_peak_byte + blocksize + 1) / blocksize);
+
+ if (endpos < target_length) {
+ ftruncate (peakfile, target_length);
+ /* error doesn't actually matter though, so continue on without testing */
+ }
+ }
+
+ if (::pwrite (peakfile, peakbuf, sizeof (PeakData) * peaks_computed, first_peak_byte) != (ssize_t) (sizeof (PeakData) * peaks_computed)) {
+ error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ _peak_byte_max = max (_peak_byte_max, (off_t) (first_peak_byte + sizeof(PeakData)*peaks_computed));
+
+ if (frames_done) {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+ PeakRangeReady (first_frame, frames_done); /* EMIT SIGNAL */
+ if (intermediate_peaks_ready) {
+ PeaksReady (); /* EMIT SIGNAL */
+ }
+ }
+
+ ret = 0;
+
+ out:
+ delete [] peakbuf;
+ if (buf2) {
+ delete [] buf2;
+ }
+ return ret;
+}
+
+void
+AudioSource::truncate_peakfile ()
+{
+ if (peakfile < 0) {
+ error << string_compose (_("programming error: %1"), "AudioSource::truncate_peakfile() called without open peakfile descriptor")
+ << endmsg;
+ return;
+ }
+
+ /* truncate the peakfile down to its natural length if necessary */
+
+ off_t end = lseek (peakfile, 0, SEEK_END);
+
+ if (end > _peak_byte_max) {
+ ftruncate (peakfile, _peak_byte_max);
+ }
+}
+
+bool
+AudioSource::file_changed (ustring path)
+{
+ struct stat stat_file;
+ struct stat stat_peak;
+
+ int e1 = stat (path.c_str(), &stat_file);
+ int e2 = stat (peak_path(path).c_str(), &stat_peak);
+
+ if (!e1 && !e2 && stat_file.st_mtime > stat_peak.st_mtime){
+ return true;
+ } else {
+ return false;
+ }
+}
+
+nframes_t
+AudioSource::available_peaks (double zoom_factor) const
+{
+ off_t end;
+
+ if (zoom_factor < _FPP) {
+ return length(); // peak data will come from the audio file
+ }
+
+ /* peak data comes from peakfile, but the filesize might not represent
+ the valid data due to ftruncate optimizations, so use _peak_byte_max state.
+ XXX - there might be some atomicity issues here, we should probably add a lock,
+ but _peak_byte_max only monotonically increases after initialization.
+ */
+
+ end = _peak_byte_max;
+
+ return (end/sizeof(PeakData)) * _FPP;
+}
+
+void
+AudioSource::update_length (nframes_t pos, nframes_t cnt)
+{
+ if (pos + cnt > _length) {
+ _length = pos+cnt;
+ }
+}
+