fix dragging that involves locked regions; auto-rebinding patch for people to experiment with (probably needs a little work)

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3164 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2008-03-21 20:22:00 +00:00
parent aa06f1f9f8
commit fec2a96cec
26 changed files with 397 additions and 91 deletions

View file

@ -42,6 +42,12 @@ class RegionFactory {
static sigc::signal<void,boost::shared_ptr<Region> > CheckNewRegion;
static boost::shared_ptr<Region> create (boost::shared_ptr<const Region>);
/* note: both of the first two should use const shared_ptr as well, but
gcc 4.1 doesn't seem to be able to disambiguate them if they do.
*/
static boost::shared_ptr<Region> create (boost::shared_ptr<Region>, nframes_t start,
nframes_t length, std::string name,
layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
@ -50,7 +56,6 @@ class RegionFactory {
layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (boost::shared_ptr<Source>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (boost::shared_ptr<Region>);
static boost::shared_ptr<Region> create (Session&, XMLNode&, bool);
static boost::shared_ptr<Region> create (SourceList &, const XMLNode&);
};

View file

@ -249,6 +249,9 @@ class Session : public PBD::StatefulDestructible
bool deletion_in_progress() const { return _state_of_the_state & Deletion; }
sigc::signal<void> DirtyChanged;
static sigc::signal<void> AutoBindingOn;
static sigc::signal<void> AutoBindingOff;
std::string sound_dir (bool with_path = true) const;
std::string peak_dir () const;
std::string dead_sound_dir () const;

View file

@ -35,8 +35,8 @@ sigc::signal<void,boost::shared_ptr<Region> > RegionFactory::CheckNewRegion;
boost::shared_ptr<Region>
RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start,
nframes_t length, std::string name,
layer_t layer, Region::Flag flags, bool announce)
nframes_t length, std::string name,
layer_t layer, Region::Flag flags, bool announce)
{
boost::shared_ptr<const AudioRegion> other;
@ -57,11 +57,11 @@ RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start,
}
boost::shared_ptr<Region>
RegionFactory::create (boost::shared_ptr<Region> region)
RegionFactory::create (boost::shared_ptr<const Region> region)
{
boost::shared_ptr<AudioRegion> other;
boost::shared_ptr<const AudioRegion> other;
if ((other = boost::dynamic_pointer_cast<AudioRegion>(region)) != 0) {
if ((other = boost::dynamic_pointer_cast<const AudioRegion>(region)) != 0) {
boost::shared_ptr<Region> ret (new AudioRegion (other));
/* pure copy constructor - no CheckNewRegion emitted */
return ret;
@ -75,8 +75,8 @@ RegionFactory::create (boost::shared_ptr<Region> region)
boost::shared_ptr<Region>
RegionFactory::create (boost::shared_ptr<AudioRegion> region, nframes_t start,
nframes_t length, std::string name,
layer_t layer, Region::Flag flags, bool announce)
nframes_t length, std::string name,
layer_t layer, Region::Flag flags, bool announce)
{
return create (boost::static_pointer_cast<Region> (region), start, length, name, layer, flags, announce);
}

View file

@ -113,6 +113,10 @@ sigc::signal<void> Session::SMPTEOffsetChanged;
sigc::signal<void> Session::StartTimeChanged;
sigc::signal<void> Session::EndTimeChanged;
sigc::signal<void> Session::AutoBindingOn;
sigc::signal<void> Session::AutoBindingOff;
int
Session::find_session (string str, string& path, string& snapshot, bool& isnew)
{

View file

@ -59,6 +59,7 @@ class BarController : public Gtk::Frame
/* export this to allow direct connection to button events */
Gtk::Widget& event_widget() { return darea; }
PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); }
protected:
Gtk::Adjustment& adjustment;

View file

@ -47,7 +47,8 @@ class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton
return true;
}
}
PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); }
private:
BindingProxy binding_proxy;
};
@ -71,6 +72,8 @@ class BindableButton : public Gtkmm2ext::StatefulButton
}
}
PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); }
private:
BindingProxy binding_proxy;
};

View file

@ -40,6 +40,7 @@ class BindingProxy : public sigc::trackable
bool button_press_handler (GdkEventButton *);
PBD::Controllable* get_controllable() { return &controllable; }
protected:
Gtkmm2ext::PopUp* prompter;

View file

@ -28,6 +28,8 @@ using namespace PBD;
sigc::signal<void,Controllable*> Controllable::Destroyed;
sigc::signal<bool,Controllable*> Controllable::StartLearning;
sigc::signal<void,Controllable*> Controllable::StopLearning;
sigc::signal<void,Controllable*,int,int> Controllable::CreateBinding;
sigc::signal<void,Controllable*> Controllable::DeleteBinding;
Glib::Mutex* Controllable::registry_lock = 0;
Controllable::Controllables Controllable::registry;

View file

