Generic MIDI control now saves+restores its state; PBD::ID now requires a buffer size for its print() method

git-svn-id: svn://localhost/ardour2/trunk@949 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2006-10-05 01:49:32 +00:00
parent 5ad68cf2c5
commit ffdf5ada61
31 changed files with 209 additions and 77 deletions

View file

@ -2162,7 +2162,7 @@ Editor::get_state ()
XMLNode* node = new XMLNode ("Editor");
char buf[32];
_id.print (buf);
_id.print (buf, sizeof (buf));
node->add_property ("id", buf);
if (is_realized()) {

View file

@ -383,13 +383,7 @@ int main (int argc, char *argv[])
cout << _("Ardour/GTK ")
<< VERSIONSTRING
<< _("\n (built using ")
<< gtk_ardour_major_version << '.'
<< gtk_ardour_minor_version << '.'
<< gtk_ardour_micro_version
<< _(" with libardour ")
<< libardour_major_version << '.'
<< libardour_minor_version << '.'
<< libardour_micro_version
<< ARDOUR::get_ardour_revision ()
#ifdef __GNUC__
<< _(" and GCC version ") << __VERSION__
#endif

View file

@ -24,6 +24,9 @@ struct ControlProtocolInfo {
bool requested;
bool mandatory;
XMLNode* state;
ControlProtocolInfo() : descriptor (0), protocol (0), state (0) {}
~ControlProtocolInfo() { if (state) { delete state; } }
};
class ControlProtocolManager : public sigc::trackable, public Stateful
@ -46,6 +49,8 @@ struct ControlProtocolInfo {
static const std::string state_node_name;
void set_protocol_states (const XMLNode&);
int set_state (const XMLNode&);
XMLNode& get_state (void);

View file

@ -295,7 +295,7 @@ public:
gain_t initial, gain_t target, bool invert_polarity);
struct GainControllable : public PBD::Controllable {
GainControllable (IO& i) : io (i) {}
GainControllable (std::string name, IO& i) : Controllable (name), io (i) {}
void set_value (float val);
float get_value (void) const;

View file

@ -114,7 +114,7 @@ class StreamPanner : public sigc::trackable, public Stateful
bool _muted;
struct PanControllable : public PBD::Controllable {
PanControllable (StreamPanner& p) : panner (p) {}
PanControllable (std::string name, StreamPanner& p) : Controllable (name), panner (p) {}
StreamPanner& panner;

View file

@ -98,9 +98,8 @@ class Plugin : public PBD::StatefulDestructible, public sigc::trackable
float step;
float smallstep;
float largestep;
bool min_unbound;
bool max_unbound;
bool min_unbound;
bool max_unbound;
};
virtual uint32_t unique_id() const = 0;
@ -162,7 +161,7 @@ class Plugin : public PBD::StatefulDestructible, public sigc::trackable
void setup_controls ();
struct PortControllable : public PBD::Controllable {
PortControllable (Plugin&, uint32_t abs_port_id,
PortControllable (std::string name, Plugin&, uint32_t abs_port_id,
float lower, float upper, bool toggled, bool logarithmic);
void set_value (float);

View file

@ -222,7 +222,7 @@ class Route : public IO
SoloControl
};
ToggleControllable (Route&, ToggleType);
ToggleControllable (std::string name, Route&, ToggleType);
void set_value (float);
float get_value (void) const;

View file

@ -93,7 +93,7 @@ class AudioRegion;
class Region;
class Playlist;
class VSTPlugin;
class ControlProtocolManager;
class ControlProtocolInfo;
struct AudioExportSpecification;
struct RouteGroup;
@ -415,7 +415,7 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible
XMLNode& get_state();
int set_state(const XMLNode& node); // not idempotent
XMLNode& get_template();
void add_instant_xml (XMLNode&, const std::string& dir);
enum StateOfTheState {
@ -904,6 +904,9 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible
PBD::Controllable* controllable_by_id (const PBD::ID&);
void add_controllable (PBD::Controllable*);
void remove_controllable (PBD::Controllable*);
protected:
friend class AudioEngine;
void set_block_size (nframes_t nframes);
@ -1667,19 +1670,19 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible
LayerModel layer_model;
CrossfadeModel xfade_model;
typedef std::list<PBD::Controllable*> Controllables;
typedef std::set<PBD::Controllable*> Controllables;
Glib::Mutex controllables_lock;
Controllables controllables;
void add_controllable (PBD::Controllable*);
void remove_controllable (PBD::Controllable*);
void reset_native_file_format();
bool first_file_data_format_reset;
bool first_file_header_format_reset;
void config_changed (const char*);
void add_control_protocol (const ControlProtocolInfo* const, XMLNode*);
XMLNode& get_control_protocol_state ();
};
} // namespace ARDOUR

View file

@ -1759,7 +1759,7 @@ AudioDiskstream::get_state ()
node->add_property ("speed", buf);
node->add_property("name", _name);
id().print (buf);
id().print (buf, sizeof (buf));
node->add_property("id", buf);
if (!capturing_sources.empty() && _session.get_record_enabled()) {

View file

@ -246,6 +246,10 @@ AudioTrack::set_state (const XMLNode& node)
sscanf (prop->value().c_str(), "%d", &x);
set_remote_control_id (x);
}
} else if (child->name() == X_("recenable")) {
_rec_enable_control.set_state (*child);
_session.add_controllable (&_rec_enable_control);
}
}
@ -273,7 +277,7 @@ AudioTrack::state(bool full_state)
for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) {
inode = new XMLNode (X_("insert"));
(*i)->id.print (buf);
(*i)->id.print (buf, sizeof (buf));
inode->add_property (X_("id"), buf);
inode->add_child_copy ((*i)->state);
@ -317,9 +321,11 @@ AudioTrack::state(bool full_state)
diskstream.
*/
_diskstream->id().print (buf);
_diskstream->id().print (buf, sizeof (buf));
root.add_property ("diskstream-id", buf);
root.add_child_nocopy (_rec_enable_control.get_state());
return root;
}

