extend replication support to provide (a) global enable/disable (b) per-track replication path (c) arbitrary numbers of replication locations

This commit is contained in:
Paul Davis 2014-08-27 18:45:50 -04:00
parent 306e57d92b
commit 8b0d7a173f
5 changed files with 148 additions and 108 deletions

View file

@ -99,8 +99,9 @@ class LIBARDOUR_API AudioDiskstream : public Diskstream
boost::shared_ptr<AudioFileSource> write_source (uint32_t n=0) {
boost::shared_ptr<ChannelList> c = channels.reader();
if (n < c->size())
return (*c)[n]->write_source;
if (n < c->size()) {
return (*c)[n]->write_sources.front();
}
return boost::shared_ptr<AudioFileSource>();
}
@ -174,8 +175,8 @@ class LIBARDOUR_API AudioDiskstream : public Diskstream
Sample *capture_wrap_buffer;
Sample *speed_buffer;
boost::shared_ptr<AudioFileSource> write_source;
boost::shared_ptr<AudioFileSource> replication_source;
typedef std::vector<boost::shared_ptr<AudioFileSource> > Sources;
Sources write_sources;
/** Information about the Port that our audio data comes from */
ChannelSource source;

View file

@ -82,6 +82,10 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
virtual std::string steal_write_source_name () { return std::string(); }
std::string replication_path () const;
std::string own_replication_path () const;
int set_own_replication_path (const std::string&);
boost::shared_ptr<ARDOUR::IO> io() const { return _io; }
void set_track (ARDOUR::Track *);
@ -333,6 +337,9 @@ class LIBARDOUR_API Diskstream : public SessionObject, public PublicDiskstream
XMLNode* deprecated_io_node;
void route_going_away ();
std::string _own_replication_path;
virtual int reset_replication_sources () { return 0; }
};
}; /* namespace ARDOUR */

View file