@ -44,6 +44,8 @@ class Controllable : public PBD::StatefulDestructible {
virtual bool can_send_feedback() const { return true; }
sigc::signal<void> LearningFinished;
static sigc::signal<void,PBD::Controllable*,int,int> CreateBinding;
static sigc::signal<void,PBD::Controllable*> DeleteBinding;
static sigc::signal<bool,PBD::Controllable*> StartLearning;
static sigc::signal<void,PBD::Controllable*> StopLearning;

View file

@ -56,9 +56,17 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
_feedback_interval = 10000; // microseconds
last_feedback_time = 0;
auto_binding = FALSE;
Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning));
Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning));
Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback));
Controllable::CreateBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::create_binding));
Controllable::DeleteBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::delete_binding));
Session::AutoBindingOn.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_on));
Session::AutoBindingOff.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_off));
}
GenericMidiControlProtocol::~GenericMidiControlProtocol ()
@ -225,6 +233,71 @@ GenericMidiControlProtocol::stop_learning (Controllable* c)
}
}
void
GenericMidiControlProtocol::delete_binding ( PBD::Controllable* control )
{
if( control != 0 ) {
Glib::Mutex::Lock lm2 (controllables_lock);
for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
MIDIControllable* existingBinding = (*iter);
if( control == &(existingBinding->get_controllable()) ) {
delete existingBinding;
controllables.erase (iter);
}
} // end for midi controllables
} // end null check
}
void
GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
{
if( control != NULL ) {
Glib::Mutex::Lock lm2 (controllables_lock);
MIDI::channel_t channel = (pos & 0xf);
MIDI::byte value = control_number;
// Create a MIDIControllable::
MIDIControllable* mc = new MIDIControllable (*_port, *control);
// 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
for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
MIDIControllable* existingBinding = (*iter);
if( (existingBinding->get_control_channel() & 0xf ) == channel &&
existingBinding->get_control_additional() == value &&
(existingBinding->get_control_type() & 0xf0 ) == MIDI::controller ) {
delete existingBinding;
controllables.erase (iter);
}
} // end for midi controllables
// Update the MIDI Controllable based on the the pos param
// Here is where a table lookup for user mappings could go; for now we'll just wing it...
mc->bind_midi( channel, MIDI::controller, value );
controllables.insert (mc);
} // end null test
}
void
GenericMidiControlProtocol::auto_binding_on()
{
auto_binding = TRUE;
}
void
GenericMidiControlProtocol::auto_binding_off()
{
auto_binding = FALSE;
}
XMLNode&
GenericMidiControlProtocol::get_state ()
{
@ -269,46 +342,47 @@ GenericMidiControlProtocol::set_state (const XMLNode& node)
_feedback_interval = 10000;
}
Controllable* c;
{
Glib::Mutex::Lock lm (pending_lock);
pending_controllables.clear ();
}
Glib::Mutex::Lock lm2 (controllables_lock);
controllables.clear ();
nlist = node.children(); // "controls"
if (nlist.empty()) {
return 0;
}
nlist = nlist.front()->children ();
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((prop = (*niter)->property ("id")) != 0) {
// Are we using the autobinding feature? If so skip this part
if ( !auto_binding ) {
Controllable* c;
{
Glib::Mutex::Lock lm (pending_lock);
pending_controllables.clear ();
}
Glib::Mutex::Lock lm2 (controllables_lock);
controllables.clear ();
nlist = node.children(); // "controls"
if (nlist.empty()) {
return 0;
}
nlist = nlist.front()->children ();
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
ID id = prop->value ();
c = Controllable::by_id (id);
if (c) {
MIDIControllable* mc = new MIDIControllable (*_port, *c);
if (mc->set_state (**niter) == 0) {
controllables.insert (mc);
}
if ((prop = (*niter)->property ("id")) != 0) {
ID id = prop->value ();
c = session->controllable_by_id (id);
} else {
warning << string_compose (_("Generic MIDI control: controllable %1 not found (ignored)"), id)
<< endmsg;
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;
}
}
}
}
} // end autobinding check
return 0;
}

View file

@ -44,6 +44,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
ARDOUR::microseconds_t last_feedback_time;
bool do_feedback;
bool auto_binding;
void _send_feedback ();
void send_feedback ();
@ -59,6 +60,13 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
void stop_learning (PBD::Controllable*);
void learning_stopped (MIDIControllable*);
void create_binding (PBD::Controllable*, int, int);
void delete_binding (PBD::Controllable*);
void auto_binding_on();
void auto_binding_off();
};
#endif /* ardour_generic_midi_control_protocol_h */

View file

@ -63,6 +63,10 @@ class MIDIControllable : public Stateful
XMLNode& get_state (void);
int set_state (const XMLNode&);
void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
MIDI::channel_t get_control_channel () { return control_channel; }
MIDI::eventType get_control_type () { return control_type; }
MIDI::byte get_control_additional () { return control_additional; }
private:
PBD::Controllable& controllable;
MIDI::Port& _port;
@ -86,8 +90,6 @@ class MIDIControllable : public Stateful
void midi_sense_controller (MIDI::Parser &, MIDI::EventTwoBytes *);
void midi_sense_program_change (MIDI::Parser &, MIDI::byte);
void midi_sense_pitchbend (MIDI::Parser &, MIDI::pitchbend_t);
void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
};
#endif // __gm_midicontrollable_h__