View file

@ -688,7 +688,7 @@ AudioRegion::state (bool full)
for (uint32_t n=0; n < sources.size(); ++n) {
snprintf (buf2, sizeof(buf2), "source-%d", n);
sources[n]->id().print (buf);
sources[n]->id().print (buf, sizeof (buf));
node.add_property (buf2, buf);
}

View file

@ -50,7 +50,7 @@ ControlProtocolManager::set_session (Session& s)
instantiate (**i);
(*i)->requested = false;
if ((*i)->state) {
if ((*i)->protocol && (*i)->state) {
(*i)->protocol->set_state (*(*i)->state);
}
}
@ -93,6 +93,10 @@ ControlProtocolManager::instantiate (ControlProtocolInfo& cpi)
Glib::Mutex::Lock lm (protocols_lock);
control_protocols.push_back (cpi.protocol);
if (cpi.state) {
cpi.protocol->set_state (*cpi.state);
}
return cpi.protocol;
}
@ -154,7 +158,7 @@ ControlProtocolManager::discover_control_protocols (string path)
vector<string *> *found;
PathScanner scanner;
cerr << "looking for control protocols in " << path << endl;
info << string_compose (_("looking for control protocols in %1"), path) << endmsg;
found = scanner (path, protocol_filter, 0, false, true);
@ -261,11 +265,20 @@ ControlProtocolManager::set_state (const XMLNode& node)
for (citer = clist.begin(); citer != clist.end(); ++citer) {
if ((*citer)->name() == X_("Protocol")) {
prop = (*citer)->property (X_("active"));
if (prop && prop->value() == X_("yes")) {
if ((prop = (*citer)->property (X_("name"))) != 0) {
ControlProtocolInfo* cpi = cpi_by_name (prop->value());
if (cpi) {
if (!(*citer)->children().empty()) {
cpi->state = (*citer)->children().front ();
} else {
cpi->state = 0;
}
if (_session) {
instantiate (*cpi);
} else {
@ -294,3 +307,34 @@ ControlProtocolManager::get_state (void)
return *root;
}
void
ControlProtocolManager::set_protocol_states (const XMLNode& node)
{
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLProperty* prop;
nlist = node.children();
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLNode* child = (*niter);
if ((prop = child->property ("name")) == 0) {
error << _("control protocol XML node has no name property. Ignored.") << endmsg;
continue;
}
ControlProtocolInfo* cpi = cpi_by_name (prop->value());
if (!cpi) {
warning << string_compose (_("control protocol \"%1\" is not known. Ignored"), prop->value()) << endmsg;
continue;
}
/* copy the node so that ownership is clear */
cpi->state = new XMLNode (*child);
}
}

View file

@ -679,9 +679,9 @@ Crossfade::get_state ()
char buf[64];
LocaleGuard lg (X_("POSIX"));
_out->id().print (buf);
_out->id().print (buf, sizeof (buf));
node->add_property ("out", buf);
_in->id().print (buf);
_in->id().print (buf, sizeof (buf));
node->add_property ("in", buf);
node->add_property ("active", (_active ? "yes" : "no"));
node->add_property ("follow-overlap", (_follow_overlap ? "yes" : "no"));

View file

@ -107,7 +107,7 @@ IO::IO (Session& s, string name,
: _session (s),
_name (name),
_default_type(default_type),
_gain_control (*this),
_gain_control (X_("gaincontrol"), *this),
_gain_automation_curve (0.0, 2.0, 1.0),
_input_minimum (input_min),
_input_maximum (input_max),
@ -1431,7 +1431,7 @@ IO::state (bool full_state)
Glib::Mutex::Lock lm (io_lock);
node->add_property("name", _name);
id().print (buf);
id().print (buf, sizeof (buf));
node->add_property("id", buf);
str = "";
@ -1513,6 +1513,7 @@ IO::state (bool full_state)
}
node->add_child_nocopy (_panner->state (full_state));
node->add_child_nocopy (_gain_control.get_state ());
snprintf (buf, sizeof(buf), "%2.12f", gain());
node->add_property ("gain", buf);
@ -1627,9 +1628,15 @@ IO::set_state (const XMLNode& node)
}
for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
if ((*iter)->name() == "Panner") {
_panner->set_state (**iter);
}
if ((*iter)->name() == X_("gaincontrol")) {
_gain_control.set_state (**iter);
_session.add_controllable (&_gain_control);
}
}
if ((prop = node.property ("automation-state")) != 0) {

View file

@ -266,7 +266,7 @@ Location::get_state (void)
node->add_child_nocopy(cd_info_node(m->first, m->second));
}
id().print (buf);
id().print (buf, sizeof (buf));
node->add_property("id", buf);
node->add_property ("name", name());
snprintf (buf, sizeof (buf), "%u", start());

View file

@ -67,7 +67,7 @@ static double direct_pan_to_control (pan_t val) {
StreamPanner::StreamPanner (Panner& p)
: parent (p),
_control (*this)
_control (X_("panner"), *this)
{
_muted = false;
@ -543,6 +543,7 @@ EqualPowerStereoPanner::state (bool full_state)
root->add_property (X_("automation-style"), buf);
StreamPanner::add_state (*root);
root->add_child_nocopy (_control.get_state ());
return *root;
}
@ -575,6 +576,13 @@ EqualPowerStereoPanner::set_state (const XMLNode& node)
}
StreamPanner::set_state (node);
for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
if ((*iter)->name() == X_("panner")) {
_control.set_state (**iter);
parent.session().add_controllable (&_control);
}
}
return 0;
}

