radically rethink export/bounce/freeze code design. probably not 100% done by freeze+unfreeze now work and behave sensibly w.r.t. processors that do routing

git-svn-id: svn://localhost/ardour2/branches/3.0@11701 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2012-03-15 21:40:17 +00:00
parent cfaf6ff7e3
commit f07ca6397f
23 changed files with 202 additions and 118 deletions

View file

@ -161,7 +161,13 @@ Editor::bounce_region_selection (bool with_processing)
InterThreadInfo itt;
boost::shared_ptr<Region> r = track->bounce_range (region->position(), region->position() + region->length(), itt, with_processing);
boost::shared_ptr<Region> r;
if (with_processing) {
r = track->bounce_range (region->position(), region->position() + region->length(), itt, track->main_outs(), false);
} else {
r = track->bounce_range (region->position(), region->position() + region->length(), itt, boost::shared_ptr<Processor>(), false);
}
}
}

View file

@ -3345,14 +3345,17 @@ Editor::unfreeze_route ()
void*
Editor::_freeze_thread (void* arg)
{
SessionEvent::create_per_thread_pool ("freeze events", 64);
return static_cast<Editor*>(arg)->freeze_thread ();
}
void*
Editor::freeze_thread ()
{
/* create event pool because we may need to talk to the session */
SessionEvent::create_per_thread_pool ("freeze events", 64);
/* create per-thread buffers for process() tree to use */
current_interthread_info->process_thread.init ();
clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
current_interthread_info->done = true;
return 0;
@ -3368,22 +3371,23 @@ Editor::freeze_route ()
/* stop transport before we start. this is important */
_session->request_transport_speed (0.0);
/* wait for just a little while, because the above call is asynchronous */
::usleep (250000);
if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
return;
}
if (!clicked_routeview->track()->bounceable()) {
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_routeview);
if (rtv && !rtv->track()->bounceable()) {
MessageDialog d (
_("This route cannot be frozen because it has more outputs than inputs. "
"You can fix this by increasing the number of inputs.")
);
d.set_title (_("Cannot freeze"));
d.run ();
return;
}
if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
MessageDialog d (
_("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
"This is typically caused by plugins that generate stereo output from mono input or vice versa.")
);
d.set_title (_("Cannot freeze"));
d.run ();
return;
}
InterThreadInfo itt;
@ -3413,16 +3417,21 @@ Editor::bounce_range_selection (bool replace, bool enable_processing)
TrackSelection views = selection->tracks;
for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable()) {
MessageDialog d (
_("You can't perform this operation because the processing of the signal "
"will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
"You can do this without processing, which is a different operation.")
);
d.set_title (_("Cannot bounce"));
d.run ();
return;
if (enable_processing) {
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
MessageDialog d (
_("You can't perform this operation because the processing of the signal "
"will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
"You can do this without processing, which is a different operation.")
);
d.set_title (_("Cannot bounce"));
d.run ();
return;
}
}
}
@ -3451,7 +3460,13 @@ Editor::bounce_range_selection (bool replace, bool enable_processing)
playlist->clear_changes ();
playlist->clear_owned_changes ();
boost::shared_ptr<Region> r = rtv->track()->bounce_range (start, start+cnt, itt, enable_processing);
boost::shared_ptr<Region> r;
if (enable_processing) {
r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
} else {
r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
}
if (!r) {
continue;

View file

@ -23,6 +23,7 @@
#include <gtkmm.h>
#include "ardour/playlist.h"
#include "ardour/timefx_request.h"
#include "ardour_dialog.h"
#include "region_selection.h"

View file

@ -20,6 +20,7 @@
#ifndef __ardour_audio_track_h__
#define __ardour_audio_track_h__
#include "ardour/interthread_info.h"
#include "ardour/track.h"
namespace ARDOUR {
@ -49,20 +50,20 @@ class AudioTrack : public Track
return DataType::AUDIO;
}
int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes, bool enable_processing = true);
void freeze_me (InterThreadInfo&);
void unfreeze ();
bool bounceable (boost::shared_ptr<Processor>, bool include_endpoint) const;
boost::shared_ptr<Region> bounce (InterThreadInfo&);
boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&, bool enable_processing);
boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&,
boost::shared_ptr<Processor> endpoint, bool include_endpoint);
int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes,
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
int set_state (const XMLNode&, int version);
boost::shared_ptr<AudioFileSource> write_source (uint32_t n = 0);
bool bounceable () const;
protected:
boost::shared_ptr<AudioDiskstream> audio_diskstream () const;
XMLNode& state (bool full);

