Route::SoloControllable::set_value (float val)
{
bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+ this is how it should be done
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route);
+
+ if (Config->get_solo_control_is_listen_control()) {
+ _session.set_listen (rl, bval);
+ } else {
+ _session.set_solo (rl, bval);
+ }
+#else
route.set_solo (bval, this);
+#endif
}
float
Route::SoloControllable::get_value (void) const
{
- return route.self_soloed() ? 1.0f : 0.0f;
+ if (Config->get_solo_control_is_listen_control()) {
+ return route.listening() ? 1.0f : 0.0f;
+ } else {
+ return route.self_soloed() ? 1.0f : 0.0f;
+ }
}
void
}
if (Glib::file_test (_path, Glib::FILE_TEST_EXISTS) && ::access (_path.c_str(), W_OK)) {
- cerr << "Session non-writable based on " << _path << endl;
_writable = false;
} else {
- cerr << "Session writable based on " << _path << endl;
_writable = true;
}
case ControllableDescriptor::Gain:
c = r->gain_control ();
break;
+
case ControllableDescriptor::Solo:
c = r->solo_control();
break;
return -1;
}
- cerr << "Path: " << endl;
- for (vector<string>::iterator x = path.begin(); x != path.end(); ++x) {
- cerr << '[' << (*x) << "] ";
- }
- cerr << endl;
-
- cerr << "Rest: " << endl;
- for (vector<string>::iterator x = rest.begin(); x != rest.end(); ++x) {
- cerr << '[' << (*x) << "] ";
- }
- cerr << endl;
-
if (path[0] == "route" || path[0] == "rid") {
_top_level_type = RemoteControlID;
session->sample_to_timecode (sample, *((Timecode::Time*)&timecode), use_offset, use_subframes);
}
+#if 0
+this stuff is waiting to go in so that all UI's can offer complex solo/mute functionality
+
+void
+BasicUI::solo_release (boost::shared_ptr<Route> r)
+{
+}
+
+void
+BasicUI::solo_press (boost::shared_ptr<Route> r, bool momentary, bool global, bool exclusive, bool isolate, bool solo_group)
+{
+ if (momentary) {
+ _solo_release = new SoloMuteRelease (_route->soloed());
+ }
+
+ if (global) {
+
+ if (_solo_release) {
+ _solo_release->routes = _session->get_routes ();
+ }
+
+ if (Config->get_solo_control_is_listen_control()) {
+ _session->set_listen (_session->get_routes(), !_route->listening(), Session::rt_cleanup, true);
+ } else {
+ _session->set_solo (_session->get_routes(), !_route->soloed(), Session::rt_cleanup, true);
+ }
+
+ } else if (exclusive) {
+
+ if (_solo_release) {
+ _solo_release->exclusive = true;
+
+ boost::shared_ptr<RouteList> routes = _session->get_routes();
+
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i)->soloed ()) {
+ _solo_release->routes_on->push_back (*i);
+ } else {
+ _solo_release->routes_off->push_back (*i);
+ }
+ }
+ }
+
+ if (Config->get_solo_control_is_listen_control()) {
+ /* ??? we need a just_one_listen() method */
+ } else {
+ _session->set_just_one_solo (_route, true);
+ }
+
+ } else if (isolate) {
+
+ // shift-click: toggle solo isolated status
+
+ _route->set_solo_isolated (!_route->solo_isolated(), this);
+ delete _solo_release;
+ _solo_release = 0;
+
+ } else if (solo_group) {
+
+ /* Primary-button1: solo mix group.
+ NOTE: Primary-button2 is MIDI learn.
+ */
+
+ if (_route->route_group()) {
+
+ if (_solo_release) {
+ _solo_release->routes = _route->route_group()->route_list();
+ }
+
+ if (Config->get_solo_control_is_listen_control()) {
+ _session->set_listen (_route->route_group()->route_list(), !_route->listening(), Session::rt_cleanup, true);
+ } else {
+ _session->set_solo (_route->route_group()->route_list(), !_route->soloed(), Session::rt_cleanup, true);
+ }
+ }
+
+ } else {
+
+ /* click: solo this route */
+
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route());
+
+ if (_solo_release) {
+ _solo_release->routes = rl;
+ }
+
+ if (Config->get_solo_control_is_listen_control()) {
+ _session->set_listen (rl, !_route->listening());
+ } else {
+ _session->set_solo (rl, !_route->soloed());
+ }
+ }
+}
+#endif
}
if (!mc) {
- mc = new MIDIControllable (*_port, *c);
+ mc = new MIDIControllable (*_port, *c, false);
}
{
MIDI::byte value = control_number;
// Create a MIDIControllable
- MIDIControllable* mc = new MIDIControllable (*_port, *control);
+ MIDIControllable* mc = new MIDIControllable (*_port, *control, false);
// Remove any old binding for this midi channel/type/value pair
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
c = session->controllable_by_id (id);
if (c) {
- MIDIControllable* mc = new MIDIControllable (*_port, *c);
+ MIDIControllable* mc = new MIDIControllable (*_port, *c, false);
if (mc->set_state (**niter, version) == 0) {
controllables.push_back (mc);
string uri;
MIDI::eventType ev;
int intval;
+ bool momentary;
if ((prop = node.property (X_("ctl"))) != 0) {
ev = MIDI::controller;
if (channel > 0) {
channel -= 1;
}
+
+ if ((prop = node.property (X_("momentary"))) != 0) {
+ momentary = string_is_affirmative (prop->value());
+ } else {
+ momentary = false;
+ }
prop = node.property (X_("uri"));
uri = prop->value();
- MIDIControllable* mc = new MIDIControllable (*_port, false);
+ MIDIControllable* mc = new MIDIControllable (*_port, momentary);
if (mc->init (uri)) {
delete mc;
#include <gtkmm/comboboxtext.h>
#include <gtkmm/label.h>
#include <gtkmm/box.h>
+#include <gtkmm/adjustment.h>
+#include <gtkmm/spinbutton.h>
#include "gtkmm2ext/utils.h"
private:
GenericMidiControlProtocol& cp;
Gtk::ComboBoxText map_combo;
+ Gtk::Adjustment bank_adjustment;
+ Gtk::SpinButton bank_spinner;
void binding_changed ();
+ void bank_change ();
};
using namespace PBD;
GMCPGUI::GMCPGUI (GenericMidiControlProtocol& p)
: cp (p)
+ , bank_adjustment (1, 1, 100, 1, 10)
+ , bank_spinner (bank_adjustment)
{
vector<string> popdowns;
popdowns.push_back (_("Reset All"));
map_combo.signal_changed().connect (sigc::mem_fun (*this, &GMCPGUI::binding_changed));
+ set_spacing (6);
set_border_width (12);
Label* label = manage (new Label (_("Available MIDI bindings:")));
hpack->pack_start (*label, false, false);
hpack->pack_start (map_combo, false, false);
+ map_combo.show ();
+ label->show ();
+ hpack->show ();
+
pack_start (*hpack, false, false);
- map_combo.show ();
+
+ bank_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &GMCPGUI::bank_change));
+
+ label = manage (new Label (_("Current Bank:")));
+ hpack = manage (new HBox);
+
+ hpack->set_spacing (6);
+ hpack->pack_start (*label, false, false);
+ hpack->pack_start (bank_spinner, false, false);
+
+
+ bank_spinner.show ();
label->show ();
hpack->show ();
+
+ pack_start (*hpack, false, false);
+
}
GMCPGUI::~GMCPGUI ()
{
}
+void
+GMCPGUI::bank_change ()
+{
+ int new_bank = bank_adjustment.get_value() - 1;
+ cp.set_current_bank (new_bank);
+}
+
void
GMCPGUI::binding_changed ()
{
#define __STDC_FORMAT_MACROS 1
#include <stdint.h>
-
-#include <cstdio> /* for sprintf, sigh */
+#include <cmath>
#include <climits>
#include "pbd/error.h"
using namespace PBD;
using namespace ARDOUR;
-MIDIControllable::MIDIControllable (Port& p, bool is_bistate)
+MIDIControllable::MIDIControllable (Port& p, bool m)
: controllable (0)
, _descriptor (0)
, _port (p)
- , bistate (is_bistate)
+ , _momentary (m)
{
_learned = false; /* from URI */
setting = false;
feedback = true; // for now
}
-MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool is_bistate)
+MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool m)
: controllable (&c)
, _descriptor (0)
, _port (p)
- , bistate (is_bistate)
-
+ , _momentary (m)
{
_learned = true; /* from controllable */
setting = false;
}
void
-MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool is_on)
+MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /* is_on */)
{
if (!controllable) {
return;
}
- if (!bistate) {
+
+ if (!controllable->is_toggle()) {
controllable->set_value (msg->note_number/127.0);
} else {
- /* Note: parser handles the use of zero velocity to
- mean note off. if we get called with is_on=true, then we
- got a *real* note on.
- */
-
- if (msg->note_number == control_additional) {
- controllable->set_value (is_on ? 1 : 0);
+ if (control_additional == msg->note_number) {
+ /* Note: parser handles the use of zero velocity to
+ mean note off. if we get called with is_on=true, then we
+ got a *real* note on.
+ */
+ controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
}
}
}
if (control_additional == msg->controller_number) {
- if (!bistate) {
- controllable->set_value (midi_to_control(msg->value));
+
+ if (!controllable->is_toggle()) {
+ controllable->set_value (midi_to_control (msg->value));
} else {
- if (msg->value > 64.0) {
+ if (msg->value > 64.0f) {
controllable->set_value (1);
} else {
controllable->set_value (0);
if (!controllable) {
return;
}
- /* XXX program change messages make no sense for bistates */
- if (!bistate) {
+ if (!controllable->is_toggle()) {
controllable->set_value (msg/127.0);
- last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
+ } else {
+ controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
}
+
+ last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
}
void
return;
}
- /* pitchbend messages make no sense for bistates */
- /* XXX gack - get rid of assumption about typeof pitchbend_t */
+ if (!controllable->is_toggle()) {
+ /* XXX gack - get rid of assumption about typeof pitchbend_t */
+ controllable->set_value ((pb/(float) SHRT_MAX));
+ } else {
+ controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
+ }
- controllable->set_value ((pb/(float) SHRT_MAX));
last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
}
case MIDI::off:
p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
- /* if this is a bistate, connect to noteOn as well,
+ /* if this is a togglee, connect to noteOn as well,
and we'll toggle back and forth between the two.
*/
- if (bistate) {
+ if (_momentary) {
p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
}
case MIDI::on:
p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
- if (bistate) {
+ if (_momentary) {
p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
}
_control_description = "MIDI control: NoteOn";
break;
case MIDI::program:
- if (!bistate) {
- p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_program_change, this, _1, _2));
- _control_description = "MIDI control: ProgramChange";
- }
+ p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_program_change, this, _1, _2));
+ _control_description = "MIDI control: ProgramChange";
break;
case MIDI::pitchbend:
- if (!bistate) {
- p.channel_pitchbend[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_pitchbend, this, _1, _2));
- _control_description = "MIDI control: Pitchbend";
- }
+ p.channel_pitchbend[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_pitchbend, this, _1, _2));
+ _control_description = "MIDI control: Pitchbend";
break;
default:
{
byte msg[3];
- if (setting || !feedback || control_type == none) {
+ if (!_learned || setting || !feedback || control_type == none) {
return;
}
msg[0] = (control_type & 0xF0) | (control_channel & 0xF);
msg[1] = control_additional;
- msg[2] = (byte) (control_to_midi(controllable->get_value()));
+
+ if (controllable->is_gain_like()) {
+ msg[2] = (byte) lrintf (gain_to_slider_position (controllable->get_value()) * 127.0f);
+ } else {
+ msg[2] = (byte) (control_to_midi(controllable->get_value()));
+ }
_port.write (msg, 3, 0);
}
{
if (control_type != none && feedback && bufsize > 2) {
- MIDI::byte gm = (MIDI::byte) (control_to_midi(controllable->get_value()));
+ MIDI::byte gm;
+
+ if (controllable->is_gain_like()) {
+ gm = (byte) lrintf (gain_to_slider_position (controllable->get_value()) * 127.0f);
+ } else {
+ gm = (byte) (control_to_midi(controllable->get_value()));
+ }
if (gm != last_value) {
*buf++ = (0xF0 & control_type) | (0xF & control_channel);
class MIDIControllable : public PBD::Stateful
{
public:
- MIDIControllable (MIDI::Port&, PBD::Controllable&, bool bistate = false);
- MIDIControllable (MIDI::Port&, bool bistate = false);
+ MIDIControllable (MIDI::Port&, PBD::Controllable&, bool momentary);
+ MIDIControllable (MIDI::Port&, bool momentary = false);
virtual ~MIDIControllable ();
int init (const std::string&);
MIDI::Port& _port;
bool setting;
MIDI::byte last_value;
- bool bistate;
+ bool _momentary;
bool _is_gain_controller;
bool _learned;
int midi_msg_id; /* controller ID or note number */