View file

@ -1420,7 +1420,7 @@ Playlist::state (bool full_state)
node->add_property (X_("name"), _name);
_orig_diskstream_id.print (buf);
_orig_diskstream_id.print (buf, sizeof (buf));
node->add_property (X_("orig_diskstream_id"), buf);
node->add_property (X_("frozen"), _frozen ? "yes" : "no");

View file

@ -95,15 +95,17 @@ Plugin::get_nth_control (uint32_t n)
Plugin::ParameterDescriptor desc;
get_parameter_descriptor (n, desc);
controls[n] = new PortControllable (*this, n, desc.lower, desc.upper, desc.toggled, desc.logarithmic);
controls[n] = new PortControllable (describe_parameter (n), *this, n,
desc.lower, desc.upper, desc.toggled, desc.logarithmic);
}
return controls[n];
}
Plugin::PortControllable::PortControllable (Plugin& p, uint32_t port_id, float low, float up, bool t, bool loga)
: plugin (p), absolute_port (port_id)
Plugin::PortControllable::PortControllable (string name, Plugin& p, uint32_t port_id,
float low, float up, bool t, bool loga)
: Controllable (name), plugin (p), absolute_port (port_id)
{
toggled = t;
logarithmic = loga;

View file

@ -233,7 +233,7 @@ Redirect::state (bool full_state)
path = _session.snap_name();
path += "-redirect-";
id().print (buf);
id().print (buf, sizeof (buf));
path += buf;
path += ".automation";

View file

@ -860,7 +860,7 @@ Region::state (bool full_state)
XMLNode *node = new XMLNode ("Region");
char buf[64];
_id.print (buf);
_id.print (buf, sizeof (buf));
node->add_property ("id", buf);
node->add_property ("name", _name);
snprintf (buf, sizeof (buf), "%u", _start);

View file

@ -55,16 +55,16 @@ uint32_t Route::order_key_cnt = 0;
Route::Route (Session& sess, string name, int input_min, int input_max, int output_min, int output_max, Flag flg, DataType default_type)
: IO (sess, name, input_min, input_max, output_min, output_max, default_type),
_flags (flg),
_solo_control (*this, ToggleControllable::SoloControl),
_mute_control (*this, ToggleControllable::MuteControl)
_solo_control (X_("solo"), *this, ToggleControllable::SoloControl),
_mute_control (X_("mute"), *this, ToggleControllable::MuteControl)
{
init ();
}
Route::Route (Session& sess, const XMLNode& node)
: IO (sess, "route"),
_solo_control (*this, ToggleControllable::SoloControl),
_mute_control (*this, ToggleControllable::MuteControl)
_solo_control (X_("solo"), *this, ToggleControllable::SoloControl),
_mute_control (X_("mute"), *this, ToggleControllable::MuteControl)
{
init ();
set_state (node);
@ -1379,6 +1379,8 @@ Route::state(bool full_state)
node->add_property ("order-keys", order_string);
node->add_child_nocopy (IO::state (full_state));
node->add_child_nocopy (_solo_control.get_state ());
node->add_child_nocopy (_mute_control.get_state ());
if (_control_outs) {
XMLNode* cnode = new XMLNode (X_("ControlOuts"));
@ -1684,6 +1686,12 @@ Route::set_state (const XMLNode& node)
} else if (child->name() == "extra") {
_extra_xml = new XMLNode (*child);
} else if (child->name() == "solo") {
_solo_control.set_state (*child);
_session.add_controllable (&_solo_control);
} else if (child->name() == "mute") {
_mute_control.set_state (*child);
_session.add_controllable (&_mute_control);
}
}
@ -2220,8 +2228,8 @@ Route::automation_snapshot (nframes_t now)
}
}
Route::ToggleControllable::ToggleControllable (Route& s, ToggleType tp)
: route (s), type(tp)
Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp)
: Controllable (name), route (s), type(tp)
{
}

