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:
Paul Davis 2011-05-16 02:17:58 +00:00
parent 88a6513e76
commit 0a9cef7720
18 changed files with 233 additions and 18 deletions

View File

@ -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>);

View File

@ -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));

View File

@ -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 ();
}
}

View File

@ -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");
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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,

View File

@ -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 ();

View File

@ -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 */

View File

@ -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 ();

View File

@ -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;

View File

@ -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;
}

View File

@ -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]);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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();
}

View File

@ -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;
}