Fix LV2 UIs with spacey paths (e.g. Pianoteq).
[ardour.git] / libs / ardour / midi_scene_changer.cc
index 57fca52e53d77c86ed1458fcdcbe42df6a802c83..8e2347ab2e15cb7f67197ad63e56f31373da7504 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "ardour/async_midi_port.h"
 #include "ardour/event_type_map.h"
+#include "ardour/midi_buffer.h"
 #include "ardour/midi_port.h"
 #include "ardour/midi_scene_change.h"
 #include "ardour/midi_scene_changer.h"
@@ -36,14 +37,19 @@ using namespace ARDOUR;
 MIDISceneChanger::MIDISceneChanger (Session& s)
        : SceneChanger (s)
        , _recording (true)
-       , last_bank_message_time (-1)
+       , have_seen_bank_changes (false)
        , last_program_message_time (-1)
        , last_delivered_program (-1)
        , last_delivered_bank (-1)
          
 {
+       /* catch any add/remove/clear etc. for all Locations */
        _session.locations()->changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
-       Location::scene_changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::gather, this));
+       _session.locations()->added.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
+       _session.locations()->removed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
+
+       /* catch class-based signal that notifies of us changes in the scene change state of any Location */
+       Location::scene_changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
 }
 
 MIDISceneChanger::~MIDISceneChanger ()
@@ -53,7 +59,7 @@ MIDISceneChanger::~MIDISceneChanger ()
 void
 MIDISceneChanger::locations_changed ()
 {
-       gather ();
+       _session.locations()->apply (*this, &MIDISceneChanger::gather);
 }
 
 /** Use the session's list of locations to collect all patch changes.
@@ -61,9 +67,8 @@ MIDISceneChanger::locations_changed ()
  * This is called whenever the locations change in anyway.
  */
 void
-MIDISceneChanger::gather ()
+MIDISceneChanger::gather (const Locations::LocationList& locations)
 {
-       const Locations::LocationList& locations (_session.locations()->list());
        boost::shared_ptr<SceneChange> sc;
 
        Glib::Threads::RWLock::WriterLock lm (scene_lock);
@@ -75,8 +80,13 @@ MIDISceneChanger::gather ()
                if ((sc = (*l)->scene_change()) != 0) {
 
                        boost::shared_ptr<MIDISceneChange> msc = boost::dynamic_pointer_cast<MIDISceneChange> (sc);
-                       
+
                        if (msc) {
+
+                               if (msc->bank() >= 0) {
+                                       have_seen_bank_changes = true;
+                               }
+                       
                                scenes.insert (std::make_pair ((*l)->start(), msc));
                        }
                }
@@ -89,6 +99,8 @@ MIDISceneChanger::rt_deliver (MidiBuffer& mbuf, framepos_t when, boost::shared_p
        uint8_t buf[4];
        size_t cnt;
 
+       MIDIOutputActivity (); /* EMIT SIGNAL */
+
        if ((cnt = msc->get_bank_msb_message (buf, sizeof (buf))) > 0) {
                mbuf.push_back (when, cnt, buf);
 
@@ -117,6 +129,8 @@ MIDISceneChanger::non_rt_deliver (boost::shared_ptr<MIDISceneChange> msc)
           non-RT/process context. Using zero means "deliver them as early as
           possible" (practically speaking, in the next process callback).
        */
+        
+       MIDIOutputActivity (); /* EMIT SIGNAL */
 
        if ((cnt = msc->get_bank_msb_message (buf, sizeof (buf))) > 0) {
                aport->write (buf, cnt, 0);
@@ -243,13 +257,12 @@ MIDISceneChanger::recording() const
 }
 
 void
-MIDISceneChanger::bank_change_input (MIDI::Parser& parser, unsigned short, int)
+  MIDISceneChanger::bank_change_input (MIDI::Parser& /*parser*/, unsigned short, int)
 {
-       if (!recording()) {
-               return;
+       if (recording()) {
+               have_seen_bank_changes = true;
        }
-
-       last_bank_message_time = parser.get_timestamp ();
+       MIDIInputActivity (); /* EMIT SIGNAL */
 }
 
 void
@@ -260,6 +273,7 @@ MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program
        last_program_message_time = time;
 
        if (!recording()) {
+               MIDIInputActivity (); /* EMIT SIGNAL */
                jump_to (input_port->channel (channel)->bank(), program);
                return;
        }
@@ -287,10 +301,29 @@ MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program
                new_mark = true;
        }
 
-       unsigned short bank = input_port->channel (channel)->bank();
+       unsigned short bank;
+
+       if (have_seen_bank_changes) {
+               bank = input_port->channel (channel)->bank();
+       } else {
+               bank = -1;
+       }
 
        MIDISceneChange* msc =new MIDISceneChange (channel, bank, program & 0x7f);
 
+       /* check for identical scene change so we can re-use color, if any */
+
+       Locations::LocationList copy (locations->list());
+
+       for (Locations::LocationList::const_iterator l = copy.begin(); l != copy.end(); ++l) {
+               boost::shared_ptr<MIDISceneChange> sc = boost::dynamic_pointer_cast<MIDISceneChange>((*l)->scene_change());
+
+               if (sc && (*sc.get()) == *msc) {
+                       msc->set_color (sc->color ());
+                       break;
+               }
+       }
+
        loc->set_scene_change (boost::shared_ptr<MIDISceneChange> (msc));
        
        /* this will generate a "changed" signal to be emitted by locations,
@@ -300,6 +333,8 @@ MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program
        if (new_mark) {
                locations->add (loc);
        }
+
+       MIDIInputActivity (); /* EMIT SIGNAL */
 }
 
 void