diff options
author | David Robillard <d@drobilla.net> | 2009-05-04 15:50:51 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2009-05-04 15:50:51 +0000 |
commit | ca10cc82a4374a5b413c06ead6cc89c53f8881ee (patch) | |
tree | 3d44716ed02d80bd1256609631c77a730d04e169 /libs | |
parent | 9b06b1da0cec57a6848cf1f7920691ae022b30e7 (diff) |
Preliminary MIDI plugin support.
git-svn-id: svn://localhost/ardour2/branches/3.0@5036 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
41 files changed, 1459 insertions, 203 deletions
diff --git a/libs/ardour/ardour/audio_port.h b/libs/ardour/ardour/audio_port.h index 85bac1f286..09bd8bdd1e 100644 --- a/libs/ardour/ardour/audio_port.h +++ b/libs/ardour/ardour/audio_port.h @@ -38,6 +38,8 @@ class AudioPort : public Port void cycle_start (nframes_t); void cycle_end (nframes_t); void cycle_split (); + + size_t raw_buffer_size(jack_nframes_t nframes) const; Buffer& get_buffer (nframes_t nframes, nframes_t offset = 0) { return get_audio_buffer (nframes, offset); diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index e023979ed8..83d85ec5d0 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -30,13 +30,14 @@ #include <vector> #include <map> -#include "ardour/plugin.h" - #include <AudioUnit/AudioUnit.h> #include <appleutility/AUParamInfo.h> #include <boost/shared_ptr.hpp> +#include "ardour/plugin.h" +#include "ardour/chan_mapping.h" + class CAComponent; class CAAudioUnit; class CAComponentDescription; diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 54154141f6..9dec7c8ec3 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -40,9 +40,10 @@ namespace ARDOUR { -class Session; -class Port; class InternalPort; +class MidiPort; +class Port; +class Session; class AudioEngine : public sigc::trackable { @@ -74,6 +75,8 @@ class AudioEngine : public sigc::trackable nframes_t frame_rate(); nframes_t frames_per_cycle(); + size_t raw_buffer_size(DataType t); + int usecs_per_cycle () const { return _usecs_per_cycle; } bool get_sync_offset (nframes_t& offset) const; @@ -213,6 +216,7 @@ class AudioEngine : public sigc::trackable bool _running; bool _has_run; nframes_t _buffer_size; + std::map<DataType,size_t> _raw_buffer_sizes; nframes_t _frame_rate; /// number of frames between each check for changes in monitor input nframes_t monitor_check_interval; diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h index f02470c3e8..72f45762c7 100644 --- a/libs/ardour/ardour/buffer_set.h +++ b/libs/ardour/ardour/buffer_set.h @@ -31,6 +31,7 @@ class Buffer; class AudioBuffer; class MidiBuffer; class PortSet; +class LV2EventBuffer; /** A set of buffers of various types. * @@ -55,7 +56,6 @@ public: void attach_buffers(PortSet& ports, nframes_t nframes, nframes_t offset = 0); - void ensure_buffers(const ChanCount& count, size_t buffer_capacity); void ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capacity); const ChanCount& available() const { return _available; } @@ -68,10 +68,7 @@ public: size_t buffer_capacity(DataType type) const; - Buffer& get(DataType type, size_t i) { - assert(i <= _count.get(type)); - return *_buffers[type][i]; - } + Buffer& get(DataType type, size_t i); AudioBuffer& get_audio(size_t i) { return (AudioBuffer&)get(DataType::AUDIO, i); @@ -81,6 +78,14 @@ public: return (MidiBuffer&)get(DataType::MIDI, i); } + /** Get a MIDI buffer translated into an LV2 MIDI buffer for use with plugins. + * The index here corresponds directly to MIDI buffer numbers (i.e. the index + * passed to get_midi), translation back and forth will happen as needed */ + LV2EventBuffer& get_lv2_midi(bool input, size_t i); + + /** Flush modified LV2 event output buffers back to Ardour buffers */ + void flush_lv2_midi(bool input, size_t i); + void read_from(BufferSet& in, nframes_t nframes); // ITERATORS @@ -137,6 +142,11 @@ private: /// Vector of vectors, indexed by DataType std::vector<BufferVec> _buffers; + + /// LV2 MIDI buffers (for conversion to/from MIDI buffers) + // + typedef std::vector< std::pair<bool, LV2EventBuffer*> > LV2Buffers; + LV2Buffers _lv2_buffers; /// Use counts (there may be more actual buffers than this) ChanCount _count; diff --git a/libs/ardour/ardour/chan_count.h b/libs/ardour/ardour/chan_count.h index ff70be6bb6..fb4b1999ca 100644 --- a/libs/ardour/ardour/chan_count.h +++ b/libs/ardour/ardour/chan_count.h @@ -20,10 +20,10 @@ #ifndef __ardour_chan_count_h__ #define __ardour_chan_count_h__ +#include <cassert> #include <ostream> #include "ardour/data_type.h" -#include <cassert> namespace ARDOUR { @@ -103,6 +103,14 @@ public: bool operator>=(const ChanCount& other) const { return ( (*this > other) || (*this == other) ); } + + static ChanCount max(const ChanCount& a, const ChanCount& b) { + ChanCount ret; + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + ret.set(*t, std::max(a.get(*t), b.get(*t))); + } + return ret; + } static const ChanCount INFINITE; static const ChanCount ZERO; diff --git a/libs/ardour/ardour/chan_mapping.h b/libs/ardour/ardour/chan_mapping.h new file mode 100644 index 0000000000..1dae20e34a --- /dev/null +++ b/libs/ardour/ardour/chan_mapping.h @@ -0,0 +1,82 @@ +/* + Copyright (C) 2009 Paul Davis + Author: Dave Robillard + + 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_chan_mapping_h__ +#define __ardour_chan_mapping_h__ + +#include <map> +#include <cassert> +#include <ostream> +#include <utility> + +#include "ardour/data_type.h" + +namespace ARDOUR { + + +/** A mapping from one set of channels to another + * (e.g. how to 'connect' two BufferSets). + */ +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; + } + + 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; +}; + +} // namespace ARDOUR + +#endif // __ardour_chan_mapping_h__ + diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 6b309bf50c..8457668756 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -365,7 +365,7 @@ class IO : public SessionObject, public AutomatableControls, public Latent 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); - void setup_peak_meters (); + virtual void setup_peak_meters (); void meter (); bool ensure_inputs_locked (ChanCount, bool clear, void *src); diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h index 855aa7189b..847b582432 100644 --- a/libs/ardour/ardour/ladspa_plugin.h +++ b/libs/ardour/ardour/ladspa_plugin.h @@ -84,7 +84,10 @@ class LadspaPlugin : public ARDOUR::Plugin void set_block_size (nframes_t nframes) {} - int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset); + int connect_and_run (BufferSet& bufs, + ChanMapping in, ChanMapping out, + nframes_t nframes, nframes_t offset); + std::string describe_parameter (Evoral::Parameter); std::string state_node_name() const { return "ladspa"; } void print_parameter (uint32_t, char*, uint32_t len) const; diff --git a/libs/ardour/ardour/lv2_event_buffer.h b/libs/ardour/ardour/lv2_event_buffer.h new file mode 100644 index 0000000000..9d35d36d55 --- /dev/null +++ b/libs/ardour/ardour/lv2_event_buffer.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2009 Paul Davis + Author: Dave Robillard + + 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_lv2_event_buffer_h__ +#define __ardour_lv2_event_buffer_h__ + +#include "lv2ext/lv2_event.h" +#include "lv2ext/lv2_event_helpers.h" + +namespace ARDOUR { + + +class LV2EventBuffer { +public: + LV2EventBuffer(size_t capacity); + ~LV2EventBuffer(); + + inline LV2_Event_Buffer* data() { return _data; } + inline const LV2_Event_Buffer* data() const { return _data; } + + inline void rewind() const { lv2_event_begin(&_iter, _data); } + + inline void reset() { + _latest_frames = 0; + _latest_subframes = 0; + _data->event_count = 0; + _data->size = 0; + rewind(); + } + + inline size_t event_count() const { return _data->event_count; } + inline uint32_t capacity() const { return _data->capacity; } + inline uint32_t size() const { return _data->size; } + inline uint32_t latest_frames() const { return _latest_frames; } + inline uint32_t latest_subframes() const { return _latest_subframes; } + + bool increment() const; + + bool is_valid() const; + + bool get_event(uint32_t* frames, + uint32_t* subframes, + uint16_t* type, + uint16_t* size, + uint8_t** data) const; + + bool append(uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data); + + bool append(const LV2_Event_Buffer* buf); + +private: + LV2_Event_Buffer* _data; ///< Contents + mutable LV2_Event_Iterator _iter; ///< Iterator into _data + uint32_t _latest_frames; ///< Latest time of all events (frames) + uint32_t _latest_subframes; ///< Latest time of all events (subframes) +}; + + +} // namespace ARDOUR + +#endif // __ardour_lv2_event_buffer_h__ diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index f235a0b4c2..7daf4ed2b1 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -33,6 +33,7 @@ #include <jack/types.h> #include <slv2/slv2.h> #include "ardour/plugin.h" +#include "ardour/uri_map.h" namespace ARDOUR { class AudioEngine; @@ -95,9 +96,12 @@ class LV2Plugin : public ARDOUR::Plugin void set_block_size (nframes_t nframes) {} - int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset); + int connect_and_run (BufferSet& bufs, + ChanMapping in, ChanMapping out, + nframes_t nframes, nframes_t offset); + std::string describe_parameter (Evoral::Parameter); - std::string state_node_name() const { return "LV2"; } + std::string state_node_name() const { return "lv2"; } void print_parameter (uint32_t, char*, uint32_t len) const; bool parameter_is_audio(uint32_t) const; @@ -107,6 +111,8 @@ class LV2Plugin : public ARDOUR::Plugin bool parameter_is_output(uint32_t) const; bool parameter_is_toggled(uint32_t) const; + static uint32_t midi_event_type() { return _midi_event_type; } + XMLNode& get_state(); int set_state(const XMLNode& node); bool save_preset(std::string uri); @@ -138,6 +144,9 @@ class LV2Plugin : public ARDOUR::Plugin LV2_Feature _data_access_feature; LV2_Feature _instance_access_feature; + static URIMap _uri_map; + static uint32_t _midi_event_type; + void init (LV2World& world, SLV2Plugin plugin, nframes_t rate); void run (nframes_t nsamples); void latency_compute_run (); diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 70e2203df4..43626fd0e7 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -44,6 +44,7 @@ public: bool push_back(const Evoral::MIDIEvent<TimeType>& event); bool push_back(const jack_midi_event_t& event); + bool push_back(TimeType time, size_t size, const uint8_t* data); uint8_t* reserve(TimeType time, size_t size); void resize(size_t); diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h index 2f3d8d1647..e4d6a41d05 100644 --- a/libs/ardour/ardour/midi_port.h +++ b/libs/ardour/ardour/midi_port.h @@ -40,6 +40,8 @@ class MidiPort : public Port { void cycle_end (nframes_t nframes); void cycle_split (); void flush_buffers (nframes_t nframes, nframes_t offset = 0); + + size_t raw_buffer_size(jack_nframes_t nframes) const; Buffer& get_buffer (nframes_t nframes, nframes_t offset = 0) { return get_midi_buffer (nframes, offset); diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index 0039a1f8ca..2cf3d3fc34 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -29,6 +29,7 @@ #include <jack/types.h> #include "ardour/chan_count.h" +#include "ardour/chan_mapping.h" #include "ardour/cycles.h" #include "ardour/latent.h" #include "ardour/plugin_insert.h" @@ -126,7 +127,9 @@ class Plugin : public PBD::StatefulDestructible, public Latent virtual void deactivate () = 0; virtual void set_block_size (nframes_t nframes) = 0; - virtual int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset) = 0; + virtual int connect_and_run (BufferSet& bufs, + ChanMapping in, ChanMapping out, + nframes_t nframes, nframes_t offset) = 0; virtual std::set<Evoral::Parameter> automatable() const = 0; virtual string describe_parameter (Evoral::Parameter) = 0; diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index 88a5ee357d..99bd492ab5 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -104,16 +104,10 @@ class PluginInsert : public Processor nframes_t signal_latency() const; boost::shared_ptr<Plugin> get_impulse_analysis_plugin(); + + void collect_signal_for_analysis(nframes_t nframes); sigc::signal<void, BufferSet*, BufferSet*> AnalysisDataGathered; - void collect_signal_for_analysis(nframes_t nframes) { - // called from outside the audio thread, so this should be safe - _signal_analysis_input_bufferset.ensure_buffers(input_streams(), nframes); - _signal_analysis_output_bufferset.ensure_buffers(output_streams(), nframes); - - _signal_analysis_collected_nframes = 0; - _signal_analysis_collect_nframes_max = nframes; - } private: /* disallow copy construction */ @@ -126,15 +120,16 @@ class PluginInsert : public Processor float default_parameter_value (const Evoral::Parameter& param); - std::vector<boost::shared_ptr<Plugin> > _plugins; + typedef std::vector<boost::shared_ptr<Plugin> > Plugins; + Plugins _plugins; boost::weak_ptr<Plugin> _impulseAnalysisPlugin; nframes_t _signal_analysis_collected_nframes; nframes_t _signal_analysis_collect_nframes_max; - BufferSet _signal_analysis_input_bufferset; - BufferSet _signal_analysis_output_bufferset; + BufferSet _signal_analysis_inputs; + BufferSet _signal_analysis_outputs; void automation_run (BufferSet& bufs, nframes_t nframes); void connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now = 0); diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index 0c77eb15dd..3f86fb8fd2 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -101,6 +101,9 @@ public: void set_latency (nframes_t); virtual void reset (); + + /** @return the size of the raw buffer (bytes) for duration @a nframes (audio frames) */ + virtual size_t raw_buffer_size(jack_nframes_t nframes) const = 0; virtual DataType type () const = 0; virtual void cycle_start (nframes_t) = 0; @@ -108,7 +111,7 @@ public: virtual void cycle_split () = 0; virtual Buffer& get_buffer (nframes_t nframes, nframes_t offset = 0) = 0; virtual void flush_buffers (nframes_t, nframes_t offset = 0) {} - + static void set_engine (AudioEngine *); sigc::signal<void, bool> MonitorInputChanged; diff --git a/libs/ardour/ardour/processor.h b/libs/ardour/ardour/processor.h index 24d3cd93c7..c2c23b8f67 100644 --- a/libs/ardour/ardour/processor.h +++ b/libs/ardour/ardour/processor.h @@ -95,8 +95,8 @@ class Processor : public SessionObject, public AutomatableControls, public Laten virtual bool is_out_of_place () const { return false; } virtual bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const = 0; - virtual ChanCount output_streams() const { return _configured_input; } virtual ChanCount input_streams () const { return _configured_input; } + virtual ChanCount output_streams() const { return _configured_output; } virtual XMLNode& state (bool full); virtual XMLNode& get_state (void); @@ -116,6 +116,7 @@ protected: bool _next_ab_is_active; 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 */ diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 183be9c4c8..6926dbd036 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -161,7 +161,7 @@ class Route : public IO } } - ChanCount max_processor_outs () const { return processor_max_outs; } + ChanCount max_processor_streams () const { return processor_max_streams; } ChanCount pre_fader_streams() const; /** A record of the stream configuration at some point in the processor list. @@ -333,11 +333,13 @@ class Route : public IO sigc::connection input_signal_connection; - ChanCount processor_max_outs; + ChanCount processor_max_streams; uint32_t _remote_control_id; uint32_t pans_required() const; ChanCount n_process_buffers (); + + void setup_peak_meters (); virtual int _set_state (const XMLNode&, bool call_base); virtual void _set_processor_states (const XMLNodeList&); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index ce238fbc2e..bc8dcb81f1 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -944,6 +944,8 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable gain_t* gain_automation_buffer () const { return _gain_automation_buffer; } pan_t** pan_automation_buffer () const { return _pan_automation_buffer; } + + void ensure_buffer_set (BufferSet& buffers, const ChanCount& howmany); /* VST support */ diff --git a/libs/ardour/ardour/uri_map.h b/libs/ardour/ardour/uri_map.h new file mode 100644 index 0000000000..78e5393271 --- /dev/null +++ b/libs/ardour/ardour/uri_map.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2009 Paul Davis + Author: Dave Robillard + + 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_uri_map_h__ +#define __ardour_uri_map_h__ + +#include <map> +#include <string> +#include <boost/utility.hpp> +#include <slv2/slv2.h> +#include "lv2ext/lv2_uri_map.h" + +namespace ARDOUR { + + +/** Implementation of the LV2 URI Map extension + */ +class URIMap : public boost::noncopyable { +public: + URIMap(); + + LV2_Feature* feature() { return &uri_map_feature; } + + uint32_t uri_to_id(const char* map, + const char* uri); + +private: + typedef std::map<std::string, uint32_t> Map; + + static uint32_t uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri); + + LV2_Feature uri_map_feature; + LV2_URI_Map_Feature uri_map_feature_data; + Map uri_map; + uint32_t next_uri_id; +}; + + +} // namespace ARDOUR + +#endif // __ardour_uri_map_h__ diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h index 7541740cf8..d4ca3b8b60 100644 --- a/libs/ardour/ardour/vst_plugin.h +++ b/libs/ardour/ardour/vst_plugin.h @@ -71,7 +71,11 @@ class VSTPlugin : public ARDOUR::Plugin void activate (); void deactivate (); void set_block_size (nframes_t nframes); - int connect_and_run (BufferSet&, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset); + + int connect_and_run (BufferSet&, + ChanMapping in, ChanMapping out, + nframes_t nframes, nframes_t offset); + string describe_parameter (Evoral::Parameter); string state_node_name() const { return "vst"; } void print_parameter (uint32_t, char*, uint32_t len) const; diff --git a/libs/ardour/audio_port.cc b/libs/ardour/audio_port.cc index c62d31a6d0..f69d6685ff 100644 --- a/libs/ardour/audio_port.cc +++ b/libs/ardour/audio_port.cc @@ -97,3 +97,9 @@ AudioPort::get_audio_buffer (nframes_t nframes, nframes_t offset) return *_buffer; } +size_t +AudioPort::raw_buffer_size(nframes_t nframes) const +{ + return nframes * sizeof(float); +} + diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 8ca85c4fe8..51a6ae71fb 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -140,14 +140,14 @@ AudioEngine::start () if (!_running) { + nframes_t blocksize = jack_get_buffer_size (_jack); + if (session) { - nframes_t blocksize = jack_get_buffer_size (_jack); - BootMessage (_("Connect session to engine")); session->set_block_size (blocksize); session->set_frame_rate (jack_get_sample_rate (_jack)); - + /* page in as much of the session process code as we can before we really start running. */ @@ -188,6 +188,8 @@ AudioEngine::start () } start_metering_thread(); + + _raw_buffer_sizes[DataType::AUDIO] = blocksize * sizeof(float); } return _running ? 0 : -1; @@ -466,6 +468,7 @@ int AudioEngine::jack_bufsize_callback (nframes_t nframes) { _buffer_size = nframes; + _raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof(float); _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0); last_monitor_check = 0; @@ -603,7 +606,11 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input) } else { throw 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) + old_buffer_size = port_buffer_size; RCUWriter<Ports> writer (ports); boost::shared_ptr<Ports> ps = writer.get_copy (); @@ -729,7 +736,7 @@ AudioEngine::disconnect (const string& source, const string& destination) Port* src = get_port_by_name_locked (s); Port* dst = get_port_by_name_locked (d); - if (src) { + if (src) { ret = src->disconnect (d); } else if (dst) { ret = dst->disconnect (s); @@ -772,6 +779,13 @@ AudioEngine::frame_rate () } } +size_t +AudioEngine::raw_buffer_size (DataType t) +{ + std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t); + return (s != _raw_buffer_sizes.end()) ? s->second : 0; +} + ARDOUR::nframes_t AudioEngine::frames_per_cycle () { @@ -1184,7 +1198,6 @@ AudioEngine::disconnect_from_jack () return 0; } - if (_running) { stop_metering_thread (); } @@ -1197,6 +1210,7 @@ AudioEngine::disconnect_from_jack () _buffer_size = 0; _frame_rate = 0; + _raw_buffer_sizes.clear(); if (_running) { _running = false; diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index b3fd239c59..f3fbbe6c1c 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -1463,8 +1463,6 @@ AudioRegion::find_silence (Sample threshold, nframes_t min_length) const while (pos < end) { - nframes_t const to_read = min (end - pos, block_size); - /* fill `loudest' with the loudest absolute sample at each instant, across all channels */ memset (loudest, 0, sizeof (Sample) * block_size); for (uint32_t n = 0; n < n_channels(); ++n) { diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index dfc607ec03..c75a15c074 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -18,10 +18,14 @@ #include <iostream> #include <algorithm> -#include "ardour/buffer_set.h" #include "ardour/buffer.h" +#include "ardour/buffer_set.h" +#include "ardour/lv2_event_buffer.h" +#include "ardour/lv2_plugin.h" +#include "ardour/midi_buffer.h" #include "ardour/port.h" #include "ardour/port_set.h" +#include "ardour/audioengine.h" namespace ARDOUR { @@ -29,8 +33,9 @@ namespace ARDOUR { BufferSet::BufferSet() : _is_mirror(false) { - for (size_t i=0; i < DataType::num_types; ++i) - _buffers.push_back( BufferVec() ); + for (size_t i=0; i < DataType::num_types; ++i) { + _buffers.push_back(BufferVec()); + } _count.reset(); _available.reset(); @@ -81,15 +86,6 @@ BufferSet::attach_buffers(PortSet& ports, nframes_t nframes, nframes_t offset) _is_mirror = true; } -void -BufferSet::ensure_buffers(const ChanCount& count, size_t buffer_capacity) -{ - for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { - ensure_buffers(*t, count.get(*t), buffer_capacity); - } -} - - /** Ensure that there are @a num_buffers buffers of type @a type available, * each of size at least @a buffer_size */ @@ -98,17 +94,10 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac { assert(type != DataType::NIL); assert(type < _buffers.size()); - assert(buffer_capacity > 0); if (num_buffers == 0) return; - // FIXME: Kludge to make MIDI buffers larger (size is bytes, not frames) - // See MidiPort::MidiPort - // We probably need a map<DataType, size_t> parameter for capacity - if (type == DataType::MIDI) - buffer_capacity *= 8; - // The vector of buffers of the type we care about BufferVec& bufs = _buffers[type]; @@ -131,13 +120,21 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac bufs.clear(); // Rebuild it - for (size_t i=0; i < num_buffers; ++i) { + for (size_t i = 0; i < num_buffers; ++i) { bufs.push_back(Buffer::create(type, buffer_capacity)); } _available.set(type, num_buffers); } + // 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) { + while (_lv2_buffers.size() < _buffers[type].size() * 2) { + _lv2_buffers.push_back(std::make_pair(false, new LV2EventBuffer(buffer_capacity))); + } + } + // Post-conditions assert(bufs[0]->type() == type); assert(bufs.size() >= num_buffers); @@ -156,6 +153,50 @@ BufferSet::buffer_capacity(DataType type) const return _buffers[type][0]->capacity(); } +Buffer& +BufferSet::get(DataType type, size_t i) +{ + assert(i <= _count.get(type)); + return *_buffers[type][i]; +} + +LV2EventBuffer& +BufferSet::get_lv2_midi(bool input, size_t i) +{ + MidiBuffer& mbuf = get_midi(i); + LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1)); + LV2EventBuffer* ebuf = b.second; + + ebuf->reset(); + if (input) { + for (MidiBuffer::iterator e = mbuf.begin(); e != mbuf.end(); ++e) { + const Evoral::MIDIEvent<nframes_t> ev(*e, false); + uint32_t type = LV2Plugin::midi_event_type(); + ebuf->append(ev.time(), 0, type, ev.size(), ev.buffer()); + } + } + return *ebuf; +} + +void +BufferSet::flush_lv2_midi(bool input, size_t i) +{ + MidiBuffer& mbuf = get_midi(i); + LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1)); + LV2EventBuffer* ebuf = b.second; + + mbuf.silence(0, 0); + for (ebuf->rewind(); ebuf->is_valid(); ebuf->increment()) { + uint32_t frames; + uint32_t subframes; + uint16_t type; + uint16_t size; + uint8_t* data; + ebuf->get_event(&frames, &subframes, &type, &size, &data); + mbuf.push_back(frames, size, data); + } +} + // FIXME: make 'in' const void BufferSet::read_from (BufferSet& in, nframes_t nframes) diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index f287414fe5..348542ce1c 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -507,34 +507,23 @@ LadspaPlugin::automatable () const } int -LadspaPlugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset) +LadspaPlugin::connect_and_run (BufferSet& bufs, + ChanMapping in_map, ChanMapping out_map, + nframes_t nframes, nframes_t offset) { - uint32_t port_index = 0; - cycles_t then, now; - - then = get_cycles (); - - const uint32_t nbufs = bufs.count().n_audio(); - - while (port_index < parameter_count()) { - if (LADSPA_IS_PORT_AUDIO (port_descriptor(port_index))) { - if (LADSPA_IS_PORT_INPUT (port_descriptor(port_index))) { - const size_t index = min(in_index, nbufs - 1); - connect_port (port_index, bufs.get_audio(index).data(offset)); - //cerr << this << ' ' << name() << " @ " << offset << " inport " << in_index << " = buf " - // << min((uint32_t)in_index,nbufs) << " = " << &bufs[min((uint32_t)in_index,nbufs)][offset] << endl; - in_index++; - - - } else if (LADSPA_IS_PORT_OUTPUT (port_descriptor (port_index))) { - const size_t index = min(out_index,nbufs - 1); - connect_port (port_index, bufs.get_audio(index).data(offset)); - // cerr << this << ' ' << name() << " @ " << offset << " outport " << out_index << " = buf " - // << min((uint32_t)out_index,nbufs) << " = " << &bufs[min((uint32_t)out_index,nbufs)][offset] << endl; - out_index++; + cycles_t now; + cycles_t then = get_cycles (); + + for (uint32_t port_index = 0; port_index < parameter_count(); ++port_index) { + if (LADSPA_IS_PORT_AUDIO(port_descriptor(port_index))) { + if (LADSPA_IS_PORT_INPUT(port_descriptor(port_index))) { + const uint32_t buf_index = in_map.get(DataType::AUDIO, port_index); + connect_port(port_index, bufs.get_audio(buf_index).data(offset)); + } else if (LADSPA_IS_PORT_OUTPUT(port_descriptor(port_index))) { + const uint32_t buf_index = out_map.get(DataType::AUDIO, port_index); + connect_port(port_index, bufs.get_audio(buf_index).data(offset)); } } - port_index++; } run_in_place (nframes); diff --git a/libs/ardour/lv2_event_buffer.cc b/libs/ardour/lv2_event_buffer.cc new file mode 100644 index 0000000000..5405701da1 --- /dev/null +++ b/libs/ardour/lv2_event_buffer.cc @@ -0,0 +1,191 @@ +/* + Copyright (C) 2009 Paul Davis + Author: Dave Robillard + + 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. +*/ + +#define __STDC_LIMIT_MACROS 1 +#include <stdint.h> +#include <iostream> +#include "ardour/lv2_event_buffer.h" +#include "lv2ext/lv2_event.h" +#include "lv2ext/lv2_event_helpers.h" + +using namespace std; + +namespace ARDOUR { + + +/** Allocate a new event buffer. + * \a capacity is in bytes (not number of events). + */ +LV2EventBuffer::LV2EventBuffer(size_t capacity) + : _latest_frames(0) + , _latest_subframes(0) +{ + if (capacity > UINT32_MAX) { + cerr << "Event buffer size " << capacity << " too large, aborting." << endl; + throw std::bad_alloc(); + } + +#ifdef NO_POSIX_MEMALIGN + _data = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); + int ret = (_data != NULL) ? 0 : -1; +#else + int ret = posix_memalign((void**)&_data, 16, sizeof(LV2_Event_Buffer) + capacity); +#endif + + if (ret != 0) { + cerr << "Failed to allocate event buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + _data->event_count = 0; + _data->capacity = (uint32_t)capacity; + _data->size = 0; + _data->data = reinterpret_cast<uint8_t*>(_data + 1); + + reset(); +} + + +LV2EventBuffer::~LV2EventBuffer() +{ + free(_data); +} + + +/** Increment the read position by one event. + * + * \return true if increment was successful, or false if end of buffer reached. + */ +bool +LV2EventBuffer::increment() const +{ + if (lv2_event_is_valid(&_iter)) { + lv2_event_increment(&_iter); + return true; + } else { + return false; + } +} + + +/** \return true iff the cursor is valid (ie get_event is safe) + */ +bool +LV2EventBuffer::is_valid() const +{ + return lv2_event_is_valid(&_iter); +} + + +/** Read an event from the current position in the buffer + * + * \return true if read was successful, or false if end of buffer reached + */ +bool +LV2EventBuffer::get_event(uint32_t* frames, + uint32_t* subframes, + uint16_t* type, + uint16_t* size, + uint8_t** data) const +{ + if (lv2_event_is_valid(&_iter)) { + LV2_Event* ev = lv2_event_get(&_iter, data); + *frames = ev->frames; + *subframes = ev->subframes; + *type = ev->type; + *size = ev->size; + return true; + } else { + return false; + } +} + + +/** Append an event to the buffer. + * + * \a timestamp must be >= the latest event in the buffer. + * + * \return true on success + */ +bool +LV2EventBuffer::append(uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data) +{ +#ifndef NDEBUG + if (lv2_event_is_valid(&_iter)) { + LV2_Event* last_event = lv2_event_get(&_iter, NULL); + assert(last_event->frames < frames + || (last_event->frames == frames && last_event->subframes <= subframes)); + } +#endif + + /*cout << "Appending event type " << type << ", size " << size + << " @ " << frames << "." << subframes << endl;*/ + + if (!lv2_event_write(&_iter, frames, subframes, type, size, data)) { + cerr << "ERROR: Failed to write event." << endl; + return false; + } else { + _latest_frames = frames; + _latest_subframes = subframes; + return true; + } +} + + +/** Append a buffer of events to the buffer. + * + * \a timestamp must be >= the latest event in the buffer. + * + * \return true on success + */ +bool +LV2EventBuffer::append(const LV2_Event_Buffer* buf) +{ + uint8_t** data = NULL; + bool ret = true; + + LV2_Event_Iterator iter; + for (lv2_event_begin(&iter, _data); lv2_event_is_valid(&iter); lv2_event_increment(&iter)) { + LV2_Event* ev = lv2_event_get(&iter, data); + +#ifndef NDEBUG + assert((ev->frames > _latest_frames) + || (ev->frames == _latest_frames + && ev->subframes >= _latest_subframes)); +#endif + + if (!(ret = append(ev->frames, ev->subframes, ev->type, ev->size, *data))) { + cerr << "ERROR: Failed to write event." << endl; + break; + } + + _latest_frames = ev->frames; + _latest_subframes = ev->subframes; + } + + return ret; +} + + +} // namespace ARDOUR + diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index c9098ae304..e7617671a0 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -34,6 +34,7 @@ #include "ardour/session.h" #include "ardour/audioengine.h" #include "ardour/audio_buffer.h" +#include "ardour/lv2_event_buffer.h" #include "ardour/lv2_plugin.h" #include "pbd/stl_delete.h" @@ -44,7 +45,12 @@ using namespace std; using namespace ARDOUR; using namespace PBD; - + +URIMap LV2Plugin::_uri_map; +uint32_t LV2Plugin::_midi_event_type = _uri_map.uri_to_id( + "http://lv2plug.in/ns/ext/event", + "http://lv2plug.in/ns/ext/midi#MidiEvent"); + LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, LV2World& world, SLV2Plugin plugin, nframes_t rate) : Plugin (e, session) , _world(world) @@ -88,7 +94,8 @@ LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate) } if (slv2_plugin_has_feature(plugin, world.in_place_broken)) { - error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), + error << string_compose( + _("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), slv2_value_as_string(_name)); slv2_value_free(_name); slv2_value_free(_author); @@ -102,10 +109,11 @@ LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate) _data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access"; _data_access_feature.data = &_data_access_extension_data; - _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 3); + _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 4); _features[0] = &_instance_access_feature; _features[1] = &_data_access_feature; - _features[2] = NULL; + _features[2] = _uri_map.feature(); + _features[3] = NULL; _sample_rate = rate; @@ -347,7 +355,7 @@ LV2Plugin::set_state(const XMLNode& node) nodes = node.children ("Port"); - for(iter = nodes.begin(); iter != nodes.end(); ++iter){ + for (iter = nodes.begin(); iter != nodes.end(); ++iter){ child = *iter; @@ -450,52 +458,55 @@ LV2Plugin::automatable () const } int -LV2Plugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset) +LV2Plugin::connect_and_run (BufferSet& bufs, + ChanMapping in_map, ChanMapping out_map, + nframes_t nframes, nframes_t offset) { - uint32_t port_index; cycles_t then, now; - port_index = 0; - then = get_cycles (); - - const uint32_t nbufs = bufs.count().n_audio(); - while (port_index < parameter_count()) { + uint32_t audio_in_index = 0; + uint32_t audio_out_index = 0; + uint32_t midi_in_index = 0; + uint32_t midi_out_index = 0; + for (uint32_t port_index = 0; port_index < parameter_count(); ++port_index) { if (parameter_is_audio(port_index)) { if (parameter_is_input(port_index)) { - const size_t index = min(in_index, nbufs - 1); + const uint32_t buf_index = in_map.get(DataType::AUDIO, audio_in_index++); slv2_instance_connect_port(_instance, port_index, - bufs.get_audio(index).data(offset)); - in_index++; + bufs.get_audio(buf_index).data(offset)); } else if (parameter_is_output(port_index)) { - const size_t index = min(out_index,nbufs - 1); + const uint32_t buf_index = out_map.get(DataType::AUDIO, audio_out_index++); slv2_instance_connect_port(_instance, port_index, - bufs.get_audio(index).data(offset)); - out_index++; + bufs.get_audio(buf_index).data(offset)); } } else if (parameter_is_midi(port_index)) { - // FIXME: Switch MIDI buffer format to LV2 event buffer if (parameter_is_input(port_index)) { - //const size_t index = min(in_index, nbufs - 1); - //slv2_instance_connect_port(_instance, port_index, - // bufs.get_midi(index).data(offset)); - // FIXME: hope it's connection optional... - slv2_instance_connect_port(_instance, port_index, NULL); - in_index++; + const uint32_t buf_index = in_map.get(DataType::MIDI, midi_in_index++); + slv2_instance_connect_port(_instance, port_index, + bufs.get_lv2_midi(true, buf_index).data()); } else if (parameter_is_output(port_index)) { - //const size_t index = min(out_index,nbufs - 1); - //slv2_instance_connect_port(_instance, port_index, - // bufs.get_midi(index).data(offset)); - // FIXME: hope it's connection optional... - slv2_instance_connect_port(_instance, port_index, NULL); - out_index++; + const uint32_t buf_index = out_map.get(DataType::MIDI, midi_out_index++); + 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; + slv2_instance_connect_port(_instance, port_index, NULL); } - port_index++; } run (nframes); + + midi_out_index = 0; + for (uint32_t port_index = 0; port_index < parameter_count(); ++port_index) { + if (parameter_is_midi(port_index) && parameter_is_output(port_index)) { + const uint32_t buf_index = out_map.get(DataType::MIDI, midi_out_index++); + bufs.flush_lv2_midi(true, buf_index); + } + } + now = get_cycles (); set_cycles ((uint32_t) (now - then)); @@ -520,8 +531,8 @@ bool LV2Plugin::parameter_is_midi (uint32_t param) const { SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param); - return slv2_port_is_a(_plugin, port, _world.event_class) - && slv2_port_supports_event(_plugin, port, _world.midi_class); + return slv2_port_is_a(_plugin, port, _world.event_class); + // && slv2_port_supports_event(_plugin, port, _world.midi_class); } bool diff --git a/libs/ardour/lv2ext/lv2_event.h b/libs/ardour/lv2ext/lv2_event.h new file mode 100644 index 0000000000..6ea90c896d --- /dev/null +++ b/libs/ardour/lv2ext/lv2_event.h @@ -0,0 +1,260 @@ +/* lv2_event.h - C header file for the LV2 events extension. + * + * Copyright (C) 2006-2007 Lars Luthman <lars.luthman@gmail.com> + * Copyright (C) 2008 Dave Robillard <dave@drobilla.net> + * + * This header is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This header 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this header; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + */ + +#ifndef LV2_EVENT_H +#define LV2_EVENT_H + +#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" +#define LV2_EVENT_AUDIO_STAMP 0 + +#include <stdint.h> + +/** @file + * This header defines the code portion of the LV2 events extension with URI + * <http://lv2plug.in/ns/ext/event> ('lv2ev'). + * + * This extension is a generic transport mechanism for time stamped events + * of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed + * events of any type; the type of events and timestamps are defined by a URI + * which is mapped to an integer by the host for performance reasons. + * + * This extension requires the host to support the LV2 URI Map extension. + * Any host which supports this extension MUST guarantee that any call to + * the LV2 URI Map uri_to_id function with the URI of this extension as the + * 'map' argument returns a value within the range of uint16_t. + */ + + +/** The best Pulses Per Quarter Note for tempo-based uint32_t timestmaps. + * Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble + * by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. + */ +static const uint32_t LV2_EVENT_PPQN = 3136573440U; + + +/** An LV2 event (header only). + * + * LV2 events are generic time-stamped containers for any type of event. + * The type field defines the format of a given event's contents. + * + * This struct defines the header of an LV2 event. An LV2 event is a single + * chunk of POD (plain old data), usually contained in a flat buffer + * (see LV2_EventBuffer below). Unless a required feature says otherwise, + * hosts may assume a deep copy of an LV2 event can be created safely + * using a simple: + * + * memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) + */ +typedef struct { + + /** The frames portion of timestamp. The units used here can optionally be + * set for a port (with the lv2ev:timeUnits property), otherwise this + * is audio frames, corresponding to the sample_count parameter of the + * LV2 run method (e.g. frame 0 is the first frame for that call to run). + */ + uint32_t frames; + + /** The sub-frames portion of timestamp. The units used here can + * optionally be set for a port (with the lv2ev:timeUnits property), + * otherwise this is 1/(2^32) of an audio frame. + */ + uint32_t subframes; + + /** The type of this event, as a number which represents some URI + * defining an event type. This value MUST be some value previously + * returned from a call to the uri_to_id function defined in the LV2 + * URI map extension (see lv2_uri_map.h). + * There are special rules which must be followed depending on the type + * of an event. If the plugin recognizes an event type, the definition + * of that event type will describe how to interpret the event, and + * any required behaviour. Otherwise, if the type is 0, this event is a + * non-POD event and lv2_event_unref MUST be called if the event is + * 'dropped' (see above). Even if the plugin does not understand an event, + * it may pass the event through to an output by simply copying (and NOT + * calling lv2_event_unref). These rules are designed to allow for generic + * event handling plugins and large non-POD events, but with minimal hassle + * on simple plugins that "don't care" about these more advanced features. + */ + uint16_t type; + + /** The size of the data portion of this event in bytes, which immediately + * follows. The header size (12 bytes) is not included in this value. + */ + uint16_t size; + + /* size bytes of data follow here */ + +} LV2_Event; + + + +/** A buffer of LV2 events (header only). + * + * Like events (which this contains) an event buffer is a single chunk of POD: + * the entire buffer (including contents) can be copied with a single memcpy. + * The first contained event begins sizeof(LV2_EventBuffer) bytes after + * the start of this struct. + * + * After this header, the buffer contains an event header (defined by struct + * LV2_Event), followed by that event's contents (padded to 64 bits), followed by + * another header, etc: + * + * | | | | | | | + * | | | | | | | | | | | | | | | | | | | | | | | | | + * |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... + */ +typedef struct { + + /** The contents of the event buffer. This may or may not reside in the + * same block of memory as this header, plugins must not assume either. + * The host guarantees this points to at least capacity bytes of allocated + * memory (though only size bytes of that are valid events). + */ + uint8_t* data; + + /** The size of this event header in bytes (including everything). + * + * This is to allow for extending this header in the future without + * breaking binary compatibility. Whenever this header is copied, + * it MUST be done using this field (and NOT the sizeof this struct). + */ + uint16_t header_size; + + /** The type of the time stamps for events in this buffer. + * As a special exception, '0' always means audio frames and subframes + * (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. + * INPUTS: The host must set this field to the numeric ID of some URI + * defining the meaning of the frames/subframes fields of contained + * events (obtained by the LV2 URI Map uri_to_id function with the URI + * of this extension as the 'map' argument, see lv2_uri_map.h). + * The host must never pass a plugin a buffer which uses a stamp type + * the plugin does not 'understand'. The value of this field must + * never change, except when connect_port is called on the input + * port, at which time the host MUST have set the stamp_type field to + * the value that will be used for all subsequent run calls. + * OUTPUTS: The plugin may set this to any value that has been returned + * from uri_to_id with the URI of this extension for a 'map' argument. + * When connected to a buffer with connect_port, output ports MUST set + * this field to the type of time stamp they will be writing. On any + * call to connect_port on an event input port, the plugin may change + * this field on any output port, it is the responsibility of the host + * to check if any of these values have changed and act accordingly. + */ + uint16_t stamp_type; + + /** The number of events in this buffer. + * INPUTS: The host must set this field to the number of events + * contained in the data buffer before calling run(). + * The plugin must not change this field. + * OUTPUTS: The plugin must set this field to the number of events it + * has written to the buffer before returning from run(). + * Any initial value should be ignored by the plugin. + */ + uint32_t event_count; + + /** The size of the data buffer in bytes. + * This is set by the host and must not be changed by the plugin. + * The host is allowed to change this between run() calls. + */ + uint32_t capacity; + + /** The size of the initial portion of the data buffer containing data. + * INPUTS: The host must set this field to the number of bytes used + * by all events it has written to the buffer (including headers) + * before calling the plugin's run(). + * The plugin must not change this field. + * OUTPUTS: The plugin must set this field to the number of bytes + * used by all events it has written to the buffer (including headers) + * before returning from run(). + * Any initial value should be ignored by the plugin. + */ + uint32_t size; + +} LV2_Event_Buffer; + + +/** Opaque pointer to host data. */ +typedef void* LV2_Event_Callback_Data; + + +/** The data field of the LV2_Feature for this extension. + * + * To support this feature the host must pass an LV2_Feature struct to the + * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" + * and data pointed to an instance of this struct. + */ +typedef struct { + + /** Opaque pointer to host data. + * + * The plugin MUST pass this to any call to functions in this struct. + * Otherwise, it must not be interpreted in any way. + */ + LV2_Event_Callback_Data callback_data; + + /** Take a reference to a non-POD event. + * + * If a plugin receives an event with type 0, it means the event is a + * pointer to some object in memory and not a flat sequence of bytes + * in the buffer. When receiving a non-POD event, the plugin already + * has an implicit reference to the event. If the event is stored AND + * passed to an output, lv2_event_ref MUST be called on that event. + * If the event is only stored OR passed through, this is not necessary + * (as the plugin already has 1 implicit reference). + * + * @param event An event received at an input that will not be copied to + * an output or stored in any way. + * @param context The calling context. (Like event types) this is a mapped + * URI, see lv2_context.h. Simple plugin with just a run() + * method should pass 0 here (the ID of the 'standard' LV2 + * run context). The host guarantees that this function is + * realtime safe iff @a context is realtime safe. + * + * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + + /** Drop a reference to a non-POD event. + * + * If a plugin receives an event with type 0, it means the event is a + * pointer to some object in memory and not a flat sequence of bytes + * in the buffer. If the plugin does not pass the event through to + * an output or store it internally somehow, it MUST call this function + * on the event (more information on using non-POD events below). + * + * @param event An event received at an input that will not be copied to + * an output or stored in any way. + * @param context The calling context. (Like event types) this is a mapped + * URI, see lv2_context.h. Simple plugin with just a run() + * method should pass 0 here (the ID of the 'standard' LV2 + * run context). The host guarantees that this function is + * realtime safe iff @a context is realtime safe. + * + * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + +} LV2_Event_Feature; + + +#endif // LV2_EVENT_H + diff --git a/libs/ardour/lv2ext/lv2_event_helpers.h b/libs/ardour/lv2ext/lv2_event_helpers.h new file mode 100644 index 0000000000..8659b9bfae --- /dev/null +++ b/libs/ardour/lv2ext/lv2_event_helpers.h @@ -0,0 +1,243 @@ +/* lv2_event_helpers.h - Helper functions for the LV2 events extension. + * + * Copyright (C) 2008 Dave Robillard <dave@drobilla.net> + * + * This header is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This header 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this header; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + */ + +#ifndef LV2_EVENT_HELPERS_H +#define LV2_EVENT_HELPERS_H + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include "lv2_event.h" + +/** @file + * This header defines some helper functions for the the LV2 events extension + * with URI <http://lv2plug.in/ns/ext/event> ('lv2ev'). + * + * These functions are provided for convenience only, use of them is not + * required for supporting lv2ev (i.e. the events extension is defined by the + * raw buffer format described in lv2_event.h and NOT by this API). + * + * Note that these functions are all static inline which basically means: + * do not take the address of these functions. */ + + +/** Pad a size to 64 bits (for event sizes) */ +static inline uint16_t +lv2_event_pad_size(uint16_t size) +{ + return (size + 7) & (~7); +} + + +/** Initialize (empty, reset..) an existing event buffer. + * The contents of buf are ignored entirely and overwritten, except capacity + * which is unmodified. */ +static inline void +lv2_event_buffer_reset(LV2_Event_Buffer* buf, uint16_t stamp_type, uint8_t *data) +{ + buf->data = data; + buf->header_size = sizeof(LV2_Event_Buffer); + buf->stamp_type = stamp_type; + buf->event_count = 0; + buf->size = 0; +} + + +/** Allocate a new, empty event buffer. */ +static inline LV2_Event_Buffer* +lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type) +{ + LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); + if (buf != NULL) { + buf->capacity = capacity; + lv2_event_buffer_reset(buf, stamp_type, (uint8_t *)(buf + 1)); + return buf; + } else { + return NULL; + } +} + + +/** An iterator over an LV2_Event_Buffer. + * + * Multiple simultaneous read iterators over a single buffer is fine, + * but changing the buffer invalidates all iterators (e.g. RW Lock). */ +typedef struct { + LV2_Event_Buffer* buf; + uint32_t offset; +} LV2_Event_Iterator; + + +/** Reset an iterator to point to the start of @a buf. + * @return True if @a iter is valid, otherwise false (buffer is empty) */ +static inline bool +lv2_event_begin(LV2_Event_Iterator* iter, + LV2_Event_Buffer* buf) +{ + iter->buf = buf; + iter->offset = 0; + return (buf->size > 0); +} + + +/** Check if @a iter is valid.. + * @return True if @a iter is valid, otherwise false (past end of buffer) */ +static inline bool +lv2_event_is_valid(LV2_Event_Iterator* iter) +{ + return (iter->offset < iter->buf->size); +} + + +/** Advance @a iter forward one event. + * @a iter must be valid. + * @return True if @a iter is valid, otherwise false (reached end of buffer) */ +static inline bool +lv2_event_increment(LV2_Event_Iterator* iter) +{ + assert(lv2_event_is_valid(iter)); + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + iter->offset += lv2_event_pad_size(sizeof(LV2_Event) + ev->size); + + return true; +} + + +/** Dereference an event iterator (get the event currently pointed at). + * @a iter must be valid. + * @a data if non-NULL, will be set to point to the contents of the event + * returned. + * @return A Pointer to the event @a iter is currently pointing at, or NULL + * if the end of the buffer is reached (in which case @a data is + * also set to NULL). */ +static inline LV2_Event* +lv2_event_get(LV2_Event_Iterator* iter, + uint8_t** data) +{ + assert(lv2_event_is_valid(iter)); + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + if (data) + *data = (uint8_t*)ev + sizeof(LV2_Event); + + return ev; +} + + +/** Write an event at @a iter. + * The event (if any) pointed to by @iter will be overwritten, and @a iter + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data) +{ + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) + return false; + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size); + ++iter->buf->event_count; + + size = lv2_event_pad_size(sizeof(LV2_Event) + size); + iter->buf->size += size; + iter->offset += size; + + return true; +} + + +/** Reserve space for an event in the buffer and return a pointer to + the memory where the caller can write the event data, or NULL if there + is not enough room in the buffer. */ +static inline uint8_t* +lv2_event_reserve(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size) +{ + size = lv2_event_pad_size(size); + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) + return NULL; + + LV2_Event* const ev = (LV2_Event*)((uint8_t*)iter->buf->data + + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + ++iter->buf->event_count; + + size = lv2_event_pad_size(sizeof(LV2_Event) + size); + iter->buf->size += size; + iter->offset += size; + + return (uint8_t*)ev + sizeof(LV2_Event); +} + + +/** Write an event at @a iter. + * The event (if any) pointed to by @iter will be overwritten, and @a iter + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write_event(LV2_Event_Iterator* iter, + const LV2_Event* ev, + const uint8_t* data) +{ + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + ev->size) + return false; + + LV2_Event* const write_ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + *write_ev = *ev; + memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size); + ++iter->buf->event_count; + + const uint16_t size = lv2_event_pad_size(sizeof(LV2_Event) + ev->size); + iter->buf->size += size; + iter->offset += size; + + return true; +} + +#endif // LV2_EVENT_HELPERS_H + diff --git a/libs/ardour/lv2ext/lv2_uri_map.h b/libs/ardour/lv2ext/lv2_uri_map.h new file mode 100644 index 0000000000..1c2f5b0883 --- /dev/null +++ b/libs/ardour/lv2ext/lv2_uri_map.h @@ -0,0 +1,88 @@ +/* lv2_uri_map.h - C header file for the LV2 URI Map extension. + * + * Copyright (C) 2008 Dave Robillard <dave@drobilla.net> + * + * This header is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This header 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this header; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + */ + +#ifndef LV2_URI_MAP_H +#define LV2_URI_MAP_H + +#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" + +#include <stdint.h> + +/** @file + * This header defines the LV2 URI Map extension with the URI + * <http://lv2plug.in/ns/ext/uri-map> (preferred prefix 'lv2urimap'). + * + * This extension defines a simple mechanism for plugins to map URIs to + * integers, usually for performance reasons (e.g. processing events + * typed by URIs in real time). The expected use case is for plugins to + * map URIs to integers for things they 'understand' at instantiation time, + * and store those values for use in the audio thread without doing any string + * comparison. This allows the extensibility of RDF with the performance of + * integers (or centrally defined enumerations). + */ + + +/** Opaque pointer to host data. */ +typedef void* LV2_URI_Map_Callback_Data; + + +/** The data field of the LV2_Feature for this extension. + * + * To support this feature the host must pass an LV2_Feature struct to the + * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" + * and data pointed to an instance of this struct. + */ +typedef struct { + + /** Opaque pointer to host data. + * + * The plugin MUST pass this to any call to functions in this struct. + * Otherwise, it must not be interpreted in any way. + */ + LV2_URI_Map_Callback_Data callback_data; + + /** Get the numeric ID of a URI from the host. + * + * @param callback_data Must be the callback_data member of this struct. + * @param map The 'context' of this URI. Certain extensions may define a + * URI that must be passed here with certain restrictions on the + * return value (e.g. limited range). This value may be NULL if + * the plugin needs an ID for a URI in general. + * @param uri The URI to be mapped to an integer ID. + * + * This function is referentially transparent - any number of calls with + * the same arguments is guaranteed to return the same value over the life + * of a plugin instance (though the same URI may return different values + * with a different map parameter). However, this function is not + * necessarily very fast: plugins should cache any IDs they might need in + * performance critical situations. + * The return value 0 is reserved and means an ID for that URI could not + * be created for whatever reason. Extensions may define more precisely + * what this means, but in general plugins should gracefully handle 0 + * and consider whatever they wanted the URI for "unsupported". + */ + uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri); + +} LV2_URI_Map_Feature; + + +#endif // LV2_URI_MAP_H + diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index 2a1426ab61..fd42c45fe7 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -124,11 +124,37 @@ MidiBuffer::push_back(const Evoral::MIDIEvent<TimeType>& ev) return false; } + push_back(ev.time(), ev.size(), ev.buffer()); + + return true; +} + + +/** Push an event into the buffer. + * @return false if operation failed (not enough room) + */ +bool +MidiBuffer::push_back(TimeType time, size_t size, const uint8_t* data) +{ + const size_t stamp_size = sizeof(TimeType); + /*cerr << "MidiBuffer: pushing event @ " << ev.time() + << " size = " << ev.size() << endl;*/ + + if (_size + stamp_size + size >= _capacity) { + cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; + return false; + } + + if (!Evoral::midi_event_is_valid(data, size)) { + cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl; + return false; + } + uint8_t* const write_loc = _data + _size; - *((TimeType*)write_loc) = ev.time(); - memcpy(write_loc + stamp_size, ev.buffer(), ev.size()); + *((TimeType*)write_loc) = time; + memcpy(write_loc + stamp_size, data, size); - _size += stamp_size + ev.size(); + _size += stamp_size + size; _silent = false; return true; diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index f2f347a076..30084c6718 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -26,12 +26,10 @@ using namespace ARDOUR; using namespace std; MidiPort::MidiPort (const std::string& name, Flags flags) - : Port (name, DataType::MIDI, flags) + : Port (name, DataType::MIDI, flags) , _has_been_mixed_down (false) { - // FIXME: size kludge (see BufferSet::ensure_buffers) - // Jack needs to tell us this - _buffer = new MidiBuffer (1024 * 32); + _buffer = new MidiBuffer (raw_buffer_size(0)); } MidiPort::~MidiPort() @@ -39,7 +37,6 @@ MidiPort::~MidiPort() delete _buffer; } - void MidiPort::cycle_start (nframes_t nframes) { @@ -133,3 +130,9 @@ MidiPort::flush_buffers (nframes_t nframes, nframes_t offset) } } +size_t +MidiPort::raw_buffer_size (nframes_t nframes) const +{ + return jack_midi_max_event_size(jack_port_get_buffer(_jack_port, nframes)); +} + diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 3b16f25f9e..8451d538bf 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -579,6 +579,7 @@ MidiTrack::process_output_buffers (BufferSet& bufs, 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); } } diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index b4902bf7df..bf67e33493 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -234,7 +234,7 @@ PluginInsert::parameter_changed (Evoral::Parameter which, float val) if (which.type() != PluginAutomation) return; - vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); + Plugins::iterator i = _plugins.begin(); /* don't set the first plugin, just all the slaves */ @@ -249,7 +249,7 @@ PluginInsert::parameter_changed (Evoral::Parameter which, float val) void PluginInsert::set_block_size (nframes_t nframes) { - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { (*i)->set_block_size (nframes); } } @@ -257,7 +257,7 @@ PluginInsert::set_block_size (nframes_t nframes) void PluginInsert::activate () { - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { (*i)->activate (); } } @@ -265,7 +265,7 @@ PluginInsert::activate () void PluginInsert::deactivate () { - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { (*i)->deactivate (); } } @@ -280,8 +280,8 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off collect_signal_nframes = nframes; } - uint32_t in_index = 0; - uint32_t out_index = 0; + ChanMapping in_map(input_streams()); + ChanMapping out_map(output_streams()); /* Note that we've already required that plugins be able to handle in-place processing. @@ -315,15 +315,19 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off //std::cerr << " streams " << input_streams().n_audio() << std::endl; //std::cerr << "filling buffer with " << collect_signal_nframes << " frames at " << _signal_analysis_collected_nframes << std::endl; for (uint32_t i = 0; i < input_streams().n_audio(); ++i) { - _signal_analysis_input_bufferset.get_audio(i).read_from( + _signal_analysis_inputs.get_audio(i).read_from( bufs.get_audio(i), collect_signal_nframes, _signal_analysis_collected_nframes); // offset is for target buffer } } - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->connect_and_run (bufs, in_index, out_index, nframes, offset); + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*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)); + } } if (collect_signal_nframes > 0) { @@ -331,7 +335,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off //std::cerr << " output, bufs " << bufs.count().n_audio() << " count, " << bufs.available().n_audio() << " available" << std::endl; //std::cerr << " streams " << output_streams().n_audio() << std::endl; for (uint32_t i = 0; i < output_streams().n_audio(); ++i) { - _signal_analysis_output_bufferset.get_audio(i).read_from( + _signal_analysis_outputs.get_audio(i).read_from( bufs.get_audio(i), collect_signal_nframes, _signal_analysis_collected_nframes); // offset is for target buffer @@ -344,8 +348,8 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off _signal_analysis_collect_nframes_max = 0; _signal_analysis_collected_nframes = 0; - AnalysisDataGathered(&_signal_analysis_input_bufferset, - &_signal_analysis_output_bufferset); + AnalysisDataGathered(&_signal_analysis_inputs, + &_signal_analysis_outputs); } } /* leave remaining channel buffers alone */ @@ -354,12 +358,12 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off void PluginInsert::silence (nframes_t nframes) { - uint32_t in_index = 0; - uint32_t out_index = 0; + ChanMapping in_map(input_streams()); + ChanMapping out_map(output_streams()); if (active()) { - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_index, out_index, nframes, 0); + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_map, out_map, nframes, 0); } } } @@ -546,12 +550,11 @@ PluginInsert::configure_io (ChanCount in, ChanCount out) // current buffer size here. each request for data fills in these // buffers and the analyser makes sure it gets enough data for the // analysis window - _signal_analysis_input_bufferset.ensure_buffers (in, session().engine().frames_per_cycle()); - _signal_analysis_input_bufferset.set_count(in); - - _signal_analysis_output_bufferset.ensure_buffers(out, session().engine().frames_per_cycle()); - _signal_analysis_output_bufferset.set_count(out); - + session().ensure_buffer_set (_signal_analysis_inputs, in); + _signal_analysis_inputs.set_count (in); + + session().ensure_buffer_set (_signal_analysis_outputs, out); + _signal_analysis_outputs.set_count (out); return Processor::configure_io (in, out); } @@ -759,7 +762,7 @@ PluginInsert::set_state(const XMLNode& node) for (niter = nlist.begin(); niter != nlist.end(); ++niter) { if ((*niter)->name() == plugin->state_node_name()) { - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { (*i)->set_state (**niter); } break; @@ -939,7 +942,7 @@ PluginInsert::PluginControl::set_value (float val) } - for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugin->_plugins.begin(); + for (Plugins::iterator i = _plugin->_plugins.begin(); i != _plugin->_plugins.end(); ++i) { (*i)->set_parameter (_list->parameter().id(), val); } @@ -989,3 +992,16 @@ PluginInsert::get_impulse_analysis_plugin() return ret; } +void +PluginInsert::collect_signal_for_analysis(nframes_t nframes) +{ + // called from outside the audio thread, so this should be safe + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + _session.ensure_buffer_set(_signal_analysis_inputs, input_streams()); + _session.ensure_buffer_set(_signal_analysis_outputs, output_streams()); + } + + _signal_analysis_collected_nframes = 0; + _signal_analysis_collect_nframes_max = nframes; +} + diff --git a/libs/ardour/processor.cc b/libs/ardour/processor.cc index e521e3702c..8ea45de406 100644 --- a/libs/ardour/processor.cc +++ b/libs/ardour/processor.cc @@ -248,12 +248,12 @@ Processor::set_state (const XMLNode& node) bool Processor::configure_io (ChanCount in, ChanCount out) { - /* this class assumes static output stream count. - Derived classes must override, and must set "out" - to reflect "in" before calling this. - */ + /* This class assumes 1:1 input:output.static output stream count. + Derived classes must override and set _configured_output appropriately + if this is not the case */ _configured_input = in; + _configured_output = out; _configured = true; return true; diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 5a600cca7e..6179af5028 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -85,7 +85,7 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type) void Route::init () { - processor_max_outs.reset(); + processor_max_streams.reset(); _muted = false; _soloed = false; _solo_safe = false; @@ -1049,7 +1049,15 @@ Route::process_output_buffers (BufferSet& bufs, ChanCount Route::n_process_buffers () { - return max (n_inputs(), processor_max_outs); + return max (n_inputs(), processor_max_streams); +} + +void +Route::setup_peak_meters() +{ + ChanCount max_streams = std::max (_inputs.count(), _outputs.count()); + max_streams = std::max (max_streams, processor_max_streams); + _meter->configure_io (max_streams, max_streams); } void @@ -1169,9 +1177,9 @@ Route::set_mute (bool yn, void *src) int Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err) { - ChanCount old_pmo = processor_max_outs; + ChanCount old_pms = processor_max_streams; - if (!_session.engine().connected()) { + if (!_session.engine().connected() || !processor) { return 1; } @@ -1220,7 +1228,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* _user_latency = 0; } - if (processor_max_outs != old_pmo || old_pmo == ChanCount::ZERO) { + if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) { reset_panner (); } @@ -1237,7 +1245,7 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err) differences between this and ::add_processor() */ - ChanCount old_pmo = processor_max_outs; + ChanCount old_pms = processor_max_streams; if (!_session.engine().connected()) { return 1; @@ -1281,7 +1289,7 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err) _user_latency = 0; } - if (processor_max_outs != old_pmo || old_pmo == ChanCount::ZERO) { + if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) { reset_panner (); } @@ -1428,7 +1436,7 @@ Route::pre_fader_streams() const void Route::clear_processors (Placement p) { - const ChanCount old_pmo = processor_max_outs; + const ChanCount old_pms = processor_max_streams; if (!_session.engine().connected()) { return; @@ -1452,11 +1460,11 @@ Route::clear_processors (Placement p) } /* FIXME: can't see how this test can ever fire */ - if (processor_max_outs != old_pmo) { + if (processor_max_streams != old_pms) { reset_panner (); } - processor_max_outs.reset(); + processor_max_streams.reset(); _have_internal_generator = false; processors_changed (); /* EMIT SIGNAL */ } @@ -1464,13 +1472,13 @@ Route::clear_processors (Placement p) int Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err) { - ChanCount old_pmo = processor_max_outs; + ChanCount old_pms = processor_max_streams; if (!_session.engine().connected()) { return 1; } - processor_max_outs.reset(); + processor_max_streams.reset(); { Glib::RWLock::WriterLock lm (_processor_lock); @@ -1538,7 +1546,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream } } - if (old_pmo != processor_max_outs) { + if (old_pms != processor_max_streams) { reset_panner (); } @@ -1570,7 +1578,7 @@ Route::_reset_processor_counts (ProcessorStreams* err) uint32_t max_audio = 0; uint32_t max_midi = 0; - processor_max_outs.reset (); + processor_max_streams.reset (); /* Step 1: build a map that links each insert to an in/out channel count @@ -1664,7 +1672,7 @@ Route::_reset_processor_counts (ProcessorStreams* err) recompute: - processor_max_outs.reset (); + processor_max_streams.reset (); prev = _processors.end(); for (r = _processors.begin(); r != _processors.end(); prev = r, ++r) { @@ -1684,14 +1692,16 @@ Route::_reset_processor_counts (ProcessorStreams* err) } 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_outs.set (DataType::AUDIO, max_audio); - processor_max_outs.set (DataType::MIDI, max_midi); - + processor_max_streams.set (DataType::AUDIO, max_audio); + processor_max_streams.set (DataType::MIDI, max_midi); + /* we're done */ return 0; @@ -1701,9 +1711,9 @@ Route::_reset_processor_counts (ProcessorStreams* err) max_midi = max ((*r)->output_streams ().n_midi(), max_midi); } - processor_max_outs.set (DataType::AUDIO, max_audio); - processor_max_outs.set (DataType::MIDI, max_midi); - + processor_max_streams.set (DataType::AUDIO, max_audio); + processor_max_streams.set (DataType::MIDI, max_midi); + return ret; } @@ -1821,7 +1831,7 @@ Route::sort_processors (ProcessorStreams* err) { ProcessorSorter comparator; Glib::RWLock::WriterLock lm (_processor_lock); - ChanCount old_pmo = processor_max_outs; + ChanCount old_pms = processor_max_streams; /* the sweet power of C++ ... */ @@ -1831,7 +1841,7 @@ Route::sort_processors (ProcessorStreams* err) if (_reset_processor_counts (err)) { _processors = as_it_was_before; - processor_max_outs = old_pmo; + processor_max_streams = old_pms; return -1; } } @@ -2332,15 +2342,18 @@ Route::_set_processor_states(const XMLNodeList &nlist) (*i)->id().print (buf, sizeof (buf)); - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { // legacy sessions (IOProcessor as a child of Processor, both is-a IO) - if (strncmp(buf,(*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { + XMLNode* ioproc_node = (*niter)->child(X_("IOProcessor")); + if (ioproc_node && strncmp(buf, ioproc_node->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { processorInStateList = true; break; - } else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { - processorInStateList = true; + } else { + XMLProperty* id_prop = (*niter)->property(X_("id")); + if (id_prop && strncmp(buf, id_prop->value().c_str(), sizeof(buf)) == 0) { + processorInStateList = true; + } break; } } @@ -2349,7 +2362,6 @@ Route::_set_processor_states(const XMLNodeList &nlist) remove_processor (*i); } - i = tmp; } @@ -2364,11 +2376,16 @@ Route::_set_processor_states(const XMLNodeList &nlist) while (o != _processors.end()) { (*o)->id().print (buf, sizeof (buf)); - if ( strncmp(buf, (*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) + XMLNode* ioproc_node = (*niter)->child(X_("IOProcessor")); + if (ioproc_node && strncmp(buf, ioproc_node->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) { break; - else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) - break; - + } else { + XMLProperty* id_prop = (*niter)->property(X_("id")); + if (id_prop && strncmp(buf, id_prop->value().c_str(), sizeof(buf)) == 0) { + break; + } + } + ++o; } @@ -2732,7 +2749,7 @@ Route::pans_required () const return 0; } - return max (n_inputs ().n_audio(), processor_max_outs.n_audio()); + return max (n_inputs ().n_audio(), processor_max_streams.n_audio()); } int diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index c5904de869..0297aa76bb 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -3849,21 +3849,24 @@ Session::ensure_buffers (ChanCount howmany) return; // too early? (is this ok?) } - // We need at least 2 MIDI scratch buffers to mix/merge - if (howmany.n_midi() < 2) { - howmany.set_midi(2); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + size_t count = std::max(_scratch_buffers->available().get(*t), howmany.get(*t)); + _scratch_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t)); + _mix_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t)); + _silent_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t)); } - // FIXME: JACK needs to tell us maximum MIDI buffer size - // Using nasty assumption (max # events == nframes) for now - - _scratch_buffers->ensure_buffers(howmany, current_block_size); - _mix_buffers->ensure_buffers(howmany, current_block_size); - _silent_buffers->ensure_buffers(howmany, current_block_size); - allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false); } +void +Session::ensure_buffer_set(BufferSet& buffers, const ChanCount& count) +{ + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + buffers.ensure_buffers(*t, count.get(*t), _engine.raw_buffer_size(*t)); + } +} + uint32_t Session::next_insert_id () { @@ -4149,7 +4152,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end, to_do = len; /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers(nchans, chunk_size); + buffers.ensure_buffers(DataType::AUDIO, nchans.n_audio(), chunk_size); buffers.set_count(nchans); for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) { diff --git a/libs/ardour/template_utils.cc b/libs/ardour/template_utils.cc index 6dac270a50..22fc6f38df 100644 --- a/libs/ardour/template_utils.cc +++ b/libs/ardour/template_utils.cc @@ -94,8 +94,6 @@ find_session_templates (vector<TemplateInfo>& template_names) continue; } - XMLNode* root = tree.root(); - TemplateInfo rti; rti.name = basename_nosuffix (fullpath); diff --git a/libs/ardour/uri_map.cc b/libs/ardour/uri_map.cc new file mode 100644 index 0000000000..fc82255ef8 --- /dev/null +++ b/libs/ardour/uri_map.cc @@ -0,0 +1,74 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard <http://drobilla.net> + * + * Ingen 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. + * + * Ingen 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define __STDC_LIMIT_MACROS 1 +#include <cassert> +#include <iostream> +#include <stdint.h> +#include "ardour/uri_map.h" + +using namespace std; + +namespace ARDOUR { + + +URIMap::URIMap() + : next_uri_id(1) +{ + uri_map_feature_data.uri_to_id = &URIMap::uri_map_uri_to_id; + uri_map_feature_data.callback_data = this; + uri_map_feature.URI = LV2_URI_MAP_URI; + uri_map_feature.data = &uri_map_feature_data; +} + + +uint32_t +URIMap::uri_to_id(const char* map, + const char* uri) +{ + return uri_map_uri_to_id(this, map, uri); +} + + +uint32_t +URIMap::uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri) +{ + // TODO: map ignored, < UINT16_MAX assumed + + URIMap* me = (URIMap*)callback_data; + uint32_t ret = 0; + + Map::iterator i = me->uri_map.find(uri); + if (i != me->uri_map.end()) { + ret = i->second; + } else { + ret = me->next_uri_id++; + me->uri_map.insert(make_pair(string(uri), ret)); + } + + /*cout << "URI MAP (" << (map ? (void*)map : NULL) + << "): " << uri << " -> " << ret << endl;*/ + + assert(ret <= UINT16_MAX); + return ret; +} + + +} // namespace ARDOUR + diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index 2566357f87..0aca137150 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -383,7 +383,9 @@ VSTPlugin::automatable () const } int -VSTPlugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset) +VSTPlugin::connect_and_run (BufferSet& bufs, + ChanMapping in_map, ChanMapping out_map, + nframes_t nframes, nframes_t offset) { float *ins[_plugin->numInputs]; float *outs[_plugin->numOutputs]; diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 91a1bd0e50..2c612f7b9f 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -124,8 +124,8 @@ def build(bld): export_status.cc export_timespan.cc export_utilities.cc - filename_extensions.cc file_source.cc + filename_extensions.cc filesystem_paths.cc filter.cc find_session.cc @@ -139,6 +139,7 @@ def build(bld): ladspa_plugin.cc location.cc location_importer.cc + lv2_event_buffer.cc meter.cc midi_buffer.cc midi_clock_slave.cc @@ -198,7 +199,7 @@ def build(bld): sndfilesource.cc source.cc source_factory.cc - strip_silence.cc + strip_silence.cc svn_revision.cc tape_file_matcher.cc template_utils.cc @@ -209,6 +210,7 @@ def build(bld): transient_detector.cc user_bundle.cc utils.cc + uri_map.cc version.cc ''' obj.export_incdirs = ['.'] |