View file

@ -84,6 +84,8 @@
#include <ardour/region_factory.h>
#include <ardour/source_factory.h>
#include <control_protocol/control_protocol.h>
#include "i18n.h"
#include <locale.h>
@ -242,7 +244,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
Curve::CurveCreated.connect (mem_fun (*this, &Session::add_curve));
AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list));
Controllable::Created.connect (mem_fun (*this, &Session::add_controllable));
Controllable::GoingAway.connect (mem_fun (*this, &Session::remove_controllable));
IO::MoreOutputs.connect (mem_fun (*this, &Session::ensure_passthru_buffers));
@ -967,6 +968,8 @@ Session::state(bool full_state)
node->add_child_nocopy (_tempo_map->get_state());
node->add_child_nocopy (get_control_protocol_state());
if (_extra_xml) {
node->add_child_copy (*_extra_xml);
}
@ -974,6 +977,25 @@ Session::state(bool full_state)
return *node;
}
XMLNode&
Session::get_control_protocol_state ()
{
ControlProtocolManager& cpm (ControlProtocolManager::instance());
XMLNode* node = new XMLNode (X_("ControlProtocols"));
cpm.foreach_known_protocol (bind (mem_fun (*this, &Session::add_control_protocol), node));
return *node;
}
void
Session::add_control_protocol (const ControlProtocolInfo* const cpi, XMLNode* node)
{
if (cpi->protocol) {
node->add_child_nocopy (cpi->protocol->get_state());
}
}
int
Session::set_state (const XMLNode& node)
{
@ -1030,6 +1052,7 @@ Session::set_state (const XMLNode& node)
EditGroups
MixGroups
Click
ControlProtocols
*/
if (use_config_midi_ports ()) {
@ -1161,6 +1184,10 @@ Session::set_state (const XMLNode& node)
_click_io->set_state (*child);
}
if ((child = find_named_node (node, "ControlProtocols")) != 0) {
ControlProtocolManager::instance().set_protocol_states (*child);
}
/* here beginneth the second phase ... */
StateReady (); /* EMIT SIGNAL */
@ -2719,7 +2746,7 @@ void
Session::add_controllable (Controllable* c)
{
Glib::Mutex::Lock lm (controllables_lock);
controllables.push_back (c);
controllables.insert (c);
}
void
@ -2730,7 +2757,12 @@ Session::remove_controllable (Controllable* c)
}
Glib::Mutex::Lock lm (controllables_lock);
controllables.remove (c);
Controllables::iterator x = controllables.find (c);
if (x != controllables.end()) {
controllables.erase (x);
}
}
Controllable*

