/* * Copyright (C) 2006-2016 David Robillard * Copyright (C) 2007-2012 Carl Hetherington * Copyright (C) 2007-2017 Paul Davis * Copyright (C) 2013 John Emmas * Copyright (C) 2014-2018 Ben Loftis * Copyright (C) 2015-2019 Robin Gareus * Copyright (C) 2015 André Nusser * Copyright (C) 2016-2017 Nick Mainsbridge * Copyright (C) 2016-2017 Tim Mayberry * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __ardour_playlist_h__ #define __ardour_playlist_h__ #include #include #include #include #include #include #include #include #include #include "pbd/undo.h" #include "pbd/stateful.h" #include "pbd/statefuldestructible.h" #include "pbd/sequence_property.h" #include "pbd/stacktrace.h" #include "evoral/Range.h" #include "ardour/ardour.h" #include "ardour/region.h" #include "ardour/session_object.h" #include "ardour/data_type.h" namespace ARDOUR { class Session; class Playlist; class Crossfade; namespace Properties { /* fake the type, since regions are handled by SequenceProperty which doesn't * care about such things. */ LIBARDOUR_API extern PBD::PropertyDescriptor regions; } class LIBARDOUR_API RegionListProperty : public PBD::SequenceProperty > > { public: RegionListProperty (Playlist&); RegionListProperty* clone () const; void get_content_as_xml (boost::shared_ptr, XMLNode &) const; boost::shared_ptr get_content_from_xml (XMLNode const &) const; private: RegionListProperty* create () const; /* copy construction only by ourselves */ RegionListProperty (RegionListProperty const & p); friend class Playlist; /* we live and die with our playlist, no lifetime management needed */ Playlist& _playlist; }; class LIBARDOUR_API Playlist : public SessionObject , public boost::enable_shared_from_this { public: static void make_property_quarks (); Playlist (Session&, const XMLNode&, DataType type, bool hidden = false); Playlist (Session&, std::string name, DataType type, bool hidden = false); Playlist (boost::shared_ptr, std::string name, bool hidden = false); Playlist (boost::shared_ptr, samplepos_t start, samplecnt_t cnt, std::string name, bool hidden = false); virtual ~Playlist (); void update (const RegionListProperty::ChangeRecord&); void clear_owned_changes (); void rdiff (std::vector&) const; boost::shared_ptr region_by_id (const PBD::ID&) const; uint32_t max_source_level () const; void set_region_ownership (); virtual void clear (bool with_signals=true); virtual void dump () const; void use(); void release(); bool used () const { return _refcnt != 0; } bool set_name (const std::string& str); int sort_id() { return _sort_id; } const DataType& data_type() const { return _type; } bool frozen() const { return _frozen; } void set_frozen (bool yn); void AddToSoloSelectedList(const Region*); void RemoveFromSoloSelectedList(const Region*); bool SoloSelectedListIncludes(const Region*); bool SoloSelectedActive(); bool hidden() const { return _hidden; } bool empty() const; bool shared () const { return !_shared_with_ids.empty(); } void share_with (const PBD::ID&); void unshare_with (const PBD::ID&); bool shared_with (const PBD::ID&) const; void reset_shares (); uint32_t n_regions() const; bool all_regions_empty() const; std::pair get_extent () const; std::pair get_extent_with_endspace() const; layer_t top_layer() const; EditMode get_edit_mode() const { return _edit_mode; } void set_edit_mode (EditMode); /* Editing operations */ void add_region (boost::shared_ptr, samplepos_t position, float times = 1, bool auto_partition = false, int32_t sub_num = 0, double quarter_note = 0.0, bool for_music = false); void remove_region (boost::shared_ptr); void get_equivalent_regions (boost::shared_ptr, std::vector >&); void get_region_list_equivalent_regions (boost::shared_ptr, std::vector >&); void get_source_equivalent_regions (boost::shared_ptr, std::vector >&); void replace_region (boost::shared_ptr old, boost::shared_ptr newr, samplepos_t pos); void split_region (boost::shared_ptr, const MusicSample& position); void split (const MusicSample& at); void shift (samplepos_t at, sampleoffset_t distance, bool move_intersected, bool ignore_music_glue); void partition (samplepos_t start, samplepos_t end, bool cut = false); void duplicate (boost::shared_ptr, samplepos_t position, float times); void duplicate (boost::shared_ptr, samplepos_t position, samplecnt_t gap, float times); void duplicate_until (boost::shared_ptr, samplepos_t position, samplecnt_t gap, samplepos_t end); void duplicate_range (AudioRange&, float times); void duplicate_ranges (std::list&, float times); void nudge_after (samplepos_t start, samplecnt_t distance, bool forwards); boost::shared_ptr combine (const RegionList&); void uncombine (boost::shared_ptr); void fade_range (std::list&); void shuffle (boost::shared_ptr, int dir); void ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude); void ripple (samplepos_t at, samplecnt_t distance, boost::shared_ptr exclude) { RegionList el; if (exclude) el.push_back (exclude); ripple (at, distance, &el); } void update_after_tempo_map_change (); boost::shared_ptr cut (std::list&, bool result_is_hidden = true); boost::shared_ptr copy (std::list&, bool result_is_hidden = true); int paste (boost::shared_ptr, samplepos_t position, float times, const int32_t sub_num); const RegionListProperty& region_list_property () const { return regions; } boost::shared_ptr region_list(); boost::shared_ptr regions_at (samplepos_t sample); uint32_t count_regions_at (samplepos_t) const; /** @param start Range start. * @param end Range end. * @return regions which have some part within this range. */ boost::shared_ptr regions_touched (samplepos_t start, samplepos_t end); boost::shared_ptr regions_with_start_within (Evoral::Range); boost::shared_ptr regions_with_end_within (Evoral::Range); uint32_t region_use_count (boost::shared_ptr) const; boost::shared_ptr find_region (const PBD::ID&) const; boost::shared_ptr top_region_at (samplepos_t sample); boost::shared_ptr top_unmuted_region_at (samplepos_t sample); boost::shared_ptr find_next_region (samplepos_t sample, RegionPoint point, int dir); samplepos_t find_next_region_boundary (samplepos_t sample, int dir); bool region_is_shuffle_constrained (boost::shared_ptr); bool has_region_at (samplepos_t const) const; bool uses_source (boost::shared_ptr src, bool shallow = false) const; void deep_sources (std::set >&) const; samplepos_t find_next_transient (samplepos_t position, int dir); void foreach_region (boost::function)>); XMLNode& get_state (); virtual int set_state (const XMLNode&, int version); XMLNode& get_template (); PBD::Signal1 InUse; PBD::Signal0 ContentsChanged; PBD::Signal1 > RegionAdded; PBD::Signal1 > RegionRemoved; PBD::Signal0 NameChanged; PBD::Signal0 LayeringChanged; /** Emitted when regions have moved (not when regions have only been trimmed) */ PBD::Signal2 > const &, bool> RangesMoved; /** Emitted when regions are extended; the ranges passed are the new extra time ranges that these regions now occupy. */ PBD::Signal1 > const &> RegionsExtended; static std::string bump_name (std::string old_name, Session&); void freeze (); void thaw (bool from_undo = false); void raise_region (boost::shared_ptr); void lower_region (boost::shared_ptr); void raise_region_to_top (boost::shared_ptr); void lower_region_to_bottom (boost::shared_ptr); const PBD::ID& get_orig_track_id () const { return _orig_track_id; } void set_orig_track_id (const PBD::ID& did); /* destructive editing */ virtual bool destroy_region (boost::shared_ptr) = 0; void sync_all_regions_with_regions (); /* special case function used by UI selection objects, which have playlists that actually own the regions within them. */ void drop_regions (); virtual boost::shared_ptr find_crossfade (const PBD::ID &) const { return boost::shared_ptr (); } samplepos_t find_next_top_layer_position (samplepos_t) const; uint32_t combine_ops() const { return _combine_ops; } void set_layer (boost::shared_ptr, double); void set_capture_insertion_in_progress (bool yn); protected: friend class Session; protected: class RegionReadLock : public Glib::Threads::RWLock::ReaderLock { public: RegionReadLock (Playlist *pl) : Glib::Threads::RWLock::ReaderLock (pl->region_lock) {} ~RegionReadLock() {} }; class RegionWriteLock : public Glib::Threads::RWLock::WriterLock { public: RegionWriteLock (Playlist *pl, bool do_block_notify = true) : Glib::Threads::RWLock::WriterLock (pl->region_lock) , playlist (pl) , block_notify (do_block_notify) { if (block_notify) { playlist->delay_notifications(); } } ~RegionWriteLock() { Glib::Threads::RWLock::WriterLock::release (); if (block_notify) { playlist->release_notifications (); } } Playlist *playlist; bool block_notify; }; RegionListProperty regions; /* the current list of regions in the playlist */ std::set > all_regions; /* all regions ever added to this playlist */ PBD::ScopedConnectionList region_state_changed_connections; PBD::ScopedConnectionList region_drop_references_connections; DataType _type; uint32_t _sort_id; mutable gint block_notifications; mutable gint ignore_state_changes; std::set > pending_adds; std::set > pending_removes; RegionList pending_bounds; bool pending_contents_change; bool pending_layering; std::set _soloSelectedRegions; /** Movements of time ranges caused by region moves; note that * region trims are not included in this list; it is used to * do automation-follows-regions. */ std::list< Evoral::RangeMove > pending_range_moves; /** Extra sections added to regions during trims */ std::list< Evoral::Range > pending_region_extensions; uint32_t in_set_state; bool in_undo; bool first_set_state; bool _hidden; bool _splicing; bool _rippling; bool _shuffling; bool _nudging; uint32_t _refcnt; EditMode _edit_mode; bool in_flush; bool in_partition; bool _frozen; bool _capture_insertion_underway; uint32_t subcnt; PBD::ID _orig_track_id; uint32_t _combine_ops; std::list _shared_with_ids; void init (bool hide); bool holding_state () const { return g_atomic_int_get (&block_notifications) != 0 || g_atomic_int_get (&ignore_state_changes) != 0; } void delay_notifications (); void release_notifications (bool from_undo = false); virtual void flush_notifications (bool from_undo = false); void clear_pending (); void _set_sort_id (); boost::shared_ptr regions_touched_locked (samplepos_t start, samplepos_t end); void notify_region_removed (boost::shared_ptr); void notify_region_added (boost::shared_ptr); void notify_layering_changed (); void notify_contents_changed (); void notify_state_changed (const PBD::PropertyChange&); void notify_region_moved (boost::shared_ptr); void notify_region_start_trimmed (boost::shared_ptr); void notify_region_end_trimmed (boost::shared_ptr); void mark_session_dirty(); void region_changed_proxy (const PBD::PropertyChange&, boost::weak_ptr); virtual bool region_changed (const PBD::PropertyChange&, boost::shared_ptr); void region_bounds_changed (const PBD::PropertyChange&, boost::shared_ptr); void region_deleted (boost::shared_ptr); void sort_regions (); void possibly_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr exclude = boost::shared_ptr()); void possibly_splice_unlocked(samplepos_t at, samplecnt_t distance, boost::shared_ptr exclude = boost::shared_ptr()); void core_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr exclude); void splice_locked (samplepos_t at, samplecnt_t distance, boost::shared_ptr exclude); void splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr exclude); void core_ripple (samplepos_t at, samplecnt_t distance, RegionList *exclude); void ripple_locked (samplepos_t at, samplecnt_t distance, RegionList *exclude); void ripple_unlocked (samplepos_t at, samplecnt_t distance, RegionList *exclude); virtual void remove_dependents (boost::shared_ptr /*region*/) {} virtual void region_going_away (boost::weak_ptr /*region*/) {} virtual XMLNode& state (bool); bool add_region_internal (boost::shared_ptr, samplepos_t position, int32_t sub_num = 0, double quarter_note = 0.0, bool for_music = false); int remove_region_internal (boost::shared_ptr); void copy_regions (RegionList&) const; void partition_internal (samplepos_t start, samplepos_t end, bool cutting, RegionList& thawlist); std::pair _get_extent() const; boost::shared_ptr cut_copy (boost::shared_ptr (Playlist::*pmf)(samplepos_t, samplecnt_t, bool), std::list& ranges, bool result_is_hidden); boost::shared_ptr cut (samplepos_t start, samplecnt_t cnt, bool result_is_hidden); boost::shared_ptr copy (samplepos_t start, samplecnt_t cnt, bool result_is_hidden); void relayer (); void begin_undo (); void end_undo (); virtual void _split_region (boost::shared_ptr, const MusicSample& position); typedef std::pair, boost::shared_ptr > TwoRegions; /* this is called before we create a new compound region */ virtual void pre_combine (std::vector >&) {} /* this is called before we create a new compound region */ virtual void post_combine (std::vector >&, boost::shared_ptr) {} /* this is called before we remove a compound region and replace it with its constituent regions */ virtual void pre_uncombine (std::vector >&, boost::shared_ptr) {} private: friend class RegionReadLock; friend class RegionWriteLock; mutable Glib::Threads::RWLock region_lock; private: void setup_layering_indices (RegionList const &); void coalesce_and_check_crossfades (std::list >); boost::shared_ptr find_regions_at (samplepos_t); samplepos_t _end_space; //this is used when we are pasting a range with extra space at the end bool _playlist_shift_active; }; } /* namespace ARDOUR */ #endif /* __ardour_playlist_h__ */