ardour
midi_scene_changer.cc
Go to the documentation of this file.
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"
31 #include "ardour/session.h"
32 
33 #include "i18n.h"
34 
35 using namespace ARDOUR;
36 
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 
56 {
57 }
58 
59 void
61 {
63 }
64 
69 void
71 {
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 
83 
84  if (msc) {
85 
86  if (msc->bank() >= 0) {
88  }
89 
90  scenes.insert (std::make_pair ((*l)->start(), msc));
91  }
92  }
93  }
94 }
95 
96 void
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 
118  }
119 }
120 
121 void
123 {
124  uint8_t buf[4];
125  size_t cnt;
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);
148  }
149 }
150 
151 void
153 {
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
183 {
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
222 {
223  input_port = mp;
224 
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
243 {
244  output_port = mp;
245 }
246 
247 void
249 {
250  _recording = yn;
251 }
252 
253 bool
255 {
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 
274 
275  if (!recording()) {
276  MIDIInputActivity (); /* EMIT SIGNAL */
277  jump_to (input_port->channel (channel)->bank(), program);
278  return;
279  }
280 
281  Locations* locations (_session.locations ());
282  Location* loc;
283  bool new_mark = false;
284  framecnt_t slop = (framecnt_t) floor ((Config->get_inter_scene_gap_msecs() / 1000.0) * _session.frame_rate());
285 
286  /* check for marker at current location */
287 
288  loc = locations->mark_at (time, slop);
289 
290  if (!loc) {
291  /* create a new marker at the desired position */
292 
293  std::string new_name;
294 
295  if (!locations->next_available_name (new_name, _("Scene "))) {
296  std::cerr << "No new marker name available\n";
297  return;
298  }
299 
300  loc = new Location (_session, time, time, new_name, Location::IsMark);
301  new_mark = true;
302  }
303 
304  unsigned short bank;
305 
307  bank = input_port->channel (channel)->bank();
308  } else {
309  bank = -1;
310  }
311 
312  MIDISceneChange* msc =new MIDISceneChange (channel, bank, program & 0x7f);
313 
314  /* check for identical scene change so we can re-use color, if any */
315 
316  Locations::LocationList copy (locations->list());
317 
318  for (Locations::LocationList::const_iterator l = copy.begin(); l != copy.end(); ++l) {
320 
321  if (sc && (*sc.get()) == *msc) {
322  msc->set_color (sc->color ());
323  break;
324  }
325  }
326 
327  loc->set_scene_change (boost::shared_ptr<MIDISceneChange> (msc));
328 
329  /* this will generate a "changed" signal to be emitted by locations,
330  and we will call ::gather() to update our list of MIDI events.
331  */
332 
333  if (new_mark) {
334  locations->add (loc);
335  }
336 
337  MIDIInputActivity (); /* EMIT SIGNAL */
338 }
339 
340 void
341 MIDISceneChanger::jump_to (int bank, int program)
342 {
343  const Locations::LocationList& locations (_session.locations()->list());
345  framepos_t where = max_framepos;
346 
347  for (Locations::LocationList::const_iterator l = locations.begin(); l != locations.end(); ++l) {
348 
349  if ((sc = (*l)->scene_change()) != 0) {
350 
352 
353  if (msc->bank() == bank && msc->program() == program && (*l)->start() < where) {
354  where = (*l)->start();
355  }
356  }
357  }
358 
359  if (where != max_framepos) {
360  _session.request_locate (where);
361  }
362 }
Glib::Threads::RWLock scene_lock
bool transport_rolling() const
Definition: session.h:592
ARDOUR::Session & _session
static PBD::Signal0< void > scene_changed
Definition: location.h:146
void bank_change_input(MIDI::Parser &, unsigned short, int channel)
std::list< Location * > LocationList
Definition: location.h:167
PBD::Signal1< void, Location * > added
Definition: location.h:209
bool get_record_enabled() const
Definition: session.h:272
void set_input_port(MIDI::Port *)
void set_color(uint32_t)
Definition: scene_change.cc:48
PBD::Signal0< void > MIDIInputActivity
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
void rt_deliver(MidiBuffer &, framepos_t, boost::shared_ptr< MIDISceneChange >)
PBD::ScopedConnectionList incoming_connections
PBD::Signal0< void > MIDIOutputActivity
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
void request_locate(framepos_t frame, bool with_roll=false)
framecnt_t frame_rate() const
Definition: session.h:365
Locations * locations()
Definition: session.h:382
void gather(const Locations::LocationList &)
#define _(Text)
Definition: i18n.h:11
size_t get_bank_lsb_message(uint8_t *buf, size_t size) const
int64_t framecnt_t
Definition: types.h:76
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
size_t get_bank_msb_message(uint8_t *buf, size_t size) const
void non_rt_deliver(boost::shared_ptr< MIDISceneChange >)
PBD::Signal1< void, Location * > removed
Definition: location.h:210
const LocationList & list()
Definition: location.h:172
Definition: amp.h:29
uint32_t color() const
Definition: scene_change.cc:55
void program_change_input(MIDI::Parser &, MIDI::byte, int channel)
void set_output_port(boost::shared_ptr< MidiPort >)
int64_t framepos_t
Definition: types.h:66
T * get() const
Definition: shared_ptr.hpp:268
bool push_back(const Evoral::MIDIEvent< TimeType > &event)
Definition: midi_buffer.cc:136
void run(framepos_t start, framepos_t end)
static const framepos_t max_framepos
Definition: types.h:78
int write(const MIDI::byte *msg, size_t msglen, MIDI::timestamp_t timestamp)
boost::shared_ptr< MidiPort > output_port
void jump_to(int bank, int program)
void apply(T &obj, void(T::*method)(const LocationList &)) const
Definition: location.h:213
PBD::Signal0< void > changed
Definition: location.h:211
size_t get_program_message(uint8_t *buf, size_t size) const