X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsource.cc;h=239c3729c950cc570d492362c3cec3c207985602;hb=9e0d03020ff47773f7d1c0414de1c74e6c9e0dac;hp=7d790a036d7f24c6a345872bb36925076a7cc543;hpb=eb3f77df5748e81c4a6bfe737cd9b5a3d721a86c;p=ardour.git diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 7d790a036d..239c3729c9 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000 Paul Davis + 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 @@ -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 @@ -28,57 +27,63 @@ #include #include #include +#include #include -#include -#include +#include +#include +#include "pbd/xml++.h" +#include "pbd/pthread_utils.h" +#include "pbd/enumwriter.h" -#include +#include "ardour/debug.h" +#include "ardour/session.h" +#include "ardour/source.h" +#include "ardour/transient_detector.h" #include "i18n.h" -using std::min; -using std::max; - +using namespace std; using namespace ARDOUR; -sigc::signal Source::SourceCreated; -pthread_t Source::peak_thread; -bool Source::have_peak_thread = false; -vector Source::pending_peak_sources; -Glib::StaticMutex Source::pending_peak_sources_lock = GLIBMM_STATIC_MUTEX_INIT; -int Source::peak_request_pipe[2]; - -bool Source::_build_missing_peakfiles = false; -bool Source::_build_peakfiles = false; - -Source::Source (bool announce) +Source::Source (Session& s, DataType type, const string& name, Flag flags) + : SessionObject(s, name) + , _type(type) + , _flags(flags) + , _timeline_position(0) { - _id = ARDOUR::new_id(); - _use_cnt = 0; - _peaks_built = false; - next_peak_clear_should_notify = true; + _analysed = false; _timestamp = 0; - _read_data_count = 0; - _write_data_count = 0; + fix_writable_flags (); } -Source::Source (const XMLNode& node) +Source::Source (Session& s, const XMLNode& node) + : SessionObject(s, "unnamed source") + , _type(DataType::AUDIO) + , _flags (Flag (Writable|CanRename)) + , _timeline_position(0) { - _use_cnt = 0; - _peaks_built = false; - next_peak_clear_should_notify = true; _timestamp = 0; - _read_data_count = 0; - _write_data_count = 0; + _analysed = false; - if (set_state (node)) { + if (set_state (node, Stateful::loading_state_version) || _type == DataType::NIL) { throw failed_constructor(); } + + fix_writable_flags (); } Source::~Source () { + DEBUG_TRACE (DEBUG::Destruction, string_compose ("Source %1 destructor %2\n", _name, this)); +} + +void +Source::fix_writable_flags () +{ + if (!_session.writable()) { + _flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename)); + } } XMLNode& @@ -87,8 +92,10 @@ Source::get_state () XMLNode *node = new XMLNode ("Source"); char buf[64]; - node->add_property ("name", _name); - snprintf (buf, sizeof(buf)-1, "%" PRIu64, _id); + node->add_property ("name", name()); + node->add_property ("type", _type.to_string()); + node->add_property (X_("flags"), enum_2_string (_flags)); + _id.print (buf, sizeof (buf)); node->add_property ("id", buf); if (_timestamp != 0) { @@ -96,15 +103,11 @@ Source::get_state () node->add_property ("timestamp", buf); } - if (_captured_for.length()) { - node->add_property ("captured-for", _captured_for); - } - return *node; } int -Source::set_state (const XMLNode& node) +Source::set_state (const XMLNode& node, int /*version*/) { const XMLProperty* prop; @@ -113,781 +116,156 @@ Source::set_state (const XMLNode& node) } else { return -1; } - + if ((prop = node.property ("id")) != 0) { - sscanf (prop->value().c_str(), "%" PRIu64, &_id); + _id = prop->value (); } else { return -1; } - if ((prop = node.property ("timestamp")) != 0) { - sscanf (prop->value().c_str(), "%ld", &_timestamp); - } - - if ((prop = node.property ("captured-for")) != 0) { - _captured_for = prop->value(); + if ((prop = node.property ("type")) != 0) { + _type = DataType(prop->value()); } - return 0; -} - -/*********************************************************************** - PEAK FILE STUFF - ***********************************************************************/ - -void* -Source::peak_thread_work (void* arg) -{ - PBD::ThreadCreated (pthread_self(), X_("Peak")); - struct pollfd pfd[1]; - - Glib::Mutex::Lock lm (pending_peak_sources_lock); - - while (true) { - - pfd[0].fd = peak_request_pipe[0]; - pfd[0].events = POLLIN|POLLERR|POLLHUP; - - pending_peak_sources_lock.unlock(); - - if (poll (pfd, 1, -1) < 0) { - - if (errno == EINTR) { - pending_peak_sources_lock.lock(); - continue; - } - - error << string_compose (_("poll on peak request pipe failed (%1)"), - strerror (errno)) - << endmsg; - break; - } - - if (pfd[0].revents & ~POLLIN) { - error << _("Error on peak thread request pipe") << endmsg; - break; - } - - if (pfd[0].revents & POLLIN) { - - char req; - - /* empty the pipe of all current requests */ - - while (1) { - size_t nread = ::read (peak_request_pipe[0], &req, sizeof (req)); - - if (nread == 1) { - switch ((PeakRequest::Type) req) { - - case PeakRequest::Build: - break; - - case PeakRequest::Quit: - pthread_exit_pbd (0); - /*NOTREACHED*/ - break; - - default: - break; - } - - } else if (nread == 0) { - break; - } else if (errno == EAGAIN) { - break; - } else { - fatal << _("Error reading from peak request pipe") << endmsg; - /*NOTREACHED*/ - } - } - } - - pending_peak_sources_lock.lock(); - - while (!pending_peak_sources.empty()) { - - Source* s = pending_peak_sources.front(); - pending_peak_sources.erase (pending_peak_sources.begin()); - - pending_peak_sources_lock.unlock(); - s->build_peaks(); - pending_peak_sources_lock.lock(); - } - } - - pthread_exit_pbd (0); - /*NOTREACHED*/ - return 0; -} - -int -Source::start_peak_thread () -{ - if (!_build_peakfiles) { - return 0; - } - - if (pipe (peak_request_pipe)) { - error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg; - return -1; + if ((prop = node.property ("timestamp")) != 0) { + sscanf (prop->value().c_str(), "%ld", &_timestamp); } - if (fcntl (peak_request_pipe[0], F_SETFL, O_NONBLOCK)) { - error << string_compose(_("UI: cannot set O_NONBLOCK on peak request pipe (%1)"), strerror (errno)) << endmsg; - return -1; - } + if ((prop = node.property (X_("flags"))) != 0) { + _flags = Flag (string_2_enum (prop->value(), _flags)); + } else { + _flags = Flag (0); - if (fcntl (peak_request_pipe[1], F_SETFL, O_NONBLOCK)) { - error << string_compose(_("UI: cannot set O_NONBLOCK on peak request pipe (%1)"), strerror (errno)) << endmsg; - return -1; } - if (pthread_create_and_store ("peak file builder", &peak_thread, 0, peak_thread_work, 0)) { - error << _("Source: could not create peak thread") << endmsg; - return -1; + /* old style, from the period when we had DestructiveFileSource */ + if ((prop = node.property (X_("destructive"))) != 0) { + _flags = Flag (_flags | Destructive); } - have_peak_thread = true; return 0; } -void -Source::stop_peak_thread () -{ - if (!have_peak_thread) { - return; - } - - void* status; - - char c = (char) PeakRequest::Quit; - ::write (peak_request_pipe[1], &c, 1); - pthread_join (peak_thread, &status); -} - -void -Source::queue_for_peaks (Source& source) +bool +Source::has_been_analysed() const { - if (have_peak_thread) { - - Glib::Mutex::Lock lm (pending_peak_sources_lock); - - source.next_peak_clear_should_notify = true; - - if (find (pending_peak_sources.begin(), - pending_peak_sources.end(), - &source) == pending_peak_sources.end()) { - pending_peak_sources.push_back (&source); - } - - char c = (char) PeakRequest::Build; - ::write (peak_request_pipe[1], &c, 1); - } + Glib::Mutex::Lock lm (_analysis_lock); + return _analysed; } -void Source::clear_queue_for_peaks () +void +Source::set_been_analysed (bool yn) { - /* this is done to cancel a group of running peak builds */ - if (have_peak_thread) { - Glib::Mutex::Lock lm (pending_peak_sources_lock); - pending_peak_sources.clear (); + { + Glib::Mutex::Lock lm (_analysis_lock); + _analysed = yn; } -} - - -bool -Source::peaks_ready (sigc::slot the_slot, sigc::connection& conn) const -{ - bool ret; - Glib::Mutex::Lock lm (_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); + if (yn) { + load_transients (get_transients_path()); + AnalysisChanged(); // EMIT SIGNAL } - - return ret; } int -Source::rename_peakfile (string newpath) +Source::load_transients (const string& path) { - /* caller must hold _lock */ + ifstream file (path.c_str()); - string 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; - } + if (!file) { + return -1; } - peakpath = newpath; + transients.clear (); - return 0; -} - -int -Source::initialize_peakfile (bool newfile, string audio_path) -{ - struct stat statbuf; - - peakpath = peak_path (audio_path); + stringstream strstr; + double val; - if (newfile) { - - if (!_build_peakfiles) { - return 0; - } - - _peaks_built = false; - - } else { + while (file.good()) { + file >> val; - 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(_("Source: cannot stat peakfile \"%1\""), peakpath) << endmsg; - return -1; - } - - } else { - - /* we found it in the peaks dir */ - } - - if (statbuf.st_size == 0) { - _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 && stat_file.st_mtime > statbuf.st_mtime){ - _peaks_built = false; - } else { - _peaks_built = true; - } + if (!file.fail()) { + nframes64_t frame = (nframes64_t) floor (val * _session.frame_rate()); + transients.push_back (frame); } } - if (!newfile && !_peaks_built && _build_missing_peakfiles && _build_peakfiles) { - build_peaks_from_scratch (); - } - return 0; } -int -Source::read_peaks (PeakData *peaks, jack_nframes_t npeaks, jack_nframes_t start, jack_nframes_t cnt, double samples_per_visual_peak) const +string +Source::get_transients_path () const { - Glib::Mutex::Lock lm (_lock); - double scale; - double expected_peaks; - PeakData::PeakDatum xmax; - PeakData::PeakDatum xmin; - int32_t to_read; - uint32_t nread; - jack_nframes_t zero_fill = 0; - int ret = -1; - PeakData* staging = 0; - Sample* raw_staging = 0; - char * workbuf = 0; - int peakfile = -1; - - expected_peaks = (cnt / (double) frames_per_peak); - scale = npeaks/expected_peaks; - -#if 0 - 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 - < _length - start) { - // cerr << "too close to end @ " << _length << " given " << start << " + " << cnt << endl; - cnt = _length - start; - jack_nframes_t old = npeaks; - npeaks = min ((jack_nframes_t) floor (cnt / samples_per_visual_peak), npeaks); - zero_fill = old - npeaks; - } - - // cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl; - - if (npeaks == cnt) { - - // cerr << "RAW DATA\n"; - - /* 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]; - workbuf = new char[cnt*4]; - - if (read_unlocked (raw_staging, start, cnt, workbuf) != cnt) { - error << _("cannot read sample data for unscaled peak computation") << endmsg; - return -1; - } - - for (jack_nframes_t i = 0; i < npeaks; ++i) { - peaks[i].max = raw_staging[i]; - peaks[i].min = raw_staging[i]; - } - - delete [] raw_staging; - delete [] workbuf; - return 0; - } - - if (scale == 1.0) { - - off_t first_peak_byte = (start / frames_per_peak) * sizeof (PeakData); - - /* open, read, close */ - - if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) { - error << string_compose(_("Source: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; - return -1; - } - - // cerr << "DIRECT PEAKS\n"; - - nread = ::pread (peakfile, peaks, sizeof (PeakData)* npeaks, first_peak_byte); - close (peakfile); - - if (nread != sizeof (PeakData) * npeaks) { - cerr << "Source[" - << _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; - } - - - jack_nframes_t tnp; - - if (scale < 1.0) { - - // cerr << "DOWNSAMPLE\n"; + vector parts; + string s; - /* the caller wants: + /* old sessions may not have the analysis directory */ - - 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 + _session.ensure_subdirs (); - So, read a block into a staging area, and then downsample from there. + s = _session.analysis_dir (); + parts.push_back (s); - to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks - */ + s = _id.to_s(); + s += '.'; + s += TransientDetector::operational_identifier(); + parts.push_back (s); - const uint32_t chunksize = (uint32_t) min (expected_peaks, 4096.0); - - staging = new PeakData[chunksize]; - - /* compute the rounded up frame position */ - - jack_nframes_t current_frame = start; - jack_nframes_t current_stored_peak = (jack_nframes_t) ceil (current_frame / (double) frames_per_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 = (jack_nframes_t) next_visual_peak_frame / frames_per_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_RDWR|O_CREAT, 0664)) < 0) { - error << string_compose(_("Source: cannot open peakpath \"%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/frames_per_peak - current_stored_peak), (jack_nframes_t) expected_peaks); - to_read = min (chunksize, tnp); - - off_t fend = lseek (peakfile, 0, SEEK_END); - - if ((nread = ::pread (peakfile, staging, sizeof (PeakData) * to_read, start_byte)) - != sizeof (PeakData) * to_read) { - cerr << "Source[" - << _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)/frames_per_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 / frames_per_peak; - } - - if (zero_fill) { - memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill); - } - - ret = 0; - - } else { - - // cerr << "UPSAMPLE\n"; - - /* 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. - */ - - jack_nframes_t frames_read = 0; - jack_nframes_t current_frame = start; - jack_nframes_t i = 0; - jack_nframes_t nvisual_peaks = 0; - jack_nframes_t chunksize = (jack_nframes_t) min (cnt, (jack_nframes_t) 4096); - raw_staging = new Sample[chunksize]; - workbuf = new char[chunksize *4]; - - jack_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 ((frames_read = read_unlocked (raw_staging, current_frame, to_read, workbuf)) < 0) { - error << string_compose(_("Source[%1]: peak read - cannot read %2 samples at offset %3") - , _name, to_read, current_frame) - << 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; - } - - if (workbuf) { - delete [] workbuf; - } - - return ret; + return Glib::build_filename (parts); } -#undef DEBUG_PEAK_BUILD - -int -Source::build_peaks () -{ - vector built; - int status = -1; - bool pr_signal = false; - list copy; - - { - Glib::Mutex::Lock lm (_lock); - copy = pending_peak_builds; - pending_peak_builds.clear (); - } - -#ifdef DEBUG_PEAK_BUILD - cerr << "build peaks with " << copy.size() << " requests pending\n"; -#endif - - for (list::iterator i = copy.begin(); i != copy.end(); ++i) { - - if ((status = do_build_peak ((*i)->frame, (*i)->cnt)) != 0) { - unlink (peakpath.c_str()); - break; - } - built.push_back (new PeakBuildRecord (*(*i))); - delete *i; - } - - { - Glib::Mutex::Lock lm (_lock); - - if (status == 0) { - _peaks_built = true; - - if (next_peak_clear_should_notify) { - next_peak_clear_should_notify = false; - pr_signal = true; - } - } - } - - if (status == 0) { - for (vector::iterator i = built.begin(); i != built.end(); ++i) { - PeakRangeReady ((*i)->frame, (*i)->cnt); /* EMIT SIGNAL */ - delete *i; - } - - if (pr_signal) { - PeaksReady (); /* EMIT SIGNAL */ - } - } - - return status; -} - -int -Source::do_build_peak (jack_nframes_t first_frame, jack_nframes_t cnt) +bool +Source::check_for_analysis_data_on_disk () { - jack_nframes_t current_frame; - Sample buf[frames_per_peak]; - Sample xmin, xmax; - uint32_t peaki; - PeakData* peakbuf; - char * workbuf = 0; - jack_nframes_t frames_read; - jack_nframes_t frames_to_read; - off_t first_peak_byte; - int peakfile = -1; - int ret = -1; - -#ifdef DEBUG_PEAK_BUILD - cerr << pthread_self() << ": " << _name << ": building peaks for " << first_frame << " to " << first_frame + cnt - 1 << endl; -#endif - - first_peak_byte = (first_frame / frames_per_peak) * sizeof (PeakData); - -#ifdef DEBUG_PEAK_BUILD - cerr << "seeking to " << first_peak_byte << " before writing new peak data\n"; -#endif - - current_frame = first_frame; - peakbuf = new PeakData[(cnt/frames_per_peak)+1]; - peaki = 0; - - workbuf = new char[max(frames_per_peak, cnt) * 4]; - - if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) { - error << string_compose(_("Source: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; - return -1; - } - - while (cnt) { - - frames_to_read = min (frames_per_peak, cnt); - - if ((frames_read = read_unlocked (buf, current_frame, frames_to_read, workbuf)) != frames_to_read) { - error << string_compose(_("%1: could not write read raw data for peak computation (%2)"), _name, strerror (errno)) << endmsg; - goto out; - } - - xmin = buf[0]; - xmax = buf[0]; - - for (jack_nframes_t n = 1; n < frames_read; ++n) { - xmax = max (xmax, buf[n]); - xmin = min (xmin, buf[n]); - -// if (current_frame < frames_read) { -// cerr << "sample = " << buf[n] << " max = " << xmax << " min = " << xmin << " max of 2 = " << max (xmax, buf[n]) << endl; -// } - } - - peakbuf[peaki].max = xmax; - peakbuf[peaki].min = xmin; - peaki++; + /* looks to see if the analysis files for this source are on disk. + if so, mark us already analysed. + */ - current_frame += frames_read; - cnt -= frames_read; - } + string path = get_transients_path (); + bool ok = true; - if (::pwrite (peakfile, peakbuf, sizeof (PeakData) * peaki, first_peak_byte) != (ssize_t) (sizeof (PeakData) * peaki)) { - error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg; - goto out; + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { + ok = false; } - ret = 0; + // XXX add other tests here as appropriate - out: - delete [] peakbuf; - if (peakfile >= 0) { - close (peakfile); - } - if (workbuf) - delete [] workbuf; - return ret; + set_been_analysed (ok); + return ok; } void -Source::build_peaks_from_scratch () +Source::mark_for_remove () { - Glib::Mutex::Lock lp (_lock); + // This operation is not allowed for sources for destructive tracks or out-of-session files. - next_peak_clear_should_notify = true; - pending_peak_builds.push_back (new PeakBuildRecord (0, _length)); - queue_for_peaks (*this); -} + /* XXX need a way to detect _within_session() condition here - move it from FileSource? + */ -bool -Source::file_changed (string 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; + if ((_flags & Destructive)) { + return; } -} -void -Source::use () -{ - _use_cnt++; + _flags = Flag (_flags | Removable | RemoveAtDestroy); } void -Source::release () +Source::set_timeline_position (int64_t pos) { - if (_use_cnt) --_use_cnt; + _timeline_position = pos; } -jack_nframes_t -Source::available_peaks (double zoom_factor) const +void +Source::set_allow_remove_if_empty (bool yn) { - int peakfile; - off_t end; - - if (zoom_factor < frames_per_peak) { - return length(); // peak data will come from the audio file - } - - /* peak data comes from peakfile */ - - if ((peakfile = ::open (peakpath.c_str(), O_RDONLY)) < 0) { - error << string_compose(_("Source: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; - return 0; + if (!writable()) { + return; } - { - Glib::Mutex::Lock lm (_lock); - end = lseek (peakfile, 0, SEEK_END); + if (yn) { + _flags = Flag (_flags | RemovableIfEmpty); + } else { + _flags = Flag (_flags & ~RemovableIfEmpty); } - - close (peakfile); - - return (end/sizeof(PeakData)) * frames_per_peak; }