@ -79,7 +79,8 @@ CONFIG_VARIABLE (float, audio_playback_buffer_seconds, "playback-buffer-seconds"
CONFIG_VARIABLE (float, midi_track_buffer_seconds, "midi-track-buffer-seconds", 1.0)
CONFIG_VARIABLE (uint32_t, disk_choice_space_threshold, "disk-choice-space-threshold", 57600000)
CONFIG_VARIABLE (bool, auto_analyse_audio, "auto-analyse-audio", false)
CONFIG_VARIABLE (std::string, replication_parent_folder, "replication-parent-folder", "")
CONFIG_VARIABLE (std::string, global_replication_path, "global-replication-path", "")
CONFIG_VARIABLE (bool, global_replication, "global-replication", false)
/* OSC */

View file

@ -33,6 +33,7 @@
#include "pbd/memento_command.h"
#include "pbd/enumwriter.h"
#include "pbd/stateful_diff_command.h"
#include "pbd/strsplit.h"
#include "ardour/analyser.h"
#include "ardour/audio_buffer.h"
@ -132,7 +133,7 @@ AudioDiskstream::~AudioDiskstream ()
void
AudioDiskstream::config_parameter_changed (const std::string& what)
{
if (what == "replication-parent-folder") {
if (what == "global-replication-path" || what == "global-replication") {
reset_replication_sources ();
}
}
@ -170,7 +171,7 @@ AudioDiskstream::non_realtime_input_change ()
}
boost::shared_ptr<ChannelList> cr = channels.reader();
if (!cr->empty() && !cr->front()->write_source) {
if (!cr->empty() && cr->front()->write_sources.empty()) {
need_write_sources = true;
}
@ -335,7 +336,7 @@ AudioDiskstream::setup_destructive_playlist ()
boost::shared_ptr<ChannelList> c = channels.reader();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
srcs.push_back ((*chan)->write_source);
srcs.push_back ((*chan)->write_sources.front());
}
/* a single full-sized region */
@ -381,14 +382,16 @@ AudioDiskstream::use_destructive_playlist ()
ChannelList::iterator chan;
boost::shared_ptr<ChannelList> c = channels.reader();
(*chan)->write_sources.clear ();
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
(*chan)->write_source = boost::dynamic_pointer_cast<AudioFileSource>(region->source (n));
assert((*chan)->write_source);
(*chan)->write_source->set_allow_remove_if_empty (false);
(*chan)->write_sources.push_back(boost::dynamic_pointer_cast<AudioFileSource>(region->source (n)));
assert (!(*chan)->write_sources.empty());
(*chan)->write_sources.front()->set_allow_remove_if_empty (false);
/* this might be false if we switched modes, so force it */
(*chan)->write_source->set_destructive (true);
(*chan)->write_sources.front()->set_destructive (true);
}
/* the source list will never be reset for a destructive track */
@ -1362,9 +1365,9 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
if (captrans.type == CaptureStart) {
// by definition, the first data we got above represents the given capture pos
(*chan)->write_source->mark_capture_start (captrans.capture_val);
if ((*chan)->replication_source) {
(*chan)->replication_source->mark_capture_start (captrans.capture_val);
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
(*s)->mark_capture_start (captrans.capture_val);
}
(*chan)->curr_capture_cnt = 0;
@ -1382,9 +1385,8 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
}
to_write = nto_write;
(*chan)->write_source->mark_capture_end ();
if ((*chan)->replication_source) {
(*chan)->replication_source->mark_capture_end ();
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
(*s)->mark_capture_end ();
}
// increment past this transition, but go no further
@ -1404,15 +1406,14 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
}
}
if ((!(*chan)->write_source) || (*chan)->write_source->write (vector.buf[0], to_write) != to_write) {
error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
if (!(*chan)->write_sources.empty()) {
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
if ((*s)->write (vector.buf[0], to_write) != to_write) {
error << string_compose(_("AudioDiskstream %1: cannot write to disk @ %2"), id(), (*s)->path()) << endmsg;
return -1;
}
if ((*chan)->replication_source && (*chan)->replication_source->write (vector.buf[0], to_write) != to_write) {
error << string_compose(_("AudioDiskstream %1: cannot write replicated data to disk"), id()) << endmsg;
/* leave the file on disk, but forget about for now */
(*chan)->replication_source.reset ();
}
}
(*chan)->capture_buf->increment_read_ptr (to_write);
@ -1427,14 +1428,11 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
to_write = min ((framecnt_t)(disk_io_chunk_frames - to_write), (framecnt_t) vector.len[1]);
if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) {
error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
if ((*s)->write (vector.buf[1], to_write) != to_write) {
error << string_compose(_("AudioDiskstream %1: cannot write to disk @ %2"), id(), (*s)->path()) << endmsg;
return -1;
}
if ((*chan)->replication_source && (*chan)->replication_source->write (vector.buf[1], to_write) != to_write) {
error << string_compose(_("AudioDiskstream %1: cannot replicated data write to disk"), id()) << endmsg;
return -1;
}
(*chan)->capture_buf->increment_read_ptr (to_write);
@ -1496,18 +1494,10 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
if ((*chan)->write_source) {
(*chan)->write_source->mark_for_remove ();
(*chan)->write_source->drop_references ();
(*chan)->write_source.reset ();
}
if ((*chan)->replication_source) {
(*chan)->replication_source->mark_for_remove ();
(*chan)->replication_source->drop_references ();
(*chan)->replication_source.reset ();
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
(*s)->mark_for_remove ();
(*s)->drop_references ();
(*s).reset (); /* clear shared ptr */
}
/* new source set up in "out" below */
@ -1524,26 +1514,19 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
boost::shared_ptr<AudioFileSource> s = (*chan)->write_source;
if (!(*chan)->write_sources.empty()) {
ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin();
if (s) {
srcs.push_back (s);
s->update_header (capture_info.front()->start, when, twhen);
s->set_captured_for (_name.val());
s->mark_immutable ();
srcs.push_back (*s);
(*s)->update_header (capture_info.front()->start, when, twhen);
(*s)->set_captured_for (_name.val());
(*s)->mark_immutable ();
if (Config->get_auto_analyse_audio()) {
Analyser::queue_source_for_analysis (s, true);
if (s == (*chan)->write_sources.begin() && Config->get_auto_analyse_audio()) {
/* queue only 1 replicated source for analysis */
Analyser::queue_source_for_analysis (*s, true);
}
}
s = (*chan)->replication_source;
if (s) {
s->update_header (capture_info.front()->start, when, twhen);
s->set_captured_for (_name.val());
s->mark_immutable ();
}
}
/* destructive tracks have a single, never changing region */
@ -1561,7 +1544,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
} else {
string whole_file_region_name;
whole_file_region_name = region_name_from_path (c->front()->write_source->name(), true);
boost::shared_ptr<AudioFileSource> src = c->front()->write_sources.front();
whole_file_region_name = region_name_from_path (src->name(), true);
/* Register a new region with the Session that
describes the entire source. Do this first
@ -1572,7 +1556,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
try {
PropertyList plist;
plist.add (Properties::start, c->front()->write_source->last_capture_start_frame());
plist.add (Properties::start, src->last_capture_start_frame());
plist.add (Properties::length, total_capture);
plist.add (Properties::name, whole_file_region_name);
boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist));
@ -1597,7 +1581,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
_playlist->set_capture_insertion_in_progress (true);
_playlist->freeze ();
for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
for (buffer_position = src->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
string region_name;
@ -1806,19 +1790,17 @@ AudioDiskstream::prep_record_enable ()
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
(*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling));
capturing_sources.push_back ((*chan)->write_source);
(*chan)->write_source->mark_streaming_write_started ();
if ((*chan)->replication_source) {
(*chan)->replication_source->mark_streaming_write_started();
capturing_sources.push_back ((*chan)->write_sources.front());
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
(*s)->mark_streaming_write_started ();
}
}
} else {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
capturing_sources.push_back ((*chan)->write_source);
(*chan)->write_source->mark_streaming_write_started ();
if ((*chan)->replication_source) {
(*chan)->replication_source->mark_streaming_write_started();
capturing_sources.push_back ((*chan)->write_sources.front());
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
(*s)->mark_streaming_write_started ();
}
}
}
@ -1964,24 +1946,29 @@ AudioDiskstream::use_new_write_source (uint32_t n)
}
ChannelInfo* chan = (*c)[n];
boost::shared_ptr<AudioFileSource> src;
chan->write_sources.clear ();
try {
if ((chan->write_source = _session.create_audio_source_for_session (
if ((src = _session.create_audio_source_for_session (
n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
throw failed_constructor();
}
chan->write_sources.push_back (src);
}
catch (failed_constructor &err) {
error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
chan->write_source.reset ();
chan->replication_source.reset ();
chan->write_sources.clear ();
return -1;
}
/* do not remove destructive files even if they are empty */
chan->write_source->set_allow_remove_if_empty (!destructive());
for (ChannelInfo::Sources::iterator s = chan->write_sources.begin(); s != chan->write_sources.end(); ++s) {
(*s)->set_allow_remove_if_empty (!destructive());
}
return 0;
}
@ -2003,35 +1990,30 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
if (!destructive()) {
if ((*chan)->write_source) {
for (ChannelInfo::Sources::iterator s = (*chan)->write_sources.begin(); s != (*chan)->write_sources.end(); ++s) {
if (mark_write_complete) {
(*chan)->write_source->mark_streaming_write_completed ();
(*chan)->write_source->done_with_peakfile_writes ();
(*s)->mark_streaming_write_completed ();
(*s)->done_with_peakfile_writes ();
}
if ((*chan)->replication_source) {
(*chan)->replication_source->mark_streaming_write_completed ();
if ((*s)->removable()) {
(*s)->mark_for_remove ();
(*s)->drop_references ();
}
}
if ((*chan)->write_source->removable()) {
(*chan)->write_source->mark_for_remove ();
(*chan)->write_source->drop_references ();
}
(*chan)->write_source.reset ();
(*chan)->replication_source.reset ();
}
(*chan)->write_sources.clear ();
use_new_write_source (n);
if (record_enabled()) {
capturing_sources.push_back ((*chan)->write_source);
capturing_sources.push_back ((*chan)->write_sources.front());
}
} else {
if ((*chan)->write_source == 0) {
if ((*chan)->write_sources.empty()) {
use_new_write_source (n);
}
}
@ -2493,7 +2475,7 @@ AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize)
AudioDiskstream::ChannelInfo::~ChannelInfo ()
{
write_source.reset ();
write_sources.clear ();
delete [] speed_buffer;
speed_buffer = 0;
@ -2539,7 +2521,8 @@ AudioDiskstream::set_name (string const & name)
}
bool
AudioDiskstream::set_write_source_name (const std::string& str) {
AudioDiskstream::set_write_source_name (const std::string& str)
{
if (_write_source_name == str) {
return true;
}
@ -2568,24 +2551,42 @@ AudioDiskstream::reset_replication_sources ()
RCUWriter<ChannelList> writer (channels);
boost::shared_ptr<ChannelList> c = writer.get_copy();
string replication_path = Config->get_replication_parent_folder();
string rpath = replication_path ();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
/* remove all but the first write source */
/* XXX note conceptual dependency on mono recording */
while ((*chan)->write_sources.size() > 1) {
(*chan)->write_sources.pop_back ();
}
}
if (rpath.empty()) {
/* Not doing replication or no path set */
return 0;
}
vector<string> rdirs;
split (rpath, rdirs, ':');
try {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
if (!(*chan)->write_source) {
if ((*chan)->write_sources.empty()) {
continue;
}
if (!replication_path.empty()) {
(*chan)->replication_source = _session.create_replicated_audio_source ((*chan)->write_source, replication_path);
} else {
(*chan)->replication_source.reset ();
for (vector<string>::iterator rd = rdirs.begin(); rd != rdirs.end(); ++rd) {
(*chan)->write_sources.push_back (_session.create_replicated_audio_source ((*chan)->write_sources.front(), *rd));
}
}
return 0;
} catch (...) {
error << string_compose (_("%1: cannot create replication sources @ %2"), _name, replication_path) << endmsg;
error << string_compose (_("%1: cannot create replication sources along %2"), _name, rpath) << endmsg;
return -1;
}
}

View file

@ -745,3 +745,33 @@ Diskstream::disengage_record_enable ()
g_atomic_int_set (&_record_enabled, 0);
}
std::string
Diskstream::own_replication_path () const
{
return _own_replication_path;
}
std::string
Diskstream::replication_path () const
{
if (!Config->get_global_replication()) {
return string ();
}
if (!_own_replication_path.empty()) {
return _own_replication_path;
}
return Config->get_global_replication_path ();
}
int
Diskstream::set_own_replication_path (const std::string& path)
{
if (_own_replication_path != path) {
_own_replication_path = path;
return reset_replication_sources ();
}
return 0;
}