diff options
Diffstat (limited to 'libs/ardour')
59 files changed, 1480 insertions, 1908 deletions
diff --git a/libs/ardour/amp.cc b/libs/ardour/amp.cc index 263ce82086..aa20f3e389 100644 --- a/libs/ardour/amp.cc +++ b/libs/ardour/amp.cc @@ -20,23 +20,123 @@ #include <cmath> #include <algorithm> #include "ardour/amp.h" -#include "ardour/buffer_set.h" #include "ardour/audio_buffer.h" +#include "ardour/buffer_set.h" +#include "ardour/configuration.h" +#include "ardour/io.h" +#include "ardour/session.h" namespace ARDOUR { +Amp::Amp(Session& s, IO& io) + : Processor(s, "Amp") + , _io(io) + , _mute(false) + , _apply_gain(true) + , _apply_gain_automation(false) + , _current_gain(1.0) + , _desired_gain(1.0) +{ +} + +bool +Amp::can_support_io_configuration (const ChanCount& in, ChanCount& out) const +{ + out = in; + return true; +} + +bool +Amp::configure_io (ChanCount in, ChanCount out) +{ + if (out != in) { // always 1:1 + return false; + } + + return Processor::configure_io (in, out); +} + +void +Amp::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes) +{ + gain_t* gab = _session.gain_automation_buffer(); + + if (_mute && !bufs.is_silent()) { + Amp::apply_gain (bufs, nframes, _current_mute_gain, _desired_mute_gain, false); + if (_desired_mute_gain == 0.0f) { + bufs.is_silent(true); + } + } + + if (_apply_gain) { + + if (_apply_gain_automation) { + + if (_io.phase_invert()) { + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { + Sample* const sp = i->data(); + for (nframes_t nx = 0; nx < nframes; ++nx) { + sp[nx] *= -gab[nx]; + } + } + } else { + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { + Sample* const sp = i->data(); + for (nframes_t nx = 0; nx < nframes; ++nx) { + sp[nx] *= gab[nx]; + } + } + } + + } else { /* manual (scalar) gain */ + + if (_current_gain != _desired_gain) { + + Amp::apply_gain (bufs, nframes, _current_gain, _desired_gain, _io.phase_invert()); + _current_gain = _desired_gain; + + } else if (_current_gain != 0.0f && (_io.phase_invert() || _current_gain != 1.0f)) { + + /* no need to interpolate current gain value, + but its non-unity, so apply it. if the gain + is zero, do nothing because we'll ship silence + below. + */ + + gain_t this_gain; + + if (_io.phase_invert()) { + this_gain = -_current_gain; + } else { + this_gain = _current_gain; + } + + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { + Sample* const sp = i->data(); + apply_gain_to_buffer(sp, nframes, this_gain); + } + + } else if (_current_gain == 0.0f) { + for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { + i->clear(); + } + } + } + } +} /** Apply a declicked gain to the audio buffers of @a bufs */ void -Amp::run_in_place (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t target, bool invert_polarity) +Amp::apply_gain (BufferSet& bufs, nframes_t nframes, + gain_t initial, gain_t target, bool invert_polarity) { - if (nframes == 0) + if (nframes == 0) { return; + } - if (bufs.count().n_audio() == 0) + if (bufs.count().n_audio() == 0) { return; - - // assert(bufs.buffer_capacity(DataType::AUDIO) >= nframes); + } // if we don't need to declick, defer to apply_simple_gain if (initial == target) { @@ -98,5 +198,18 @@ Amp::apply_simple_gain (BufferSet& bufs, nframes_t nframes, gain_t target) { } +XMLNode& +Amp::state (bool full_state) +{ + return get_state(); +} + +XMLNode& +Amp::get_state() +{ + XMLNode* node = new XMLNode(state_node_name); + node->add_property("type", "amp"); + return *node; +} } // namespace ARDOUR diff --git a/libs/ardour/ardour/amp.h b/libs/ardour/ardour/amp.h index 402a29542d..fa9de724ad 100644 --- a/libs/ardour/ardour/amp.h +++ b/libs/ardour/ardour/amp.h @@ -20,22 +20,63 @@ #define __ardour_amp_h__ #include "ardour/types.h" +#include "ardour/chan_count.h" +#include "ardour/processor.h" namespace ARDOUR { class BufferSet; +class IO; /** Applies a declick operation to all audio inputs, passing the same number of * audio outputs, and passing through any other types unchanged. - * - * FIXME: make this a Processor. */ -class Amp { +class Amp : public Processor { public: - static void run_in_place (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t target, bool invert_polarity); + Amp(Session& s, IO& io); + + bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const; + bool configure_io (ChanCount in, ChanCount out); + + void run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes); + + bool apply_gain() const { return _apply_gain; } + void apply_gain(bool yn) { _apply_gain = yn; } + + bool apply_gain_automation() const { return _apply_gain_automation; } + void apply_gain_automation(bool yn) { _apply_gain_automation = yn; } + + void muute(bool yn) { _mute = yn; } + + void set_gain(float current, float desired) { + _current_gain = current; + _desired_gain = desired; + } + + void apply_mute(bool yn, float current=1.0, float desired=0.0) { + _mute = yn; + _current_mute_gain = current; + _desired_mute_gain = desired; + } + + XMLNode& state (bool full); + XMLNode& get_state(); + + static void apply_gain (BufferSet& bufs, nframes_t nframes, + gain_t initial, gain_t target, bool invert_polarity); static void apply_simple_gain(BufferSet& bufs, nframes_t nframes, gain_t target); + +private: + IO& _io; + bool _mute; + bool _apply_gain; + bool _apply_gain_automation; + float _current_gain; + float _desired_gain; + float _current_mute_gain; + float _desired_mute_gain; }; diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h index 22092b5e1b..484887e0b7 100644 --- a/libs/ardour/ardour/audio_track.h +++ b/libs/ardour/ardour/audio_track.h @@ -41,12 +41,6 @@ class AudioTrack : public Track int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int declick, bool can_record, bool rec_monitors_input); - - int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool state_changing, bool can_record, bool rec_monitors_input); - - int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool can_record, bool rec_monitors_input); boost::shared_ptr<AudioDiskstream> audio_diskstream() const; diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h index 9d7ba6d6ae..3f10929fe8 100644 --- a/libs/ardour/ardour/buffer_set.h +++ b/libs/ardour/ardour/buffer_set.h @@ -66,7 +66,10 @@ public: const ChanCount& count() const { return _count; } ChanCount& count() { return _count; } - void set_count(const ChanCount& count) { _count = count; } + void is_silent(bool yn) { _is_silent = yn; } + bool is_silent() const { return _is_silent; } + + void set_count(const ChanCount& count) { assert(count <= _available); _count = count; } size_t buffer_capacity(DataType type) const; @@ -161,6 +164,9 @@ private: /// Whether we (don't) 'own' the contained buffers (otherwise we mirror a PortSet) bool _is_mirror; + + /// Whether the buffer set should be considered silent + bool _is_silent; }; diff --git a/libs/ardour/ardour/chan_count.h b/libs/ardour/ardour/chan_count.h index fb4b1999ca..c9b543c4ba 100644 --- a/libs/ardour/ardour/chan_count.h +++ b/libs/ardour/ardour/chan_count.h @@ -23,6 +23,7 @@ #include <cassert> #include <ostream> +#include "pbd/xml++.h" #include "ardour/data_type.h" namespace ARDOUR { @@ -35,6 +36,7 @@ namespace ARDOUR { */ class ChanCount { public: + ChanCount(const XMLNode& node); ChanCount() { reset(); } // Convenience constructor for making single-typed streams (stereo, mono, etc) @@ -104,6 +106,14 @@ public: return ( (*this > other) || (*this == other) ); } + static ChanCount min(const ChanCount& a, const ChanCount& b) { + ChanCount ret; + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + ret.set(*t, std::min(a.get(*t), b.get(*t))); + } + return ret; + } + static ChanCount max(const ChanCount& a, const ChanCount& b) { ChanCount ret; for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { @@ -111,6 +121,8 @@ public: } return ret; } + + XMLNode* state(const std::string& name) const; static const ChanCount INFINITE; static const ChanCount ZERO; diff --git a/libs/ardour/ardour/chan_mapping.h b/libs/ardour/ardour/chan_mapping.h index 1dae20e34a..5f948a77b0 100644 --- a/libs/ardour/ardour/chan_mapping.h +++ b/libs/ardour/ardour/chan_mapping.h @@ -26,6 +26,7 @@ #include <utility> #include "ardour/data_type.h" +#include "ardour/chan_count.h" namespace ARDOUR { @@ -36,47 +37,26 @@ namespace ARDOUR { class ChanMapping { public: ChanMapping() {} - ChanMapping(ChanCount identity) { - for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - for (size_t i = 0; i <= identity.get(*t); ++i) - set(*t, i, i); - } - } - - uint32_t get(DataType t, uint32_t from) { - Mappings::iterator tm = _mappings.find(t); - assert(tm != _mappings.end()); - TypeMapping::iterator m = tm->second.find(from); - assert(m != tm->second.end()); - return m->second; - } + ChanMapping(ARDOUR::ChanCount identity); + + uint32_t get(DataType t, uint32_t from); + void set(DataType t, uint32_t from, uint32_t to); + void offset_from(DataType t, int32_t delta); + void offset_to(DataType t, int32_t delta); - void set(DataType t, uint32_t from, uint32_t to) { - Mappings::iterator tm = _mappings.find(t); - if (tm == _mappings.end()) { - tm = _mappings.insert(std::make_pair(t, TypeMapping())).first; - } - tm->second.insert(std::make_pair(from, to)); - } - - /** Increase the 'to' field of every mapping for type @a t by @a delta */ - void offset(DataType t, uint32_t delta) { - Mappings::iterator tm = _mappings.find(t); - if (tm != _mappings.end()) { - for (TypeMapping::iterator m = tm->second.begin(); m != tm->second.end(); ++m) { - m->second += delta; - } - } - } - -private: typedef std::map<uint32_t, uint32_t> TypeMapping; typedef std::map<DataType, TypeMapping> Mappings; + Mappings mappings() { return _mappings; } + const Mappings mappings() const { return _mappings; } + +private: Mappings _mappings; }; } // namespace ARDOUR +std::ostream& operator<<(std::ostream& o, const ARDOUR::ChanMapping& m); + #endif // __ardour_chan_mapping_h__ diff --git a/libs/ardour/ardour/click.h b/libs/ardour/ardour/click.h index e50e0a29cb..ae744478f1 100644 --- a/libs/ardour/ardour/click.h +++ b/libs/ardour/ardour/click.h @@ -26,17 +26,11 @@ namespace ARDOUR { class ClickIO : public IO { - public: - ClickIO (Session& s, const string& name, - - int input_min = -1, int input_max = -1, - - int output_min = -1, int output_max = -1) - : IO (s, name, input_min, input_max, output_min, output_max) {} - +public: + ClickIO (Session& s, const string& name) : IO (s, name) {} ~ClickIO() {} - protected: +protected: uint32_t pans_required () const { return 1; } }; diff --git a/libs/ardour/ardour/control_outputs.h b/libs/ardour/ardour/control_outputs.h new file mode 100644 index 0000000000..72d9534ddf --- /dev/null +++ b/libs/ardour/ardour/control_outputs.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2006 Paul Davis + + 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., + 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_control_outputs_h__ +#define __ardour_control_outputs_h__ + +#include <string> +#include "ardour/types.h" +#include "ardour/chan_count.h" +#include "ardour/io_processor.h" + +namespace ARDOUR { + +class BufferSet; +class IO; + +class ControlOutputs : public IOProcessor { +public: + ControlOutputs(Session& s, IO* io); + + bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const; + bool configure_io (ChanCount in, ChanCount out); + + void run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes); + + bool deliver() const { return _deliver; } + void deliver(bool yn) { _deliver = yn; } + + XMLNode& state (bool full); + XMLNode& get_state(); + +private: + bool _deliver; +}; + + +} // namespace ARDOUR + +#endif // __ardour_control_outputs_h__ + diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 8457668756..3e36d10be3 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -52,32 +52,31 @@ class XMLNode; namespace ARDOUR { -class Session; +class Amp; class AudioEngine; -class UserBundle; +class AudioPort; +class BufferSet; class Bundle; +class MidiPort; class Panner; class PeakMeter; class Port; -class AudioPort; -class MidiPort; -class BufferSet; +class Session; +class UserBundle; /** A collection of input and output ports with connections. * * An IO can contain ports of varying types, making routes/inserts/etc with * varied combinations of types (eg MIDI and audio) possible. */ - class IO : public SessionObject, public AutomatableControls, public Latent { public: static const string state_node_name; - IO (Session&, const string& name, - int input_min = -1, int input_max = -1, - int output_min = -1, int output_max = -1, - DataType default_type = DataType::AUDIO); + IO (Session&, const string& name, DataType default_type = DataType::AUDIO, + ChanCount in_min=ChanCount::ZERO, ChanCount in_max=ChanCount::INFINITE, + ChanCount out_min=ChanCount::ZERO, ChanCount out_max=ChanCount::INFINITE); IO (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); @@ -109,7 +108,7 @@ class IO : public SessionObject, public AutomatableControls, public Latent BufferSet& output_buffers() { return *_output_buffers; } - gain_t gain () const { return _desired_gain; } + gain_t gain () const { return _gain_control->user_float(); } virtual gain_t effective_gain () const; void set_denormal_protection (bool yn, void *src); @@ -118,11 +117,16 @@ class IO : public SessionObject, public AutomatableControls, public Latent void set_phase_invert (bool yn, void *src); bool phase_invert() const { return _phase_invert; } - Panner& panner() { return *_panner; } - PeakMeter& peak_meter() { return *_meter; } - const Panner& panner() const { return *_panner; } void reset_panner (); + boost::shared_ptr<Amp> amp() const { return _amp; } + + PeakMeter& peak_meter() { return *_meter.get(); } + const PeakMeter& peak_meter() const { return *_meter.get(); } + boost::shared_ptr<PeakMeter> shared_peak_meter() const { return _meter; } + + boost::shared_ptr<Panner> panner() const { return _panner; } + int ensure_io (ChanCount in, ChanCount out, bool clear, void *src); int connect_input_ports_to_bundle (boost::shared_ptr<Bundle>, void *); @@ -133,8 +137,8 @@ class IO : public SessionObject, public AutomatableControls, public Latent BundleList bundles_connected_to_inputs (); BundleList bundles_connected_to_outputs (); - boost::shared_ptr<Bundle> bundle_for_inputs () { return _bundle_for_inputs; } - boost::shared_ptr<Bundle> bundle_for_outputs () { return _bundle_for_outputs; } + boost::shared_ptr<Bundle> bundle_for_inputs () { return _bundle_for_inputs; } + boost::shared_ptr<Bundle> bundle_for_outputs () { return _bundle_for_outputs; } int add_input_port (string source, void *src, DataType type = DataType::NIL); int add_output_port (string destination, void *src, DataType type = DataType::NIL); @@ -271,22 +275,24 @@ class IO : public SessionObject, public AutomatableControls, public Latent mutable Glib::Mutex io_lock; protected: - Panner* _panner; BufferSet* _output_buffers; //< Set directly to output port buffers bool _active; gain_t _gain; - gain_t _effective_gain; - gain_t _desired_gain; Glib::Mutex declick_lock; PortSet _outputs; PortSet _inputs; - PeakMeter* _meter; bool no_panner_reset; bool _phase_invert; bool _denormal_protection; XMLNode* deferred_state; DataType _default_type; nframes_t _output_offset; + ChanCount _configured_inputs; + ChanCount _configured_outputs; + + boost::shared_ptr<Amp> _amp; + boost::shared_ptr<PeakMeter> _meter; + boost::shared_ptr<Panner> _panner; virtual void prepare_inputs (nframes_t nframes); virtual void flush_outputs (nframes_t nframes); @@ -301,8 +307,6 @@ class IO : public SessionObject, public AutomatableControls, public Latent virtual void set_gain (gain_t g, void *src); void inc_gain (gain_t delta, void *src); - bool apply_gain_automation; - virtual int load_automation (std::string path); /* AudioTrack::deprecated_use_diskstream_connections() needs these */ @@ -361,6 +365,7 @@ class IO : public SessionObject, public AutomatableControls, public Latent void bundle_changed (Bundle::Change); + int get_port_counts (const XMLNode& node); int create_ports (const XMLNode&); int make_connections (const XMLNode&); boost::shared_ptr<Bundle> find_possible_bundle (const string &desired_name, const string &default_name, const string &connection_type_name); diff --git a/libs/ardour/ardour/io_processor.h b/libs/ardour/ardour/io_processor.h index 1a12a3271e..e7dffb3138 100644 --- a/libs/ardour/ardour/io_processor.h +++ b/libs/ardour/ardour/io_processor.h @@ -45,8 +45,9 @@ class IO; class IOProcessor : public Processor { public: - IOProcessor (Session&, const string& name, Placement, - int input_min = -1, int input_max = -1, int output_min = -1, int output_max = -1, + IOProcessor (Session&, const string& proc_name, const string io_name="", + ARDOUR::DataType default_type = DataType::AUDIO); + IOProcessor (Session&, IO* io, const string& proc_name, ARDOUR::DataType default_type = DataType::AUDIO); virtual ~IOProcessor (); diff --git a/libs/ardour/ardour/meter.h b/libs/ardour/ardour/meter.h index 972e1b6760..0a49ddf99f 100644 --- a/libs/ardour/ardour/meter.h +++ b/libs/ardour/ardour/meter.h @@ -35,12 +35,12 @@ class Session; */ class PeakMeter : public Processor { public: - PeakMeter(Session& s) : Processor(s, "meter", PreFader) {} + PeakMeter(Session& s) : Processor(s, "Meter") {} void reset (); void reset_max (); - bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const { return true; } + bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const; bool configure_io (ChanCount in, ChanCount out); /** Compute peaks */ @@ -61,11 +61,11 @@ public: return minus_infinity(); } } + + XMLNode& state (bool full); + XMLNode& get_state(); private: - /* disallow copy construction */ - PeakMeter (PeakMeter const &); - friend class IO; void meter(); diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 424b9d2c92..fe8290d5d9 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -41,17 +41,6 @@ public: int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int declick, bool can_record, bool rec_monitors_input); - int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool state_changing, bool can_record, bool rec_monitors_input); - - int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool can_record, bool rec_monitors_input); - - void process_output_buffers (BufferSet& bufs, - nframes_t start_frame, nframes_t end_frame, - nframes_t nframes, bool with_redirects, int declick, - bool meter); - boost::shared_ptr<MidiDiskstream> midi_diskstream() const; int use_diskstream (string name); diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 9e54696048..dcb34c8a04 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -205,7 +205,7 @@ class Panner : public Processor virtual ~Panner (); void clear_panners (); - + bool empty() const { return _streampanners.empty(); } /// The fundamental Panner function void set_automation_state (AutoState); diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index 99bd492ab5..89512df40c 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -44,7 +44,7 @@ class Plugin; class PluginInsert : public Processor { public: - PluginInsert (Session&, boost::shared_ptr<Plugin>, Placement); + PluginInsert (Session&, boost::shared_ptr<Plugin>); PluginInsert (Session&, const XMLNode&); ~PluginInsert (); diff --git a/libs/ardour/ardour/port_insert.h b/libs/ardour/ardour/port_insert.h index 27d251cc45..076e4af9d1 100644 --- a/libs/ardour/ardour/port_insert.h +++ b/libs/ardour/ardour/port_insert.h @@ -40,7 +40,7 @@ class Session; class PortInsert : public IOProcessor { public: - PortInsert (Session&, Placement); + PortInsert (Session&); PortInsert (Session&, const XMLNode&); ~PortInsert (); diff --git a/libs/ardour/ardour/processor.h b/libs/ardour/ardour/processor.h index 1167930d61..547cc87f88 100644 --- a/libs/ardour/ardour/processor.h +++ b/libs/ardour/ardour/processor.h @@ -49,16 +49,21 @@ class Processor : public SessionObject, public AutomatableControls, public Laten public: static const string state_node_name; - Processor(Session&, const string& name, Placement p); // TODO: remove placement (use sort key) + Processor(Session&, const string& name); virtual ~Processor() { } + /** Configuration of a processor on a bus + * (i.e. how to apply to a BufferSet) + */ + struct Mapping { + ChanCount in; + ChanCount out; + }; + uint32_t sort_key() const { return _sort_key; } void set_sort_key (uint32_t key); - Placement placement() const { return _placement; } - void set_placement (Placement); - bool active () const { return _active; } bool get_next_ab_is_active () const { return _next_ab_is_active; } @@ -108,7 +113,6 @@ class Processor : public SessionObject, public AutomatableControls, public Laten static sigc::signal<void,Processor*> ProcessorCreated; sigc::signal<void> ActiveChanged; - sigc::signal<void> PlacementChanged; sigc::signal<void,ChanCount,ChanCount> ConfigurationChanged; protected: @@ -118,9 +122,9 @@ protected: bool _configured; ChanCount _configured_input; ChanCount _configured_output; - Placement _placement; uint32_t _sort_key; void* _gui; /* generic, we don't know or care what this is */ + Mapping _mapping; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 6926dbd036..2b54810577 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -44,10 +44,12 @@ namespace ARDOUR { -class Processor; +class Amp; +class ControlOutputs; class IOProcessor; -class Send; +class Processor; class RouteGroup; +class Send; enum mute_type { PRE_FADER = 0x1, @@ -70,8 +72,10 @@ class Route : public IO ControlOut = 0x4 }; - Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max, - Flag flags = Flag(0), DataType default_type = DataType::AUDIO); + Route (Session&, std::string name, Flag flags = Flag(0), + DataType default_type = DataType::AUDIO, + ChanCount in=ChanCount::ZERO, ChanCount out=ChanCount::ZERO); + Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); virtual ~Route(); @@ -149,6 +153,15 @@ class Route : public IO method (boost::weak_ptr<Processor> (*i)); } } + + void foreach_processor (Placement p, sigc::slot<void, boost::weak_ptr<Processor> > method) { + Glib::RWLock::ReaderLock lm (_processor_lock); + ProcessorList::iterator start, end; + placement_range(p, start, end); + for (ProcessorList::iterator i = start; i != end; ++i) { + method (boost::weak_ptr<Processor> (*i)); + } + } boost::shared_ptr<Processor> nth_processor (uint32_t n) { Glib::RWLock::ReaderLock lm (_processor_lock); @@ -170,12 +183,12 @@ class Route : public IO struct ProcessorStreams { ProcessorStreams(size_t i=0, ChanCount c=ChanCount()) : index(i), count(c) {} - size_t index; ///< Index of processor where configuration failed + uint32_t index; ///< Index of processor where configuration failed ChanCount count; ///< Input requested of processor }; - int add_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0); - int add_processors (const ProcessorList&, ProcessorStreams* err = 0); + int add_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0, ProcessorList::iterator* iter=0, Placement=PreFader); + int add_processors (const ProcessorList&, ProcessorStreams* err = 0, Placement placement=PreFader); int remove_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0); int sort_processors (ProcessorStreams* err = 0); void disable_processors (Placement); @@ -226,7 +239,7 @@ class Route : public IO sigc::signal<void,void*> SelectedChanged; int set_control_outs (const vector<std::string>& ports); - IO* control_outs() { return _control_outs; } + boost::shared_ptr<ControlOutputs> control_outs() { return _control_outs; } bool feeds (boost::shared_ptr<Route>); std::set<boost::shared_ptr<Route> > fed_by; @@ -276,12 +289,11 @@ class Route : public IO nframes_t check_initial_delay (nframes_t, nframes_t&); void passthru (nframes_t start_frame, nframes_t end_frame, - nframes_t nframes, int declick, bool meter_inputs); + nframes_t nframes, int declick); virtual void process_output_buffers (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, - nframes_t nframes, bool with_processors, int declick, - bool meter); + nframes_t nframes, bool with_processors, int declick); Flag _flags; int _pending_declick; @@ -296,8 +308,7 @@ class Route : public IO nframes_t _roll_delay; ProcessorList _processors; Glib::RWLock _processor_lock; - IO *_control_outs; - Glib::Mutex _control_outs_lock; + boost::shared_ptr<ControlOutputs> _control_outs; RouteGroup *_edit_group; RouteGroup *_mix_group; std::string _comment; @@ -326,8 +337,7 @@ class Route : public IO virtual XMLNode& state(bool); void passthru_silence (nframes_t start_frame, nframes_t end_frame, - nframes_t nframes, int declick, - bool meter); + nframes_t nframes, int declick); void silence (nframes_t nframes); @@ -361,24 +371,18 @@ class Route : public IO void input_change_handler (IOChange, void *src); void output_change_handler (IOChange, void *src); - int reset_processor_counts (ProcessorStreams*); /* locked */ - int _reset_processor_counts (ProcessorStreams*); /* unlocked */ - - /** processor I/O channels and plugin count handling */ - struct ProcessorCount { - boost::shared_ptr<ARDOUR::Processor> processor; - ChanCount in; - ChanCount out; + bool _in_configure_processors; - ProcessorCount (boost::shared_ptr<ARDOUR::Processor> ins) : processor(ins) {} - }; + int configure_processors (ProcessorStreams*); + int configure_processors_unlocked (ProcessorStreams*); - int32_t apply_some_processor_counts (std::list<ProcessorCount>& iclist); - bool check_some_processor_counts (std::list<ProcessorCount>& iclist, - ChanCount required_inputs, ProcessorStreams* err_streams); - void set_deferred_state (); - void add_processor_from_xml (const XMLNode&); + bool add_processor_from_xml (const XMLNode&, ProcessorList::iterator* iter=0); + + void placement_range( + Placement p, + ProcessorList::iterator& start, + ProcessorList::iterator& end); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/send.h b/libs/ardour/ardour/send.h index 2e7d2c239c..77bb6d9017 100644 --- a/libs/ardour/ardour/send.h +++ b/libs/ardour/ardour/send.h @@ -34,7 +34,7 @@ namespace ARDOUR { class Send : public IOProcessor { public: - Send (Session&, Placement); + Send (Session&); Send (Session&, const XMLNode&); virtual ~Send (); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 8bef24a7e2..1e3855c308 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -255,6 +255,7 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable void set_clean (); bool dirty() const { return _state_of_the_state & Dirty; } void set_deletion_in_progress (); + void clear_deletion_in_progress (); bool deletion_in_progress() const { return _state_of_the_state & Deletion; } sigc::signal<void> DirtyChanged; diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 29a4aa8e25..96826e3eaa 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -44,16 +44,16 @@ class Track : public Route virtual int set_mode (TrackMode m) { return false; } virtual bool can_use_mode (TrackMode m, bool& bounce_required) { return false; } sigc::signal<void> TrackModeChanged; + + int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, + bool state_changing, bool can_record, bool rec_monitors_input); + + int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, + bool can_record, bool rec_monitors_input); virtual int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int declick, bool can_record, bool rec_monitors_input) = 0; - virtual int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool state_changing, bool can_record, bool rec_monitors_input) = 0; - - virtual int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool can_record, bool rec_monitors_input) = 0; - void toggle_monitor_input (); bool can_record(); diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 5c73d826bf..bb74d5fc16 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -2172,7 +2172,8 @@ AudioDiskstream::set_block_size (nframes_t nframes) boost::shared_ptr<ChannelList> c = channels.reader(); for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan)->speed_buffer) delete [] (*chan)->speed_buffer; + if ((*chan)->speed_buffer) + delete [] (*chan)->speed_buffer; (*chan)->speed_buffer = new Sample[speed_buffer_size]; } } @@ -2195,9 +2196,11 @@ AudioDiskstream::allocate_temporary_buffers () boost::shared_ptr<ChannelList> c = channels.reader(); for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { - if ((*chan)->playback_wrap_buffer) delete [] (*chan)->playback_wrap_buffer; + if ((*chan)->playback_wrap_buffer) + delete [] (*chan)->playback_wrap_buffer; (*chan)->playback_wrap_buffer = new Sample[required_wrap_size]; - if ((*chan)->capture_wrap_buffer) delete [] (*chan)->capture_wrap_buffer; + if ((*chan)->capture_wrap_buffer) + delete [] (*chan)->capture_wrap_buffer; (*chan)->capture_wrap_buffer = new Sample[required_wrap_size]; } @@ -2270,7 +2273,7 @@ int AudioDiskstream::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t how_many) { while (how_many-- && !c->empty()) { - delete c->back(); + //delete c->back(); // FIXME: crash (thread safe with RCU?) c->pop_back(); } diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index d814709798..79b1240ab0 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -26,22 +26,23 @@ #include "evoral/Curve.hpp" -#include "ardour/audio_track.h" +#include "ardour/amp.h" +#include "ardour/audio_buffer.h" #include "ardour/audio_diskstream.h" -#include "ardour/session.h" -#include "ardour/io_processor.h" +#include "ardour/audio_track.h" +#include "ardour/audioplaylist.h" #include "ardour/audioregion.h" #include "ardour/audiosource.h" +#include "ardour/buffer_set.h" +#include "ardour/io_processor.h" +#include "ardour/panner.h" +#include "ardour/playlist_factory.h" +#include "ardour/plugin_insert.h" +#include "ardour/processor.h" #include "ardour/region_factory.h" #include "ardour/route_group_specialized.h" -#include "ardour/processor.h" -#include "ardour/plugin_insert.h" -#include "ardour/audioplaylist.h" -#include "ardour/playlist_factory.h" -#include "ardour/panner.h" +#include "ardour/session.h" #include "ardour/utils.h" -#include "ardour/buffer_set.h" -#include "ardour/audio_buffer.h" #include "i18n.h" using namespace std; @@ -145,7 +146,7 @@ AudioTrack::deprecated_use_diskstream_connections () if ((prop = node.property ("gain")) != 0) { set_gain (atof (prop->value().c_str()), this); - _gain = _desired_gain; + _gain = _gain_control->user_float(); } if ((prop = node.property ("input-connection")) != 0) { @@ -452,91 +453,6 @@ AudioTrack::set_state_part_two () return; } -int -AudioTrack::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool session_state_changing, bool can_record, bool rec_monitors_input) -{ - if (n_outputs().n_total() == 0) { - return 0; - } - - if (!_active) { - silence (nframes); - return 0; - } - - if (session_state_changing) { - - /* XXX is this safe to do against transport state changes? */ - - passthru_silence (start_frame, end_frame, nframes, 0, false); - return 0; - } - - audio_diskstream()->check_record_status (start_frame, nframes, can_record); - - bool send_silence; - - if (_have_internal_generator) { - /* since the instrument has no input streams, - there is no reason to send any signal - into the route. - */ - send_silence = true; - } else { - - if (!Config->get_tape_machine_mode()) { - /* - ADATs work in a strange way.. - they monitor input always when stopped.and auto-input is engaged. - */ - if ((Config->get_monitoring_model() == SoftwareMonitoring) && (Config->get_auto_input () || _diskstream->record_enabled())) { - send_silence = false; - } else { - send_silence = true; - } - } else { - /* - Other machines switch to input on stop if the track is record enabled, - regardless of the auto input setting (auto input only changes the - monitoring state when the transport is rolling) - */ - if ((Config->get_monitoring_model() == SoftwareMonitoring) && _diskstream->record_enabled()) { - send_silence = false; - } else { - send_silence = true; - } - } - } - - apply_gain_automation = false; - - if (send_silence) { - - /* if we're sending silence, but we want the meters to show levels for the signal, - meter right here. - */ - - if (_have_internal_generator) { - passthru_silence (start_frame, end_frame, nframes, 0, true); - } else { - if (_meter_point == MeterInput) { - just_meter_input (start_frame, end_frame, nframes); - } - passthru_silence (start_frame, end_frame, nframes, 0, false); - } - - } else { - - /* we're sending signal, but we may still want to meter the input. - */ - - passthru (start_frame, end_frame, nframes, 0, (_meter_point == MeterInput)); - } - - return 0; -} - int AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int declick, bool can_record, bool rec_monitors_input) @@ -580,7 +496,7 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, } _silent = false; - apply_gain_automation = false; + _amp->apply_gain_automation(false); if ((dret = diskstream->process (transport_frame, nframes, can_record, rec_monitors_input)) != 0) { silence (nframes); @@ -599,7 +515,7 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, at least potentially (depending on monitoring options) */ - passthru (start_frame, end_frame, nframes, 0, true); + passthru (start_frame, end_frame, nframes, false); } else if ((b = diskstream->playback_buffer(0)) != 0) { @@ -691,11 +607,13 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK); if (am.locked() && gain_control()->automation_playback()) { - apply_gain_automation = gain_control()->list()->curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); + _amp->apply_gain_automation( + gain_control()->list()->curve().rt_safe_get_vector ( + start_frame, end_frame, _session.gain_automation_buffer(), nframes)); } } - process_output_buffers (bufs, start_frame, end_frame, nframes, (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick, (_meter_point != MeterInput)); + process_output_buffers (bufs, start_frame, end_frame, nframes, (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick); } else { /* problem with the diskstream; just be quiet for a bit */ @@ -706,35 +624,11 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, } int -AudioTrack::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool can_record, bool rec_monitors_input) -{ - if (n_outputs().n_total() == 0 && _processors.empty()) { - return 0; - } - - if (!_active) { - silence (nframes); - return 0; - } - - _silent = true; - apply_gain_automation = false; - - silence (nframes); - - return audio_diskstream()->process (_session.transport_frame(), nframes, can_record, rec_monitors_input); -} - -int AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes, bool enable_processing) { - gain_t gain_automation[nframes]; gain_t gain_buffer[nframes]; float mix_buffer[nframes]; ProcessorList::iterator i; - bool post_fader_work = false; - gain_t this_gain = _gain; boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); Glib::RWLock::ReaderLock rlock (_processor_lock); @@ -767,8 +661,9 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes } // If no processing is required, there's no need to go any further. - if (!enable_processing) + if (!enable_processing) { return 0; + } /* note: only run processors during export. other layers in the machinery will already have checked that there are no external port processors. @@ -776,57 +671,11 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes for (i = _processors.begin(); i != _processors.end(); ++i) { boost::shared_ptr<Processor> processor; - if ((processor = boost::dynamic_pointer_cast<Processor>(*i)) != 0) { - switch (processor->placement()) { - case PreFader: - processor->run_in_place (buffers, start, start+nframes, nframes); - break; - case PostFader: - post_fader_work = true; - break; - } + processor->run_in_place (buffers, start, start+nframes, nframes); } } - if (gain_control()->automation_state() == Play) { - - gain_control()->list()->curve().get_vector (start, start + nframes, gain_automation, nframes); - - for (BufferSet::audio_iterator bi = buffers.audio_begin(); bi != buffers.audio_end(); ++bi) { - Sample *b = bi->data(); - for (nframes_t n = 0; n < nframes; ++n) { - b[n] *= gain_automation[n]; - } - } - - } else { - - for (BufferSet::audio_iterator bi = buffers.audio_begin(); bi != buffers.audio_end(); ++bi) { - Sample *b = bi->data(); - for (nframes_t n = 0; n < nframes; ++n) { - b[n] *= this_gain; - } - } - } - - if (post_fader_work) { - - for (i = _processors.begin(); i != _processors.end(); ++i) { - boost::shared_ptr<PluginInsert> processor; - - if ((processor = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) { - switch ((*i)->placement()) { - case PreFader: - break; - case PostFader: - processor->run_in_place (buffers, start, start+nframes, nframes); - break; - } - } - } - } - return 0; } diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 51a6ae71fb..91cea6ba71 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -469,6 +469,8 @@ AudioEngine::jack_bufsize_callback (nframes_t nframes) { _buffer_size = nframes; _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof(float); + cout << "FIXME: Assuming maximum MIDI buffer size " << nframes * 4 << "bytes" << endl; + _raw_buffer_sizes[DataType::MIDI] = nframes * 4; _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0); last_monitor_check = 0; @@ -604,13 +606,14 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input) } else if (dtype == DataType::MIDI) { newport = new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput)); } else { - throw unknown_type(); + throw PortRegistrationFailure("unable to create port (unknown type)"); } size_t& old_buffer_size = _raw_buffer_sizes[newport->type()]; size_t port_buffer_size = newport->raw_buffer_size(0); - if (port_buffer_size > old_buffer_size) + if (port_buffer_size > old_buffer_size) { old_buffer_size = port_buffer_size; + } RCUWriter<Ports> writer (ports); boost::shared_ptr<Ports> ps = writer.get_copy (); @@ -621,8 +624,13 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input) return newport; } - catch (...) { - throw PortRegistrationFailure("unable to create port (unknown type?)"); + catch (PortRegistrationFailure& err) { + throw err; + } catch (std::exception& e) { + throw PortRegistrationFailure(string_compose( + _("unable to create port: %1"), e.what()).c_str()); + } catch (...) { + throw PortRegistrationFailure("unable to create port (unknown error)"); } } @@ -1256,6 +1264,10 @@ AudioEngine::reconnect_to_jack () nframes_t blocksize = jack_get_buffer_size (_jack); session->set_block_size (blocksize); session->set_frame_rate (jack_get_sample_rate (_jack)); + + _raw_buffer_sizes[DataType::AUDIO] = blocksize * sizeof(float); + cout << "FIXME: Assuming maximum MIDI buffer size " << blocksize * 4 << "bytes" << endl; + _raw_buffer_sizes[DataType::MIDI] = blocksize * 4; } last_monitor_check = 0; diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index c43623b358..0fa4e9c67a 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -242,7 +242,6 @@ Automatable::set_automation_state (const XMLNode& node, Evoral::Parameter legacy boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param); add_control(newcontrol); newcontrol->set_list(al); - warning << "Control did not exist"; } } else { @@ -268,7 +267,9 @@ Automatable::get_automation_state () for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) { boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list()); - node->add_child_nocopy (l->get_state ()); + if (!l->empty()) { + node->add_child_nocopy (l->get_state ()); + } } return *node; @@ -410,7 +411,12 @@ Automatable::control_factory(const Evoral::Parameter& param) } else if (param.type() == GainAutomation) { control = new IO::GainControl( X_("gaincontrol"), (IO*)this, param); } else if (param.type() == PanAutomation) { - control = new Panner::PanControllable( ((Panner *)this)->session(), X_("panner"), *(Panner *)this, param); + Panner* me = dynamic_cast<Panner*>(this); + if (me) { + control = new Panner::PanControllable(me->session(), X_("panner"), *me, param); + } else { + cerr << "ERROR: PanAutomation for non-Panner" << endl; + } } else { control = new AutomationControl(_a_session, param); } diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index 545d980178..005a4dae65 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -34,6 +34,7 @@ namespace ARDOUR { /** Create a new, empty BufferSet */ BufferSet::BufferSet() : _is_mirror(false) + , _is_silent(false) { for (size_t i=0; i < DataType::num_types; ++i) { _buffers.push_back(BufferVec()); @@ -84,6 +85,7 @@ BufferSet::attach_buffers(PortSet& ports, nframes_t nframes, nframes_t offset) } _count = ports.count(); + _available = ports.count(); _is_mirror = true; } @@ -97,8 +99,9 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac assert(type != DataType::NIL); assert(type < _buffers.size()); - if (num_buffers == 0) + if (num_buffers == 0) { return; + } // The vector of buffers of the type we care about BufferVec& bufs = _buffers[type]; @@ -132,7 +135,7 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac #ifdef HAVE_SLV2 // Ensure enough low level MIDI format buffers are available for conversion // in both directions (input & output, out-of-place) - if (type == DataType::MIDI && _lv2_buffers.size() < _buffers[type].size() * 2) { + if (type == DataType::MIDI && _lv2_buffers.size() < _buffers[type].size() * 2 + 1) { while (_lv2_buffers.size() < _buffers[type].size() * 2) { _lv2_buffers.push_back(std::make_pair(false, new LV2EventBuffer(buffer_capacity))); } @@ -160,7 +163,7 @@ BufferSet::buffer_capacity(DataType type) const Buffer& BufferSet::get(DataType type, size_t i) { - assert(i <= _count.get(type)); + assert(i < _available.get(type)); return *_buffers[type][i]; } diff --git a/libs/ardour/chan_count.cc b/libs/ardour/chan_count.cc index 1c892c1a87..e60104dad8 100644 --- a/libs/ardour/chan_count.cc +++ b/libs/ardour/chan_count.cc @@ -22,11 +22,28 @@ #include <stdint.h> #include "ardour/chan_count.h" +#include "i18n.h" + +static const char* state_node_name = "Channels"; + namespace ARDOUR { // infinite/zero chan count stuff, for setting minimums and maximums, etc. // FIXME: implement this in a less fugly way +ChanCount::ChanCount(const XMLNode& node) +{ + reset(); + XMLNodeConstIterator iter = node.children().begin(); + for ( ; iter != node.children().end(); ++iter) { + if ((*iter)->name() == X_(state_node_name)) { + const string& type_str = (*iter)->property("type")->value(); + const string& count_str = (*iter)->property("count")->value(); + set(DataType(type_str), atol(count_str.c_str())); + } + } +} + ChanCount infinity_factory() { @@ -39,6 +56,21 @@ infinity_factory() return ret; } +XMLNode* +ChanCount::state(const std::string& name) const +{ + XMLNode* node = new XMLNode (name); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + uint32_t count = get(*t); + if (count > 0) { + XMLNode* n = new XMLNode(X_(state_node_name)); + n->add_property("type", (*t).to_string()); + n->add_property("count", count); + node->add_child_nocopy(*n); + } + } + return node; +} // Statics const ChanCount ChanCount::INFINITE = infinity_factory(); diff --git a/libs/ardour/configuration.cc b/libs/ardour/configuration.cc index 611ebddb59..bff9fb16d6 100644 --- a/libs/ardour/configuration.cc +++ b/libs/ardour/configuration.cc @@ -103,7 +103,7 @@ Configuration::load_state () } if (statbuf.st_size != 0) { - cerr << string_compose (_("loading system configuration file %1"), rcfile) << endl; + cerr << string_compose (_("Loading system configuration file %1"), rcfile) << endl; if (!tree.read (rcfile.c_str())) { error << string_compose(_("Ardour: cannot read system configuration file \"%1\""), rcfile) << endmsg; @@ -140,7 +140,7 @@ Configuration::load_state () } if (statbuf.st_size != 0) { - cerr << string_compose (_("loading user configuration file %1"), rcfile) << endl; + cerr << string_compose (_("Loading user configuration file %1"), rcfile) << endl; if (!tree.read (rcfile)) { error << string_compose(_("Ardour: cannot read configuration file \"%1\""), rcfile) << endmsg; diff --git a/libs/ardour/control_outputs.cc b/libs/ardour/control_outputs.cc new file mode 100644 index 0000000000..2acd3c6d9e --- /dev/null +++ b/libs/ardour/control_outputs.cc @@ -0,0 +1,79 @@ +/* + Copyright (C) 2006 Paul Davis + + 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., + 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <cmath> +#include <algorithm> +#include "ardour/control_outputs.h" +#include "ardour/audio_buffer.h" +#include "ardour/buffer_set.h" +#include "ardour/configuration.h" +#include "ardour/io.h" +#include "ardour/session.h" + +using namespace std; + +namespace ARDOUR { + +ControlOutputs::ControlOutputs(Session& s, IO* io) + : IOProcessor(s, io, "Control Outs") + , _deliver(true) +{ +} + +bool +ControlOutputs::can_support_io_configuration (const ChanCount& in, ChanCount& out) const +{ + out = in; + return true; +} + +bool +ControlOutputs::configure_io (ChanCount in, ChanCount out) +{ + if (out != in) { // always 1:1 + return false; + } + + return Processor::configure_io (in, out); +} + +void +ControlOutputs::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes) +{ + if (_deliver) { + _io->deliver_output (bufs, start_frame, end_frame, nframes); + } else { + _io->silence (nframes); + } +} + +XMLNode& +ControlOutputs::state (bool full_state) +{ + return get_state(); +} + +XMLNode& +ControlOutputs::get_state() +{ + XMLNode* node = new XMLNode(state_node_name); + node->add_property("type", "control-outputs"); + return *node; +} + +} // namespace ARDOUR diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index 4cce7d4dc3..bca3c74241 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -441,17 +441,15 @@ Diskstream::playlist_ranges_moved (list< Evoral::RangeMove<nframes_t> > const & ); /* move panner automation */ - Panner & p = _io->panner (); - for (uint32_t i = 0; i < p.npanners (); ++i) { - - boost::shared_ptr<AutomationList> pan_alist = p.streampanner(i).pan_control()->alist(); - XMLNode & before = pan_alist->get_state (); - pan_alist->move_ranges (movements); - _session.add_command ( - new MementoCommand<AutomationList> ( - *pan_alist.get(), &before, &pan_alist->get_state () - ) - ); + boost::shared_ptr<Panner> p = _io->panner (); + if (p) { + for (uint32_t i = 0; i < p->npanners (); ++i) { + boost::shared_ptr<AutomationList> pan_alist = p->streampanner(i).pan_control()->alist(); + XMLNode & before = pan_alist->get_state (); + pan_alist->move_ranges (movements); + _session.add_command (new MementoCommand<AutomationList> ( + *pan_alist.get(), &before, &pan_alist->get_state ())); + } } /* move processor automation */ diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 1c354a5db0..722c0fad49 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -107,7 +107,7 @@ int ARDOUR::setup_midi () { if (Config->midi_ports.size() == 0) { - warning << _("no MIDI ports specified: no MMC or MTC control possible") << endmsg; + //warning << _("no MIDI ports specified: no MMC or MTC control possible") << endmsg; return 0; } diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 410d085142..ef9605f467 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -449,7 +449,7 @@ Session::import_audiofiles (ImportStatus& status) frame_rate(), cnt, status.total); write_audio_data_to_new_files (source.get(), status, newfiles); } else if (smf_reader.get()) { // midi - status.doing_what = string_compose(_("loading MIDI file %1"), *p); + status.doing_what = string_compose(_("Loading MIDI file %1"), *p); write_midi_data_to_new_files (smf_reader.get(), status, newfiles); } } diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 7e77996d39..3e5b138fb7 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -102,36 +102,27 @@ static double direct_gain_to_control (gain_t gain) { * and friends if no type is explicitly requested (to avoid breakage). */ IO::IO (Session& s, const string& name, - int input_min, int input_max, int output_min, int output_max, - DataType default_type) - : SessionObject(s, name), - AutomatableControls (s), - _output_buffers (new BufferSet()), - _active(true), - _default_type (default_type), - _input_minimum (ChanCount::ZERO), - _input_maximum (ChanCount::INFINITE), - _output_minimum (ChanCount::ZERO), - _output_maximum (ChanCount::INFINITE) -{ - _panner = new Panner (name, _session); - _meter = new PeakMeter (_session); - - if (input_min > 0) { - _input_minimum = ChanCount(_default_type, input_min); - } - if (input_max >= 0) { - _input_maximum = ChanCount(_default_type, input_max); - } - if (output_min > 0) { - _output_minimum = ChanCount(_default_type, output_min); - } - if (output_max >= 0) { - _output_maximum = ChanCount(_default_type, output_max); - } + DataType default_type, + ChanCount in_min, ChanCount in_max, ChanCount out_min, ChanCount out_max) + : SessionObject (s, name) + , AutomatableControls (s) + , _output_buffers (new BufferSet()) + , _active (true) + , _default_type (default_type) + , _amp (new Amp(s, *this)) + , _meter (new PeakMeter(s)) + , _panner (new Panner(name, s)) + , _input_minimum (ChanCount::ZERO) + , _input_maximum (ChanCount::INFINITE) + , _output_minimum (ChanCount::ZERO) + , _output_maximum (ChanCount::INFINITE) +{ + _input_minimum = in_min; + _output_minimum = out_min; + _input_maximum = in_max; + _output_maximum = out_max; _gain = 1.0; - _desired_gain = 1.0; pending_state_node = 0; no_panner_reset = false; _phase_invert = false; @@ -143,8 +134,6 @@ IO::IO (Session& s, const string& name, _gain_control = boost::shared_ptr<GainControl>( new GainControl( X_("gaincontrol"), this, Evoral::Parameter(GainAutomation), gl )); add_control(_gain_control); - - apply_gain_automation = false; { // IO::Meter is emitted from another thread so the @@ -162,21 +151,18 @@ IO::IO (Session& s, const string& name, } IO::IO (Session& s, const XMLNode& node, DataType dt) - : SessionObject(s, "unnamed io"), - AutomatableControls (s), - _output_buffers (new BufferSet()), - _active(true), - _default_type (dt) -{ - _meter = new PeakMeter (_session); - _panner = 0; + : SessionObject(s, "unnamed io") + , AutomatableControls (s) + , _output_buffers (new BufferSet()) + , _active(true) + , _default_type (dt) + , _amp (new Amp(s, *this)) + , _meter(new PeakMeter (_session)) +{ deferred_state = 0; no_panner_reset = false; - _desired_gain = 1.0; _gain = 1.0; - apply_gain_automation = false; - boost::shared_ptr<AutomationList> gl( new AutomationList(Evoral::Parameter(GainAutomation))); @@ -218,9 +204,6 @@ IO::~IO () } m_meter_connection.disconnect(); - - delete _meter; - delete _panner; } void @@ -241,12 +224,26 @@ IO::silence (nframes_t nframes) void IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes) { - // FIXME: type specific code doesn't actually need to be here, it will go away in time + // Attach output buffers to port buffers + output_buffers().attach_buffers (_outputs, nframes, _output_offset); - /* ********** AUDIO ********** */ + // Use the panner to distribute audio to output port buffers + if (_panner && _panner->npanners() && !_panner->bypassed()) { + + _panner->run_out_of_place(bufs, output_buffers(), start_frame, end_frame, nframes); - // Apply gain if gain automation isn't playing - if ( ! apply_gain_automation) { + // Do a 1:1 copy of data to output ports + } else { + if (bufs.count().n_audio() > 0 && _outputs.count().n_audio () > 0) { + copy_to_outputs (bufs, DataType::AUDIO, nframes); + } + if (bufs.count().n_midi() > 0 && _outputs.count().n_midi () > 0) { + copy_to_outputs (bufs, DataType::MIDI, nframes); + } + } + + // Apply gain to output buffers if gain automation isn't playing + if ( ! _amp->apply_gain_automation()) { gain_t dg = _gain; // desired gain @@ -254,44 +251,17 @@ IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK); if (dm.locked()) { - dg = _desired_gain; + dg = _gain_control->user_float(); } } if (dg != _gain || dg != 1.0) { - Amp::run_in_place(bufs, nframes, _gain, dg, _phase_invert); + Amp::apply_gain(output_buffers(), nframes, _gain, dg, _phase_invert); _gain = dg; } } - /* do this so that any processing that comes after deliver_outputs() - can use the output buffers. - */ - - output_buffers().attach_buffers (_outputs, nframes, _output_offset); - - // Use the panner to distribute audio to output port buffers - - if (0 && _panner && _panner->npanners() && !_panner->bypassed()) { - - /* blech .. we shouldn't be creating and tearing this down every process() - cycle. XXX fix me to not waste cycles and do memory allocation etc. - */ - - _panner->run_out_of_place(bufs, output_buffers(), start_frame, end_frame, nframes); - - } else { - - /* do a 1:1 copy of data to output ports */ - - if (bufs.count().n_audio() > 0 && _outputs.count().n_audio () > 0) { - copy_to_outputs (bufs, DataType::AUDIO, nframes); - } - if (bufs.count().n_midi() > 0 && _outputs.count().n_midi () > 0) { - copy_to_outputs (bufs, DataType::MIDI, nframes); - } - } } void @@ -302,9 +272,8 @@ IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes) PortSet::iterator o = _outputs.begin(type); BufferSet::iterator i = bufs.begin(type); BufferSet::iterator prev = i; - + while (i != bufs.end(type) && o != _outputs.end (type)) { - Buffer& port_buffer (o->get_buffer (nframes)); port_buffer.read_from (*i, nframes, _output_offset); prev = i; @@ -312,8 +281,7 @@ IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes) ++o; } - /* extra outputs get a copy of the last buffer */ - + // Copy last buffer to any extra outputs while (o != _outputs.end(type)) { Buffer& port_buffer (o->get_buffer (nframes)); port_buffer.read_from (*prev, nframes, _output_offset); @@ -894,14 +862,19 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) bool out_changed = false; bool need_pan_reset = false; - in = min (_input_maximum, in); + assert(in != ChanCount::INFINITE); + assert(out != ChanCount::INFINITE); - out = min (_output_maximum, out); + in = ChanCount::min (_input_maximum, in); + out = ChanCount::min (_output_maximum, out); if (in == n_inputs() && out == n_outputs() && !clear) { return 0; } + _configured_inputs = in; + _configured_outputs = out; + { BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm (io_lock); @@ -943,9 +916,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) } /* create any necessary new input ports */ - while (n_inputs().get(*t) < nin) { - string portname = build_legal_port_name (*t, true); try { @@ -959,7 +930,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) setup_peak_meters (); reset_panner (); /* pass it on */ - throw AudioEngine::PortRegistrationFailure(); + throw err; } _inputs.add (port); @@ -983,7 +954,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) setup_peak_meters (); reset_panner (); /* pass it on */ - throw AudioEngine::PortRegistrationFailure (); + throw err; } _outputs.add (port); @@ -1124,7 +1095,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) { bool changed = false; - if (_output_maximum < ChanCount::INFINITE) { + if (_output_maximum != ChanCount::INFINITE) { count = min (_output_maximum, count); if (count == n_outputs() && !clear) { return 0; @@ -1152,11 +1123,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) gain_t IO::effective_gain () const { - if (_gain_control->automation_playback()) { - return _gain_control->get_value(); - } else { - return _desired_gain; - } + return _gain_control->get_value(); } void @@ -1305,16 +1272,10 @@ IO::state (bool full_state) snprintf (buf, sizeof(buf), "%2.12f", gain()); node->add_property ("gain", buf); - /* To make backwards compatibility a bit easier, write ChanCount::INFINITE to the session file - as -1. - */ - - int const in_max = _input_maximum == ChanCount::INFINITE ? -1 : _input_maximum.get(_default_type); - int const out_max = _output_maximum == ChanCount::INFINITE ? -1 : _output_maximum.get(_default_type); - - snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max); - - node->add_property ("iolimits", buf); + /* port counts */ + + node->add_child_nocopy(*n_inputs().state("Inputs")); + node->add_child_nocopy(*n_outputs().state("Outputs")); /* automation */ @@ -1358,9 +1319,9 @@ IO::set_state (const XMLNode& node) sscanf (prop->value().c_str(), "%d,%d,%d,%d", &in_min, &in_max, &out_min, &out_max); - /* Correct for the difference between the way we write things to session files and the - way things are described by ChanCount; see comments in io.h about what the different - ChanCount values mean. */ + // Legacy numbers: + // minimum == -1 => minimum == 0 + // maximum == -1 => maximum == infinity if (in_min < 0) { _input_minimum = ChanCount::ZERO; @@ -1389,7 +1350,7 @@ IO::set_state (const XMLNode& node) if ((prop = node.property ("gain")) != 0) { set_gain (atof (prop->value().c_str()), this); - _gain = _desired_gain; + _gain = _gain_control->user_float(); } if ((prop = node.property ("automation-state")) != 0 || (prop = node.property ("automation-style")) != 0) { @@ -1400,16 +1361,16 @@ IO::set_state (const XMLNode& node) // Old school Panner. if ((*iter)->name() == "Panner") { - if (_panner == 0) { - _panner = new Panner (_name, _session); + if (!_panner) { + _panner = boost::shared_ptr<Panner>(new Panner (_name, _session)); } _panner->set_state (**iter); } if ((*iter)->name() == "Processor") { if ((*iter)->property ("type") && ((*iter)->property ("type")->value() == "panner" ) ) { - if (_panner == 0) { - _panner = new Panner (_name, _session); + if (!_panner) { + _panner = boost::shared_ptr<Panner>(new Panner (_name, _session)); } _panner->set_state (**iter); } @@ -1427,6 +1388,8 @@ IO::set_state (const XMLNode& node) } } + get_port_counts (node); + if (ports_legal) { if (create_ports (node)) { @@ -1438,8 +1401,10 @@ IO::set_state (const XMLNode& node) port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal)); } - if( !_panner ) - _panner = new Panner( _name, _session ); + if (!_panner) { + _panner = boost::shared_ptr<Panner>(new Panner (_name, _session)); + } + if (panners_legal) { reset_panner (); } else { @@ -1573,6 +1538,7 @@ IO::ports_became_legal () port_legal_c.disconnect (); + get_port_counts (*pending_state_node); ret = create_ports (*pending_state_node); if (connecting_legal) { @@ -1676,48 +1642,64 @@ IO::find_possible_bundle (const string &desired_name, const string &default_name } int -IO::create_ports (const XMLNode& node) +IO::get_port_counts (const XMLNode& node) { XMLProperty const * prop; - uint32_t num_inputs = 0; - uint32_t num_outputs = 0; + XMLNodeConstIterator iter; + ChanCount num_inputs = n_inputs(); + ChanCount num_outputs = n_outputs(); + + for (iter = node.children().begin(); iter != node.children().end(); ++iter) { + if ((*iter)->name() == X_("Inputs")) { + num_inputs = ChanCount::max(num_inputs, ChanCount(**iter)); + } else if ((*iter)->name() == X_("Outputs")) { + num_outputs = ChanCount::max(num_inputs, ChanCount(**iter)); + } + } if ((prop = node.property ("input-connection")) != 0) { boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("in"), _("input")); - if (c) { - num_inputs = c->nchannels (); - } else { - num_inputs = 0; + num_inputs = ChanCount::max(num_inputs, ChanCount(c->type(), c->nchannels())); } } else if ((prop = node.property ("inputs")) != 0) { - num_inputs = count (prop->value().begin(), prop->value().end(), '{'); + num_inputs = ChanCount::max(num_inputs, ChanCount(_default_type, + count (prop->value().begin(), prop->value().end(), '{'))); } if ((prop = node.property ("output-connection")) != 0) { - boost::shared_ptr<Bundle> c = find_possible_bundle(prop->value(), _("out"), _("output")); - + boost::shared_ptr<Bundle> c = find_possible_bundle (prop->value(), _("out"), _("output")); if (c) { - num_outputs = c->nchannels (); - } else { - num_outputs = 0; + num_outputs = ChanCount::max(num_outputs, ChanCount(c->type(), c->nchannels())); } } else if ((prop = node.property ("outputs")) != 0) { - num_outputs = count (prop->value().begin(), prop->value().end(), '{'); + num_outputs = ChanCount::max(num_outputs, ChanCount(_default_type, + count (prop->value().begin(), prop->value().end(), '{'))); } + + _configured_inputs = num_inputs; + _configured_outputs = num_outputs; + _input_minimum = ChanCount::min(_input_minimum, num_inputs); + _input_maximum = ChanCount::max(_input_maximum, num_inputs); + _output_minimum = ChanCount::min(_output_minimum, num_outputs); + _output_maximum = ChanCount::max(_output_maximum, num_outputs); + + return 0; +} + +int +IO::create_ports (const XMLNode& node) +{ no_panner_reset = true; - if (ensure_io (ChanCount (_default_type, num_inputs), - ChanCount (_default_type, num_outputs), - true, this)) { - + if (ensure_io (_input_minimum, _output_minimum, true, this)) { error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg; return -1; } @@ -1730,7 +1712,6 @@ IO::create_ports (const XMLNode& node) return 0; } - int IO::make_connections (const XMLNode& node) { @@ -2282,8 +2263,8 @@ IO::meter () void IO::clear_automation () { - data().clear (); // clears gain automation - _panner->data().clear(); + data().clear_controls (); // clears gain automation + _panner->data().clear_controls (); } void @@ -2325,10 +2306,12 @@ IO::set_parameter_automation_state (Evoral::Parameter param, AutoState state) void IO::inc_gain (gain_t factor, void *src) { - if (_desired_gain == 0.0f) + float desired_gain = _gain_control->user_float(); + if (desired_gain == 0.0f) { set_gain (0.000001f + (0.000001f * factor), src); - else - set_gain (_desired_gain + (_desired_gain * factor), src); + } else { + set_gain (desired_gain + (desired_gain * factor), src); + } } void @@ -2350,7 +2333,7 @@ IO::set_gain (gain_t val, void *src) { Glib::Mutex::Lock dm (declick_lock); - _desired_gain = val; + _gain_control->set_float(val, false); } if (_session.transport_stopped()) { @@ -2470,7 +2453,7 @@ IO::build_legal_port_name (DataType type, bool in) } snprintf (buf2, name_size+1, "%s %d", buf1, port_number); - + return string (buf2); } diff --git a/libs/ardour/io_processor.cc b/libs/ardour/io_processor.cc index 53ad91b9bf..53fbda24c7 100644 --- a/libs/ardour/io_processor.cc +++ b/libs/ardour/io_processor.cc @@ -43,12 +43,19 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -IOProcessor::IOProcessor (Session& s, const string& name, Placement p, - int input_min, int input_max, - int output_min, int output_max, - DataType dtype) - : Processor(s, name, p) - , _io (new IO(s, name, input_min, input_max, output_min, output_max, dtype)) +IOProcessor::IOProcessor (Session& s, const string& proc_name, const string io_name, DataType dtype) + : Processor(s, proc_name) + , _io (new IO(s, io_name != "" ? io_name : proc_name, dtype)) +{ + _active = false; + _sort_key = 0; + _gui = 0; + _extra_xml = 0; +} + +IOProcessor::IOProcessor (Session& s, IO* io, const string& proc_name, DataType dtype) + : Processor(s, proc_name) + , _io (io) { _active = false; _sort_key = 0; diff --git a/libs/ardour/lv2_event_buffer.cc b/libs/ardour/lv2_event_buffer.cc index dd4a925877..6afd395a56 100644 --- a/libs/ardour/lv2_event_buffer.cc +++ b/libs/ardour/lv2_event_buffer.cc @@ -41,6 +41,11 @@ LV2EventBuffer::LV2EventBuffer(size_t capacity) throw std::bad_alloc(); } + if (capacity == 0) { + cerr << "ERROR: LV2 event buffer of size 0 created." << endl; + capacity = 1024; + } + #ifdef NO_POSIX_MEMALIGN _data = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); int ret = (_data != NULL) ? 0 : -1; @@ -139,7 +144,8 @@ LV2EventBuffer::append(uint32_t frames, #endif /*cout << "Appending event type " << type << ", size " << size - << " @ " << frames << "." << subframes << endl;*/ + << " @ " << frames << "." << subframes << endl; + cout << "Buffer capacity " << _data->capacity << ", size " << _data->size << endl;*/ if (!lv2_event_write(&_iter, frames, subframes, type, size, data)) { cerr << "ERROR: Failed to write event." << endl; diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 5b4696b48c..8000bd8e44 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -268,8 +268,8 @@ LV2Plugin::get_state() if (parameter_is_input(i) && parameter_is_control(i)) { child = new XMLNode("Port"); - snprintf(buf, sizeof(buf), "%u", i); - child->add_property("number", string(buf)); + /*snprintf(buf, sizeof(buf), "%u", i); + child->add_property("number", string(buf));*/ child->add_property("symbol", port_symbol(i)); snprintf(buf, sizeof(buf), "%+f", _shadow_data[i]); child->add_property("value", string(buf)); @@ -343,8 +343,8 @@ LV2Plugin::set_state(const XMLNode& node) XMLProperty *prop; XMLNodeConstIterator iter; XMLNode *child; - const char *port; - const char *data; + const char *sym; + const char *value; uint32_t port_id; LocaleGuard lg (X_("POSIX")); @@ -359,22 +359,29 @@ LV2Plugin::set_state(const XMLNode& node) child = *iter; - if ((prop = child->property("number")) != 0) { - port = prop->value().c_str(); + if ((prop = child->property("symbol")) != 0) { + sym = prop->value().c_str(); } else { - warning << _("LV2: no lv2 port number") << endmsg; + warning << _("LV2: port has no symbol, ignored") << endmsg; + continue; + } + + map<string,uint32_t>::iterator i = _port_indices.find(sym); + if (i != _port_indices.end()) { + port_id = i->second; + } else { + warning << _("LV2: port has unknown index, ignored") << endmsg; continue; } if ((prop = child->property("value")) != 0) { - data = prop->value().c_str(); + value = prop->value().c_str(); } else { - warning << _("LV2: no lv2 port data") << endmsg; + warning << _("LV2: port has no value, ignored") << endmsg; continue; } - sscanf (port, "%" PRIu32, &port_id); - set_parameter (port_id, atof(data)); + set_parameter (port_id, atof(value)); } latency_compute_run (); @@ -462,9 +469,7 @@ LV2Plugin::connect_and_run (BufferSet& bufs, ChanMapping in_map, ChanMapping out_map, nframes_t nframes, nframes_t offset) { - cycles_t then, now; - - then = get_cycles (); + cycles_t then = get_cycles (); uint32_t audio_in_index = 0; uint32_t audio_out_index = 0; @@ -474,25 +479,29 @@ LV2Plugin::connect_and_run (BufferSet& bufs, if (parameter_is_audio(port_index)) { if (parameter_is_input(port_index)) { const uint32_t buf_index = in_map.get(DataType::AUDIO, audio_in_index++); + //cerr << port_index << " : " << " AUDIO IN " << buf_index << endl; slv2_instance_connect_port(_instance, port_index, bufs.get_audio(buf_index).data(offset)); } else if (parameter_is_output(port_index)) { const uint32_t buf_index = out_map.get(DataType::AUDIO, audio_out_index++); + //cerr << port_index << " : " << " AUDIO OUT " << buf_index << endl; slv2_instance_connect_port(_instance, port_index, bufs.get_audio(buf_index).data(offset)); } } else if (parameter_is_midi(port_index)) { if (parameter_is_input(port_index)) { const uint32_t buf_index = in_map.get(DataType::MIDI, midi_in_index++); + //cerr << port_index << " : " << " MIDI IN " << buf_index << endl; slv2_instance_connect_port(_instance, port_index, bufs.get_lv2_midi(true, buf_index).data()); } else if (parameter_is_output(port_index)) { const uint32_t buf_index = out_map.get(DataType::MIDI, midi_out_index++); + //cerr << port_index << " : " << " MIDI OUT " << buf_index << endl; slv2_instance_connect_port(_instance, port_index, bufs.get_lv2_midi(false, buf_index).data()); } } else if (!parameter_is_control(port_index)) { - std::cerr << "WARNING: Unknown LV2 port type, ignored" << endl; + // Optional port (it'd better be if we've made it this far...) slv2_instance_connect_port(_instance, port_index, NULL); } } @@ -507,7 +516,7 @@ LV2Plugin::connect_and_run (BufferSet& bufs, } } - now = get_cycles (); + cycles_t now = get_cycles (); set_cycles ((uint32_t) (now - then)); return 0; @@ -683,7 +692,7 @@ LV2PluginInfo::discover (void* lv2_world) LV2World* world = (LV2World*)lv2_world; SLV2Plugins plugins = slv2_world_get_all_plugins(world->world); - cerr << "Discovered " << slv2_plugins_size (plugins) << " Lv2 plugins\n"; + cerr << "LV2: Discovered " << slv2_plugins_size (plugins) << " plugins\n"; for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { SLV2Plugin p = slv2_plugins_get_at(plugins, i); diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 30d4d1a1d8..d8fee1ac47 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -27,6 +27,8 @@ #include "ardour/audio_buffer.h" #include "ardour/runtime_functions.h" +using namespace std; + namespace ARDOUR { @@ -38,44 +40,39 @@ namespace ARDOUR { void PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes) { + const uint32_t n_audio = min(_configured_input.n_audio(), bufs.count().n_audio()); + const uint32_t n_midi = min(_configured_input.n_midi(), bufs.count().n_midi()); + uint32_t n = 0; - uint32_t meterable = std::min(bufs.count().n_total(), (uint32_t)_peak_power.size()); - uint32_t limit = std::min (meterable, (uint32_t)bufs.count().n_midi()); - - // Meter what we have (midi) - for ( ; n < limit; ++n) { - float val = 0; - - // GUI needs a better MIDI meter, not much information can be - // expressed through peaks alone - for (MidiBuffer::iterator i = bufs.get_midi(n).begin(); i != bufs.get_midi(n).end(); ++i) { - const Evoral::MIDIEvent<nframes_t> ev(*i, false); + + // Meter MIDI in to the first n_midi peaks + for (uint32_t i = 0; i < n_midi; ++i, ++n) { + float val = 0.0f; + for (MidiBuffer::iterator e = bufs.get_midi(i).begin(); e != bufs.get_midi(i).end(); ++e) { + const Evoral::MIDIEvent<nframes_t> ev(*e, false); if (ev.is_note_on()) { const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0; - //printf("V %d -> %f\n", (int)((Byte)ev.buffer[2]), this_vel); - if (this_vel > val) + if (this_vel > val) { val = this_vel; + } } else { val += 1.0 / bufs.get_midi(n).capacity(); - if (val > 1.0) + if (val > 1.0) { val = 1.0; + } } } - _peak_power[n] = val; - } - - limit = std::min (meterable, bufs.count().n_audio()); - // Meter what we have (audio) - for ( ; n < limit; ++n) { - _peak_power[n] = compute_peak (bufs.get_audio(n).data(), nframes, _peak_power[n]); + // Meter audio in to the rest of the peaks + for (uint32_t i = 0; i < n_audio; ++i, ++n) { + _peak_power[n] = compute_peak (bufs.get_audio(i).data(), nframes, _peak_power[n]); } // Zero any excess peaks - for (size_t n = meterable; n < _peak_power.size(); ++n) { - _peak_power[n] = 0; + for (uint32_t i = n; i < _peak_power.size(); ++i) { + _peak_power[i] = 0.0f; } } @@ -83,7 +80,7 @@ void PeakMeter::reset () { for (size_t i = 0; i < _peak_power.size(); ++i) { - _peak_power[i] = 0; + _peak_power[i] = 0.0f; } } @@ -96,13 +93,19 @@ PeakMeter::reset_max () } bool +PeakMeter::can_support_io_configuration (const ChanCount& in, ChanCount& out) const +{ + out = in; + return true; +} + +bool PeakMeter::configure_io (ChanCount in, ChanCount out) { - /* we're transparent no matter what. fight the power. */ - if (out != in) { + if (out != in) { // always 1:1 return false; } - + uint32_t limit = in.n_total(); while (_peak_power.size() > limit) { @@ -165,4 +168,18 @@ PeakMeter::meter () } } +XMLNode& +PeakMeter::state (bool full_state) +{ + return get_state(); +} + +XMLNode& +PeakMeter::get_state() +{ + XMLNode* node = new XMLNode(state_node_name); + node->add_property("type", "meter"); + return *node; +} + } // namespace ARDOUR diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index a0ef073c3a..f8d9f1eb58 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -139,8 +139,9 @@ MidiDiskstream::~MidiDiskstream () void MidiDiskstream::non_realtime_locate (nframes_t position) { - assert(_write_source); - _write_source->set_timeline_position (position); + if (_write_source) { + _write_source->set_timeline_position (position); + } seek(position, false); } @@ -156,7 +157,10 @@ MidiDiskstream::non_realtime_input_change () } if (input_change_pending & ConfigurationChanged) { - assert(_io->n_inputs() == _n_channels); + if (_io->n_inputs().n_midi() != _n_channels.n_midi()) { + error << "Can not feed IO " << _io->n_inputs() + << " with diskstream " << _n_channels << endl; + } } get_input_sources (); @@ -547,8 +551,6 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can_ } else if (nominally_recording) { - cerr << "B" << endl; - /* can't do actual capture yet - waiting for latency effects to finish before we start*/ playback_distance = nframes; diff --git a/libs/ardour/midi_patch_manager.cc b/libs/ardour/midi_patch_manager.cc index 39a5be9b5a..b555c12934 100644 --- a/libs/ardour/midi_patch_manager.cc +++ b/libs/ardour/midi_patch_manager.cc @@ -21,12 +21,16 @@ #include <sigc++/sigc++.h> #include <boost/shared_ptr.hpp> +#include "pbd/compose.h" #include "pbd/file_utils.h" +#include "pbd/error.h" #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/midi_patch_manager.h" +#include "i18n.h" + using namespace std; using namespace sigc; using namespace ARDOUR; @@ -55,12 +59,11 @@ MidiPatchManager::refresh() path path_to_patches = _session->session_directory().midi_patch_path(); - cerr << "Path to patches: " << path_to_patches.to_string() << endl; + info << string_compose(_("looking for MIDI patches in %1"), path_to_patches.to_string()) << endmsg; - if(!exists(path_to_patches)) { + if (!exists(path_to_patches)) { return; } - cerr << "Path to patches: " << path_to_patches.to_string() << " exists" << endl; assert(is_directory(path_to_patches)); @@ -69,17 +72,15 @@ MidiPatchManager::refresh() find_matching_files_in_directory(path_to_patches, pattern, result); - cerr << "patchfiles result contains " << result.size() << " elements" << endl; + cerr << "Loading " << result.size() << " MIDI patches from " << path_to_patches.to_string() << endl; - for(vector<path>::iterator i = result.begin(); i != result.end(); ++i) { - cerr << "processing patchfile " << i->to_string() << endl; - + for (vector<path>::iterator i = result.begin(); i != result.end(); ++i) { boost::shared_ptr<MIDINameDocument> document(new MIDINameDocument(i->to_string())); - for(MIDINameDocument::MasterDeviceNamesList::const_iterator device = - document->master_device_names_by_model().begin(); - device != document->master_device_names_by_model().end(); - ++device) { - cerr << "got model " << device->first << endl; + for (MIDINameDocument::MasterDeviceNamesList::const_iterator device = + document->master_device_names_by_model().begin(); + device != document->master_device_names_by_model().end(); + ++device) { + //cerr << "got model " << device->first << endl; // have access to the documents by model name _documents[device->first] = document; // build a list of all master devices from all documents diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index b66d6c6b13..59c19981a5 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -139,7 +139,7 @@ MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, sframes_t source_start, Evoral::Sequence<double>::const_iterator& i = _model_iter; if (_last_read_end == 0 || start != _last_read_end) { // || !i.valid()) { - cerr << "MidiSource seeking to " << start << " from " << _last_read_end << endl; + //cerr << "MidiSource seeking to " << start << " from " << _last_read_end << endl; for (i = _model->begin(); i != _model->end(); ++i) { if (BEATS_TO_FRAMES(i->time()) >= start) { break; diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 1fe15a16bf..030e8ff905 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -25,20 +25,20 @@ #include "midi++/events.h" #include "evoral/midi_util.h" -#include "ardour/midi_track.h" -#include "ardour/midi_diskstream.h" -#include "ardour/session.h" +#include "ardour/amp.h" +#include "ardour/buffer_set.h" #include "ardour/io_processor.h" +#include "ardour/meter.h" +#include "ardour/midi_diskstream.h" +#include "ardour/midi_playlist.h" #include "ardour/midi_region.h" #include "ardour/midi_source.h" -#include "ardour/route_group_specialized.h" -#include "ardour/processor.h" -#include "ardour/midi_playlist.h" +#include "ardour/midi_track.h" #include "ardour/panner.h" +#include "ardour/processor.h" +#include "ardour/route_group_specialized.h" +#include "ardour/session.h" #include "ardour/utils.h" -#include "ardour/buffer_set.h" -#include "ardour/meter.h" - #include "i18n.h" @@ -60,10 +60,6 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo set_input_minimum(ChanCount(DataType::MIDI, 1)); set_input_maximum(ChanCount(DataType::MIDI, 1)); - set_output_minimum(ChanCount(DataType::MIDI, 1)); - set_output_maximum(ChanCount(DataType::MIDI, 1)); - - PortCountChanged(ChanCount(DataType::MIDI, 2)); /* EMIT SIGNAL */ } MidiTrack::MidiTrack (Session& sess, const XMLNode& node) @@ -75,10 +71,6 @@ MidiTrack::MidiTrack (Session& sess, const XMLNode& node) set_input_minimum(ChanCount(DataType::MIDI, 1)); set_input_maximum(ChanCount(DataType::MIDI, 1)); - set_output_minimum(ChanCount(DataType::MIDI, 1)); - set_output_maximum(ChanCount(DataType::MIDI, 1)); - - PortCountChanged(ChanCount(DataType::MIDI, 2)); /* EMIT SIGNAL */ } MidiTrack::~MidiTrack () @@ -370,87 +362,6 @@ MidiTrack::set_state_part_two () return; } -int -MidiTrack::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool session_state_changing, bool can_record, bool rec_monitors_input) -{ - if (n_outputs().n_midi() == 0) { - return 0; - } - - if (!_active) { - silence (nframes); - } - - if (session_state_changing) { - - /* XXX is this safe to do against transport state changes? */ - - passthru_silence (start_frame, end_frame, nframes, 0, false); - return 0; - } - - midi_diskstream()->check_record_status (start_frame, nframes, can_record); - - bool send_silence; - - if (_have_internal_generator) { - /* since the instrument has no input streams, - there is no reason to send any signal - into the route. - */ - send_silence = true; - } else { - - if (Config->get_auto_input()) { - if (Config->get_monitoring_model() == SoftwareMonitoring) { - send_silence = false; - } else { - send_silence = true; - } - } else { - if (_diskstream->record_enabled()) { - if (Config->get_monitoring_model() == SoftwareMonitoring) { - send_silence = false; - } else { - send_silence = true; - } - } else { - send_silence = true; - } - } - } - - apply_gain_automation = false; - - if (send_silence) { - - /* if we're sending silence, but we want the meters to show levels for the signal, - meter right here. - */ - - if (_have_internal_generator) { - passthru_silence (start_frame, end_frame, nframes, 0, true); - } else { - if (_meter_point == MeterInput) { - just_meter_input (start_frame, end_frame, nframes); - } - passthru_silence (start_frame, end_frame, nframes, 0, false); - } - - } else { - - /* we're sending signal, but we may still want to meter the input. - */ - - passthru (start_frame, end_frame, nframes, 0, (_meter_point == MeterInput)); - } - - flush_outputs (nframes); - - return 0; -} - int MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int declick, bool can_record, bool rec_monitors_input) @@ -509,7 +420,7 @@ MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, at least potentially (depending on monitoring options) */ - passthru (start_frame, end_frame, nframes, 0, true); + passthru (start_frame, end_frame, nframes, 0); } else { /* @@ -529,7 +440,7 @@ MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame); process_output_buffers (bufs, start_frame, end_frame, nframes, - (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick, (_meter_point != MeterInput)); + (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick); } @@ -538,68 +449,6 @@ MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, return 0; } -int -MidiTrack::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, - bool can_record, bool rec_monitors_input) -{ - if (n_outputs().n_midi() == 0 && _processors.empty()) { - return 0; - } - - if (!_active) { - silence (nframes); - return 0; - } - - _silent = true; - apply_gain_automation = false; - - silence (nframes); - - return midi_diskstream()->process (_session.transport_frame(), nframes, can_record, rec_monitors_input); -} - -void -MidiTrack::process_output_buffers (BufferSet& bufs, - nframes_t start_frame, nframes_t end_frame, - nframes_t nframes, bool with_processors, int declick, - bool meter) -{ - /* There's no such thing as a MIDI bus for the time being. - * We'll do all the MIDI route work here for now, but the long-term goal is to have - * Route::process_output_buffers handle everything */ - - if (meter && (_meter_point == MeterInput || _meter_point == MeterPreFader)) { - _meter->run_in_place(bufs, start_frame, end_frame, nframes); - } - - // Run all processors - if (with_processors) { - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); - if (rm.locked()) { - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams())); - (*i)->run_in_place (bufs, start_frame, end_frame, nframes); - } - } - } - - if (meter && (_meter_point == MeterPostFader)) { - _meter->run_in_place(bufs, start_frame, end_frame, nframes); - } - - // Main output stage - if (muted()) { - IO::silence (nframes); - } else { - - // Write 'automation' controllers (e.g. CC events from a UI slider) - write_controller_messages(bufs.get_midi(0), start_frame, end_frame, nframes); - - deliver_output(bufs, start_frame, end_frame, nframes); - } -} - void MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start, nframes_t end, nframes_t nframes) { diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 23c87f0a2e..83923a44d6 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -703,7 +703,7 @@ Multi2dPanner::set_state (const XMLNode& node) /*---------------------------------------------------------------------- */ Panner::Panner (string name, Session& s) - : Processor(s, name, PostFader) + : Processor(s, name) { //set_name_old_auto (name); set_name (name); @@ -1012,23 +1012,30 @@ Panner::set_automation_state (AutoState state) AutoState Panner::automation_state () const { + boost::shared_ptr<AutomationList> l; if (!empty()) { - return ((AutomationList*)_streampanners.front()->pan_control()->list().get())->automation_state (); - } else { - return Off; + boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control(); + if (control) { + l = boost::dynamic_pointer_cast<AutomationList>(control->list()); + } } + + return l ? l->automation_state() : Off; } AutoStyle Panner::automation_style () const { + boost::shared_ptr<AutomationList> l; if (!empty()) { - return ((AutomationList*)_streampanners.front()->pan_control()->list().get())->automation_style (); - } else { - return Absolute; + boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control(); + if (control) { + l = boost::dynamic_pointer_cast<AutomationList>(control->list()); + } } -} + return l ? l->automation_style() : Absolute; +} struct PanPlugins { string name; diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index bf67e33493..41c65ba4fe 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -58,10 +58,10 @@ using namespace PBD; const string PluginInsert::port_automation_node_name = "PortAutomation"; -PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug, Placement placement) - : Processor (s, plug->name(), placement), - _signal_analysis_collected_nframes(0), - _signal_analysis_collect_nframes_max(0) +PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug) + : Processor (s, plug->name()) + , _signal_analysis_collected_nframes(0) + , _signal_analysis_collect_nframes_max(0) { /* the first is the master */ @@ -78,7 +78,7 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug, Placemen } PluginInsert::PluginInsert (Session& s, const XMLNode& node) - : Processor (s, "unnamed plugin insert", PreFader), + : Processor (s, "unnamed plugin insert"), _signal_analysis_collected_nframes(0), _signal_analysis_collect_nframes_max(0) { @@ -160,14 +160,10 @@ PluginInsert::output_streams() const ChanCount out = _plugins.front()->get_info()->n_outputs; if (out == ChanCount::INFINITE) { - return _plugins.front()->output_streams (); - } else { - out.set_audio (out.n_audio() * _plugins.size()); out.set_midi (out.n_midi() * _plugins.size()); - return out; } } @@ -182,7 +178,6 @@ PluginInsert::input_streams() const } else { in.set_audio (in.n_audio() * _plugins.size()); in.set_midi (in.n_midi() * _plugins.size()); - return in; } } @@ -323,10 +318,10 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off } for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->connect_and_run (bufs, in_map, out_map, nframes, offset); + (*i)->connect_and_run(bufs, in_map, out_map, nframes, offset); for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - in_map.offset(*t, input_streams().get(*t)); - out_map.offset(*t, output_streams().get(*t)); + in_map.offset_to(*t, natural_input_streams().get(*t)); + out_map.offset_to(*t, natural_output_streams().get(*t)); } } @@ -562,61 +557,60 @@ PluginInsert::configure_io (ChanCount in, ChanCount out) bool PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out) const { + // Plugin has flexible I/O, so delegate to it if (_plugins.front()->reconfigurable_io()) { - /* plugin has flexible I/O, so delegate to it */ return _plugins.front()->can_support_io_configuration (in, out); } + ChanCount inputs = _plugins[0]->get_info()->n_inputs; ChanCount outputs = _plugins[0]->get_info()->n_outputs; - ChanCount inputs = _plugins[0]->get_info()->n_inputs; - if ((inputs.n_total() == 0) - || (inputs.n_total() == 1 && outputs == inputs) - || (inputs.n_total() == 1 && outputs == inputs - && ((inputs.n_audio() == 0 && in.n_audio() == 0) - || (inputs.n_midi() == 0 && in.n_midi() == 0))) - || (inputs == in)) { + // Plugin inputs match requested inputs exactly + if (inputs == in) { out = outputs; return true; } - bool can_replicate = true; - - /* if number of inputs is a factor of the requested input - configuration for every type, we can replicate. - */ + // See if replication is possible + // We can replicate if there exists a single factor f such that, for every type, + // the number of plugin inputs * f = the requested number of inputs + uint32_t f = 0; + bool can_replicate = true; for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - if (inputs.get(*t) >= in.get(*t) || (inputs.get(*t) % in.get(*t) != 0)) { + // No inputs of this type + if (inputs.get(*t) == 0 && in.get(*t) == 0) { + continue; + + // Plugin has more inputs than requested, can not replicate + } else if (inputs.get(*t) >= in.get(*t)) { can_replicate = false; break; + + // Plugin inputs is not a factor of requested inputs, can not replicate + } else if (inputs.get(*t) == 0 || in.get(*t) % inputs.get(*t) != 0) { + can_replicate = false; + break; + + // Potential factor not set yet + } else if (f == 0) { + f = in.get(*t) / inputs.get(*t);; } - } - if (!can_replicate || (in.n_total() % inputs.n_total() != 0)) { - return false; + // Factor for this type does not match another type, can not replicate + if (f != (in.get(*t) / inputs.get(*t))) { + can_replicate = false; + break; + } } - if (inputs.n_total() == 0) { - /* instrument plugin, always legal, but throws away any existing streams */ - out = outputs; - } else if (inputs.n_total() == 1 && outputs == inputs - && ((inputs.n_audio() == 0 && in.n_audio() == 0) - || (inputs.n_midi() == 0 && in.n_midi() == 0))) { - /* mono, single-typed plugin, replicate as needed to match in */ - out = in; - } else if (inputs == in) { - /* exact match */ - out = outputs; - } else { - /* replicate - note that we've already verified that - the replication count is constant across all data types. - */ + if (can_replicate) { for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - out.set (*t, outputs.get(*t) * (in.get(*t) / inputs.get(*t))); + out.set (*t, outputs.get(*t) * f); } + return true; + } else { + return false; } - - return true; } /* Number of plugin instances required to support a given channel configuration. @@ -798,7 +792,7 @@ PluginInsert::set_state(const XMLNode& node) cnodes = (*niter)->children ("Port"); - for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){ + for (iter = cnodes.begin(); iter != cnodes.end(); ++iter) { child = *iter; diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index ce7a94a517..0b5b9276f1 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -127,7 +127,7 @@ PluginManager::PluginManager () } #ifdef HAVE_SLV2 - cerr << "Creating a new lv2 world\n"; + cerr << "LV2: Creating world" << endl; _lv2_world = new LV2World(); #endif diff --git a/libs/ardour/po/el_GR.po b/libs/ardour/po/el_GR.po index 9ddd3e7188..fb1856dbdd 100644 --- a/libs/ardour/po/el_GR.po +++ b/libs/ardour/po/el_GR.po @@ -446,7 +446,7 @@ msgid "automation list: no y-coordinate stored for control point (point ignored) msgstr "λίστα αυτοματισμοÏ: καμία y-συντεταγμÎνη αποθηκευμÎνη για σημείο ελÎγχου (το σημείο αγνοήθηκε)" #: libs/ardour/configuration.cc:80 -msgid "loading system configuration file %1" +msgid "Loading system configuration file %1" msgstr "Ανάκληση αÏχείου Ïυθμίσεων συστήματος %1" #: libs/ardour/configuration.cc:83 @@ -458,7 +458,7 @@ msgid "Ardour: system configuration file \"%1\" not loaded successfully." msgstr "Ardour: το αÏχείο διαÏÏυθμίσεως του συστήματος \"%1\" δεν φοÏτώθηκε επιτυχώς." #: libs/ardour/configuration.cc:105 -msgid "loading user configuration file %1" +msgid "Loading user configuration file %1" msgstr "Ανάκληση αÏχείου Ïυθμίσεων χÏήστη %1" #: libs/ardour/configuration.cc:108 diff --git a/libs/ardour/po/it_IT.po b/libs/ardour/po/it_IT.po index 2ce02827bd..61ec7853ed 100644 --- a/libs/ardour/po/it_IT.po +++ b/libs/ardour/po/it_IT.po @@ -467,7 +467,7 @@ msgstr "" #: libs/ardour/configuration.cc:80 #, fuzzy -msgid "loading system configuration file %1" +msgid "Loading system configuration file %1" msgstr "" "Ardour: impossibile leggere il file di configurazione di sistema \"%1\"" @@ -484,7 +484,7 @@ msgstr "" #: libs/ardour/configuration.cc:105 #, fuzzy -msgid "loading user configuration file %1" +msgid "Loading user configuration file %1" msgstr "Ardour: impossibile la lettura del file di configurazione \"%1\"" #: libs/ardour/configuration.cc:108 diff --git a/libs/ardour/po/pl_PL.po b/libs/ardour/po/pl_PL.po index f2bdc8d91a..c189796689 100644 --- a/libs/ardour/po/pl_PL.po +++ b/libs/ardour/po/pl_PL.po @@ -365,7 +365,7 @@ msgid "AutomationList: passed XML node called %1, not \"AutomationList\" - ignor msgstr "" #: libs/ardour/configuration.cc:97 -msgid "loading system configuration file %1" +msgid "Loading system configuration file %1" msgstr "" #: libs/ardour/configuration.cc:100 @@ -381,7 +381,7 @@ msgid "your system Ardour configuration file is empty. This probably means that msgstr "" #: libs/ardour/configuration.cc:131 -msgid "loading user configuration file %1" +msgid "Loading user configuration file %1" msgstr "" #: libs/ardour/configuration.cc:134 diff --git a/libs/ardour/po/ru_RU.po b/libs/ardour/po/ru_RU.po index aeeb1bf547..c1c4850ce2 100644 --- a/libs/ardour/po/ru_RU.po +++ b/libs/ardour/po/ru_RU.po @@ -430,7 +430,7 @@ msgstr "" #: libs/ardour/configuration.cc:80 #, fuzzy -msgid "loading system configuration file %1" +msgid "Loading system configuration file %1" msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÒÏÇÒÁÍÍÙ \"%1\"" #: libs/ardour/configuration.cc:83 @@ -443,7 +443,7 @@ msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÒÏÇÒÁÍÍÙ \"%1\"." #: libs/ardour/configuration.cc:105 #, fuzzy -msgid "loading user configuration file %1" +msgid "Loading user configuration file %1" msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÏÌØÚÏ×ÁÔÅÌÑ \"%1\"" #: libs/ardour/configuration.cc:108 diff --git a/libs/ardour/po/sv_SE.po b/libs/ardour/po/sv_SE.po index 58be548f8e..19fe623de9 100644 --- a/libs/ardour/po/sv_SE.po +++ b/libs/ardour/po/sv_SE.po @@ -423,7 +423,7 @@ msgid "" msgstr "" #: libs/ardour/configuration.cc:87 -msgid "loading system configuration file %1" +msgid "Loading system configuration file %1" msgstr "" #: libs/ardour/configuration.cc:90 @@ -435,7 +435,7 @@ msgid "Ardour: system configuration file \"%1\" not loaded successfully." msgstr "" #: libs/ardour/configuration.cc:111 -msgid "loading user configuration file %1" +msgid "Loading user configuration file %1" msgstr "" #: libs/ardour/configuration.cc:114 diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index d5021026de..ebe31a1fde 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -210,6 +210,7 @@ Port::total_latency () const int Port::reestablish () { + cerr << "RE-REGISTER: " << _name.c_str() << endl; _jack_port = jack_port_register (_engine->jack(), _name.c_str(), type().to_jack_type(), _flags, 0); if (_jack_port == 0) { diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc index 8f53be8534..606b055dab 100644 --- a/libs/ardour/port_insert.cc +++ b/libs/ardour/port_insert.cc @@ -40,29 +40,15 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -PortInsert::PortInsert (Session& s, Placement p) - : IOProcessor (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1) +PortInsert::PortInsert (Session& s) + : IOProcessor (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), "") { init (); ProcessorCreated (this); /* EMIT SIGNAL */ } -void -PortInsert::init () -{ - if (_io->add_input_port ("", this)) { - error << _("PortInsert: cannot add input port") << endmsg; - throw failed_constructor(); - } - - if (_io->add_output_port ("", this)) { - error << _("PortInsert: cannot add output port") << endmsg; - throw failed_constructor(); - } -} - PortInsert::PortInsert (Session& s, const XMLNode& node) - : IOProcessor (s, "unnamed port insert", PreFader) + : IOProcessor (s, "unnamed port insert") { if (set_state (node)) { throw failed_constructor(); @@ -77,6 +63,15 @@ PortInsert::~PortInsert () } void +PortInsert::init () +{ + if (_io->ensure_io(output_streams(), input_streams(), false, this)) { // sic + error << _("PortInsert: cannot create ports") << endmsg; + throw failed_constructor(); + } +} + +void PortInsert::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes) { if (_io->n_outputs().n_total() == 0) { @@ -180,10 +175,10 @@ PortInsert::configure_io (ChanCount in, ChanCount out) to the number of input ports we need. */ - _io->set_output_maximum (in); + /*_io->set_output_maximum (in); _io->set_output_minimum (in); _io->set_input_maximum (out); - _io->set_input_minimum (out); + _io->set_input_minimum (out);*/ if (_io->ensure_io (out, in, false, this) != 0) { return false; diff --git a/libs/ardour/processor.cc b/libs/ardour/processor.cc index 63848e8c40..b48b0aebb0 100644 --- a/libs/ardour/processor.cc +++ b/libs/ardour/processor.cc @@ -58,13 +58,12 @@ sigc::signal<void,Processor*> Processor::ProcessorCreated; // Always saved as Processor, but may be IOProcessor or Send in legacy sessions const string Processor::state_node_name = "Processor"; -Processor::Processor(Session& session, const string& name, Placement p) +Processor::Processor(Session& session, const string& name) : SessionObject(session, name) , AutomatableControls(session) , _active(false) , _next_ab_is_active(false) , _configured(false) - , _placement(p) , _gui(0) { } @@ -75,15 +74,6 @@ Processor::set_sort_key (uint32_t key) _sort_key = key; } -void -Processor::set_placement (Placement p) -{ - if (_placement != p) { - _placement = p; - PlacementChanged (); /* EMIT SIGNAL */ - } -} - XMLNode& Processor::get_state (void) { @@ -120,26 +110,28 @@ Processor::state (bool full_state) node->add_property("name", _name); node->add_property("active", active() ? "yes" : "no"); - node->add_property("placement", enum_2_string (_placement)); if (_extra_xml){ node->add_child_copy (*_extra_xml); } if (full_state) { - XMLNode& automation = Automatable::get_automation_state(); - - for (set<Evoral::Parameter>::iterator x = _visible_controls.begin(); x != _visible_controls.end(); ++x) { - if (x != _visible_controls.begin()) { - sstr << ' '; + if (!automation.children().empty() + || !automation.properties().empty() + || !_visible_controls.empty()) { + + for (set<Evoral::Parameter>::iterator x = _visible_controls.begin(); + x != _visible_controls.end(); ++x) { + if (x != _visible_controls.begin()) { + sstr << ' '; + } + sstr << *x; } - sstr << *x; - } - - automation.add_property ("visible", sstr.str()); - node->add_child_nocopy (automation); + automation.add_property ("visible", sstr.str()); + node->add_child_nocopy (automation); + } } return *node; @@ -164,7 +156,6 @@ Processor::set_state (const XMLNode& node) if ((*niter)->name() == X_("Automation")) { - XMLProperty *prop; if ((prop = (*niter)->property ("path")) != 0) { @@ -203,7 +194,8 @@ Processor::set_state (const XMLNode& node) } if ((prop = node.property ("active")) == 0) { - warning << _("XML node describing a processor is missing the `active' field, trying legacy active flag from child node") << endmsg; + warning << _("XML node describing a processor is missing the `active' field," + "trying legacy active flag from child node") << endmsg; if (legacy_active) { prop = legacy_active; } else { @@ -217,31 +209,6 @@ Processor::set_state (const XMLNode& node) ActiveChanged (); /* EMIT_SIGNAL */ } - if ((prop = node.property ("placement")) == 0) { - warning << _("XML node describing a processor is missing the `placement' field, trying legacy placement flag from child node") << endmsg; - if (legacy_placement) { - prop = legacy_placement; - } else { - error << _("No child node with placement property") << endmsg; - return -1; - } - } - - /* hack to handle older sessions before we only used EnumWriter */ - - string pstr; - - if (prop->value() == "pre") { - pstr = "PreFader"; - } else if (prop->value() == "post") { - pstr = "PostFader"; - } else { - pstr = prop->value(); - } - - Placement p = Placement (string_2_enum (pstr, p)); - set_placement (p); - return 0; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 6179af5028..931ae9a996 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -29,29 +29,30 @@ #include "evoral/Curve.hpp" -#include "ardour/timestamps.h" +#include "ardour/amp.h" +#include "ardour/audio_port.h" #include "ardour/audioengine.h" -#include "ardour/route.h" #include "ardour/buffer.h" -#include "ardour/processor.h" -#include "ardour/plugin_insert.h" -#include "ardour/port_insert.h" -#include "ardour/send.h" -#include "ardour/session.h" -#include "ardour/utils.h" +#include "ardour/buffer_set.h" #include "ardour/configuration.h" +#include "ardour/control_outputs.h" #include "ardour/cycle_timer.h" -#include "ardour/route_group.h" -#include "ardour/port.h" -#include "ardour/audio_port.h" -#include "ardour/ladspa_plugin.h" -#include "ardour/panner.h" #include "ardour/dB.h" -#include "ardour/amp.h" +#include "ardour/ladspa_plugin.h" #include "ardour/meter.h" -#include "ardour/buffer_set.h" #include "ardour/mix.h" +#include "ardour/panner.h" +#include "ardour/plugin_insert.h" +#include "ardour/port.h" +#include "ardour/port_insert.h" +#include "ardour/processor.h" #include "ardour/profile.h" +#include "ardour/route.h" +#include "ardour/route_group.h" +#include "ardour/send.h" +#include "ardour/session.h" +#include "ardour/timestamps.h" +#include "ardour/utils.h" #include "i18n.h" @@ -62,21 +63,22 @@ using namespace PBD; uint32_t Route::order_key_cnt = 0; sigc::signal<void,const char*> Route::SyncOrderKeys; -Route::Route (Session& sess, string name, - int in_min, int in_max, int out_min, int out_max, - Flag flg, DataType default_type) - : IO (sess, name, in_min, in_max, out_min, out_max, default_type) +Route::Route (Session& sess, string name, Flag flg, + DataType default_type, ChanCount in, ChanCount out) + : IO (sess, name, default_type, in, ChanCount::INFINITE, out, ChanCount::INFINITE) , _flags (flg) , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)) , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) { + _configured_inputs = in; + _configured_outputs = out; init (); } Route::Route (Session& sess, const XMLNode& node, DataType default_type) : IO (sess, *node.child ("IO"), default_type) - , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)) - , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) + , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)) + , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl)) { init (); _set_state (node, false); @@ -104,6 +106,7 @@ Route::init () _declickable = false; _pending_declick = true; _remote_control_id = 0; + _in_configure_processors = false; _edit_group = 0; _mix_group = 0; @@ -118,10 +121,13 @@ Route::init () mute_gain = 1.0; desired_mute_gain = 1.0; - _control_outs = 0; - input_changed.connect (mem_fun (this, &Route::input_change_handler)); output_changed.connect (mem_fun (this, &Route::output_change_handler)); + + _amp->set_sort_key (0); + _meter->set_sort_key (1); + add_processor (_amp, NULL); + add_processor (_meter, NULL); } Route::~Route () @@ -132,8 +138,6 @@ Route::~Route () for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) { free ((void*)(i->first)); } - - delete _control_outs; } void @@ -217,7 +221,6 @@ Route::ensure_track_or_route_name(string name, Session &session) return newname; } - void Route::inc_gain (gain_t fraction, void *src) { @@ -291,21 +294,15 @@ Route::set_gain (gain_t val, void *src) */ void Route::process_output_buffers (BufferSet& bufs, - nframes_t start_frame, nframes_t end_frame, nframes_t nframes, - bool with_processors, int declick, bool meter) + nframes_t start_frame, nframes_t end_frame, nframes_t nframes, + bool with_processors, int declick) { - // This is definitely very audio-only for now - assert(_default_type == DataType::AUDIO); - ProcessorList::iterator i; - bool post_fader_work = false; bool mute_declick_applied = false; gain_t dmg, dsg, dg; - IO *co; - bool mute_audible; - bool solo_audible; bool no_monitor; - gain_t* gab = _session.gain_automation_buffer(); + + bufs.is_silent(false); switch (Config->get_monitoring_model()) { case HardwareMonitoring: @@ -317,96 +314,89 @@ Route::process_output_buffers (BufferSet& bufs, } declick = _pending_declick; - - { - Glib::Mutex::Lock cm (_control_outs_lock, Glib::TRY_LOCK); - - if (cm.locked()) { - co = _control_outs; - } else { - co = 0; - } - } + const bool recording_without_monitoring = no_monitor && record_enabled() + && (!Config->get_auto_input() || _session.actively_recording()); + + + /* ------------------------------------------------------------------------------------------- + SET UP GAIN (FADER) + ----------------------------------------------------------------------------------------- */ + { Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK); if (dm.locked()) { dmg = desired_mute_gain; dsg = desired_solo_gain; - dg = _desired_gain; + dg = _gain_control->user_float(); } else { dmg = mute_gain; dsg = solo_gain; dg = _gain; } } + + // apply gain at the amp if... + _amp->apply_gain( + // we're not recording + !(record_enabled() && _session.actively_recording()) + // or (we are recording, and) software monitoring is required + || Config->get_monitoring_model() == SoftwareMonitoring); + + // mute at the amp if... + _amp->apply_mute( + !_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader, + mute_gain, dmg); + + _amp->set_gain (_gain, dg); + + + /* ------------------------------------------------------------------------------------------- + SET UP CONTROL OUTPUTS + ----------------------------------------------------------------------------------------- */ + + boost::shared_ptr<ControlOutputs> co = _control_outs; + if (co) { + // deliver control outputs unless we're ... + co->deliver (!( + dsg == 0 || // muted by solo of another track + (dmg == 0 && _mute_affects_control_outs) || // or muted by mute of this track + !recording_without_monitoring )); // or rec-enabled w/o s/w monitoring + } + /* ------------------------------------------------------------------------------------------- GLOBAL DECLICK (for transport changes etc.) ----------------------------------------------------------------------------------------- */ if (declick > 0) { - Amp::run_in_place (bufs, nframes, 0.0, 1.0, false); + Amp::apply_gain (bufs, nframes, 0.0, 1.0, false); _pending_declick = 0; } else if (declick < 0) { - Amp::run_in_place (bufs, nframes, 1.0, 0.0, false); + Amp::apply_gain (bufs, nframes, 1.0, 0.0, false); _pending_declick = 0; - } else { - - /* no global declick */ - + } else { // no global declick if (solo_gain != dsg) { - Amp::run_in_place (bufs, nframes, solo_gain, dsg, false); + Amp::apply_gain (bufs, nframes, solo_gain, dsg, false); solo_gain = dsg; } } /* ------------------------------------------------------------------------------------------- - INPUT METERING & MONITORING + PRE-FADER MUTING ----------------------------------------------------------------------------------------- */ - if (meter && (_meter_point == MeterInput)) { - _meter->run_in_place(bufs, start_frame, end_frame, nframes); - } - if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); + Amp::apply_gain (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } + if (mute_gain == 0.0f && dmg == 0.0f) { + bufs.is_silent(true); + } - if ((_meter_point == MeterInput) && co) { - - solo_audible = dsg > 0; - mute_audible = dmg > 0;// || !_mute_affects_pre_fader; - - if ( // muted by solo of another track - - !solo_audible || - - // muted by mute of this track - - !mute_audible || - - // rec-enabled but not s/w monitoring - - // TODO: this is probably wrong - - ( no_monitor && record_enabled() - && (!Config->get_auto_input() || _session.actively_recording()) ) - - ) { - - co->silence (nframes); - - } else { - - co->deliver_output (bufs, start_frame, end_frame, nframes); - - } - } /* ------------------------------------------------------------------------------------------- DENORMAL CONTROL @@ -423,300 +413,78 @@ Route::process_output_buffers (BufferSet& bufs, } } + /* ------------------------------------------------------------------------------------------- - PRE-FADER REDIRECTS + PROCESSORS (including Amp (fader) and Meter) ----------------------------------------------------------------------------------------- */ if (with_processors) { Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); if (rm.locked()) { - if (mute_gain > 0 || !_mute_affects_pre_fader) { + //if (!bufs.is_silent()) { for (i = _processors.begin(); i != _processors.end(); ++i) { - switch ((*i)->placement()) { - case PreFader: - (*i)->run_in_place (bufs, start_frame, end_frame, nframes); - break; - case PostFader: - post_fader_work = true; - break; - } + bufs.set_count(ChanCount::max(bufs.count(), (*i)->input_streams())); + (*i)->run_in_place (bufs, start_frame, end_frame, nframes); + bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams())); } - } else { + /*} else { for (i = _processors.begin(); i != _processors.end(); ++i) { - switch ((*i)->placement()) { - case PreFader: - (*i)->silence (nframes); - break; - case PostFader: - post_fader_work = true; - break; - } + (*i)->silence (nframes); + bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams())); } - } - } - } - - /* When we entered this method, the number of bufs was set by n_process_buffers(), so - * it may be larger than required. Consider e.g a mono track with two redirects A and B. - * If A has one input and three outputs, and B three inputs and one output, n_process_buffers() - * will be 3. In this case, now we've done pre-fader redirects, we can reset the number of bufs. - */ - bufs.set_count (pre_fader_streams()); - - if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) { - Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); - mute_gain = dmg; - mute_declick_applied = true; - } - - /* ------------------------------------------------------------------------------------------- - PRE-FADER METERING & MONITORING - ----------------------------------------------------------------------------------------- */ - - if (meter && (_meter_point == MeterPreFader)) { - _meter->run_in_place(bufs, start_frame, end_frame, nframes); - } - - - if ((_meter_point == MeterPreFader) && co) { - - solo_audible = dsg > 0; - mute_audible = dmg > 0 || !_mute_affects_pre_fader; - - if ( // muted by solo of another track - - !solo_audible || - - // muted by mute of this track - - !mute_audible || - - // rec-enabled but not s/w monitoring - - ( no_monitor && record_enabled() - && (!Config->get_auto_input() || _session.actively_recording()) ) - - ) { - - co->silence (nframes); - - } else { - - co->deliver_output (bufs, start_frame, end_frame, nframes); + }*/ } - } - - /* ------------------------------------------------------------------------------------------- - GAIN STAGE - ----------------------------------------------------------------------------------------- */ - - /* if not recording or recording and requiring any monitor signal, then apply gain */ - - if ( // not recording - - !(record_enabled() && _session.actively_recording()) || - - // OR recording - - // AND software monitoring required - Config->get_monitoring_model() == SoftwareMonitoring) { - - if (apply_gain_automation) { - - if (_phase_invert) { - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - - for (nframes_t nx = 0; nx < nframes; ++nx) { - sp[nx] *= -gab[nx]; - } - } - } else { - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - - for (nframes_t nx = 0; nx < nframes; ++nx) { - sp[nx] *= gab[nx]; - } - } - } - - if (apply_gain_automation && _session.transport_rolling() && nframes > 0) { - _effective_gain = gab[nframes-1]; - } - - } else { - - /* manual (scalar) gain */ - - if (_gain != dg) { - - Amp::run_in_place (bufs, nframes, _gain, dg, _phase_invert); - _gain = dg; - - } else if (_gain != 0 && (_phase_invert || _gain != 1.0)) { - - /* no need to interpolate current gain value, - but its non-unity, so apply it. if the gain - is zero, do nothing because we'll ship silence - below. - */ - - gain_t this_gain; - - if (_phase_invert) { - this_gain = -_gain; - } else { - this_gain = _gain; - } - - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - apply_gain_to_buffer(sp,nframes,this_gain); - } - - } else if (_gain == 0) { - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - i->clear(); - } - } + if (!_processors.empty()) { + bufs.set_count(ChanCount::max(bufs.count(), _processors.back()->output_streams())); } - - } else { - - /* actively recording, no monitoring required; leave buffers as-is to save CPU cycles */ - } + /* ------------------------------------------------------------------------------------------- - POST-FADER REDIRECTS - ----------------------------------------------------------------------------------------- */ - - /* note that post_fader_work cannot be true unless with_processors was also true, - so don't test both - */ - - if (post_fader_work) { - - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); - if (rm.locked()) { - if (mute_gain > 0 || !_mute_affects_post_fader) { - for (i = _processors.begin(); i != _processors.end(); ++i) { - switch ((*i)->placement()) { - case PreFader: - break; - case PostFader: - (*i)->run_in_place (bufs, start_frame, end_frame, nframes); - break; - } - } - } else { - for (i = _processors.begin(); i != _processors.end(); ++i) { - switch ((*i)->placement()) { - case PreFader: - break; - case PostFader: - (*i)->silence (nframes); - break; - } - } - } - } - } - - if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) { - Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); - mute_gain = dmg; - mute_declick_applied = true; - } - - /* ------------------------------------------------------------------------------------------- - CONTROL OUTPUT STAGE - ----------------------------------------------------------------------------------------- */ - - if ((_meter_point == MeterPostFader) && co) { - - solo_audible = solo_gain > 0; - mute_audible = dmg > 0 || !_mute_affects_control_outs; - - if ( // silent anyway - - (_gain == 0 && !apply_gain_automation) || - - // muted by solo of another track - - !solo_audible || - - // muted by mute of this track - - !mute_audible || - - // recording but not s/w monitoring - - ( no_monitor && record_enabled() - && (!Config->get_auto_input() || _session.actively_recording()) ) - - ) { - - co->silence (nframes); - - } else { - - co->deliver_output (bufs, start_frame, end_frame, nframes); - } - } - - /* ------------------------------------------------------------------------------------------- - GLOBAL MUTE + POST-FADER MUTING ----------------------------------------------------------------------------------------- */ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) { - Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); + Amp::apply_gain (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } + if (mute_gain == 0.0f && dmg == 0.0f) { + bufs.is_silent(true); + } + /* ------------------------------------------------------------------------------------------- MAIN OUTPUT STAGE ----------------------------------------------------------------------------------------- */ - solo_audible = dsg > 0; - mute_audible = dmg > 0 || !_mute_affects_main_outs; + bool solo_audible = dsg > 0; + bool mute_audible = dmg > 0 || !_mute_affects_main_outs; if (n_outputs().get(_default_type) == 0) { /* relax */ - } else if (no_monitor && record_enabled() - && (!Config->get_auto_input() || _session.actively_recording())) { - + } else if (recording_without_monitoring) { + IO::silence (nframes); - - } else { - if ( // silent anyway + } else { - (_gain == 0 && !apply_gain_automation) || + if ( // we're silent anyway + (_gain == 0 && !_amp->apply_gain_automation()) || - // muted by solo of another track, but not using control outs for solo - + // or muted by solo of another track, but not using control outs for solo (!solo_audible && (Config->get_solo_model() != SoloBus)) || - // muted by mute of this track - + // or muted by mute of this track !mute_audible - ) { /* don't use Route::silence() here, because that causes all outputs (sends, port processors, etc. to be silent). */ - - if (_meter_point == MeterPostFader) { - peak_meter().reset(); - } - IO::silence (nframes); } else { @@ -731,321 +499,19 @@ Route::process_output_buffers (BufferSet& bufs, POST-FADER METERING ----------------------------------------------------------------------------------------- */ + /* TODO: Processor-list-ification needs to go further for this to be cleanly possible... if (meter && (_meter_point == MeterPostFader)) { if ((_gain == 0 && !apply_gain_automation) || dmg == 0) { _meter->reset(); } else { _meter->run_in_place(output_buffers(), start_frame, end_frame, nframes); } - } -} - -#ifdef NEW_POB -/** Process this route for one (sub) cycle (process thread) - * - * @param bufs Scratch buffers to use for the signal path - * @param start_frame Initial transport frame - * @param end_frame Final transport frame - * @param nframes Number of frames to output (to ports) - * - * Note that (end_frame - start_frame) may not be equal to nframes when the - * transport speed isn't 1.0 (eg varispeed). - */ -void -Route::process_output_buffers (BufferSet& bufs, - nframes_t start_frame, nframes_t end_frame, nframes_t nframes, - bool with_processors, int declick, bool meter) -{ - // This is definitely very audio-only for now - assert(_default_type == DataType::AUDIO); - - ProcessorList::iterator i; - bool post_fader_work = false; - bool mute_declick_applied = false; - gain_t dmg, dsg, dg; - IO *co; - bool mute_audible; - bool solo_audible; - bool no_monitor; - gain_t* gab = _session.gain_automation_buffer(); - - switch (Config->get_monitoring_model()) { - case HardwareMonitoring: - case ExternalMonitoring: - no_monitor = true; - break; - default: - no_monitor = false; - } - - declick = _pending_declick; - - { - Glib::Mutex::Lock cm (_control_outs_lock, Glib::TRY_LOCK); - - if (cm.locked()) { - co = _control_outs; - } else { - co = 0; - } - } - - { - Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK); - - if (dm.locked()) { - dmg = desired_mute_gain; - dsg = desired_solo_gain; - dg = _desired_gain; - } else { - dmg = mute_gain; - dsg = solo_gain; - dg = _gain; - } - } - - /* ------------------------------------------------------------------------------------------- - GLOBAL DECLICK (for transport changes etc.) - input metering & monitoring (control outs) - denormal control - pre-fader redirects - pre-fader metering & monitoring (control outs) - gain stage - post-fader redirects - global mute - main output - post-fader metering & monitoring (control outs) - ----------------------------------------------------------------------------------------- */ - - { - Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK); - for (i = processors.begin(); i != processors.end(); ++i) { - (*i)->run_in_place (bufs, start_frame, end_frame, nframes); - } - } - - - /* ------------------------------------------------------------------------------------------- - INPUT METERING & MONITORING - ----------------------------------------------------------------------------------------- */ - - if (meter && (_meter_point == MeterInput)) { - _meter->run_in_place(bufs, start_frame, end_frame, nframes); - } - - if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - Amp::run_in_place (bufs, nframes, mute_gain, dmg, false); - mute_gain = dmg; - mute_declick_applied = true; - } - - /* ------------------------------------------------------------------------------------------- - PRE-FADER REDIRECTS - ----------------------------------------------------------------------------------------- */ - - // This really should already be true... - bufs.set_count(pre_fader_streams()); - - - if ((_meter_point == MeterPreFader) && co) { - - solo_audible = dsg > 0; - mute_audible = dmg > 0 || !_mute_affects_pre_fader; - - if ( // muted by solo of another track - - !solo_audible || - - // muted by mute of this track - - !mute_audible || - - // rec-enabled but not s/w monitoring - - ( no_monitor && record_enabled() - && (!Config->get_auto_input() || _session.actively_recording()) ) - - ) { - - co->silence (nframes); - - } else { - - co->deliver_output (bufs, start_frame, end_frame, nframes); - } - } - - /* ------------------------------------------------------------------------------------------- - GAIN STAGE - ----------------------------------------------------------------------------------------- */ - - /* if not recording or recording and requiring any monitor signal, then apply gain */ - - if ( // not recording - - !(record_enabled() && _session.actively_recording()) || - - // OR recording - - // AND software monitoring required - - Config->get_monitoring_model() == SoftwareMonitoring) { - - if (apply_gain_automation) { - - if (_phase_invert) { - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - - for (nframes_t nx = 0; nx < nframes; ++nx) { - sp[nx] *= -gab[nx]; - } - } - } else { - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - - for (nframes_t nx = 0; nx < nframes; ++nx) { - sp[nx] *= gab[nx]; - } - } - } - - if (apply_gain_automation && _session.transport_rolling() && nframes > 0) { - _effective_gain = gab[nframes-1]; - } - - } else { - - /* manual (scalar) gain */ - - if (_gain != dg) { - - Amp::run_in_place (bufs, nframes, _gain, dg, _phase_invert); - _gain = dg; - - } else if (_gain != 0 && (_phase_invert || _gain != 1.0)) { - - /* no need to interpolate current gain value, - but its non-unity, so apply it. if the gain - is zero, do nothing because we'll ship silence - below. - */ - - gain_t this_gain; - - if (_phase_invert) { - this_gain = -_gain; - } else { - this_gain = _gain; - } - - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - Sample* const sp = i->data(); - apply_gain_to_buffer(sp,nframes,this_gain); - } - - } else if (_gain == 0) { - for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) { - i->clear(); - } - } - } - - } else { - - /* actively recording, no monitoring required; leave buffers as-is to save CPU cycles */ - - } - - /* ------------------------------------------------------------------------------------------- - CONTROL OUTPUT STAGE - ----------------------------------------------------------------------------------------- */ - - if ((_meter_point == MeterPostFader) && co) { - - solo_audible = solo_gain > 0; - mute_audible = dmg > 0 || !_mute_affects_control_outs; - - if ( // silent anyway - - (_gain == 0 && !apply_gain_automation) || - - // muted by solo of another track - - !solo_audible || - - // muted by mute of this track - - !mute_audible || - - // recording but not s/w monitoring - - (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) + }*/ - ) { - - co->silence (nframes); - - } else { - - co->deliver_output (bufs, start_frame, end_frame, nframes); - } - } - - /* ------------------------------------------------------------------------------------------- - MAIN OUTPUT STAGE - ----------------------------------------------------------------------------------------- */ - - solo_audible = dsg > 0; - mute_audible = dmg > 0 || !_mute_affects_main_outs; - - if (n_outputs().get(_default_type) == 0) { - - /* relax */ - - } else if (no_monitor && record_enabled() - && (!Config->get_auto_input() || _session.actively_recording())) { - - IO::silence (nframes); - - } else { - - if ( // silent anyway - - (_gain == 0 && !apply_gain_automation) || - - // muted by solo of another track, but not using control outs for solo - - (!solo_audible && (Config->get_solo_model() != SoloBus)) || - - // muted by mute of this track - - !mute_audible - - ) { - - /* don't use Route::silence() here, because that causes - all outputs (sends, port processors, etc. to be silent). - */ - - if (_meter_point == MeterPostFader) { - peak_meter().reset(); - } - - IO::silence (nframes); - - } else { - - deliver_output(bufs, start_frame, end_frame, nframes); - - } - - } + // at this point we've reached the desired mute gain regardless + mute_gain = dmg; } -#endif /* NEW_POB */ - ChanCount Route::n_process_buffers () { @@ -1061,7 +527,7 @@ Route::setup_peak_meters() } void -Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, int declick, bool meter_first) +Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, int declick) { BufferSet& bufs = _session.get_scratch_buffers(n_process_buffers()); @@ -1069,20 +535,13 @@ Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, collect_input (bufs, nframes); - if (meter_first) { - _meter->run_in_place(bufs, start_frame, end_frame, nframes); - meter_first = false; - } else { - meter_first = true; - } - - process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, meter_first); + process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick); } void -Route::passthru_silence (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, int declick, bool meter) +Route::passthru_silence (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, int declick) { - process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick, meter); + process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick); } void @@ -1114,7 +573,6 @@ Route::catch_up_on_solo_mute_override () } { - Glib::Mutex::Lock lm (declick_lock); if (_muted) { @@ -1166,7 +624,7 @@ Route::set_mute (bool yn, void *src) Glib::Mutex::Lock lm (declick_lock); - if (_soloed && Config->get_solo_mute_override()){ + if (_soloed && Config->get_solo_mute_override()) { desired_mute_gain = 1.0f; } else { desired_mute_gain = (yn?0.0f:1.0f); @@ -1174,8 +632,24 @@ Route::set_mute (bool yn, void *src) } } +static void +dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs) +{ + cerr << name << " {" << endl; + for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin(); + p != procs.end(); ++p) { + cerr << "\t" << (*p)->sort_key() << ": " << (*p)->name() << endl; + } + cerr << "}" << endl; +} + +/** Add a processor to the route. + * If @a iter is not NULL, it must point to an iterator in _processors and the new + * processor will be inserted immediately before this location. Otherwise, + * @a position is used. + */ int -Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err) +Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, ProcessorList::iterator* iter, Placement placement) { ChanCount old_pms = processor_max_streams; @@ -1189,18 +663,66 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* boost::shared_ptr<PluginInsert> pi; boost::shared_ptr<PortInsert> porti; - //processor->set_default_type(_default_type); + ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor); + + if (processor == _amp || processor == _meter) { + // Ensure only one amp and one meter are in the list at any time + if (loc != _processors.end()) { + if (iter) { + if (*iter == loc) { // Already in place, do nothing + return 0; + } else { // New position given, relocate + _processors.erase(loc); + } + } else { // Insert at end + _processors.erase(loc); + loc = _processors.end(); + } + } + + } else { + if (loc != _processors.end()) { + cerr << "ERROR: Processor added to route twice!" << endl; + return 1; + } + } + + // Use position given by user + if (iter) { + loc = *iter; + + // Insert immediately before the amp + } else if (placement == PreFader) { + loc = find(_processors.begin(), _processors.end(), _amp); + + // Insert at end + } else { + loc = _processors.end(); + } + + // Update sort keys + if (loc == _processors.end()) { + processor->set_sort_key(_processors.size()); + } else { + processor->set_sort_key((*loc)->sort_key()); + for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) { + (*p)->set_sort_key((*p)->sort_key() + 1); + } + } - _processors.push_back (processor); + _processors.insert(loc, processor); // Set up processor list channels. This will set processor->[input|output]_streams(), // configure redirect ports properly, etc. - if (_reset_processor_counts (err)) { - _processors.pop_back (); - _reset_processor_counts (0); // it worked before we tried to add it ... + if (configure_processors_unlocked (err)) { + dump_processors(_name, _processors); + ProcessorList::iterator ploc = loc; + --ploc; + _processors.erase(ploc); + configure_processors_unlocked (0); // it worked before we tried to add it ... return -1; } - + if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) { if (pi->natural_input_streams() == ChanCount::ZERO) { @@ -1211,13 +733,8 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* } // Ensure peak vector sizes before the plugin is activated - - ChanCount potential_max_streams; - - potential_max_streams.set (DataType::AUDIO, max (processor->input_streams().n_audio(), - processor->output_streams().n_audio())); - potential_max_streams.set (DataType::MIDI, max (processor->input_streams().n_midi(), - processor->output_streams().n_midi())); + ChanCount potential_max_streams = ChanCount::max( + processor->input_streams(), processor->output_streams()); _meter->configure_io (potential_max_streams, potential_max_streams); @@ -1232,13 +749,15 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* reset_panner (); } + dump_processors (_name, _processors); processors_changed (); /* EMIT SIGNAL */ + return 0; } int -Route::add_processors (const ProcessorList& others, ProcessorStreams* err) +Route::add_processors (const ProcessorList& others, ProcessorStreams* err, Placement placement) { /* NOTE: this is intended to be used ONLY when copying processors from another Route. Hence the subtle @@ -1257,10 +776,18 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err) ProcessorList::iterator existing_end = _processors.end(); --existing_end; - ChanCount potential_max_streams; + ChanCount potential_max_streams = ChanCount::max(input_minimum(), output_minimum()); for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { + // Ensure meter only appears in the list once + if (*i == _meter) { + ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i); + if (m != _processors.end()) { + _processors.erase(m); + } + } + boost::shared_ptr<PluginInsert> pi; if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) { @@ -1274,12 +801,16 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err) // Ensure peak vector sizes before the plugin is activated _meter->configure_io (potential_max_streams, potential_max_streams); - _processors.push_back (*i); + ProcessorList::iterator loc = (placement == PreFader) + ? find(_processors.begin(), _processors.end(), _amp) + : _processors.end(); + + _processors.insert (loc, *i); - if (_reset_processor_counts (err)) { + if (configure_processors_unlocked (err)) { ++existing_end; _processors.erase (existing_end, _processors.end()); - _reset_processor_counts (0); // it worked before we tried to add it ... + configure_processors_unlocked (0); // it worked before we tried to add it ... return -1; } @@ -1292,11 +823,26 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err) if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) { reset_panner (); } - + + dump_processors (_name, _processors); processors_changed (); /* EMIT SIGNAL */ + return 0; } +void +Route::placement_range(Placement p, ProcessorList::iterator& start, ProcessorList::iterator& end) +{ + if (p == PreFader) { + start = _processors.begin(); + end = find(_processors.begin(), _processors.end(), _amp); + } else { + start = find(_processors.begin(), _processors.end(), _amp); + ++start; + end = _processors.end(); + } +} + /** Turn off all processors with a given placement * @param p Placement of processors to disable */ @@ -1305,10 +851,11 @@ Route::disable_processors (Placement p) { Glib::RWLock::ReaderLock lm (_processor_lock); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->placement() == p) { - (*i)->deactivate (); - } + ProcessorList::iterator start, end; + placement_range(p, start, end); + + for (ProcessorList::iterator i = start; i != end; ++i) { + (*i)->deactivate (); } _session.set_dirty (); @@ -1336,8 +883,11 @@ Route::disable_plugins (Placement p) { Glib::RWLock::ReaderLock lm (_processor_lock); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (boost::dynamic_pointer_cast<PluginInsert> (*i) && (*i)->placement() == p) { + ProcessorList::iterator start, end; + placement_range(p, start, end); + + for (ProcessorList::iterator i = start; i != end; ++i) { + if (boost::dynamic_pointer_cast<PluginInsert> (*i)) { (*i)->deactivate (); } } @@ -1417,7 +967,10 @@ Route::pre_fader_streams() const /* Find the last pre-fader redirect that isn't a send; sends don't affect the number * of streams. */ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->placement() == PreFader && boost::dynamic_pointer_cast<Send> (*i) == 0) { + if ((*i) == _amp) { + break; + } + if (boost::dynamic_pointer_cast<Send> (*i) == 0) { processor = *i; } } @@ -1441,21 +994,38 @@ Route::clear_processors (Placement p) if (!_session.engine().connected()) { return; } + + bool already_deleting = _session.deletion_in_progress(); + if (!already_deleting) { + _session.set_deletion_in_progress(); + } { Glib::RWLock::WriterLock lm (_processor_lock); ProcessorList new_list; - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->placement() == p) { - /* it's the placement we want to get rid of */ + ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp); + if (p == PreFader) { + // Get rid of PreFader processors + for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) { (*i)->drop_references (); - } else { - /* it's a different placement, so keep it */ + } + // Keep the rest + for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) { + new_list.push_back (*i); + } + } else { + // Keep PreFader processors + for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) { new_list.push_back (*i); } + new_list.push_back (_amp); + // Get rid of PostFader processors + for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) { + (*i)->drop_references (); + } } - + _processors = new_list; } @@ -1467,11 +1037,19 @@ Route::clear_processors (Placement p) processor_max_streams.reset(); _have_internal_generator = false; processors_changed (); /* EMIT SIGNAL */ + + if (!already_deleting) { + _session.clear_deletion_in_progress(); + } } int Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err) { + if (processor == _amp || processor == _meter) { + return 0; + } + ChanCount old_pms = processor_max_streams; if (!_session.engine().connected()) { @@ -1490,7 +1068,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream ProcessorList::iterator tmp; - /* move along, see failure case for reset_processor_counts() + /* move along, see failure case for configure_processors() where we may need to reprocessor the processor. */ @@ -1524,11 +1102,11 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream return 1; } - if (_reset_processor_counts (err)) { + if (configure_processors_unlocked (err)) { /* get back to where we where */ _processors.insert (i, processor); /* we know this will work, because it worked before :) */ - _reset_processor_counts (0); + configure_processors_unlocked (0); return -1; } @@ -1551,224 +1129,72 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream } processor->drop_references (); - + + dump_processors (_name, _processors); processors_changed (); /* EMIT SIGNAL */ - return 0; -} -int -Route::reset_processor_counts (ProcessorStreams* err) -{ - Glib::RWLock::WriterLock lm (_processor_lock); - return _reset_processor_counts (err); + return 0; } - int -Route::_reset_processor_counts (ProcessorStreams* err) -{ - ProcessorList::iterator r; - uint32_t insert_cnt = 0; - uint32_t send_cnt = 0; - map<Placement,list<ProcessorCount> > proc_map; - ProcessorList::iterator prev; - ChanCount initial_streams = n_inputs (); - ChanCount previous_initial_streams = n_inputs (); - int ret = -1; - uint32_t max_audio = 0; - uint32_t max_midi = 0; - - processor_max_streams.reset (); - - /* Step 1: build a map that links each insert to an in/out channel count - - Divide inserts up by placement so we get the signal flow - properly modelled. we need to do this because the _processors - list is not sorted by placement, and because other reasons may - exist now or in the future for this separate treatment. - */ - - /* ... but it should/will be... */ - - for (r = _processors.begin(); r != _processors.end(); ++r) { - - boost::shared_ptr<PluginInsert> plugin_insert; - boost::shared_ptr<PortInsert> port_insert; - - if ((plugin_insert = boost::dynamic_pointer_cast<PluginInsert>(*r)) != 0) { - - ++insert_cnt; - proc_map[(*r)->placement()].push_back (ProcessorCount (*r)); - - /* reset plugin counts back to one for now so - that we have a predictable, controlled - state to try to configure. - */ - - plugin_insert->set_count (1); - - } else if ((port_insert = boost::dynamic_pointer_cast<PortInsert>(*r)) != 0) { - - ++insert_cnt; - proc_map[(*r)->placement()].push_back (ProcessorCount (*r)); - - } else if (boost::dynamic_pointer_cast<Send> (*r) != 0) { - ++send_cnt; - } - } - - if (insert_cnt == 0) { - if (send_cnt) { - goto recompute; - } else { - ret = 0; - goto streamcount; - } - } - - /* Now process each placement in order, checking to see if we - can really do what has been requested. - */ - - /* A: PreFader */ - - if (check_some_processor_counts (proc_map[PreFader], n_inputs (), err)) { - goto streamcount; - } - - if (!proc_map[PreFader].empty()) { - previous_initial_streams = n_inputs (); - for (list<ProcessorCount>::iterator i = proc_map[PreFader].begin(); i != proc_map[PreFader].end(); i++) { - if (i->processor->can_support_io_configuration (previous_initial_streams, initial_streams) == false) { - goto streamcount; - } - previous_initial_streams = initial_streams; - } - } - - /* B: PostFader */ - - if (check_some_processor_counts (proc_map[PostFader], initial_streams, err)) { - goto streamcount; - } - - if (!proc_map[PostFader].empty()) { - for (list<ProcessorCount>::iterator i = proc_map[PostFader].begin(); i != proc_map[PostFader].end(); i++) { - if (i->processor->can_support_io_configuration (previous_initial_streams, initial_streams) == false) { - goto streamcount; - } - previous_initial_streams = initial_streams; - } - } - - /* OK, everything can be set up correctly, so lets do it */ - - apply_some_processor_counts (proc_map[PreFader]); - apply_some_processor_counts (proc_map[PostFader]); - - /* recompute max outs of any processor */ - - ret = 0; - - recompute: - - processor_max_streams.reset (); - prev = _processors.end(); - - for (r = _processors.begin(); r != _processors.end(); prev = r, ++r) { - boost::shared_ptr<Send> s; - - if ((s = boost::dynamic_pointer_cast<Send> (*r)) != 0) { - - /* don't pay any attention to send output configuration, since it doesn't - affect the route. - */ - - if (r == _processors.begin()) { - s->expect_inputs (n_inputs()); - } else { - s->expect_inputs ((*prev)->output_streams()); - } - - } else { - - max_audio = max ((*r)->input_streams ().n_audio(), max_audio); - max_midi = max ((*r)->input_streams ().n_midi(), max_midi); - max_audio = max ((*r)->output_streams ().n_audio(), max_audio); - max_midi = max ((*r)->output_streams ().n_midi(), max_midi); - } - } - - processor_max_streams.set (DataType::AUDIO, max_audio); - processor_max_streams.set (DataType::MIDI, max_midi); - - /* we're done */ - return 0; - - streamcount: - for (r = _processors.begin(); r != _processors.end(); ++r) { - max_audio = max ((*r)->output_streams ().n_audio(), max_audio); - max_midi = max ((*r)->output_streams ().n_midi(), max_midi); - } - - processor_max_streams.set (DataType::AUDIO, max_audio); - processor_max_streams.set (DataType::MIDI, max_midi); - - return ret; -} - -int32_t -Route::apply_some_processor_counts (list<ProcessorCount>& iclist) +Route::configure_processors (ProcessorStreams* err) { - list<ProcessorCount>::iterator i; - - for (i = iclist.begin(); i != iclist.end(); ++i) { - - ProcessorCount& pc (*i); - - if (pc.processor->configure_io (pc.in, pc.out)) { - return -1; - } - - /* make sure that however many we have, they are all active */ - - pc.processor->activate (); + if (!_in_configure_processors) { + Glib::RWLock::WriterLock lm (_processor_lock); + return configure_processors_unlocked (err); } - return 0; } -/** Returns whether \a iclist can be configured and run starting with - * \a required_inputs at the first processor's inputs. - * If false is returned, \a iclist can not be run with \a required_inputs, and \a err is set. - * Otherwise, \a err is set to the output of the list. +/** Configure the input/output configuration of each processor in the processors list. + * Return 0 on success, otherwise configuration is impossible. */ -bool -Route::check_some_processor_counts (list<ProcessorCount>& iclist, ChanCount required_inputs, ProcessorStreams* err) +int +Route::configure_processors_unlocked (ProcessorStreams* err) { - list<ProcessorCount>::iterator i; - size_t index = 0; - - if (err) { - err->index = 0; - err->count = required_inputs; + if (_in_configure_processors) { + return 0; } - for (i = iclist.begin(); i != iclist.end(); ++i, ++index) { + _in_configure_processors = true; - if (!(*i).processor->can_support_io_configuration (required_inputs, (*i).out)) { + // Check each processor in order to see if we can configure as requested + ChanCount in = _configured_inputs; + ChanCount out; + list< pair<ChanCount,ChanCount> > configuration; + uint32_t index = 0; + for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) { + (*p)->set_sort_key(index); + if ((*p)->can_support_io_configuration(in, out)) { + configuration.push_back(make_pair(in, out)); + in = out; + } else { if (err) { err->index = index; - err->count = required_inputs; + err->count = in; } - return true; + _in_configure_processors = false; + return -1; } - - (*i).in = required_inputs; - required_inputs = (*i).out; + } + + // We can, so configure everything + list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin(); + for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) { + (*p)->configure_io(c->first, c->second); + (*p)->activate(); + processor_max_streams = ChanCount::max(processor_max_streams, c->first); + processor_max_streams = ChanCount::max(processor_max_streams, c->second); + out = c->second; } - return false; + // Ensure route outputs match last processor's outputs + if (out != n_outputs()) { + ensure_io(_configured_inputs, out, false, this); + } + + _in_configure_processors = false; + return 0; } void @@ -1805,9 +1231,16 @@ Route::all_processors_active (Placement p, bool state) if (_processors.empty()) { return; } + ProcessorList::iterator start, end; + placement_range(p, start, end); + bool before_amp = true; for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->placement() == p) { + if ((*i) == _amp) { + before_amp = false; + continue; + } + if (p == PreFader && before_amp) { if (state) { (*i)->activate (); } else { @@ -1839,7 +1272,7 @@ Route::sort_processors (ProcessorStreams* err) _processors.sort (comparator); - if (_reset_processor_counts (err)) { + if (configure_processors_unlocked (err)) { _processors = as_it_was_before; processor_max_streams = old_pms; return -1; @@ -1925,7 +1358,7 @@ Route::state(bool full_state) if (_control_outs) { XMLNode* cnode = new XMLNode (X_("ControlOuts")); - cnode->add_child_nocopy (_control_outs->state (full_state)); + cnode->add_child_nocopy (_control_outs->io()->state (full_state)); node->add_child_nocopy (*cnode); } @@ -2033,8 +1466,8 @@ Route::set_deferred_state () deferred_state = 0; } -void -Route::add_processor_from_xml (const XMLNode& node) +bool +Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator* iter) { const XMLProperty *prop; @@ -2043,15 +1476,15 @@ Route::add_processor_from_xml (const XMLNode& node) try { boost::shared_ptr<Send> send (new Send (_session, node)); - add_processor (send); + add_processor (send, 0, iter); + return true; } catch (failed_constructor &err) { error << _("Send construction failed") << endmsg; - return; + return false; } - // use "Processor" in XML? } else if (node.name() == "Processor") { try { @@ -2076,13 +1509,21 @@ Route::add_processor_from_xml (const XMLNode& node) processor.reset (new Send (_session, node)); have_insert = true; + + } else if (prop->value() == "meter") { + + processor = _meter; + + } else if (prop->value() == "amp") { + + processor = _amp; } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; } - add_processor (processor); + return (add_processor (processor, 0, iter) == 0); } else { error << _("Processor XML node has no type property") << endmsg; @@ -2091,9 +1532,10 @@ Route::add_processor_from_xml (const XMLNode& node) catch (failed_constructor &err) { warning << _("processor could not be created. Ignored.") << endmsg; - return; + return false; } } + return false; } int @@ -2238,6 +1680,7 @@ Route::_set_state (const XMLNode& node, bool call_base) } XMLNodeList processor_nodes; + bool has_meter_processor = false; // legacy sessions don't for (niter = nlist.begin(); niter != nlist.end(); ++niter){ @@ -2245,12 +1688,18 @@ Route::_set_state (const XMLNode& node, bool call_base) if (child->name() == X_("Send") || child->name() == X_("Processor")) { processor_nodes.push_back(child); + if ((prop = child->property (X_("type"))) != 0 && prop->value() == "meter") { + has_meter_processor = true; + } } } _set_processor_states(processor_nodes); - + if (!has_meter_processor) { + set_meter_point(_meter_point, NULL); + } + processors_changed (); for (niter = nlist.begin(); niter != nlist.end(); ++niter){ child = *niter; @@ -2267,8 +1716,8 @@ Route::_set_state (const XMLNode& node, bool call_base) string coutname = _name; coutname += _("[control]"); - delete _control_outs; - _control_outs = new IO (_session, coutname); + _control_outs = boost::shared_ptr<ControlOutputs> ( + new ControlOutputs (_session, new IO (_session, coutname))); /* fix up the control out name in the XML before setting it. Otherwise track templates don't work because the control @@ -2281,7 +1730,9 @@ Route::_set_state (const XMLNode& node, bool call_base) prop->set_value (coutname); } - _control_outs->set_state (**(child->children().begin())); + _control_outs->io()->set_state (**(child->children().begin())); + _control_outs->set_sort_key (_meter->sort_key() + 1); + add_processor (_control_outs, 0); } else if (child->name() == X_("Comment")) { @@ -2303,8 +1754,7 @@ Route::_set_state (const XMLNode& node, bool call_base) _mute_control->set_state (*child); _session.add_controllable (_mute_control); } - } - else if (child->name() == X_("RemoteControl")) { + } else if (child->name() == X_("RemoteControl")) { if ((prop = child->property (X_("id"))) != 0) { int32_t x; sscanf (prop->value().c_str(), "%d", &x); @@ -2365,6 +1815,7 @@ Route::_set_processor_states(const XMLNodeList &nlist) i = tmp; } + Placement placement = PreFader; // Iterate through state list and make sure all processors are on the track and in the correct order, // set the state of existing processors according to the new state on the same go @@ -2389,50 +1840,33 @@ Route::_set_processor_states(const XMLNodeList &nlist) ++o; } + // If the processor (*niter) is not on the route, + // create it and move it to the correct location if (o == _processors.end()) { - // If the processor (*niter) is not on the route, we need to create it - // and move it to the correct location - - ProcessorList::iterator prev_last = _processors.end(); - --prev_last; // We need this to check whether adding succeeded - - add_processor_from_xml (**niter); - - ProcessorList::iterator last = _processors.end(); - --last; + if (add_processor_from_xml (**niter, &i)) { + --i; // move iterator to the newly inserted processor + } else { + cerr << "Error restoring route: unable to restore processor" << endl; + } - if (prev_last == last) { - cerr << "Could not fully restore state as some processors were not possible to create" << endl; - continue; + // Otherwise, we found the processor (*niter) on the route, + // ensure it is at the location provided in the XML state + } else { + if (i != o) { + boost::shared_ptr<Processor> tmp = (*o); + _processors.erase(o); // remove the old copy + _processors.insert(i, tmp); // insert the processor at the correct location + --i; // move iterator to the correct processor } - boost::shared_ptr<Processor> tmp = (*last); - // remove the processor from the wrong location - _processors.erase(last); - // processor the new processor at the current location - _processors.insert(i, tmp); - - --i; // move pointer to the newly processored processor - continue; + (*i)->set_state((**niter)); } - // We found the processor (*niter) on the route, first we must make sure the processor - // is at the location provided in the XML state - if (i != o) { - boost::shared_ptr<Processor> tmp = (*o); - // remove the old copy - _processors.erase(o); - // processor the processor at the correct location - _processors.insert(i, tmp); - - --i; // move pointer so it points to the right processor + if (*i == _amp) { + placement = PostFader; } - - (*i)->set_state( (**niter) ); } - - processors_changed (); } void @@ -2450,7 +1884,7 @@ Route::silence (nframes_t nframes) IO::silence (nframes); if (_control_outs) { - _control_outs->silence (nframes); + _control_outs->io()->silence (nframes); } { @@ -2479,13 +1913,8 @@ Route::silence (nframes_t nframes) int Route::set_control_outs (const vector<string>& ports) { - Glib::Mutex::Lock lm (_control_outs_lock); vector<string>::const_iterator i; - size_t limit; - delete _control_outs; - _control_outs = 0; - if (is_control() || is_master()) { /* no control outs for these two special busses */ return 0; @@ -2498,28 +1927,31 @@ Route::set_control_outs (const vector<string>& ports) string coutname = _name; coutname += _("[control]"); - _control_outs = new IO (_session, coutname); + IO* out_io = new IO (_session, coutname); + boost::shared_ptr<ControlOutputs> out_proc(new ControlOutputs (_session, out_io)); - /* our control outs need as many outputs as we - have audio outputs. we track the changes in ::output_change_handler(). - */ - - // XXX its stupid that we have to get this value twice - - limit = n_outputs().n_audio(); - - if (_control_outs->ensure_io (ChanCount::ZERO, ChanCount (DataType::AUDIO, n_outputs().get (DataType::AUDIO)), true, this)) { + /* As an IO, our control outs need as many IO outputs as we have outputs + * (we track the changes in ::output_change_handler()). + * As a processor, the control outs is an identity processor + * (i.e. it does not modify its input buffers whatsoever) + */ + if (out_io->ensure_io (ChanCount::ZERO, n_outputs(), true, this)) { return -1; } - + /* now connect to the named ports */ - for (size_t n = 0; n < limit; ++n) { - if (_control_outs->connect_output (_control_outs->output (n), ports[n % ports.size()], this)) { - error << string_compose (_("could not connect %1 to %2"), _control_outs->output(n)->name(), ports[n]) << endmsg; + for (size_t n = 0; n < n_outputs().n_total(); ++n) { + if (out_io->connect_output (out_io->output (n), ports[n % ports.size()], this)) { + error << string_compose (_("could not connect %1 to %2"), + out_io->output(n)->name(), ports[n]) << endmsg; return -1; } } + + _control_outs = out_proc; + _control_outs->set_sort_key (_meter->sort_key() + 1); + add_processor (_control_outs, NULL); return 0; } @@ -2631,11 +2063,11 @@ Route::feeds (boost::shared_ptr<Route> other) if (_control_outs) { - no = _control_outs->n_outputs().n_total(); + no = _control_outs->io()->n_outputs().n_total(); for (i = 0; i < no; ++i) { for (j = 0; j < ni; ++j) { - if (_control_outs->output(i)->connected_to (other->input (j)->name())) { + if (_control_outs->io()->output(i)->connected_to (other->input (j)->name())) { return true; } } @@ -2651,22 +2083,22 @@ Route::set_mute_config (mute_type t, bool onoff, void *src) switch (t) { case PRE_FADER: _mute_affects_pre_fader = onoff; - pre_fader_changed(src); /* EMIT SIGNAL */ + pre_fader_changed(src); /* EMIT SIGNAL */ break; case POST_FADER: _mute_affects_post_fader = onoff; - post_fader_changed(src); /* EMIT SIGNAL */ + post_fader_changed(src); /* EMIT SIGNAL */ break; case CONTROL_OUTS: _mute_affects_control_outs = onoff; - control_outs_changed(src); /* EMIT SIGNAL */ + control_outs_changed(src); /* EMIT SIGNAL */ break; case MAIN_OUTS: _mute_affects_main_outs = onoff; - main_outs_changed(src); /* EMIT SIGNAL */ + main_outs_changed(src); /* EMIT SIGNAL */ break; } } @@ -2723,22 +2155,22 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f } void -Route::input_change_handler (IOChange change, void *ignored) +Route::input_change_handler (IOChange change, void *src) { - if (change & ConfigurationChanged) { - reset_processor_counts (0); + if ((change & ConfigurationChanged)) { + configure_processors (0); } } void -Route::output_change_handler (IOChange change, void *ignored) +Route::output_change_handler (IOChange change, void *src) { - if (change & ConfigurationChanged) { + if ((change & ConfigurationChanged)) { if (_control_outs) { - _control_outs->ensure_io (ChanCount::ZERO, ChanCount(DataType::AUDIO, n_outputs().n_audio()), true, this); + _control_outs->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this); } - reset_processor_counts (0); + configure_processors (0); } } @@ -2765,10 +2197,10 @@ Route::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, return 0; } - apply_gain_automation = false; + _amp->apply_gain_automation(false); - if (n_inputs().n_total()) { - passthru (start_frame, end_frame, nframes, 0, false); + if (n_inputs() != ChanCount::ZERO) { + passthru (start_frame, end_frame, nframes, 0); } else { silence (nframes); } @@ -2829,7 +2261,7 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int _silent = false; - apply_gain_automation = false; + _amp->apply_gain_automation(false); { Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK); @@ -2837,13 +2269,14 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int if (am.locked() && _session.transport_rolling()) { if (_gain_control->automation_playback()) { - apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector ( - start_frame, end_frame, _session.gain_automation_buffer(), nframes); + _amp->apply_gain_automation( + _gain_control->list()->curve().rt_safe_get_vector ( + start_frame, end_frame, _session.gain_automation_buffer(), nframes)); } } } - passthru (start_frame, end_frame, nframes, declick, false); + passthru (start_frame, end_frame, nframes, declick); return 0; } @@ -2912,7 +2345,35 @@ Route::set_meter_point (MeterPoint p, void *src) { if (_meter_point != p) { _meter_point = p; + + // Move meter in the processors list + ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter); + _processors.erase(loc); + switch (p) { + case MeterInput: + loc = _processors.begin(); + break; + case MeterPreFader: + loc = find(_processors.begin(), _processors.end(), _amp); + break; + case MeterPostFader: + loc = _processors.end(); + break; + } + _processors.insert(loc, _meter); + + // Update sort key + if (loc == _processors.end()) { + _meter->set_sort_key(_processors.size()); + } else { + _meter->set_sort_key((*loc)->sort_key()); + for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) { + (*p)->set_sort_key((*p)->sort_key() + 1); + } + } + meter_change (src); /* EMIT SIGNAL */ + processors_changed (); /* EMIT SIGNAL */ _session.set_dirty (); } } @@ -3054,6 +2515,7 @@ Route::set_block_size (nframes_t nframes) for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->set_block_size (nframes); } + _session.ensure_buffers(processor_max_streams); } void diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index ef6f954dd2..9246db9458 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -35,15 +35,15 @@ using namespace ARDOUR; using namespace PBD; -Send::Send (Session& s, Placement p) - : IOProcessor (s, string_compose (_("send %1"), (bitslot = s.next_send_id()) + 1), p) +Send::Send (Session& s) + : IOProcessor (s, string_compose (_("send %1"), (bitslot = s.next_send_id()) + 1)) { _metering = false; ProcessorCreated (this); /* EMIT SIGNAL */ } Send::Send (Session& s, const XMLNode& node) - : IOProcessor (s, "send", PreFader) + : IOProcessor (s, "send") { _metering = false; @@ -183,10 +183,10 @@ Send::configure_io (ChanCount in, ChanCount out) return false; } - _io->set_output_maximum (in); + /*_io->set_output_maximum (in); _io->set_output_minimum (in); _io->set_input_maximum (ChanCount::ZERO); - _io->set_input_minimum (ChanCount::ZERO); + _io->set_input_minimum (ChanCount::ZERO);*/ if (_io->ensure_io (ChanCount::ZERO, in, false, this) != 0) { return false; diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 0297aa76bb..db66c2dc30 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -112,6 +112,9 @@ Session::Session (AudioEngine &eng, string mix_template) : _engine (eng), + phi (0), + target_phi (0), + phase (0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -196,6 +199,9 @@ Session::Session (AudioEngine &eng, nframes_t initial_length) : _engine (eng), + phi (0), + target_phi (0), + phase (0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -264,14 +270,18 @@ Session::Session (AudioEngine &eng, int control_id = 1; if (control_out_channels) { - shared_ptr<Route> r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut)); + ChanCount count(DataType::AUDIO, control_out_channels); + shared_ptr<Route> r (new Route (*this, _("monitor"), Route::ControlOut, + DataType::AUDIO, count, count)); r->set_remote_control_id (control_id++); rl.push_back (r); } if (master_out_channels) { - shared_ptr<Route> r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut)); + ChanCount count(DataType::AUDIO, master_out_channels); + shared_ptr<Route> r (new Route (*this, _("master"), Route::MasterOut, + DataType::AUDIO, count, count)); r->set_remote_control_id (control_id); rl.push_back (r); @@ -543,7 +553,7 @@ Session::when_engine_running () try { XMLNode* child = 0; - _click_io.reset (new ClickIO (*this, "click", 0, 0, -1, -1)); + _click_io.reset (new ClickIO (*this, "click")); if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) { @@ -656,36 +666,20 @@ Session::when_engine_running () /* create master/control ports */ if (_master_out) { - uint32_t n; - /* force the master to ignore any later call to this */ - if (_master_out->pending_state_node) { _master_out->ports_became_legal(); } /* no panner resets till we are through */ - _master_out->defer_pan_reset (); - while (_master_out->n_inputs().n_audio() - < _master_out->input_maximum().n_audio()) { - if (_master_out->add_input_port ("", this, DataType::AUDIO)) { - error << _("cannot setup master inputs") - << endmsg; - break; - } - } - n = 0; - while (_master_out->n_outputs().n_audio() - < _master_out->output_maximum().n_audio()) { - if (_master_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this, DataType::AUDIO)) { - error << _("cannot setup master outputs") - << endmsg; - break; - } - n++; - } + /* create ports */ + _master_out->set_input_minimum(ChanCount(DataType::AUDIO, n_physical_inputs)); + _master_out->set_output_minimum(ChanCount(DataType::AUDIO, n_physical_outputs)); + _master_out->ensure_io ( + _master_out->input_minimum (), _master_out->output_minimum (), + true, this); _master_out->allow_pan_reset (); @@ -765,30 +759,15 @@ Session::hookup_io () IO::enable_ports (); if (_control_out) { - uint32_t n; vector<string> cports; - while (_control_out->n_inputs().n_audio() < _control_out->input_maximum().n_audio()) { - if (_control_out->add_input_port ("", this)) { - error << _("cannot setup control inputs") - << endmsg; - break; - } - } - n = 0; - while (_control_out->n_outputs().n_audio() < _control_out->output_maximum().n_audio()) { - if (_control_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this)) { - error << _("cannot set up master outputs") - << endmsg; - break; - } - n++; - } - + _control_out->ensure_io( + _control_out->input_minimum(), _control_out->output_minimum(), + false, this); uint32_t ni = _control_out->n_inputs().get (DataType::AUDIO); - for (n = 0; n < ni; ++n) { + for (uint32_t n = 0; n < ni; ++n) { cports.push_back (_control_out->input(n)->name()); } @@ -1313,7 +1292,6 @@ Session::set_block_size (nframes_t nframes) */ { - current_block_size = nframes; ensure_buffers(_scratch_buffers->available()); @@ -1757,14 +1735,13 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod uint32_t nphysical_out = physoutputs.size(); for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) { - port = ""; if (Config->get_output_auto_connect() & AutoConnectPhysical) { port = physoutputs[(channels_used+x)%nphysical_out]; } else if (Config->get_output_auto_connect() & AutoConnectMaster) { - if (_master_out) { - port = _master_out->input (x%_master_out->n_inputs().n_audio())->name(); + if (_master_out && _master_out->n_inputs().n_audio() > 0) { + port = _master_out->input (x % _master_out->n_inputs().n_audio())->name(); } } @@ -1906,7 +1883,7 @@ Session::new_audio_route (int input_channels, int output_channels, uint32_t how_ } while (bus_id < (UINT_MAX-1)); try { - shared_ptr<Route> bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO)); + shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO)); if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) { error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"), diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 44e8bb0392..7d14035c93 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -339,8 +339,7 @@ Session::second_stage_init (bool new_session) /* handle this one in a different way than all others, so that its clear what happened */ catch (AudioEngine::PortRegistrationFailure& err) { - error << _("Unable to create all required ports") - << endmsg; + error << err.what() << endmsg; return -1; } @@ -2831,7 +2830,12 @@ void Session::set_deletion_in_progress () { _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion); +} +void +Session::clear_deletion_in_progress () +{ + _state_of_the_state = StateOfTheState (_state_of_the_state & (~Deletion)); } void diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index ea9f30322b..40848dc9ed 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -117,7 +117,7 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& destination, sframes_t sour const uint64_t start_ticks = (uint64_t)(converter.from(start) * ppqn()); if (_last_read_end == 0 || start != _last_read_end) { - cerr << "SMFSource::read_unlocked seeking to " << start << endl; + //cerr << "SMFSource::read_unlocked seeking to " << start << endl; Evoral::SMF::seek_to_start(); while (time < start_ticks) { ret = read_event(&ev_delta_t, &ev_size, &ev_buffer); @@ -374,10 +374,10 @@ SMFSource::load_model (bool lock, bool force_reload) if (! _model) { _model = boost::shared_ptr<MidiModel>(new MidiModel(this)); - cerr << _name << " loaded new model " << _model.get() << endl; + //cerr << _name << " loaded new model " << _model.get() << endl; } else { - cerr << _name << " reloading model " << _model.get() - << " (" << _model->n_notes() << " notes)" <<endl; + /*cerr << _name << " reloading model " << _model.get() + << " (" << _model->n_notes() << " notes)" << endl;*/ _model->clear(); } diff --git a/libs/ardour/template_utils.cc b/libs/ardour/template_utils.cc index 22fc6f38df..3f380c0fd6 100644 --- a/libs/ardour/template_utils.cc +++ b/libs/ardour/template_utils.cc @@ -102,7 +102,7 @@ find_session_templates (vector<TemplateInfo>& template_names) template_names.push_back (rti); } - free (templates); + delete templates; } void diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 2ce73ce727..1e344403b1 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -20,18 +20,19 @@ #include <sigc++/retype_return.h> #include <sigc++/bind.h> -#include "ardour/track.h" -#include "ardour/diskstream.h" -#include "ardour/session.h" -#include "ardour/io_processor.h" +#include "ardour/amp.h" +#include "ardour/audioplaylist.h" #include "ardour/audioregion.h" #include "ardour/audiosource.h" -#include "ardour/route_group_specialized.h" -#include "ardour/processor.h" -#include "ardour/audioplaylist.h" +#include "ardour/diskstream.h" +#include "ardour/io_processor.h" #include "ardour/panner.h" -#include "ardour/utils.h" #include "ardour/port.h" +#include "ardour/processor.h" +#include "ardour/route_group_specialized.h" +#include "ardour/session.h" +#include "ardour/track.h" +#include "ardour/utils.h" #include "i18n.h" @@ -40,7 +41,7 @@ using namespace ARDOUR; using namespace PBD; Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, DataType default_type) - : Route (sess, name, 1, -1, -1, -1, flag, default_type) + : Route (sess, name, flag, default_type) , _rec_enable_control (new RecEnableControllable(*this)) { _declickable = true; @@ -231,7 +232,114 @@ Track::set_latency_delay (nframes_t longest_session_latency) void Track::zero_diskstream_id_in_xml (XMLNode& node) { - if (node.property ("diskstream-id")) { - node.add_property ("diskstream-id", "0"); - } + if (node.property ("diskstream-id")) { + node.add_property ("diskstream-id", "0"); + } +} + +int +Track::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, + bool session_state_changing, bool can_record, bool rec_monitors_input) +{ + if (n_outputs().n_total() == 0) { + return 0; + } + + if (!_active) { + silence (nframes); + return 0; + } + + if (session_state_changing) { + + /* XXX is this safe to do against transport state changes? */ + + passthru_silence (start_frame, end_frame, nframes, 0); + return 0; + } + + diskstream()->check_record_status (start_frame, nframes, can_record); + + bool send_silence; + + if (_have_internal_generator) { + /* since the instrument has no input streams, + there is no reason to send any signal + into the route. + */ + send_silence = true; + } else { + if (!Config->get_tape_machine_mode()) { + /* + ADATs work in a strange way.. + they monitor input always when stopped.and auto-input is engaged. + */ + if ((Config->get_monitoring_model() == SoftwareMonitoring) + && (Config->get_auto_input () || _diskstream->record_enabled())) { + send_silence = false; + } else { + send_silence = true; + } + } else { + /* + Other machines switch to input on stop if the track is record enabled, + regardless of the auto input setting (auto input only changes the + monitoring state when the transport is rolling) + */ + if ((Config->get_monitoring_model() == SoftwareMonitoring) + && _diskstream->record_enabled()) { + send_silence = false; + } else { + send_silence = true; + } + } + } + + _amp->apply_gain_automation(false); + + if (send_silence) { + + /* if we're sending silence, but we want the meters to show levels for the signal, + meter right here. + */ + + if (_have_internal_generator) { + passthru_silence (start_frame, end_frame, nframes, 0); + } else { + if (_meter_point == MeterInput) { + just_meter_input (start_frame, end_frame, nframes); + } + passthru_silence (start_frame, end_frame, nframes, 0); + } + + } else { + + /* we're sending signal, but we may still want to meter the input. + */ + + passthru (start_frame, end_frame, nframes, false); + } + + return 0; +} + +int +Track::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, + bool can_record, bool rec_monitors_input) +{ + if (n_outputs().n_total() == 0 && _processors.empty()) { + return 0; + } + + if (!_active) { + silence (nframes); + return 0; + } + + _silent = true; + _amp->apply_gain_automation(false); + + silence (nframes); + + return diskstream()->process (_session.transport_frame(), nframes, can_record, rec_monitors_input); } diff --git a/libs/ardour/wscript b/libs/ardour/wscript index dfc9cc0156..adcf3a5f0b 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -97,7 +97,9 @@ def build(bld): buffer_set.cc bundle.cc chan_count.cc + chan_mapping.cc configuration.cc + control_outputs.cc control_protocol_manager.cc control_protocol_search_path.cc crossfade.cc |