X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_scene_changer.cc;h=ce2559180f9baa777bceb39c8f2fd0871b3b16b2;hb=8c04457be14673385287def048c387b3953fa8e8;hp=49e835ca80e8eaafd94966fd91dd39a3312f756a;hpb=2cf411e4be0b10e6ecf47d2070963299b6a810e7;p=ardour.git diff --git a/libs/ardour/midi_scene_changer.cc b/libs/ardour/midi_scene_changer.cc index 49e835ca80..ce2559180f 100644 --- a/libs/ardour/midi_scene_changer.cc +++ b/libs/ardour/midi_scene_changer.cc @@ -17,31 +17,39 @@ */ -#include "evoral/MIDIEvent.hpp" +#include "evoral/Event.hpp" +#include "midi++/channel.h" #include "midi++/parser.h" #include "midi++/port.h" +#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" #include "ardour/session.h" -#include "i18n.h" +#include "pbd/i18n.h" 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) - + { - _session.locations()->changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this, _1)); - Location::scene_changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::gather, this)); + /* catch any add/remove/clear etc. for all Locations */ + _session.locations()->changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, 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 () @@ -49,21 +57,22 @@ MIDISceneChanger::~MIDISceneChanger () } void -MIDISceneChanger::locations_changed (Locations::Change) +MIDISceneChanger::locations_changed () { - gather (); + _session.locations()->apply (*this, &MIDISceneChanger::gather); } /** Use the session's list of locations to collect all patch changes. - * + * * 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 sc; + Glib::Threads::RWLock::WriterLock lm (scene_lock); + scenes.clear (); for (Locations::LocationList::const_iterator l = locations.begin(); l != locations.end(); ++l) { @@ -73,18 +82,29 @@ MIDISceneChanger::gather () boost::shared_ptr msc = boost::dynamic_pointer_cast (sc); if (msc) { - scenes.insert (std::make_pair (msc->time(), msc)); + + if (msc->bank() >= 0) { + have_seen_bank_changes = true; + } + + scenes.insert (std::make_pair ((*l)->start(), msc)); } } } } void -MIDISceneChanger::deliver (MidiBuffer& mbuf, framepos_t when, boost::shared_ptr msc) +MIDISceneChanger::rt_deliver (MidiBuffer& mbuf, framepos_t when, boost::shared_ptr msc) { + if (!msc->active()) { + return; + } + 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); @@ -101,12 +121,51 @@ MIDISceneChanger::deliver (MidiBuffer& mbuf, framepos_t when, boost::shared_ptr< last_delivered_program = msc->program(); } } - + +void +MIDISceneChanger::non_rt_deliver (boost::shared_ptr msc) +{ + if (!msc->active()) { + return; + } + + uint8_t buf[4]; + size_t cnt; + boost::shared_ptr aport = boost::dynamic_pointer_cast(output_port); + + /* We use zero as the timestamp for these messages because we are in a + 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); + + if ((cnt = msc->get_bank_lsb_message (buf, sizeof (buf))) > 0) { + aport->write (buf, cnt, 0); + } + + last_delivered_bank = msc->bank(); + } + + if ((cnt = msc->get_program_message (buf, sizeof (buf))) > 0) { + aport->write (buf, cnt, 0); + last_delivered_program = msc->program(); + } +} void MIDISceneChanger::run (framepos_t start, framepos_t end) { - if (!output_port || recording()) { + if (!output_port || recording() || !_session.transport_rolling()) { + return; + } + + Glib::Threads::RWLock::ReaderLock lm (scene_lock, Glib::Threads::TRY_LOCK); + + if (!lm.locked()) { return; } @@ -120,8 +179,8 @@ MIDISceneChanger::run (framepos_t start, framepos_t end) if (i->first >= end) { break; } - - deliver (mbuf, i->first - start, i->second); + + rt_deliver (mbuf, i->first - start, i->second); ++i; } @@ -130,35 +189,63 @@ MIDISceneChanger::run (framepos_t start, framepos_t end) void MIDISceneChanger::locate (framepos_t pos) { - Scenes::const_iterator i = scenes.upper_bound (pos); + boost::shared_ptr msc; - if (i == scenes.end()) { - return; + { + Glib::Threads::RWLock::ReaderLock lm (scene_lock); + + if (scenes.empty()) { + return; + } + + Scenes::const_iterator i = scenes.lower_bound (pos); + + if (i != scenes.end()) { + + if (i->first != pos) { + /* i points to first scene with position > pos, so back + * up, if possible. + */ + if (i != scenes.begin()) { + --i; + } else { + return; + } + } + } else { + /* go back to the final scene and use it */ + --i; + } + + msc = i->second; } - if (i->second->program() != last_delivered_program || i->second->bank() != last_delivered_bank) { - // MidiBuffer& mbuf (output_port->get_midi_buffer (end-start)); - // deliver (mbuf, i->first, i->second); + if (msc->program() != last_delivered_program || msc->bank() != last_delivered_bank) { + non_rt_deliver (msc); } -} +} void -MIDISceneChanger::set_input_port (MIDI::Port* mp) +MIDISceneChanger::set_input_port (boost::shared_ptr mp) { - input_port = mp; + incoming_connections.drop_connections(); + input_port.reset (); + + boost::shared_ptr async = boost::dynamic_pointer_cast (mp); + + if (async) { + + input_port = mp; - incoming_bank_change_connection.disconnect (); - incoming_program_change_connection.disconnect (); - - if (input_port) { - /* midi port is asynchronous. MIDI parsing will be carried out * by the MIDI UI thread which will emit the relevant signals * and thus invoke our callbacks as necessary. */ - input_port->parser()->bank_change.connect_same_thread (incoming_bank_change_connection, boost::bind (&MIDISceneChanger::bank_change_input, this, _1, _2)); - input_port->parser()->program_change.connect_same_thread (incoming_program_change_connection, boost::bind (&MIDISceneChanger::program_change_input, this, _1, _2)); + for (int channel = 0; channel < 16; ++channel) { + async->parser()->channel_bank_change[channel].connect_same_thread (incoming_connections, boost::bind (&MIDISceneChanger::bank_change_input, this, _1, _2, channel)); + async->parser()->channel_program_change[channel].connect_same_thread (incoming_connections, boost::bind (&MIDISceneChanger::program_change_input, this, _1, _2, channel)); + } } } @@ -175,76 +262,84 @@ MIDISceneChanger::set_recording (bool yn) } bool -MIDISceneChanger::recording() const +MIDISceneChanger::recording() const { return _session.transport_rolling() && _session.get_record_enabled(); } void -MIDISceneChanger::bank_change_input (MIDI::Parser& parser, unsigned short bank) + 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 (); - current_bank = bank; + MIDIInputActivity (); /* EMIT SIGNAL */ } void -MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program) +MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program, int channel) { framecnt_t time = parser.get_timestamp (); - frameoffset_t delta = time - last_program_message_time; last_program_message_time = time; if (!recording()) { - jump_to (current_bank, program); + + MIDIInputActivity (); /* EMIT SIGNAL */ + + int bank = -1; + if (have_seen_bank_changes) { + bank = boost::dynamic_pointer_cast(input_port)->channel (channel)->bank(); + } + + jump_to (bank, program); return; } Locations* locations (_session.locations ()); Location* loc; bool new_mark = false; - framecnt_t slop = (framecnt_t) floor ((Config->get_inter_scene_gap_msecs() / 1000.0) * _session.frame_rate()); /* check for marker at current location */ - loc = locations->mark_at (time, slop); + loc = locations->mark_at (time, Config->get_inter_scene_gap_frames()); if (!loc) { /* create a new marker at the desired position */ - + std::string new_name; if (!locations->next_available_name (new_name, _("Scene "))) { std::cerr << "No new marker name available\n"; return; } - - loc = new Location (_session, time, time, new_name, Location::IsMark); + + loc = new Location (_session, time, time, new_name, Location::IsMark, 0); new_mark = true; } - uint8_t bank; - uint8_t channel = (program & 0xf0) >> 8; + int bank = -1; + if (have_seen_bank_changes) { + bank = boost::dynamic_pointer_cast(input_port)->channel (channel)->bank(); + } - /* if we received a bank change message within the last 2 msec, use the - * current bank value, otherwise lookup the current bank number and use - * that. - */ + MIDISceneChange* msc =new MIDISceneChange (channel, bank, program & 0x7f); - if (time - last_bank_message_time < (2 * _session.frame_rate() / 1000.0)) { - bank = current_bank; - } else { - bank = -1; - } + /* check for identical scene change so we can re-use color, if any */ + + Locations::LocationList copy (locations->list()); - MIDISceneChange* msc =new MIDISceneChange (loc->start(), channel, bank, program & 0x7f); + for (Locations::LocationList::const_iterator l = copy.begin(); l != copy.end(); ++l) { + boost::shared_ptr sc = boost::dynamic_pointer_cast((*l)->scene_change()); + + if (sc && (*sc.get()) == *msc) { + msc->set_color (sc->color ()); + break; + } + } loc->set_scene_change (boost::shared_ptr (msc)); - + /* this will generate a "changed" signal to be emitted by locations, and we will call ::gather() to update our list of MIDI events. */ @@ -252,6 +347,8 @@ MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program if (new_mark) { locations->add (loc); } + + MIDIInputActivity (); /* EMIT SIGNAL */ } void