if (prompter.run () == 1) {
_session->remove_last_capture ();
+ _regions->redisplay ();
}
} else {
_session->remove_last_capture();
+ _regions->redisplay ();
}
}
} else {
_change_connection.block (true);
- cerr << "\tpush to region selection\n";
_editor->set_selected_regionview_from_region_list (region, Selection::Add);
_change_connection.block (false);
boost::shared_ptr<Region> compared_region = (*i)[_columns.region];
if (r == compared_region) {
- cerr << "\tpush into region list\n";
_display.get_selection()->select(*i);
break;
}
if (!(*i).children().empty()) {
- cerr << "\tlook for " << r->name() << " among children of " << (compared_region ? compared_region->name() : string ("NO REGION")) << endl;
if (set_selected_in_subrow(r, (*i), 2)) {
break;
}
boost::shared_ptr<Region> region = (*i)[_columns.region];
if (!region->automatic()) {
- cerr << "level 1 : Updating " << region->name() << "\n";
populate_row(region, (*i));
}
boost::shared_ptr<Region> region = (*i)[_columns.region];
if (!region->automatic()) {
- cerr << "level " << level << " : Updating " << region->name() << "\n";
populate_row(region, (*i));
}
class IO;
class Playlist;
class Processor;
-class Region;
+class Source;
class Session;
class Track;
class Location;
int set_loop (Location *loc);
- std::list<boost::shared_ptr<Region> >& last_capture_regions () { return _last_capture_regions; }
+ std::list<boost::shared_ptr<Source> >& last_capture_sources () { return _last_capture_sources; }
void handle_input_change (IOChange, void *src);
- void remove_region_from_last_capture (boost::weak_ptr<Region> wregion);
-
void move_processor_automation (boost::weak_ptr<Processor>,
std::list<Evoral::RangeMove<framepos_t> > const &);
virtual bool realtime_set_speed (double, bool global_change);
- std::list<boost::shared_ptr<Region> > _last_capture_regions;
+ std::list<boost::shared_ptr<Source> > _last_capture_sources;
virtual int use_pending_capture_data (XMLNode& node) = 0;
RegionListProperty (Playlist&);
boost::shared_ptr<Region> lookup_id (const PBD::ID& id);
- void diff (PBD::PropertyList& undo, PBD::PropertyList& redo) const;
+ void diff (PBD::PropertyList& undo, PBD::PropertyList& redo, Command*) const;
+ bool involves (boost::shared_ptr<Region>);
private:
friend class Playlist;
namespace ARDOUR {
class Playlist;
-class Region;
+class Source;
class Location;
/** Public interface to a Diskstream */
virtual boost::shared_ptr<Playlist> playlist () = 0;
virtual void monitor_input (bool) = 0;
virtual bool destructive () const = 0;
- virtual std::list<boost::shared_ptr<Region> > & last_capture_regions () = 0;
+ virtual std::list<boost::shared_ptr<Source> > & last_capture_sources () = 0;
virtual void set_capture_offset () = 0;
virtual void reset_write_sources (bool, bool force = false) = 0;
virtual float playback_buffer_load () const = 0;
return _pending_explicit_relayer;
}
+ void drop_sources ();
+
protected:
friend class RegionFactory;
#define __ardour_region_factory_h__
#include <map>
+#include <set>
#include <glibmm/thread.h>
#include "pbd/id.h"
/** create a region with specified sources @param srcs and XML state */
static boost::shared_ptr<Region> create (SourceList& srcs, const XMLNode&);
+ static void get_regions_using_source (boost::shared_ptr<Source>, std::set<boost::shared_ptr<Region> >& );
+
static void map_remove (boost::shared_ptr<Region>);
+ static void map_remove_with_equivalents (boost::shared_ptr<Region>);
static void delete_all_regions ();
static const RegionMap& regions() { return region_map; }
static uint32_t nregions ();
int cleanup_sources (CleanupReport&);
int cleanup_trash_sources (CleanupReport&);
- int destroy_region (boost::shared_ptr<Region>);
- int destroy_regions (std::list<boost::shared_ptr<Region> >);
+ int destroy_sources (std::list<boost::shared_ptr<Source> >);
int remove_last_capture ();
template<class T> void foreach (T *obj, void (T::*func)(boost::shared_ptr<Playlist>));
void get (std::vector<boost::shared_ptr<Playlist> >&);
void unassigned (std::list<boost::shared_ptr<Playlist> > & list);
+ void destroy_region (boost::shared_ptr<Region>);
private:
friend class Session;
int load (Session &, const XMLNode&);
int load_unused (Session &, const XMLNode&);
boost::shared_ptr<Playlist> XMLPlaylistFactory (Session &, const XMLNode&);
-
+
mutable Glib::Mutex lock;
typedef std::set<boost::shared_ptr<Playlist> > List;
List playlists;
#include <string>
#include <set>
+#include <glib.h>
+
#include <boost/utility.hpp>
#include "pbd/statefuldestructible.h"
Glib::Mutex& mutex() { return _lock; }
Flag flags() const { return _flags; }
+ void inc_use_count () { g_atomic_int_inc (&_use_count); }
+ void dec_use_count () {
+ gint oldval = g_atomic_int_exchange_and_add (&_use_count, -1);
+ assert (oldval > 0);
+ }
+
+ int use_count() const { return g_atomic_int_get (&_use_count); }
+ bool used() const { return use_count() > 0; }
+
protected:
DataType _type;
Flag _flags;
mutable Glib::Mutex _lock;
mutable Glib::Mutex _analysis_lock;
Glib::Mutex _playlist_lock;
+ gint _use_count; /* atomic */
private:
void fix_writable_flags ();
class Session;
class Playlist;
class RouteGroup;
+class Source;
class Region;
class Diskstream;
boost::shared_ptr<Playlist> playlist ();
void monitor_input (bool);
bool destructive () const;
- std::list<boost::shared_ptr<Region> > & last_capture_regions ();
+ std::list<boost::shared_ptr<Source> > & last_capture_sources ();
void set_capture_offset ();
void reset_write_sources (bool, bool force = false);
float playback_buffer_load () const;
was_recording = true;
}
- if (can_record && !_last_capture_regions.empty()) {
- _last_capture_regions.clear ();
+ if (can_record && !_last_capture_sources.empty()) {
+ _last_capture_sources.clear ();
}
if (rec_nframes) {
plist.add (Properties::start, c->front()->write_source->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));
rx->set_automatic (true);
rx->set_whole_file (true);
/* XXX what now? */
}
- _last_capture_regions.push_back (region);
+ _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
// cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
continue; /* XXX is this OK? */
}
- region->DropReferences.connect_same_thread (*this, boost::bind (&Diskstream::remove_region_from_last_capture, this, boost::weak_ptr<Region>(region)));
-
- _last_capture_regions.push_back (region);
-
i_am_the_modifier++;
_playlist->add_region (region, (*ci)->start, 1, non_layered());
i_am_the_modifier--;
boost::shared_ptr<ChannelList> c = channels.reader();
uint32_t n;
+ cerr << name() << " resetting write sources, recrodable " << recordable() << " chans = " << c->size() << endl;
+
if (!_session.writable() || !recordable()) {
return;
}
AudioDiskstream::ChannelInfo::resize_capture (nframes_t capture_bufsize)
{
delete capture_buf;
+
capture_buf = new RingBufferNPT<Sample> (capture_bufsize);
memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
}
AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
{
boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
+
+ if (!r) {
+ return false;
+ }
+
bool changed = false;
Crossfades::iterator c, ctmp;
set<boost::shared_ptr<Crossfade> > unique_xfades;
- if (r == 0) {
- fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
- << endmsg;
- /*NOTREACHED*/
- return false;
- }
-
{
RegionLock rlock (this);
for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ /* connect only once to HeaderPositionOffsetChanged, even if sources are replicated
+ */
+
if (unique_srcs.find (*i) == unique_srcs.end ()) {
unique_srcs.insert (*i);
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
XMLNode& node (Region::state (full));
XMLNode *child;
char buf[64];
- char buf2[64];
LocaleGuard lg (X_("POSIX"));
-
- // XXX these should move into Region
-
- for (uint32_t n=0; n < _sources.size(); ++n) {
- snprintf (buf2, sizeof(buf2), "source-%d", n);
- _sources[n]->id().print (buf, sizeof (buf));
- node.add_property (buf2, buf);
- }
-
- for (uint32_t n=0; n < _master_sources.size(); ++n) {
- snprintf (buf2, sizeof(buf2), "master-source-%d", n);
- _master_sources[n]->id().print (buf, sizeof (buf));
- node.add_property (buf2, buf);
- }
-
snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
node.add_property ("channels", buf);
child->add_property ("default", "yes");
}
- if (full && _extra_xml) {
- node.add_child_copy (*_extra_xml);
- }
-
return node;
}
return true;
}
-void
-Diskstream::remove_region_from_last_capture (boost::weak_ptr<Region> wregion)
-{
- boost::shared_ptr<Region> region (wregion.lock());
-
- if (!region) {
- return;
- }
-
- _last_capture_regions.remove (region);
-}
-
void
Diskstream::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const & movements_frames)
{
}
- if (can_record && !_last_capture_regions.empty()) {
- _last_capture_regions.clear ();
+ if (can_record && !_last_capture_sources.empty()) {
+ _last_capture_sources.clear ();
}
if (nominally_recording || rec_nframes) {
/* XXX what now? */
}
- _last_capture_regions.push_back (region);
-
- // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
+ _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
_playlist->clear_history ();
_playlist->freeze ();
continue; /* XXX is this OK? */
}
- region->DropReferences.connect_same_thread (*this, boost::bind (&Diskstream::remove_region_from_last_capture, this, boost::weak_ptr<Region>(region)));
-
- _last_capture_regions.push_back (region);
-
// cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl;
i_am_the_modifier++;
int
MidiDiskstream::use_new_write_source (uint32_t n)
{
+ cerr << name() << " use new write source for n = " << n << " recordable ? " << recordable() << endl;
+
if (!recordable()) {
return 1;
}
MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
{
boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
- bool changed = false;
- if (r == 0) {
- PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
- << endmsg;
- /*NOTREACHED*/
+ if (!r) {
return false;
}
+ bool changed = false;
+
{
RegionLock rlock (this);
RegionList::iterator i;
XMLNode&
MidiRegion::state (bool full)
{
- XMLNode& node (Region::state (full));
- char buf[64];
- char buf2[64];
- LocaleGuard lg (X_("POSIX"));
-
- // XXX these should move into Region
-
- for (uint32_t n=0; n < _sources.size(); ++n) {
- snprintf (buf2, sizeof(buf2), "source-%d", n);
- _sources[n]->id().print (buf, sizeof(buf));
- node.add_property (buf2, buf);
- }
-
- for (uint32_t n=0; n < _master_sources.size(); ++n) {
- snprintf (buf2, sizeof(buf2), "master-source-%d", n);
- _master_sources[n]->id().print (buf, sizeof (buf));
- node.add_property (buf2, buf);
- }
-
- if (full && _extra_xml) {
- node.add_child_copy (*_extra_xml);
- }
-
- return node;
+ return Region::state (full);
}
int
}
void
-RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
+RegionListProperty::diff (PropertyList& undo, PropertyList& redo, Command* cmd) const
{
if (changed()) {
/* list of the removed/added regions since clear_history() was last called */
RegionListProperty* b = copy_for_history ();
b->invert_changes ();
+ if (cmd) {
+ /* whenever one of the regions emits DropReferences, make sure
+ that the Destructible we've been told to notify hears about
+ it. the Destructible is likely to be the Command being built
+ with this diff().
+ */
+
+ for (set<boost::shared_ptr<Region> >::iterator i = a->change().added.begin(); i != a->change().added.end(); ++i) {
+ (*i)->DropReferences.connect_same_thread (*cmd, boost::bind (&Destructible::drop_references, cmd));
+ }
+ }
+
undo.add (b);
redo.add (a);
}
Region::~Region ()
{
DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
+ drop_sources ();
}
void
}
XMLNode&
-Region::state (bool /*full_state*/)
+Region::state (bool full)
{
XMLNode *node = new XMLNode ("Region");
char buf[64];
+ char buf2[64];
+ LocaleGuard lg (X_("POSIX"));
const char* fe = NULL;
add_properties (*node);
node->add_property ("bbt-position", str.str());
}
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "source-%d", n);
+ _sources[n]->id().print (buf, sizeof(buf));
+ node->add_property (buf2, buf);
+ }
+
+ for (uint32_t n=0; n < _master_sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "master-source-%d", n);
+ _master_sources[n]->id().print (buf, sizeof (buf));
+ node->add_property (buf2, buf);
+ }
+
+ if (full && _extra_xml) {
+ node->add_child_copy (*_extra_xml);
+ }
+
return *node;
}
}
if (send) {
- cerr << _name << ": final change to be sent: ";
- for (PropertyChange::iterator i = what_changed.begin(); i != what_changed.end(); ++i) {
- cerr << g_quark_to_string ((GQuark) *i) << ' ';
- }
- cerr << endl;
send_change (what_changed);
}
void
Region::source_deleted (boost::weak_ptr<Source>)
{
- _sources.clear ();
+ drop_sources ();
if (!_session.deletion_in_progress()) {
/* this is a very special case: at least one of the region's
void
Region::set_master_sources (const SourceList& srcs)
{
+ for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
+ (*i)->dec_use_count ();
+ }
+
_master_sources = srcs;
assert (_sources.size() == _master_sources.size());
+
+ for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
+ (*i)->inc_use_count ();
+ }
}
bool
sframes_t
Region::source_length(uint32_t n) const
{
+ assert (n < _sources.size());
return _sources[n]->length(_position - _start);
}
framecnt_t maxlen = 0;
- for (uint32_t n=0; n < _sources.size(); ++n) {
+ for (uint32_t n = 0; n < _sources.size(); ++n) {
maxlen = max (maxlen, source_length(n) - _start);
}
framecnt_t maxlen = 0;
- for (uint32_t n=0; n < _sources.size(); ++n) {
+ for (uint32_t n = 0; n < _sources.size(); ++n) {
maxlen = max (maxlen, source_length(n) - new_start);
}
return true;
}
- for (uint32_t n=0; n < _sources.size(); ++n) {
+ for (uint32_t n = 0; n < _sources.size(); ++n) {
if (pos > source_length(n) - _length) {
return false;
}
return true;
}
- for (uint32_t n=0; n < _sources.size(); ++n) {
+ for (uint32_t n = 0; n < _sources.size(); ++n) {
if (new_start > source_length(n) - _length) {
new_start = source_length(n) - _length;
}
_transients.clear ();
}
+void
+Region::drop_sources ()
+{
+ for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
+ (*i)->dec_use_count ();
+ }
+
+ _sources.clear ();
+
+ for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
+ (*i)->dec_use_count ();
+ }
+
+ _master_sources.clear ();
+}
void
Region::use_sources (SourceList const & s)
set<boost::shared_ptr<Source> > unique_srcs;
for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
- _sources.push_back (*i);
- (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
- unique_srcs.insert (*i);
- }
- for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
+ _sources.push_back (*i);
+ (*i)->inc_use_count ();
_master_sources.push_back (*i);
- if (unique_srcs.find (*i) == unique_srcs.end()) {
- (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
- }
+ (*i)->inc_use_count ();
+
+ /* connect only once to DropReferences, even if sources are replicated
+ */
+
+ if (unique_srcs.find (*i) == unique_srcs.end ()) {
+ unique_srcs.insert (*i);
+ (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
+ }
}
}
}
if (ret) {
+ cerr << "Pure copy constructor region " << ret << " named " << ret->name() << endl;
map_add (ret);
/* pure copy constructor - no property list */
if (ret) {
ret->set_properties (plist);
+ cerr << "Partial copy constructor region\n";
map_add (ret);
if (announce) {
if (ret) {
ret->set_properties (plist);
+ cerr << "New sources copy constructor region\n";
map_add (ret);
if (announce) {
if (ret) {
ret->set_properties (plist);
+ cerr << "de-novo constructor region " << ret << " named " << ret->name() << endl;
map_add (ret);
if (announce) {
boost::bind (&RegionFactory::region_changed, _1, boost::weak_ptr<Region> (r))
);
+ cerr << "Added region with ID = " << r->id() << " named " << r->name() << endl;
+
update_region_name_map (r);
}
if (i != region_map.end()) {
region_map.erase (i);
+ cerr << "Removed region with ID = " << r->id() << " named " << r->name() << endl;;
}
+
+}
+
+void
+RegionFactory::map_remove_with_equivalents (boost::shared_ptr<Region> r)
+{
+ Glib::Mutex::Lock lm (region_map_lock);
+
+ for (RegionMap::iterator i = region_map.begin(); i != region_map.end(); ) {
+ RegionMap::iterator tmp = i;
+ ++tmp;
+
+ if (r->region_list_equivalent (i->second)) {
+ cerr << "Removed equivalent region " << i->second->name() << '/' << i->first << endl;
+ region_map.erase (i);
+ } else if (r == i->second) {
+ cerr << "Removed actual region " << i->second->name() << '/' << i->first << endl;
+ region_map.erase (i);
+ }
+
+ i = tmp;
+ }
+
+
}
boost::shared_ptr<Region>
error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg;
return old;
}
+
+void
+RegionFactory::get_regions_using_source (boost::shared_ptr<Source> s, std::set<boost::shared_ptr<Region> >& r)
+{
+ Glib::Mutex::Lock lm (region_map_lock);
+
+ for (RegionMap::iterator i = region_map.begin(); i != region_map.end(); ++i) {
+ if (i->second->uses_source (s)) {
+ r.insert (i->second);
+ }
+ }
+}
auto_connect_route (track, existing_inputs, existing_outputs);
track->non_realtime_input_change();
+
if (route_group) {
route_group->add (track);
}
}
int
-Session::destroy_region (boost::shared_ptr<Region> region)
+Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
{
- vector<boost::shared_ptr<Source> > srcs;
-
- {
- if (region->playlist()) {
- region->playlist()->destroy_region (region);
- }
+ set<boost::shared_ptr<Region> > relevant_regions;
- for (uint32_t n = 0; n < region->n_channels(); ++n) {
- srcs.push_back (region->source (n));
- }
+ for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ++s) {
+ RegionFactory::get_regions_using_source (*s, relevant_regions);
}
- region->drop_references ();
+ cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl;
+
+ for (set<boost::shared_ptr<Region> >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) {
+ set<boost::shared_ptr<Region> >::iterator tmp;
+
+ tmp = r;
+ ++tmp;
+
+ cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl;
- for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
+ playlists->destroy_region (*r);
+ RegionFactory::map_remove (*r);
- (*i)->mark_for_remove ();
- (*i)->drop_references ();
+ (*r)->drop_sources ();
+ (*r)->drop_references ();
+
+ cerr << "\tdone UC = " << (*r).use_count() << endl;
+
+ relevant_regions.erase (r);
+
+ r = tmp;
+ }
+
+ for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ) {
- cerr << "source was not used by any playlist\n";
- }
+ {
+ Glib::Mutex::Lock ls (source_lock);
+ /* remove from the main source list */
+ sources.erase ((*s)->id());
+ }
- return 0;
-}
+ (*s)->mark_for_remove ();
+ (*s)->drop_references ();
+
+ s = srcs.erase (s);
+ }
-int
-Session::destroy_regions (list<boost::shared_ptr<Region> > regions)
-{
- for (list<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
- destroy_region (*i);
- }
return 0;
}
int
Session::remove_last_capture ()
{
- list<boost::shared_ptr<Region> > r;
+ list<boost::shared_ptr<Source> > srcs;
boost::shared_ptr<RouteList> rl = routes.reader ();
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
continue;
}
- list<boost::shared_ptr<Region> >& l = tr->last_capture_regions();
+ list<boost::shared_ptr<Source> >& l = tr->last_capture_sources();
if (!l.empty()) {
- r.insert (r.end(), l.begin(), l.end());
+ srcs.insert (srcs.end(), l.begin(), l.end());
l.clear ();
}
}
- destroy_regions (r);
+ destroy_sources (srcs);
save_state (_current_snapshot_name);
}
if (result.second) {
- set_dirty();
- }
- boost::shared_ptr<AudioFileSource> afs;
+ /* yay, new source */
- if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
- if (Config->get_auto_analyse_audio()) {
- Analyser::queue_source_for_analysis (source, false);
- }
- }
+ set_dirty();
+
+ boost::shared_ptr<AudioFileSource> afs;
+
+ if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (source, false);
+ }
+ }
+ }
}
void
}
}
+void
+SessionPlaylists::destroy_region (boost::shared_ptr<Region> r)
+{
+ Glib::Mutex::Lock lm (lock);
+
+ for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ (*i)->destroy_region (r);
+ }
+
+ for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ (*i)->destroy_region (r);
+ }
+}
+
+
void
SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
{
XMLNode* child = node->add_child ("Playlists");
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
if (!(*i)->hidden()) {
- if (!(*i)->empty()) {
- if (full_state) {
- child->add_child_nocopy ((*i)->get_state());
- } else {
- child->add_child_nocopy ((*i)->get_template());
- }
- }
- }
+ if (full_state) {
+ child->add_child_nocopy ((*i)->get_state());
+ } else {
+ child->add_child_nocopy ((*i)->get_template());
+ }
+ }
}
child = node->add_child ("UnusedPlaylists");
for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
- /* Don't save information about non-destructive file sources that are empty */
- /* FIXME: MIDI breaks if this is made FileSource like it should be... */
+ /* Don't save information about non-destructive file sources that are empty
+ and unused by any regions.
+ */
- boost::shared_ptr<AudioFileSource> fs;
- if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (siter->second)) != 0) {
+ cerr << "Source " << siter->second->name() << " has UC = " << siter->second->used()
+ << " length = " << siter->second->length (0)
+ << endl;
+
+ boost::shared_ptr<FileSource> fs;
+ if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) != 0) {
if (!fs->destructive()) {
- if (fs->length(fs->timeline_position()) == 0) {
+ if (fs->length(fs->timeline_position()) == 0 && !fs->used()) {
continue;
}
}
, _type(type)
, _flags(flags)
, _timeline_position(0)
+ , _use_count (0)
{
_analysed = false;
_timestamp = 0;
, _type(DataType::AUDIO)
, _flags (Flag (Writable|CanRename))
, _timeline_position(0)
+ , _use_count (0)
{
_timestamp = 0;
_analysed = false;
return _diskstream->destructive ();
}
-list<boost::shared_ptr<Region> > &
-Track::last_capture_regions ()
+list<boost::shared_ptr<Source> > &
+Track::last_capture_sources ()
{
- return _diskstream->last_capture_regions ();
+ return _diskstream->last_capture_sources ();
}
void
void
SMF::end_write() THROW_FILE_ERROR
{
+#if 0
+ /* don't create empty MIDI files
+ */
+
+ smf_rewind (_smf); // smf_save() would have done this anyway
+ if (smf_peek_next_event (_smf) == 0) {
+ return;
+ }
+#endif
+
PBD::StdioFileDescriptor d (_file_path, "w+");
FILE* f = d.allocate ();
if (f == 0) {
#ifndef __lib_pbd_command_h__
#define __lib_pbd_command_h__
+#include <string>
+
+#include "pbd/signals.h"
#include "pbd/statefuldestructible.h"
-#include <boost/utility.hpp>
-class Command : public PBD::StatefulDestructible, public boost::noncopyable
+class Command : public PBD::StatefulDestructible, public PBD::ScopedConnectionList
{
public:
virtual ~Command() { /* NOTE: derived classes must call drop_references() */ }
void set_name (const std::string& str) { _name = str; }
const std::string& name() const { return _name; }
-
+
virtual void undo() = 0;
virtual void redo() { (*this)(); }
: PropertyTemplate<T> (q, v)
{}
- void diff (PropertyList& undo, PropertyList& redo) const {
+ void diff (PropertyList& undo, PropertyList& redo, Command* /*ignored*/) const {
if (this->_have_old) {
undo.add (new Property<T> (this->property_id(), this->_old));
redo.add (new Property<T> (this->property_id(), this->_current));
: PropertyTemplate<std::string> (q, v)
{}
- void diff (PropertyList& before, PropertyList& after) const {
+ void diff (PropertyList& before, PropertyList& after, Command* /*ignored*/) const {
if (this->_have_old) {
before.add (new Property<std::string> (PropertyDescriptor<std::string> (this->property_id()), this->_old));
after.add (new Property<std::string> (PropertyDescriptor<std::string> (this->property_id()), this->_current));
#include "pbd/xml++.h"
+class Command;
+
namespace PBD {
class PropertyList;
{}
virtual ~PropertyBase () {}
-
+
/** Forget about any old value for this state */
virtual void clear_history () = 0;
* the last call to clear_history, and one that allows redo
* of those changes.
*/
- virtual void diff (PropertyList& undo, PropertyList& redo) const = 0;
+ virtual void diff (PropertyList& undo, PropertyList& redo, Command*) const = 0;
virtual PropertyBase* maybe_clone_self_if_found_in_history_node (const XMLNode&) const { return 0; }
/* history management */
void clear_history ();
- void diff (PropertyList&, PropertyList&) const;
+ void diff (PropertyList&, PropertyList&, Command*) const;
bool changed() const;
/* create a property list from an XMLNode
namespace PBD
{
-class Stateful;
+class StatefulDestructible;
class PropertyList;
/** A Command which stores its action as the differences between the before and after
class StatefulDiffCommand : public Command
{
public:
- StatefulDiffCommand (boost::shared_ptr<Stateful>);
- StatefulDiffCommand (boost::shared_ptr<Stateful>, XMLNode const &);
+ StatefulDiffCommand (boost::shared_ptr<StatefulDestructible>);
+ StatefulDiffCommand (boost::shared_ptr<StatefulDestructible>, XMLNode const &);
~StatefulDiffCommand ();
void operator() ();
void undo ();
-
+
XMLNode& get_state ();
private:
#include <sigc++/bind.h>
#include <sys/time.h>
-#include "pbd/signals.h"
#include "pbd/command.h"
typedef sigc::slot<void> UndoAction;
-class UndoTransaction : public Command, public PBD::ScopedConnectionList
+class UndoTransaction : public Command
{
public:
UndoTransaction ();
}
void
-Stateful::diff (PropertyList& before, PropertyList& after) const
+Stateful::diff (PropertyList& before, PropertyList& after, Command* cmd) const
{
for (OwnedPropertyList::const_iterator i = _properties->begin(); i != _properties->end(); ++i) {
- i->second->diff (before, after);
+ i->second->diff (before, after, cmd);
}
}
* @param s Stateful object.
*/
-StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<Stateful> s)
- : _object (s)
+StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<StatefulDestructible> s)
+ : _object (s)
, _undo (new PropertyList)
, _redo (new PropertyList)
{
- s->diff (*_undo, *_redo);
+ s->diff (*_undo, *_redo, this);
+
+ /* if the stateful object that this command refers to goes away,
+ be sure to notify owners of this command.
+ */
+
+ s->DropReferences.connect_same_thread (*this, boost::bind (&Destructible::drop_references, this));
}
-StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<Stateful> s, XMLNode const & n)
+StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<StatefulDestructible> s, XMLNode const & n)
: _object (s)
, _undo (0)
, _redo (0)
assert (_undo != 0);
assert (_redo != 0);
+
+ /* if the stateful object that this command refers to goes away,
+ be sure to notify owners of this command.
+ */
+
+ s->DropReferences.connect_same_thread (*this, boost::bind (&Destructible::drop_references, this));
}
StatefulDiffCommand::~StatefulDiffCommand ()
{
+ drop_references ();
+
delete _undo;
delete _redo;
}
UndoTransaction::UndoTransaction (const UndoTransaction& rhs)
: Command(rhs._name)
- , PBD::ScopedConnectionList ()
, _clearing(false)
{
clear ();
}
void
-UndoTransaction::add_command (Command *const action)
+UndoTransaction::add_command (Command *const cmd)
{
/* catch death of command (e.g. caused by death of object to
which it refers. command_death() is a normal static function
so there is no need to manage this connection.
*/
- action->DropReferences.connect_same_thread (*this, boost::bind (&command_death, this, action));
- actions.push_back (action);
+ cmd->DropReferences.connect_same_thread (*this, boost::bind (&command_death, this, cmd));
+ actions.push_back (cmd);
}
void
return;
}
- struct timeval start, end, diff;
- gettimeofday (&start, 0);
-
{
UndoRedoSignaller exception_safe_signaller (*this);
ut->undo ();
RedoList.push_back (ut);
}
- gettimeofday (&end, 0);
- timersub (&end, &start, &diff);
- cerr << "Undo-pre-signals took " << diff.tv_sec << '.' << diff.tv_usec << endl;
-
}
- gettimeofday (&end, 0);
- timersub (&end, &start, &diff);
- cerr << "Undo took " << diff.tv_sec << '.' << diff.tv_usec << endl;
-
Changed (); /* EMIT SIGNAL */
}
return;
}
- struct timeval start, end, diff;
- gettimeofday (&start, 0);
-
{
UndoRedoSignaller exception_safe_signaller (*this);
ut->redo ();
UndoList.push_back (ut);
}
- gettimeofday (&end, 0);
- timersub (&end, &start, &diff);
- cerr << "Redo-pre-signals took " << diff.tv_sec << '.' << diff.tv_usec << endl;
}
- gettimeofday (&end, 0);
- timersub (&end, &start, &diff);
- cerr << "Redo took " << diff.tv_sec << '.' << diff.tv_usec << endl;
-
Changed (); /* EMIT SIGNAL */
}