View file

@ -29,6 +29,7 @@
#include "ardour/ardour.h"
#include "ardour/automatable.h"
#include "ardour/automation_list.h"
#include "ardour/interthread_info.h"
#include "ardour/logcurve.h"
#include "ardour/region.h"

View file

@ -52,6 +52,8 @@ public:
static bool role_requires_output_ports (Role r) { return r == Main || r == Send || r == Insert; }
bool does_routing() const { return true; }
/* Delivery to an existing output */
Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);

View file

@ -25,6 +25,7 @@
#include <stdint.h>
#include "ardour/interthread_info.h"
#include "ardour/types.h"
namespace ARDOUR {

View file

@ -52,6 +52,8 @@ class IOProcessor : public Processor
bool set_name (const std::string& str);
bool does_routing() const { return true; }
virtual ChanCount natural_output_streams() const;
virtual ChanCount natural_input_streams () const;

View file

@ -21,6 +21,7 @@
#define __ardour_midi_stretch_h__
#include "ardour/filter.h"
#include "ardour/timefx_request.h"
namespace ARDOUR {

View file

@ -20,6 +20,7 @@
#ifndef __ardour_midi_track_h__
#define __ardour_midi_track_h__
#include "ardour/interthread_info.h"
#include "ardour/track.h"
#include "ardour/midi_ring_buffer.h"
#include "ardour/midi_state_tracker.h"
@ -54,15 +55,15 @@ public:
return DataType::MIDI;
}
int export_stuff (BufferSet& bufs, framecnt_t nframes, framepos_t end_frame);
void freeze_me (InterThreadInfo&);
void unfreeze ();
bool bounceable (boost::shared_ptr<Processor>, bool) const { return false; }
boost::shared_ptr<Region> bounce (InterThreadInfo&);
boost::shared_ptr<Region> bounce_range (
framepos_t start, framepos_t end, InterThreadInfo&, bool enable_processing
);
boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&,
boost::shared_ptr<Processor> endpoint, bool include_endpoint);
int export_stuff (BufferSet& bufs, framecnt_t nframes, framepos_t end_frame,
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
int set_state (const XMLNode&, int version);
@ -100,10 +101,6 @@ public:
uint16_t get_channel_mask ();
boost::shared_ptr<MidiPlaylist> midi_playlist ();
bool bounceable () const {
return false;
}
PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded;
boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;

View file

@ -25,6 +25,7 @@
#endif
#include "ardour/filter.h"
#include "ardour/timefx_request.h"
namespace ARDOUR {
class AudioRegion;

View file

@ -9,6 +9,7 @@
namespace ARDOUR {
class ThreadBuffers;
class BufferSet;
class ProcessThread
{

View file

@ -58,6 +58,8 @@ class Processor : public SessionObject, public Automatable, public Latent
bool active () const { return _pending_active; }
virtual bool does_routing() const { return false; }
bool get_next_ab_is_active () const { return _next_ab_is_active; }
void set_next_ab_is_active (bool yn) { _next_ab_is_active = yn; }

View file

@ -21,6 +21,7 @@
#define __ardour_rbeffect_h__
#include "ardour/filter.h"
#include "ardour/timefx_request.h"
namespace ARDOUR {

View file

@ -52,6 +52,7 @@
#include "ardour/ardour.h"
#include "ardour/chan_count.h"
#include "ardour/delivery.h"
#include "ardour/interthread_info.h"
#include "ardour/rc_configuration.h"
#include "ardour/session_configuration.h"
#include "ardour/session_event.h"
@ -607,8 +608,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
/* flattening stuff */
boost::shared_ptr<Region> write_one_track (AudioTrack&, framepos_t start, framepos_t end,
bool overwrite, std::vector<boost::shared_ptr<Source> >&, InterThreadInfo& wot,
bool enable_processing = true);
bool overwrite, std::vector<boost::shared_ptr<Source> >&, InterThreadInfo& wot,
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
int freeze_all (InterThreadInfo&);
/* session-wide solo/mute/rec-enable */

View file

@ -25,6 +25,7 @@
#endif
#include "ardour/filter.h"
#include "ardour/timefx_request.h"
#ifdef USE_RUBBERBAND

View file

@ -21,6 +21,7 @@
#include <boost/shared_ptr.hpp>
#include "ardour/interthread_info.h"
#include "ardour/route.h"
#include "ardour/public_diskstream.h"
@ -87,8 +88,14 @@ class Track : public Route, public PublicDiskstream
virtual void freeze_me (InterThreadInfo&) = 0;
virtual void unfreeze () = 0;
/** @return true if the track can be bounced, or false otherwise.
*/
virtual bool bounceable (boost::shared_ptr<Processor> endpoint, bool include_endpoint) const = 0;
virtual boost::shared_ptr<Region> bounce (InterThreadInfo&) = 0;
virtual boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&, bool enable_processing = true) = 0;
virtual boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&,
boost::shared_ptr<Processor> endpoint, bool include_endpoint) = 0;
virtual int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes,
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export) = 0;
XMLNode& get_state();
XMLNode& get_template();
@ -104,11 +111,6 @@ class Track : public Route, public PublicDiskstream
void set_block_size (pframes_t);
/** @return true if the track can be bounced, or false if it cannot because
* it has more outputs than diskstream channels.
*/
virtual bool bounceable () const = 0;
/* PublicDiskstream interface */
boost::shared_ptr<Playlist> playlist ();
void request_jack_monitors_input (bool);

View file

@ -422,15 +422,6 @@ namespace ARDOUR {
AutoConnectMaster = 0x2
};
struct InterThreadInfo {
InterThreadInfo () : done (false), cancel (false), progress (0), thread (0) {}
volatile bool done;
volatile bool cancel;
volatile float progress;
pthread_t thread;
};
enum SampleFormat {
FormatFloat = 0,
FormatInt24,
@ -500,19 +491,6 @@ namespace ARDOUR {
SrcFastest
};
struct TimeFXRequest : public InterThreadInfo {
TimeFXRequest()
: time_fraction(0), pitch_fraction(0),
quick_seek(false), antialias(false), opts(0) {}
float time_fraction;
float pitch_fraction;
/* SoundTouch */
bool quick_seek;
bool antialias;
/* RubberBand */
int opts; // really RubberBandStretcher::Options
};
typedef std::list<framepos_t> AnalysisFeatureList;
typedef std::list<boost::shared_ptr<Route> > RouteList;

View file

@ -38,6 +38,7 @@
#include "ardour/meter.h"
#include "ardour/playlist_factory.h"
#include "ardour/plugin_insert.h"
#include "ardour/port_insert.h"
#include "ardour/processor.h"
#include "ardour/region_factory.h"
#include "ardour/route_group_specialized.h"
@ -482,7 +483,8 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
}
int
AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nframes, bool enable_processing)
AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nframes,
boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export)
{
boost::scoped_array<gain_t> gain_buffer (new gain_t[nframes]);
boost::scoped_array<Sample> mix_buffer (new Sample[nframes]);
@ -517,41 +519,110 @@ AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nfram
}
// If no processing is required, there's no need to go any further.
if (!enable_processing) {
if (!endpoint && !include_endpoint) {
return 0;
}
/* note: only run processors during export. other layers in the machinery
will already have checked that there are no external port processors.
Also, don't run deliveries that write to real output ports, and don't
run meters.
*/
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
boost::shared_ptr<Processor> processor = boost::dynamic_pointer_cast<Processor> (*i);
boost::shared_ptr<Delivery> delivery = boost::dynamic_pointer_cast<Delivery> (*i);
boost::shared_ptr<PeakMeter> meter = boost::dynamic_pointer_cast<PeakMeter> (*i);
if (processor && (!delivery || !Delivery::role_requires_output_ports (delivery->role())) && !meter) {
processor->run (buffers, start, start+nframes, nframes, true);
if (!include_endpoint && (*i) == endpoint) {
break;
}
/* if we're not exporting, stop processing if we come across a routing processor.
*/
if (!for_export && (*i)->does_routing()) {
break;
}
/* even for export, don't run any processor that does routing.
oh, and don't bother with the peak meter either.
*/
if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
(*i)->run (buffers, start, start+nframes, nframes, true);
}
if ((*i) == endpoint) {
break;
}
}
return 0;
}
boost::shared_ptr<Region>
AudioTrack::bounce (InterThreadInfo& itt)
bool
AudioTrack::bounceable (boost::shared_ptr<Processor> endpoint, bool include_endpoint) const
{
vector<boost::shared_ptr<Source> > srcs;
return _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(), false, srcs, itt);
if (!endpoint && !include_endpoint) {
/* no processing - just read from the playlist and create new
files: always possible.
*/
return true;
}
Glib::RWLock::ReaderLock lm (_processor_lock);
uint32_t naudio = n_inputs().n_audio();
for (ProcessorList::const_iterator r = _processors.begin(); r != _processors.end(); ++r) {
/* if we're not including the endpoint, potentially stop
right here before we test matching i/o valences.
*/
if (!include_endpoint && (*r) == endpoint) {
return true;
}
/* ignore any processors that do routing, because we will not
* use them during a bounce/freeze/export operation.
*/
if ((*r)->does_routing()) {
continue;
}
/* does the output from the last considered processor match the
* input to this one?
*/
if (naudio != (*r)->input_streams().n_audio()) {
return false;
}
/* we're including the endpoint - if we just hit it,
then stop.
*/
if ((*r) == endpoint) {
return true;
}
/* save outputs of this processor to test against inputs
of the next one.
*/
naudio = (*r)->output_streams().n_audio();
}
return true;
}
boost::shared_ptr<Region>
AudioTrack::bounce_range (framepos_t start, framepos_t end, InterThreadInfo& itt, bool enable_processing)
AudioTrack::bounce (InterThreadInfo& itt)
{
return bounce_range (_session.current_start_frame(), _session.current_end_frame(), itt, main_outs(), false);
}
boost::shared_ptr<Region>
AudioTrack::bounce_range (framepos_t start, framepos_t end, InterThreadInfo& itt,
boost::shared_ptr<Processor> endpoint, bool include_endpoint)
{
vector<boost::shared_ptr<Source> > srcs;
return _session.write_one_track (*this, start, end, false, srcs, itt, enable_processing);
return _session.write_one_track (*this, start, end, false, srcs, itt, endpoint, include_endpoint, false);
}
void
@ -594,7 +665,8 @@ AudioTrack::freeze_me (InterThreadInfo& itt)
boost::shared_ptr<Region> res;
if ((res = _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(), true, srcs, itt)) == 0) {
if ((res = _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(), true, srcs, itt,
main_outs(), false, false)) == 0) {
return;
}
@ -605,21 +677,20 @@ AudioTrack::freeze_me (InterThreadInfo& itt)
for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
boost::shared_ptr<Processor> processor;
if (!(*r)->does_routing()) {
if ((processor = boost::dynamic_pointer_cast<Processor>(*r)) != 0) {
FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo ((*r)->get_state(), (*r));
FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo ((*r)->get_state(), processor);
frii->id = processor->id();
frii->id = (*r)->id();
_freeze_record.processor_info.push_back (frii);
/* now deactivate the processor */
processor->deactivate ();
_session.set_dirty ();
(*r)->deactivate ();
}
_session.set_dirty ();
}
}
@ -692,12 +763,6 @@ AudioTrack::write_source (uint32_t n)
return ds->write_source (n);
}
bool
AudioTrack::bounceable () const
{
return n_inputs().n_audio() >= n_outputs().n_audio();
}
boost::shared_ptr<Diskstream>
AudioTrack::diskstream_factory (XMLNode const & node)
{

View file

@ -113,14 +113,14 @@ PortExportChannel::set_state (XMLNode * node, Session & session)
}
}
RegionExportChannelFactory::RegionExportChannelFactory (Session * session, AudioRegion const & region, AudioTrack & track, Type type) :
region (region),
track (track),
type (type),
frames_per_cycle (session->engine().frames_per_cycle ()),
buffers_up_to_date (false),
region_start (region.position()),
position (region_start)
RegionExportChannelFactory::RegionExportChannelFactory (Session * session, AudioRegion const & region, AudioTrack & track, Type type)
: region (region)
, track (track)
, type (type)
, frames_per_cycle (session->engine().frames_per_cycle ())
, buffers_up_to_date (false)
, region_start (region.position())
, position (region_start)
{
switch (type) {
case Raw:
@ -190,10 +190,10 @@ RegionExportChannelFactory::update_buffers (framecnt_t frames)
region.read_at (buffers.get_audio (channel).data(), mixdown_buffer.get(), gain_buffer.get(), position, frames, channel);
}
break;
case Processed:
track.export_stuff (buffers, position, frames);
case Processed:
track.export_stuff (buffers, position, frames, track.main_outs(), true, true);
break;
default:
default:
throw ExportFailed ("Unhandled type in ExportChannelFactory::update_buffers");
}

