From 0a9cef7720ed9bd83442d284d18831437b80a482 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 16 May 2011 02:17:58 +0000 Subject: very basic Join (regions) editing operation. not finished yet, no undoable, no sensible name for new region, etc. etc git-svn-id: svn://localhost/ardour2/branches/3.0@9518 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/editor.h | 1 + gtk2_ardour/editor_actions.cc | 1 + gtk2_ardour/editor_ops.cc | 24 +++++++++ gtk2_ardour/route_time_axis.cc | 21 ++++++++ gtk2_ardour/route_time_axis.h | 2 +- libs/ardour/ardour/audio_playlist_source.h | 1 + libs/ardour/ardour/audiosource.h | 18 ++++++- libs/ardour/ardour/playlist.h | 4 ++ libs/ardour/ardour/region.h | 1 + libs/ardour/ardour/source.h | 4 +- libs/ardour/ardour/source_factory.h | 3 +- libs/ardour/audio_playlist_source.cc | 36 ++++++++++--- libs/ardour/audiosource.cc | 28 ++++++++++ libs/ardour/playlist.cc | 82 ++++++++++++++++++++++++++++++ libs/ardour/region.cc | 11 ++++ libs/ardour/session_state.cc | 2 + libs/ardour/source.cc | 3 ++ libs/ardour/source_factory.cc | 9 ++-- 18 files changed, 233 insertions(+), 18 deletions(-) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 8150f44ae3..83d8b394c3 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1099,6 +1099,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void duplicate_some_regions (RegionSelection&, float times); void duplicate_selection (float times); void region_fill_selection (); + void join_regions (); void region_fill_track (); void audition_playlist_region_standalone (boost::shared_ptr); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index bf2af13f52..0081a806aa 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -1372,6 +1372,7 @@ Editor::register_region_actions () reg_sens (_region_actions, "play-selected-regions", _("Play"), sigc::mem_fun(*this, &Editor::play_selected_region)); reg_sens (_region_actions, "bounce-region", _("Bounce"), sigc::mem_fun (*this, &Editor::bounce_region_selection)); + reg_sens (_region_actions, "join-regions", _("Join"), sigc::mem_fun (*this, &Editor::join_regions)); reg_sens (_region_actions, "analyze-region", _("Spectral Analysis..."), sigc::mem_fun (*this, &Editor::analyze_region_selection)); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 935694a640..5f361ae153 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -6401,3 +6401,27 @@ Editor::toggle_region_mute () commit_reversible_command (); } +void +Editor::join_regions () +{ + /* foreach track with selected regions, take all selected regions + and join them into a new region containing the subregions (as a + playlist) + */ + + typedef set RTVS; + RTVS tracks; + + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + RouteTimeAxisView* rtv = dynamic_cast(&(*i)->get_time_axis_view()); + + if (rtv) { + tracks.insert (rtv); + } + } + + for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) { + (*i)->join_regions (); + } +} + diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 8871b7bbe1..449718d15a 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -2477,3 +2477,24 @@ RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show); } + +static +void add_region_to_list (RegionView* rv, Playlist::RegionList* l) +{ + l->push_back (rv->region()); +} + +void +RouteTimeAxisView::join_regions () +{ + assert (is_track()); + + if (!_view) { + return; + } + + Playlist::RegionList selected_regions; + + _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions)); + track()->playlist()->join (selected_regions, "foshizzle"); +} diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index c68dc573ea..d596de3df7 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -94,7 +94,7 @@ public: /* Editing operations */ void cut_copy_clear (Selection&, Editing::CutCopyOp); bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth); - + void join_regions (); void toggle_automation_track (const Evoral::Parameter& param); /* The editor calls these when mapping an operation across multiple tracks */ diff --git a/libs/ardour/ardour/audio_playlist_source.h b/libs/ardour/ardour/audio_playlist_source.h index d6047de1fc..868e8c743e 100644 --- a/libs/ardour/ardour/audio_playlist_source.h +++ b/libs/ardour/ardour/audio_playlist_source.h @@ -65,6 +65,7 @@ class AudioPlaylistSource : public AudioSource { framecnt_t _playlist_length; uint32_t _playlist_channel; std::string _peak_path; + uint32_t _level; /* how recursive is this? */ }; } /* namespace */ diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 5f06d3ee09..d0d34a55c4 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -106,10 +106,26 @@ class AudioSource : virtual public Source, /** @return true if the each source sample s must be clamped to -1 < s < 1 */ virtual bool clamped_at_unity () const = 0; + static void allocate_working_buffers (); + protected: static bool _build_missing_peakfiles; static bool _build_peakfiles; + static size_t _working_buffers_size; + + /* these collections of working buffers for supporting + playlist's reading from potentially nested/recursive + sources assume SINGLE THREADED reads by the butler + thread, or a lock around calls that use them. + */ + + static std::vector _mixdown_buffers; + static std::vector _gain_buffers; + static Glib::StaticMutex _level_buffer_lock; + + static void ensure_buffers_for_level (uint32_t); + framecnt_t _length; std::string peakpath; std::string _captured_for; @@ -129,7 +145,7 @@ class AudioSource : virtual public Source, virtual framecnt_t write_unlocked (Sample *dst, framecnt_t cnt) = 0; virtual std::string peak_path(std::string audio_path) = 0; virtual std::string find_broken_peakfile (std::string missing_peak_path, - std::string audio_path) { return std::string(); } + std::string audio_path) { return peak_path (audio_path); } virtual int read_peaks_with_fpp (PeakData *peaks, framecnt_t npeaks, framepos_t start, framecnt_t cnt, diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index e21199e642..e23ba264fc 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -97,6 +97,8 @@ public: 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); @@ -137,6 +139,8 @@ public: void partition (framepos_t start, framepos_t end, bool cut = false); void duplicate (boost::shared_ptr, framepos_t position, float times); void nudge_after (framepos_t start, framecnt_t distance, bool forwards); + void join (const RegionList&, const std::string&); + void shuffle (boost::shared_ptr, int dir); void update_after_tempo_map_change (); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 17ea1362d8..719c00acf8 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -114,6 +114,7 @@ class Region layer_t layer () const { return _layer; } framecnt_t source_length(uint32_t n) const; + uint32_t max_source_level () const; /* these two are valid ONLY during a StateChanged signal handler */ diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index aadb7a51b3..4af94d44b8 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -106,17 +106,19 @@ class Source : public SessionObject virtual void dec_use_count (); int use_count() const { return g_atomic_int_get (&_use_count); } bool used() const { return use_count() > 0; } + uint32_t level() const { return _level; } protected: DataType _type; Flag _flags; time_t _timestamp; - framepos_t _timeline_position; + framepos_t _timeline_position; bool _analysed; mutable Glib::Mutex _lock; mutable Glib::Mutex _analysis_lock; Glib::Mutex _playlist_lock; gint _use_count; /* atomic */ + uint32_t _level; /* how deeply nested is this source w.r.t a disk file */ private: void fix_writable_flags (); diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h index b8bc5e72b5..427bf10a56 100644 --- a/libs/ardour/ardour/source_factory.h +++ b/libs/ardour/ardour/source_factory.h @@ -57,8 +57,7 @@ class SourceFactory { static boost::shared_ptr createFromPlaylist (DataType type, Session& s, boost::shared_ptr p, const std::string& name, - uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, Source::Flag flags, - bool announce, bool defer_peaks); + uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks); static Glib::Cond* PeaksToBuild; static Glib::StaticMutex peak_building_lock; diff --git a/libs/ardour/audio_playlist_source.cc b/libs/ardour/audio_playlist_source.cc index 568b72129d..7d1e528ee6 100644 --- a/libs/ardour/audio_playlist_source.cc +++ b/libs/ardour/audio_playlist_source.cc @@ -24,6 +24,7 @@ #include #include +#include #include "pbd/error.h" #include "pbd/convert.h" @@ -48,6 +49,7 @@ AudioPlaylistSource::AudioPlaylistSource (Session& s, const std::string& name, b uint32_t chn, frameoffset_t begin, framecnt_t len, bool copy, Source::Flag flags) : Source (s, DataType::AUDIO, name) , AudioSource (s, name) + , _playlist (p) , _playlist_channel (chn) { /* PlaylistSources are never writable, renameable, removable or destructive */ @@ -63,7 +65,10 @@ AudioPlaylistSource::AudioPlaylistSource (Session& s, const std::string& name, b _playlist_length = len; } - _peak_path = tempnam (_session.session_directory().peak_path().to_string().c_str(), "apspk"); + _length = len; + _peak_path = Glib::build_filename (_session.session_directory().peak_path().to_string(), name); + _level = _playlist->max_source_level () + 1; + ensure_buffers_for_level (_level); } AudioPlaylistSource::AudioPlaylistSource (Session& s, const XMLNode& node) @@ -113,11 +118,15 @@ AudioPlaylistSource::set_state (const XMLNode& node, int /* version */) /* get playlist */ boost::shared_ptr p = _session.playlists->by_id (id); + _playlist = boost::dynamic_pointer_cast(p); - if (!p) { + if (!_playlist) { throw failed_constructor (); } + pair extent = _playlist->get_extent(); + _length = extent.second - extent.first; + /* other properties */ if ((prop = node.property (X_("name"))) == 0) { @@ -149,6 +158,9 @@ AudioPlaylistSource::set_state (const XMLNode& node, int /* version */) _peak_path = prop->value (); + _level = _playlist->max_source_level (); + ensure_buffers_for_level (_level); + return 0; } @@ -172,8 +184,11 @@ AudioPlaylistSource::read_unlocked (Sample* dst, framepos_t start, framecnt_t cn to_zero = 0; } - _playlist->read (dst, 0, 0, start+_playlist_offset, to_read, _playlist_channel); - + { + Glib::Mutex::Lock lm (_level_buffer_lock); + _playlist->read (dst, _mixdown_buffers[_level-1], _gain_buffers[_level-1], start+_playlist_offset, to_read, _playlist_channel); + } + if (to_zero) { memset (dst+to_read, 0, sizeof (Sample) * to_zero); } @@ -230,6 +245,9 @@ AudioPlaylistSource::setup_peakfile () { /* the peak data is setup once and once only */ + + cerr << "looking for peakfile " << _peak_path << endl; + if (!Glib::file_test (_peak_path, Glib::FILE_TEST_EXISTS)) { /* the 2nd argument here will be passed @@ -237,9 +255,12 @@ AudioPlaylistSource::setup_peakfile () since our peak file path is fixed and not dependent on anything. */ - - return initialize_peakfile (true, string()); - } + cerr << "build it!\n"; + return initialize_peakfile (false, string()); + } else { + cerr << "exists!\n"; + } + return 0; } @@ -248,3 +269,4 @@ AudioPlaylistSource::peak_path (string /*audio_path*/) { return _peak_path; } + diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index ac98837183..ffb8068ea5 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -38,6 +38,7 @@ #include "pbd/pthread_utils.h" #include "ardour/audiosource.h" +#include "ardour/audio_diskstream.h" #include "ardour/cycle_timer.h" #include "ardour/session.h" #include "ardour/transient_detector.h" @@ -49,6 +50,10 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +Glib::StaticMutex AudioSource::_level_buffer_lock = GLIBMM_STATIC_MUTEX_INIT; +vector AudioSource::_mixdown_buffers; +vector AudioSource::_gain_buffers; +size_t AudioSource::_working_buffers_size = 0; bool AudioSource::_build_missing_peakfiles = false; /** true if we want peakfiles (e.g. if we are displaying a GUI) */ @@ -958,3 +963,26 @@ AudioSource::mark_streaming_write_completed () PeaksReady (); /* EMIT SIGNAL */ } } + +void +AudioSource::allocate_working_buffers() +{ + assert(AudioDiskstream::disk_io_frames() > 0); + _working_buffers_size = AudioDiskstream::disk_io_frames(); + /* we don't need any buffers allocated until + a level 1 audiosource is created, at which + time we'll call ::ensure_buffers_for_level() + with the right value and do the right thing. + */ +} + +void +AudioSource::ensure_buffers_for_level (uint32_t level) +{ + Glib::Mutex::Lock lm (_level_buffer_lock); + + while (_mixdown_buffers.size() < level) { + _mixdown_buffers.push_back (new Sample[_working_buffers_size]); + _gain_buffers.push_back (new gain_t[_working_buffers_size]); + } +} diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index ea85d9fcb3..5d77310ccb 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -41,6 +41,7 @@ #include "ardour/playlist_factory.h" #include "ardour/transient_detector.h" #include "ardour/session_playlists.h" +#include "ardour/source_factory.h" #include "i18n.h" @@ -3084,3 +3085,84 @@ Playlist::find_next_top_layer_position (framepos_t t) const return max_framepos; } + +void +Playlist::join (const RegionList& r, const std::string& name) +{ + PropertyList plist; + uint32_t channels = 0; + uint32_t layer = 0; + framepos_t earliest_position = max_framepos; + + boost::shared_ptr pl = PlaylistFactory::create (_type, _session, name, true); + + for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) { + earliest_position = min (earliest_position, (*i)->position()); + } + + for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) { + + /* copy the region */ + + boost::shared_ptr original_region = (*i); + boost::shared_ptr copied_region = RegionFactory::create (original_region, false); + + /* make position relative to zero */ + + pl->add_region (copied_region, original_region->position() - earliest_position); + + /* use the maximum number of channels for any region */ + + channels = max (channels, original_region->n_channels()); + + /* it will go above the layer of the highest existing region */ + + layer = max (layer, original_region->layer()); + } + + /* now create a new PlaylistSource for each channel in the new playlist */ + + SourceList sources; + pair extent = pl->get_extent(); + + for (uint32_t chn = 0; chn < channels; ++chn) { + sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, name, chn, 0, extent.second, false, false)); + } + + /* now a new region using the list of sources */ + + plist.add (Properties::start, 0); + plist.add (Properties::length, extent.second); + plist.add (Properties::name, name); + plist.add (Properties::layer, layer+1); + + boost::shared_ptr compound_region = RegionFactory::create (sources, plist, true); + + /* remove all the selected regions from the current playlist + */ + + freeze (); + + for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) { + remove_region (*i); + } + + /* add the new region at the right location */ + + add_region (compound_region, earliest_position); + + thaw (); +} + +uint32_t +Playlist::max_source_level () const +{ + RegionLock rlock (const_cast (this)); + uint32_t lvl = 0; + + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + lvl = max (lvl, (*i)->max_source_level()); + } + + return lvl; +} diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index ff9d31fd2d..93223709e7 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -1660,3 +1660,14 @@ Region::can_trim () const return ct; } +uint32_t +Region::max_source_level () const +{ + uint32_t lvl = 0; + + for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { + lvl = max (lvl, (*i)->level()); + } + + return lvl; +} diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 00523ad933..604fc4bc52 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -69,6 +69,7 @@ #include "ardour/amp.h" #include "ardour/audio_diskstream.h" +#include "ardour/audio_playlist_source.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" @@ -220,6 +221,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) _speakers.reset (new Speakers); AudioDiskstream::allocate_working_buffers(); + AudioSource::allocate_working_buffers (); /* default short fade = 15ms */ diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 409f3e1cdb..00b642b21b 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -53,6 +53,7 @@ Source::Source (Session& s, DataType type, const string& name, Flag flags) , _flags(flags) , _timeline_position(0) , _use_count (0) + , _level (0) { _analysed = false; _timestamp = 0; @@ -65,6 +66,7 @@ Source::Source (Session& s, const XMLNode& node) , _flags (Flag (Writable|CanRename)) , _timeline_position(0) , _use_count (0) + , _level (0) { _timestamp = 0; _analysed = false; @@ -308,3 +310,4 @@ Source::writable () const { return (_flags & Writable) && _session.writable(); } + diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 891e431549..1bf3956694 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -326,8 +326,7 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat boost::shared_ptr SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr p, const std::string& name, - uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, Source::Flag flags, - bool announce, bool defer_peaks) + uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks) { if (type == DataType::AUDIO) { try { @@ -335,7 +334,7 @@ SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr< boost::shared_ptr ap = boost::dynamic_pointer_cast(p); if (ap) { - Source* src = new AudioPlaylistSource (s, name, ap, chn, start, len, copy, flags); + Source* src = new AudioPlaylistSource (s, name, ap, chn, start, len, copy, Source::Flag (0)); boost::shared_ptr ret (src); if (setup_peakfile (ret, defer_peaks)) { @@ -344,9 +343,7 @@ SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr< ret->check_for_analysis_data_on_disk (); - if (announce) { - SourceCreated (ret); - } + /* we never announce these sources */ return ret; } -- cgit v1.2.3