Preliminary MIDI plugin support.

git-svn-id: svn://localhost/ardour2/branches/3.0@5036 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard
2009-05-04 15:50:51 +00:00
parent 9b06b1da0c
commit ca10cc82a4
46 changed files with 1505 additions and 285 deletions

View File

@@ -29,11 +29,12 @@ using namespace ARDOUR;
using namespace PBD;
void
LV2PluginUI::lv2_ui_write(LV2UI_Controller controller,
uint32_t port_index,
uint32_t buffer_size,
uint32_t format,
const void* buffer)
LV2PluginUI::lv2_ui_write(
LV2UI_Controller controller,
uint32_t port_index,
uint32_t buffer_size,
uint32_t format,
const void* buffer)
{
LV2PluginUI* me = (LV2PluginUI*)controller;
if (*(float*)buffer != me->_values[port_index])

View File

@@ -118,12 +118,6 @@ Panner2d::reset (uint32_t n_inputs)
case 2:
pucks[0]->set_text ("R");
assert(existing_pucks >= 0);
// FIXME: Impossible (unsigned)
if (existing_pucks < 0) {
pucks[0]->x.set_value (0.5f);
pucks[1]->y.set_value (0.25f);
}
pucks[0]->visible = true;
pucks[1]->set_text ("L");
if (existing_pucks < 2) {

View File

@@ -25,6 +25,7 @@
#include "gui_thread.h"
#include "ardour/audio_buffer.h"
#include "ardour/data_type.h"
#include "ardour/chan_mapping.h"
#include <gtkmm/box.h>
#include <gtkmm/button.h>
@@ -310,18 +311,16 @@ PluginEqGui::run_impulse_analysis()
// Create the impulse, can't use silence() because consecutive calls won't work
for (uint32_t i = 0; i < inputs; ++i) {
ARDOUR::AudioBuffer &buf = _bufferset.get_audio(i);
ARDOUR::Sample *d = buf.data();
ARDOUR::AudioBuffer& buf = _bufferset.get_audio(i);
ARDOUR::Sample* d = buf.data();
memset(d, 0, sizeof(ARDOUR::Sample)*_buffer_size);
*d = 1.0;
}
uint32_t x,y;
x=y=0;
ARDOUR::ChanMapping in_map(_plugin->get_info()->n_inputs);
ARDOUR::ChanMapping out_map(_plugin->get_info()->n_outputs);
_plugin->connect_and_run(_bufferset, x, y, _buffer_size, (nframes_t)0);
_plugin->connect_and_run(_bufferset, in_map, out_map, _buffer_size, (nframes_t)0);
nframes_t f = _plugin->signal_latency();
// Adding user_latency() could be interesting
@@ -378,8 +377,9 @@ PluginEqGui::run_impulse_analysis()
memset(d, 0, sizeof(ARDOUR::Sample)*_buffer_size);
}
x=y=0;
_plugin->connect_and_run(_bufferset, x, y, _buffer_size, (nframes_t)0);
in_map = ARDOUR::ChanMapping(_plugin->get_info()->n_inputs);
out_map = ARDOUR::ChanMapping(_plugin->get_info()->n_outputs);
_plugin->connect_and_run(_bufferset, in_map, out_map, _buffer_size, (nframes_t)0);
}
} while ( frames_left > 0);

View File

@@ -303,25 +303,15 @@ PluginSelector::refiller (const PluginInfoList& plugs, const::std::string& filte
newrow[plugin_columns.creator] = creator;
if ((*i)->n_inputs.n_total() < 0) { // FIXME: Impossible (unsigned)
newrow[plugin_columns.audio_ins] = "various";
newrow[plugin_columns.midi_ins] = "various";
} else {
snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_audio());
newrow[plugin_columns.audio_ins] = buf;
snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_midi());
newrow[plugin_columns.midi_ins] = buf;
}
if ((*i)->n_outputs.n_total() < 0) { // FIXME: Impossible (unsigned)
newrow[plugin_columns.audio_outs] = "various";
newrow[plugin_columns.midi_outs] = "various";
} else {
snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_audio());
newrow[plugin_columns.audio_outs] = buf;
snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_midi());
newrow[plugin_columns.midi_outs] = buf;
}
snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_audio());
newrow[plugin_columns.audio_ins] = buf;
snprintf (buf, sizeof(buf), "%d", (*i)->n_inputs.n_midi());
newrow[plugin_columns.midi_ins] = buf;
snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_audio());
newrow[plugin_columns.audio_outs] = buf;
snprintf (buf, sizeof(buf), "%d", (*i)->n_outputs.n_midi());
newrow[plugin_columns.midi_outs] = buf;
newrow[plugin_columns.plugin] = *i;
}