View file

@ -478,7 +478,8 @@ MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framep
}
int
MidiTrack::export_stuff (BufferSet& /*bufs*/, framecnt_t /*nframes*/, framepos_t /*end_frame*/)
MidiTrack::export_stuff (BufferSet& /*bufs*/, framecnt_t /*nframes*/, framepos_t /*end_frame*/,
boost::shared_ptr<Processor> /*endpoint*/, bool /*include_endpoint*/, bool /*forexport*/)
{
return -1;
}
@ -492,7 +493,8 @@ MidiTrack::bounce (InterThreadInfo& /*itt*/)
boost::shared_ptr<Region>
MidiTrack::bounce_range (framepos_t /*start*/, framepos_t /*end*/, InterThreadInfo& /*itt*/, bool /*enable_processing*/)
MidiTrack::bounce_range (framepos_t /*start*/, framepos_t /*end*/, InterThreadInfo& /*itt*/,
boost::shared_ptr<Processor> /*endpoint*/, bool /*include_endpoint*/)
{
std::cerr << "MIDI bounce range currently unsupported" << std::endl;
return boost::shared_ptr<Region> ();

View file

@ -3901,7 +3901,9 @@ Session::freeze_all (InterThreadInfo& itt)
boost::shared_ptr<Region>
Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
InterThreadInfo& itt, bool enable_processing)
InterThreadInfo& itt,
boost::shared_ptr<Processor> endpoint, bool include_endpoint,
bool for_export)
{
boost::shared_ptr<Region> result;
boost::shared_ptr<Playlist> playlist;
@ -3997,7 +3999,7 @@ Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
this_chunk = min (to_do, chunk_size);
if (track.export_stuff (buffers, start, this_chunk, enable_processing)) {
if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export)) {
goto out;
}

View file

@ -62,7 +62,8 @@ class MIDIInvokable : public PBD::Stateful
MIDI::channel_t control_channel;
MIDI::byte* data;
size_t data_size;
bool _parameterized;
void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on);
void midi_sense_note_on (MIDI::Parser &p, MIDI::EventTwoBytes *tb);
void midi_sense_note_off (MIDI::Parser &p, MIDI::EventTwoBytes *tb);