View file

@ -71,7 +71,7 @@ Source::get_state ()
char buf[64];
node->add_property ("name", _name);
_id.print (buf);
_id.print (buf, sizeof (buf));
node->add_property ("id", buf);
if (_timestamp != 0) {

View file

@ -119,7 +119,7 @@ Track::freeze_state() const
}
Track::RecEnableControllable::RecEnableControllable (Track& s)
: track (s)
: Controllable (X_("recenable")), track (s)
{
}

View file

@ -1,26 +1,40 @@
#include <pbd/controllable.h>
#include <pbd/xml++.h>
#include <pbd/error.h>
#include "i18n.h"
using namespace PBD;
sigc::signal<void,Controllable*> Controllable::Created;
sigc::signal<void,Controllable*> Controllable::GoingAway;
sigc::signal<bool,Controllable*> Controllable::StartLearning;
sigc::signal<void,Controllable*> Controllable::StopLearning;
Controllable::Controllable ()
Controllable::Controllable (std::string name)
: _name (name)
{
Created (this);
}
XMLNode&
Controllable::get_state ()
{
XMLNode* node = new XMLNode (X_("Controllable"));
XMLNode* node = new XMLNode (_name);
char buf[64];
_id.print (buf);
_id.print (buf, sizeof (buf));
node->add_property (X_("id"), buf);
return *node;
}
int
Controllable::set_state (const XMLNode& node)
{
const XMLProperty* prop = node.property (X_("id"));
if (prop) {
_id = prop->value();
return 0;
} else {
error << _("Controllable state node has no ID property") << endmsg;
return -1;
}
}

