fix bank ID when MIDI Scene change handles a program change message
[ardour.git] / libs / ardour / midi_scene_changer.cc
1 /*
2     Copyright (C) 2014 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "evoral/MIDIEvent.hpp"
21 #include "midi++/channel.h"
22 #include "midi++/parser.h"
23 #include "midi++/port.h"
24
25 #include "ardour/async_midi_port.h"
26 #include "ardour/event_type_map.h"
27 #include "ardour/midi_buffer.h"
28 #include "ardour/midi_port.h"
29 #include "ardour/midi_scene_change.h"
30 #include "ardour/midi_scene_changer.h"
31 #include "ardour/session.h"
32
33 #include "i18n.h"
34
35 using namespace ARDOUR;
36
37 MIDISceneChanger::MIDISceneChanger (Session& s)
38         : SceneChanger (s)
39         , _recording (true)
40         , have_seen_bank_changes (false)
41         , last_program_message_time (-1)
42         , last_delivered_program (-1)
43         , last_delivered_bank (-1)
44           
45 {
46         /* catch any add/remove/clear etc. for all Locations */
47         _session.locations()->changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
48         _session.locations()->added.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
49         _session.locations()->removed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
50
51         /* catch class-based signal that notifies of us changes in the scene change state of any Location */
52         Location::scene_changed.connect_same_thread (*this, boost::bind (&MIDISceneChanger::locations_changed, this));
53 }
54
55 MIDISceneChanger::~MIDISceneChanger ()
56 {
57 }
58
59 void
60 MIDISceneChanger::locations_changed ()
61 {
62         _session.locations()->apply (*this, &MIDISceneChanger::gather);
63 }
64
65 /** Use the session's list of locations to collect all patch changes.
66  * 
67  * This is called whenever the locations change in anyway.
68  */
69 void
70 MIDISceneChanger::gather (const Locations::LocationList& locations)
71 {
72         boost::shared_ptr<SceneChange> sc;
73
74         Glib::Threads::RWLock::WriterLock lm (scene_lock);
75
76         scenes.clear ();
77
78         for (Locations::LocationList::const_iterator l = locations.begin(); l != locations.end(); ++l) {
79
80                 if ((sc = (*l)->scene_change()) != 0) {
81
82                         boost::shared_ptr<MIDISceneChange> msc = boost::dynamic_pointer_cast<MIDISceneChange> (sc);
83
84                         if (msc) {
85
86                                 if (msc->bank() >= 0) {
87                                         have_seen_bank_changes = true;
88                                 }
89                         
90                                 scenes.insert (std::make_pair ((*l)->start(), msc));
91                         }
92                 }
93         }
94 }
95
96 void
97 MIDISceneChanger::rt_deliver (MidiBuffer& mbuf, framepos_t when, boost::shared_ptr<MIDISceneChange> msc)
98 {
99         uint8_t buf[4];
100         size_t cnt;
101
102         MIDIOutputActivity (); /* EMIT SIGNAL */
103
104         if ((cnt = msc->get_bank_msb_message (buf, sizeof (buf))) > 0) {
105                 mbuf.push_back (when, cnt, buf);
106
107                 if ((cnt = msc->get_bank_lsb_message (buf, sizeof (buf))) > 0) {
108                         mbuf.push_back (when, cnt, buf);
109                 }
110
111                 last_delivered_bank = msc->bank();
112         }
113
114         if ((cnt = msc->get_program_message (buf, sizeof (buf))) > 0) {
115                 mbuf.push_back (when, cnt, buf);
116
117                 last_delivered_program = msc->program();
118         }
119 }
120
121 void
122 MIDISceneChanger::non_rt_deliver (boost::shared_ptr<MIDISceneChange> msc)
123 {
124         uint8_t buf[4];
125         size_t cnt;
126         boost::shared_ptr<AsyncMIDIPort> aport = boost::dynamic_pointer_cast<AsyncMIDIPort>(output_port);
127
128         /* We use zero as the timestamp for these messages because we are in a
129            non-RT/process context. Using zero means "deliver them as early as
130            possible" (practically speaking, in the next process callback).
131         */
132         
133         MIDIOutputActivity (); /* EMIT SIGNAL */
134
135         if ((cnt = msc->get_bank_msb_message (buf, sizeof (buf))) > 0) {
136                 aport->write (buf, cnt, 0);
137
138                 if ((cnt = msc->get_bank_lsb_message (buf, sizeof (buf))) > 0) {
139                         aport->write (buf, cnt, 0);
140                 }
141
142                 last_delivered_bank = msc->bank();
143         }
144
145         if ((cnt = msc->get_program_message (buf, sizeof (buf))) > 0) {
146                 aport->write (buf, cnt, 0);
147                 last_delivered_program = msc->program();
148         }
149 }
150
151 void
152 MIDISceneChanger::run (framepos_t start, framepos_t end)
153 {
154         if (!output_port || recording() || !_session.transport_rolling()) {
155                 return;
156         }
157
158         Glib::Threads::RWLock::ReaderLock lm (scene_lock, Glib::Threads::TRY_LOCK);
159         
160         if (!lm.locked()) {
161                 return;
162         }
163
164         /* get lower bound of events to consider */
165
166         Scenes::const_iterator i = scenes.lower_bound (start);
167         MidiBuffer& mbuf (output_port->get_midi_buffer (end-start));
168
169         while (i != scenes.end()) {
170
171                 if (i->first >= end) {
172                         break;
173                 }
174         
175                 rt_deliver (mbuf, i->first - start, i->second);
176                 
177                 ++i;
178         }
179 }
180
181 void
182 MIDISceneChanger::locate (framepos_t pos)
183 {
184         boost::shared_ptr<MIDISceneChange> msc;
185
186         {
187                 Glib::Threads::RWLock::ReaderLock lm (scene_lock);
188
189                 if (scenes.empty()) {
190                         return;
191                 }
192                 
193                 Scenes::const_iterator i = scenes.lower_bound (pos);
194                 
195                 if (i != scenes.end()) {
196
197                         if (i->first != pos) {
198                                 /* i points to first scene with position > pos, so back
199                                  * up, if possible.
200                                  */
201                                 if (i != scenes.begin()) {
202                                         --i;
203                                 } else {
204                                         return;
205                                 }
206                         }
207                 } else {
208                         /* go back to the final scene and use it */
209                         --i;
210                 }
211
212                 msc = i->second;
213         }
214
215         if (msc->program() != last_delivered_program || msc->bank() != last_delivered_bank) {
216                 non_rt_deliver (msc);
217         }
218 }               
219
220 void
221 MIDISceneChanger::set_input_port (MIDI::Port* mp)
222 {
223         input_port = mp;
224
225         incoming_connections.drop_connections();
226         
227         if (input_port) {
228                 
229                 /* midi port is asynchronous. MIDI parsing will be carried out
230                  * by the MIDI UI thread which will emit the relevant signals
231                  * and thus invoke our callbacks as necessary.
232                  */
233
234                 for (int channel = 0; channel < 16; ++channel) {
235                         input_port->parser()->channel_bank_change[channel].connect_same_thread (incoming_connections, boost::bind (&MIDISceneChanger::bank_change_input, this, _1, _2, channel));
236                         input_port->parser()->channel_program_change[channel].connect_same_thread (incoming_connections, boost::bind (&MIDISceneChanger::program_change_input, this, _1, _2, channel));
237                 }
238         }
239 }
240
241 void
242 MIDISceneChanger::set_output_port (boost::shared_ptr<MidiPort> mp)
243 {
244         output_port = mp;
245 }
246
247 void
248 MIDISceneChanger::set_recording (bool yn)
249 {
250         _recording = yn;
251 }
252
253 bool
254 MIDISceneChanger::recording() const 
255 {
256         return _session.transport_rolling() && _session.get_record_enabled();
257 }
258
259 void
260   MIDISceneChanger::bank_change_input (MIDI::Parser& /*parser*/, unsigned short, int)
261 {
262         if (recording()) {
263                 have_seen_bank_changes = true;
264         }
265         MIDIInputActivity (); /* EMIT SIGNAL */
266 }
267
268 void
269 MIDISceneChanger::program_change_input (MIDI::Parser& parser, MIDI::byte program, int channel)
270 {
271         framecnt_t time = parser.get_timestamp ();
272
273         last_program_message_time = time;
274
275         if (!recording()) {
276
277                 MIDIInputActivity (); /* EMIT SIGNAL */
278         
279                 int bank = -1;
280                 if (have_seen_bank_changes) {
281                         bank = input_port->channel (channel)->bank();
282                 }
283         
284                 jump_to (input_port->channel (channel)->bank(), program);
285                 return;
286         }
287
288         Locations* locations (_session.locations ());
289         Location* loc;
290         bool new_mark = false;
291         framecnt_t slop = (framecnt_t) floor ((Config->get_inter_scene_gap_msecs() / 1000.0) * _session.frame_rate());
292
293         /* check for marker at current location */
294
295         loc = locations->mark_at (time, slop);
296
297         if (!loc) {
298                 /* create a new marker at the desired position */
299                 
300                 std::string new_name;
301
302                 if (!locations->next_available_name (new_name, _("Scene "))) {
303                         std::cerr << "No new marker name available\n";
304                         return;
305                 }
306                 
307                 loc = new Location (_session, time, time, new_name, Location::IsMark);
308                 new_mark = true;
309         }
310
311         int bank = -1;
312         if (have_seen_bank_changes) {
313                 bank = input_port->channel (channel)->bank();
314         }
315
316         MIDISceneChange* msc =new MIDISceneChange (channel, bank, program & 0x7f);
317
318         /* check for identical scene change so we can re-use color, if any */
319
320         Locations::LocationList copy (locations->list());
321
322         for (Locations::LocationList::const_iterator l = copy.begin(); l != copy.end(); ++l) {
323                 boost::shared_ptr<MIDISceneChange> sc = boost::dynamic_pointer_cast<MIDISceneChange>((*l)->scene_change());
324
325                 if (sc && (*sc.get()) == *msc) {
326                         msc->set_color (sc->color ());
327                         break;
328                 }
329         }
330
331         loc->set_scene_change (boost::shared_ptr<MIDISceneChange> (msc));
332         
333         /* this will generate a "changed" signal to be emitted by locations,
334            and we will call ::gather() to update our list of MIDI events.
335         */
336
337         if (new_mark) {
338                 locations->add (loc);
339         }
340
341         MIDIInputActivity (); /* EMIT SIGNAL */
342 }
343
344 void
345 MIDISceneChanger::jump_to (int bank, int program)
346 {
347         const Locations::LocationList& locations (_session.locations()->list());
348         boost::shared_ptr<SceneChange> sc;
349         framepos_t where = max_framepos;
350
351         for (Locations::LocationList::const_iterator l = locations.begin(); l != locations.end(); ++l) {
352
353                 if ((sc = (*l)->scene_change()) != 0) {
354
355                         boost::shared_ptr<MIDISceneChange> msc = boost::dynamic_pointer_cast<MIDISceneChange> (sc);
356
357                         if (msc->bank() == bank && msc->program() == program && (*l)->start() < where) {
358                                 where = (*l)->start();
359                         }
360                 }
361         }
362
363         if (where != max_framepos) {
364                 _session.request_locate (where);
365         }
366 }