#include <ctime>
#include <cmath>
#include <iomanip>
+#include <fstream>
#include <algorithm>
#include <vector>
-#include <pbd/xml++.h>
-#include <pbd/pthread_utils.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
-#include <ardour/audiosource.h>
-#include <ardour/cycle_timer.h>
-#include <ardour/session.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 "ardour/runtime_functions.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)
+ : Source (s, DataType::AUDIO, name)
+ , _length (0)
{
_peaks_built = false;
_peak_byte_max = 0;
AudioSource::AudioSource (Session& s, const XMLNode& node)
: Source (s, node)
+ , _length (0)
{
+
_peaks_built = false;
_peak_byte_max = 0;
peakfile = -1;
::close (peakfile);
}
- if (peak_leftovers) {
- delete [] peak_leftovers;
- }
+ delete [] peak_leftovers;
}
XMLNode&
{
const XMLProperty* prop;
- Source::set_state (node);
-
if ((prop = node.property ("captured-for")) != 0) {
_captured_for = prop->value();
}
return 0;
}
+sframes_t
+AudioSource::length (sframes_t pos) const
+{
+ return _length;
+}
+
+void
+AudioSource::update_length (sframes_t pos, sframes_t cnt)
+{
+ if (pos + cnt > _length) {
+ _length = pos + cnt;
+ }
+}
+
+
/***********************************************************************
PEAK FILE STUFF
***********************************************************************/
AudioSource::peaks_ready (sigc::slot<void> the_slot, sigc::connection& conn) const
{
bool ret;
- Glib::Mutex::Lock lm (_lock);
+ 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.
peakpath = peak_path (audio_path);
- /* Nasty band-aid for older sessions that were created before we
- used libsndfile for all audio files.
- */
+ /* if the peak file should be there, but isn't .... */
- if (!newfile && access (peakpath.c_str(), R_OK) != 0) {
- ustring str = old_peak_path (audio_path);
- if (access (str.c_str(), R_OK) == 0) {
- peakpath = str;
- }
+ if (!newfile && !Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
+ peakpath = find_broken_peakfile (peakpath, audio_path);
}
- if (newfile) {
-
- if (!_build_peakfiles) {
- return 0;
+ 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 {
-
- 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;
- }
-
+
+ /* we found it in the peaks dir, so check it out */
+
+ if (statbuf.st_size == 0 || ((nframes_t) statbuf.st_size < ((length(_timeline_position) / _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);
- /* we found it in the peaks dir, so check it out */
-
- if (statbuf.st_size == 0) {
+ if (err) {
_peaks_built = false;
+ _peak_byte_max = 0;
} 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 && stat_file.st_mtime > statbuf.st_mtime){
+
+ /* 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 {
}
nframes_t
-AudioSource::read (Sample *dst, nframes_t start, nframes_t cnt) const
+AudioSource::read (Sample *dst, sframes_t start, nframes_t cnt, int channel) const
{
Glib::Mutex::Lock lm (_lock);
return read_unlocked (dst, start, cnt);
return write_unlocked (dst, cnt);
}
+int
+AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, sframes_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);
+}
+
+/** @param peaks Buffer to write peak data.
+ * @param npeaks Number of peaks to write.
+ */
+
int
-AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_visual_peak) const
+AudioSource::read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, sframes_t start, nframes_t cnt,
+ double samples_per_visual_peak, nframes_t samples_per_file_peak) const
{
Glib::Mutex::Lock lm (_lock);
double scale;
Sample* raw_staging = 0;
int _peakfile = -1;
- expected_peaks = (cnt / (double) frames_per_peak);
+ expected_peaks = (cnt / (double) samples_per_file_peak);
scale = npeaks/expected_peaks;
#undef DEBUG_READ_PEAKS
npeaks = min ((nframes_t) floor (cnt / samples_per_visual_peak), npeaks);
zero_fill = old - npeaks;
}
-
- // cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl;
+ // cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl;
+
if (npeaks == cnt) {
#ifdef DEBUG_READ_PEAKS
if (scale == 1.0) {
- off_t first_peak_byte = (start / frames_per_peak) * sizeof (PeakData);
+ 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 \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ error << string_compose(_("AudioSource: cannot open peakpath (a) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
return -1;
}
/* compute the rounded up frame position */
nframes_t current_frame = start;
- nframes_t current_stored_peak = (nframes_t) ceil (current_frame / (double) frames_per_peak);
+ 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 / frames_per_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;
/* open ... close during out: handling */
if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) {
- error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ error << string_compose(_("AudioSource: cannot open peakpath (b) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
return 0;
}
if (i == stored_peaks_read) {
uint32_t start_byte = current_stored_peak * sizeof(PeakData);
- tnp = min ((_length/frames_per_peak - current_stored_peak), (nframes_t) expected_peaks);
+ tnp = min ((nframes_t)(_length/samples_per_file_peak - current_stored_peak), (nframes_t) expected_peaks);
to_read = min (chunksize, tnp);
#ifdef DEBUG_READ_PEAKS
<< ')'
<< " at start_byte = " << start_byte
<< " _length = " << _length << " versus len = " << fend
- << " expected maxpeaks = " << (_length - current_frame)/frames_per_peak
+ << " expected maxpeaks = " << (_length - current_frame)/samples_per_file_peak
<< " npeaks was " << npeaks
<< endl;
goto out;
//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 / frames_per_peak;
+ stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
}
if (zero_fill) {
while (nvisual_peaks < npeaks) {
if (i == frames_read) {
-
- to_read = min (chunksize, (_length - current_frame));
+
+ to_read = min (chunksize, nframes_t(_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;
+ npeaks -= zero_fill;
break;
}
close (_peakfile);
}
- if (staging) {
- delete [] staging;
- }
-
- if (raw_staging) {
- delete [] raw_staging;
- }
+ delete [] staging;
+ delete [] raw_staging;
#ifdef DEBUG_READ_PEAKS
cerr << "RP DONE\n";
{
nframes_t current_frame;
nframes_t cnt;
- Sample buf[frames_per_peak];
+ 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;
{
current_frame = 0;
cnt = _length;
_peaks_built = false;
+ buf = new Sample[bufsize];
while (cnt) {
- frames_to_read = min (frames_per_peak, 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 ();
+ done_with_peakfile_writes (false);
goto out;
}
- if (compute_and_write_peaks (buf, current_frame, frames_read, true)) {
+ 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();
- _peaks_built = true;
}
- done_with_peakfile_writes ();
+ done_with_peakfile_writes ((cnt == 0));
}
-
- /* lock no longer held, safe to signal */
-
- if (_peaks_built) {
- PeaksReady (); /* EMIT SIGNAL */
- ret = 0;
+
+ {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+
+ if (_peaks_built) {
+ PeaksReady (); /* EMIT SIGNAL */
+ ret = 0;
+ }
}
out:
unlink (peakpath.c_str());
}
+ delete [] buf;
+
return ret;
}
AudioSource::prepare_for_peakfile_writes ()
{
if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) {
- error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ error << string_compose(_("AudioSource: cannot open peakpath (c) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
return -1;
}
return 0;
}
void
-AudioSource::done_with_peakfile_writes ()
+AudioSource::done_with_peakfile_writes (bool done)
{
if (peak_leftover_cnt) {
- compute_and_write_peaks (0, 0, 0, true);
+ compute_and_write_peaks (0, 0, 0, true, false, _FPP);
+ }
+
+ if (done) {
+ _peaks_built = true;
}
if (peakfile >= 0) {
}
int
-AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force)
+AudioSource::compute_and_write_peaks (Sample* buf, sframes_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, sframes_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;
+ PeakData* peakbuf = 0;
int ret = -1;
nframes_t current_frame;
nframes_t frames_done;
x.min = peak_leftovers[0];
x.max = peak_leftovers[0];
- Session::find_peaks (peak_leftovers + 1, peak_leftover_cnt - 1, &x.min, &x.max);
- off_t byte = (peak_leftover_frame / frames_per_peak) * sizeof (PeakData);
+ 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;
_peak_byte_max = max (_peak_byte_max, (off_t) (byte + sizeof(PeakData)));
- PeakRangeReady (peak_leftover_frame, peak_leftover_cnt); /* EMIT SIGNAL */
- PeaksReady (); /* EMIT SIGNAL */
+ {
+ 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 */
to_do = cnt;
}
- peakbuf = new PeakData[(to_do/frames_per_peak)+1];
+ 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 frames_per_peak to do, save them till
+ and there are less than fpp to do, save them till
next time
*/
- if (force && (to_do < frames_per_peak)) {
+ if (force && (to_do < fpp)) {
/* keep the left overs around for next time */
if (peak_leftover_size < to_do) {
break;
}
- nframes_t this_time = min (frames_per_peak, to_do);
+ nframes_t this_time = min (fpp, to_do);
peakbuf[peaks_computed].max = buf[0];
peakbuf[peaks_computed].min = buf[0];
- Session::find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max);
+ ARDOUR::find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max);
peaks_computed++;
buf += this_time;
current_frame += this_time;
}
- first_peak_byte = (first_frame / frames_per_peak) * sizeof (PeakData);
+ first_peak_byte = (first_frame / fpp) * sizeof (PeakData);
if (can_truncate_peaks()) {
_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 */
- PeaksReady (); /* EMIT SIGNAL */
+ if (intermediate_peaks_ready) {
+ PeaksReady (); /* EMIT SIGNAL */
+ }
}
ret = 0;
out:
delete [] peakbuf;
- if (buf2) {
- delete [] buf2;
- }
+ delete [] buf2;
+
return ret;
}
nframes_t
AudioSource::available_peaks (double zoom_factor) const
{
- off_t end;
-
- if (zoom_factor < frames_per_peak) {
- return length(); // peak data will come from the audio file
+ if (zoom_factor < _FPP) {
+ return length(_timeline_position); // peak data will come from the audio file
}
/* peak data comes from peakfile, but the filesize might not represent
but _peak_byte_max only monotonically increases after initialization.
*/
- end = _peak_byte_max;
-
- return (end/sizeof(PeakData)) * frames_per_peak;
-}
+ off_t end = _peak_byte_max;
-void
-AudioSource::update_length (nframes_t pos, nframes_t cnt)
-{
- if (pos + cnt > _length) {
- _length = pos+cnt;
- }
+ return (end/sizeof(PeakData)) * _FPP;
}