View file

@ -40,16 +40,15 @@ ID::string_assign (string str)
}
void
ID::print (char* buf) const
ID::print (char* buf, uint32_t bufsize) const
{
/* XXX sizeof buf is unknown. bad API design */
snprintf (buf, 32, "%" PRIu64, _id);
snprintf (buf, bufsize, "%" PRIu64, _id);
}
string ID::to_s() const
{
char buf[32]; // see print()
print(buf);
print(buf, sizeof (buf));
return string(buf);
}
@ -64,7 +63,7 @@ ostream&
operator<< (ostream& ostr, const ID& _id)
{
char buf[32];
_id.print (buf);
_id.print (buf, sizeof (buf));
ostr << buf;
return ostr;
}

View file

@ -1,6 +1,8 @@
#ifndef __pbd_controllable_h__
#define __pbd_controllable_h__
#include <string>
#include <sigc++/trackable.h>
#include <sigc++/signal.h>
@ -13,7 +15,7 @@ namespace PBD {
class Controllable : public virtual sigc::trackable, public Stateful {
public:
Controllable ();
Controllable (std::string name);
virtual ~Controllable() { GoingAway (this); }
virtual void set_value (float) = 0;
@ -23,22 +25,20 @@ class Controllable : public virtual sigc::trackable, public Stateful {
sigc::signal<void> LearningFinished;
static sigc::signal<void,Controllable*> Created;
static sigc::signal<void,Controllable*> GoingAway;
static sigc::signal<bool,PBD::Controllable*> StartLearning;
static sigc::signal<void,PBD::Controllable*> StopLearning;
sigc::signal<void> Changed;
const PBD::ID& id() const { return _id; }
int set_state (const XMLNode&) { return 0; }
int set_state (const XMLNode&);
XMLNode& get_state ();
std::string name() const { return _name; }
private:
PBD::ID _id;
std::string _name;
};
}

View file

@ -27,7 +27,7 @@ class ID {
return _id < other._id;
}
void print (char* buf) const;
void print (char* buf, uint32_t bufsize) const;
std::string to_s() const;
static uint64_t counter() { return _counter; }

View file

@ -39,7 +39,7 @@ using namespace PBD;
#include "i18n.h"
GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
: ControlProtocol (s, _("GenericMIDI"))
: ControlProtocol (s, _("Generic MIDI"))
{
MIDI::Manager* mm = MIDI::Manager::instance();
@ -173,7 +173,10 @@ GenericMidiControlProtocol::stop_learning (Controllable* c)
XMLNode&
GenericMidiControlProtocol::get_state ()
{
XMLNode* node = new XMLNode (_name); /* node name must match protocol name */
XMLNode* node = new XMLNode ("Protocol");
node->add_property (X_("name"), _name);
XMLNode* children = new XMLNode (X_("controls"));
node->add_child_nocopy (*children);
@ -215,19 +218,24 @@ GenericMidiControlProtocol::set_state (const XMLNode& node)
XMLProperty* prop;
if ((prop = (*niter)->property ("id")) != 0) {
ID id = prop->value ();
c = session->controllable_by_id (id);
if (c) {
MIDIControllable* mc = new MIDIControllable (*_port, *c);
if (mc->set_state (**niter) == 0) {
controllables.insert (mc);
}
} else {
warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
id)
<< endmsg;
}
}
}
return 0;
}

View file

@ -345,6 +345,8 @@ MIDIControllable::set_state (const XMLNode& node)
return -1;
}
bind_midi (control_channel, control_type, control_additional);
return 0;
}

View file

@ -1577,7 +1577,8 @@ TranzportControlProtocol::print (int row, int col, const char *text)
XMLNode&
TranzportControlProtocol::get_state ()
{
XMLNode* node = new XMLNode (_name); /* node name must match protocol name */
XMLNode* node = new XMLNode (X_("Protocol"));
node->add_property (X_("name"), _name);
return *node;
}