View File

@@ -434,51 +434,30 @@ ProcessorBox::weird_plugin_dialog (Plugin& p, Route::ProcessorStreams streams, b
ArdourDialog dialog (_("ardour: weird plugin dialog"));
Label label;
/* i hate this kind of code */
string text = string_compose(_("You attempted to add the plugin \"%1\" at index %2.\n"),
p.name(), streams.index);
if (streams.count > p.get_info()->n_inputs) {
label.set_text (string_compose (_(
"You attempted to add a plugin (%1).\n"
"The plugin has %2 inputs\n"
"but at the processorion point, there are\n"
"%3 active signal streams.\n"
"\n"
"This makes no sense - you are throwing away\n"
"part of the signal."),
p.name(),
p.get_info()->n_inputs.n_total(),
streams.count.n_total()));
} else if (streams.count < p.get_info()->n_inputs) {
label.set_text (string_compose (_(
"You attempted to add a plugin (%1).\n"
"The plugin has %2 inputs\n"
"but at the processorion point there are\n"
"only %3 active signal streams.\n"
"\n"
"This makes no sense - unless the plugin supports\n"
"side-chain inputs. A future version of Ardour will\n"
"support this type of configuration."),
p.name(),
p.get_info()->n_inputs.n_total(),
streams.count.n_total()));
} else {
label.set_text (string_compose (_(
"You attempted to add a plugin (%1).\n"
"\n"
"The I/O configuration doesn't make sense:\n"
"\n"
"The plugin has %2 inputs and %3 outputs.\n"
"The track/bus has %4 inputs and %5 outputs.\n"
"The processorion point, has %6 active signals.\n"
"\n"
"Ardour does not understand what to do in such situations.\n"),
p.name(),
p.get_info()->n_inputs.n_total(),
p.get_info()->n_outputs.n_total(),
io->n_inputs().n_total(),
io->n_outputs().n_total(),
streams.count.n_total()));
bool has_midi = streams.count.n_midi() > 0 || p.get_info()->n_inputs.n_midi() > 0;
bool has_audio = streams.count.n_audio() > 0 || p.get_info()->n_inputs.n_audio() > 0;
text += _("\nThis plugin has:\n");
if (has_midi) {
text += string_compose("\t%1 ", p.get_info()->n_inputs.n_midi()) + _("MIDI input(s)\n");
}
if (has_audio) {
text += string_compose("\t%1 ", p.get_info()->n_inputs.n_audio()) + _("audio input(s)\n");
}
text += "\nBut at the insertion point, there are:\n";
if (has_midi) {
text += string_compose("\t%1 ", streams.count.n_midi()) + _("MIDI channel(s)\n");
}
if (has_audio) {
text += string_compose("\t%1 ", streams.count.n_audio()) + _("audio channel(s)\n");
}
text += "\nArdour is unable to insert this plugin here.\n";
label.set_text(text);
dialog.get_vbox()->pack_start (label);
dialog.add_button (Stock::OK, RESPONSE_ACCEPT);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
cycles_t now;
cycles_t then = get_cycles ();
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++;
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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -124,11 +124,37 @@ MidiBuffer::push_back(const Evoral::MIDIEvent<TimeType>& ev)
return false;
}
uint8_t* const write_loc = _data + _size;
*((TimeType*)write_loc) = ev.time();
memcpy(write_loc + stamp_size, ev.buffer(), ev.size());
push_back(ev.time(), ev.size(), ev.buffer());
return true;
}
_size += stamp_size + ev.size();
/** 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) = time;
memcpy(write_loc + stamp_size, data, size);
_size += stamp_size + size;
_silent = false;
return true;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -94,8 +94,6 @@ find_session_templates (vector<TemplateInfo>& template_names)
continue;
}
XMLNode* root = tree.root();
TemplateInfo rti;
rti.name = basename_nosuffix (fullpath);

74
libs/ardour/uri_map.cc Normal file
View File

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

View File

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

View File

@@ -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 = ['.']