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
This commit is contained in:
parent
88a6513e76
commit
0a9cef7720
@ -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<ARDOUR::Region>);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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<RouteTimeAxisView*> RTVS;
|
||||
RTVS tracks;
|
||||
|
||||
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
|
||||
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
|
||||
|
||||
if (rtv) {
|
||||
tracks.insert (rtv);
|
||||
}
|
||||
}
|
||||
|
||||
for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
|
||||
(*i)->join_regions ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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<Sample*> _mixdown_buffers;
|
||||
static std::vector<gain_t*> _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,
|
||||
|
@ -97,6 +97,8 @@ public:
|
||||
|
||||
boost::shared_ptr<Region> 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<Region>, 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<Region>, int dir);
|
||||
void update_after_tempo_map_change ();
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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 ();
|
||||
|
@ -57,8 +57,7 @@ class SourceFactory {
|
||||
|
||||
static boost::shared_ptr<Source> createFromPlaylist
|
||||
(DataType type, Session& s, boost::shared_ptr<Playlist> 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;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include <glibmm/fileutils.h>
|
||||
#include <glibmm/miscutils.h>
|
||||
|
||||
#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<Playlist> p = _session.playlists->by_id (id);
|
||||
_playlist = boost::dynamic_pointer_cast<AudioPlaylist>(p);
|
||||
|
||||
if (!p) {
|
||||
if (!_playlist) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
pair<framepos_t,framepos_t> 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;
|
||||
}
|
||||
|
||||
|
@ -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<Sample*> AudioSource::_mixdown_buffers;
|
||||
vector<gain_t*> 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]);
|
||||
}
|
||||
}
|
||||
|
@ -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<Playlist> 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<Region> original_region = (*i);
|
||||
boost::shared_ptr<Region> 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<framepos_t,framepos_t> 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<Region> 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<Playlist *> (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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -326,8 +326,7 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat
|
||||
|
||||
boost::shared_ptr<Source>
|
||||
SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr<Playlist> 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<AudioPlaylist> ap = boost::dynamic_pointer_cast<AudioPlaylist>(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<Source> 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user