124ef95615754603afbb89204eb883d33803279d
[ardour.git] / libs / surfaces / generic_midi / midicontrollable.cc
1 /*
2     Copyright (C) 1998-2006 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 <cstdio> /* for sprintf, sigh */
21 #include <climits>
22 #include "pbd/error.h"
23 #include "pbd/xml++.h"
24 #include "midi++/port.h"
25 #include "midi++/channel.h"
26 #include "ardour/automation_control.h"
27
28 #include "midicontrollable.h"
29
30 using namespace sigc;
31 using namespace std;
32 using namespace MIDI;
33 using namespace PBD;
34 using namespace ARDOUR;
35
36 MIDIControllable::MIDIControllable (Port& p, const string& c, bool is_bistate)
37         : controllable (0), _current_uri (c), _port (p), bistate (is_bistate)
38 {
39         init ();
40 }
41
42 MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool is_bistate)
43         : controllable (&c), _current_uri (c.uri()), _port (p), bistate (is_bistate)
44 {
45         init ();
46 }
47
48 MIDIControllable::~MIDIControllable ()
49 {
50         drop_external_control ();
51 }
52
53 void
54 MIDIControllable::init ()
55 {
56         setting = false;
57         last_value = 0; // got a better idea ?
58         control_type = none;
59         _control_description = "MIDI Control: none";
60         control_additional = (byte) -1;
61         connections = 0;
62         feedback = true; // for now
63
64         /* use channel 0 ("1") as the initial channel */
65
66         midi_rebind (0);
67 }
68
69 void
70 MIDIControllable::midi_forget ()
71 {
72         /* stop listening for incoming messages, but retain
73            our existing event + type information.
74         */
75
76         if (connections > 0) {
77                 midi_sense_connection[0].disconnect ();
78         }
79
80         if (connections > 1) {
81                 midi_sense_connection[1].disconnect ();
82         }
83
84         connections = 0;
85         midi_learn_connection.disconnect ();
86
87 }
88
89 void
90 MIDIControllable::reacquire_controllable ()
91 {
92         if (!_current_uri.empty()) {
93                 controllable = Controllable::by_uri (_current_uri);
94         } else {
95                 controllable = 0;
96         }
97 }
98
99 void
100 MIDIControllable::midi_rebind (channel_t c)
101 {
102         if (c >= 0) {
103                 bind_midi (c, control_type, control_additional);
104         } else {
105                 midi_forget ();
106         }
107 }
108
109 void
110 MIDIControllable::learn_about_external_control ()
111 {
112         drop_external_control ();
113         midi_learn_connection = _port.input()->any.connect (mem_fun (*this, &MIDIControllable::midi_receiver));
114 }
115
116 void
117 MIDIControllable::stop_learning ()
118 {
119         midi_learn_connection.disconnect ();
120 }
121
122 void
123 MIDIControllable::drop_external_control ()
124 {
125         if (connections > 0) {
126                 midi_sense_connection[0].disconnect ();
127         }
128         if (connections > 1) {
129                 midi_sense_connection[1].disconnect ();
130         }
131
132         connections = 0;
133         midi_learn_connection.disconnect ();
134
135         control_type = none;
136         control_additional = (byte) -1;
137 }
138
139 float
140 MIDIControllable::control_to_midi(float val)
141 {
142         float control_min = 0.0f;
143         float control_max = 1.0f;
144         ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(controllable);
145         if (ac) {
146                 control_min = ac->parameter().min();
147                 control_max = ac->parameter().max();
148         }
149
150         const float control_range = control_max - control_min;
151         const float midi_range    = 127.0f; // TODO: NRPN etc.
152
153         return (val - control_min) / control_range * midi_range;
154 }
155
156 float
157 MIDIControllable::midi_to_control(float val)
158 {
159         float control_min = 0.0f;
160         float control_max = 1.0f;
161         ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(controllable);
162         if (ac) {
163                 control_min = ac->parameter().min();
164                 control_max = ac->parameter().max();
165         }
166
167         const float control_range = control_max - control_min;
168         const float midi_range    = 127.0f; // TODO: NRPN etc.
169
170         return val / midi_range * control_range + control_min;
171 }
172
173 void
174 MIDIControllable::midi_sense_note_on (Parser &p, EventTwoBytes *tb)
175 {
176         midi_sense_note (p, tb, true);
177 }
178
179 void
180 MIDIControllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
181 {
182         midi_sense_note (p, tb, false);
183 }
184
185 void
186 MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool is_on)
187 {
188         if (!controllable) { 
189                 return;
190         }
191
192         if (!bistate) {
193                 controllable->set_value (msg->note_number/127.0);
194         } else {
195
196                 /* Note: parser handles the use of zero velocity to
197                    mean note off. if we get called with is_on=true, then we
198                    got a *real* note on.
199                 */
200
201                 if (msg->note_number == control_additional) {
202                         controllable->set_value (is_on ? 1 : 0);
203                 }
204         }
205
206         last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
207 }
208
209 void
210 MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
211 {
212         if (!controllable) { 
213                 return;
214         }
215
216         if (controllable->touching()) {
217                 return; // to prevent feedback fights when e.g. dragging a UI slider
218         }
219
220         if (control_additional == msg->controller_number) {
221                 if (!bistate) {
222                         controllable->set_value (midi_to_control(msg->value));
223                 } else {
224                         if (msg->value > 64.0) {
225                                 controllable->set_value (1);
226                         } else {
227                                 controllable->set_value (0);
228                         }
229                 }
230
231                 last_value = (MIDI::byte) (control_to_midi(controllable->get_value())); // to prevent feedback fights
232         }
233 }
234
235 void
236 MIDIControllable::midi_sense_program_change (Parser &, byte msg)
237 {
238         if (!controllable) { 
239                 return;
240         }
241         /* XXX program change messages make no sense for bistates */
242
243         if (!bistate) {
244                 controllable->set_value (msg/127.0);
245                 last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
246         }
247 }
248
249 void
250 MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
251 {
252         if (!controllable) { 
253                 return;
254         }
255
256         /* pitchbend messages make no sense for bistates */
257
258         /* XXX gack - get rid of assumption about typeof pitchbend_t */
259
260         controllable->set_value ((pb/(float) SHRT_MAX));
261         last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
262 }
263
264 void
265 MIDIControllable::midi_receiver (Parser &, byte *msg, size_t /*len*/)
266 {
267         /* we only respond to channel messages */
268
269         if ((msg[0] & 0xF0) < 0x80 || (msg[0] & 0xF0) > 0xE0) {
270                 return;
271         }
272
273         /* if the our port doesn't do input anymore, forget it ... */
274
275         if (!_port.input()) {
276                 return;
277         }
278
279         bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
280
281         controllable->LearningFinished ();
282 }
283
284 void
285 MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
286 {
287         char buf[64];
288
289         drop_external_control ();
290
291         control_type = ev;
292         control_channel = chn;
293         control_additional = additional;
294
295         if (_port.input() == 0) {
296                 return;
297         }
298
299         Parser& p = *_port.input();
300
301         int chn_i = chn;
302         switch (ev) {
303         case MIDI::off:
304                 midi_sense_connection[0] = p.channel_note_off[chn_i].connect
305                         (mem_fun (*this, &MIDIControllable::midi_sense_note_off));
306
307                 /* if this is a bistate, connect to noteOn as well,
308                    and we'll toggle back and forth between the two.
309                 */
310
311                 if (bistate) {
312                         midi_sense_connection[1] = p.channel_note_on[chn_i].connect
313                                 (mem_fun (*this, &MIDIControllable::midi_sense_note_on));
314                         connections = 2;
315                 } else {
316                         connections = 1;
317                 }
318                 _control_description = "MIDI control: NoteOff";
319                 break;
320
321         case MIDI::on:
322                 midi_sense_connection[0] = p.channel_note_on[chn_i].connect
323                         (mem_fun (*this, &MIDIControllable::midi_sense_note_on));
324                 if (bistate) {
325                         midi_sense_connection[1] = p.channel_note_off[chn_i].connect
326                                 (mem_fun (*this, &MIDIControllable::midi_sense_note_off));
327                         connections = 2;
328                 } else {
329                         connections = 1;
330                 }
331                 _control_description = "MIDI control: NoteOn";
332                 break;
333
334         case MIDI::controller:
335                 midi_sense_connection[0] = p.channel_controller[chn_i].connect
336                         (mem_fun (*this, &MIDIControllable::midi_sense_controller));
337                 connections = 1;
338                 snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional);
339                 _control_description = buf;
340                 break;
341
342         case MIDI::program:
343                 if (!bistate) {
344                         midi_sense_connection[0] = p.channel_program_change[chn_i].connect
345                                 (mem_fun (*this,
346                                        &MIDIControllable::midi_sense_program_change));
347                         connections = 1;
348                         _control_description = "MIDI control: ProgramChange";
349                 }
350                 break;
351
352         case MIDI::pitchbend:
353                 if (!bistate) {
354                         midi_sense_connection[0] = p.channel_pitchbend[chn_i].connect
355                                 (mem_fun (*this, &MIDIControllable::midi_sense_pitchbend));
356                         connections = 1;
357                         _control_description = "MIDI control: Pitchbend";
358                 }
359                 break;
360
361         default:
362                 break;
363         }
364 }
365
366 void
367 MIDIControllable::send_feedback ()
368 {
369         byte msg[3];
370
371         if (setting || !feedback || control_type == none) {
372                 return;
373         }
374
375         msg[0] = (control_type & 0xF0) | (control_channel & 0xF);
376         msg[1] = control_additional;
377         msg[2] = (byte) (control_to_midi(controllable->get_value()));
378
379         _port.write (msg, 3, 0);
380 }
381
382 MIDI::byte*
383 MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/)
384 {
385         if (control_type != none && feedback && bufsize > 2) {
386
387                 MIDI::byte gm = (MIDI::byte) (control_to_midi(controllable->get_value()));
388
389                 if (gm != last_value) {
390                         *buf++ = (0xF0 & control_type) | (0xF & control_channel);
391                         *buf++ = control_additional; /* controller number */
392                         *buf++ = gm;
393                         last_value = gm;
394                         bufsize -= 3;
395                 }
396         }
397
398         return buf;
399 }
400
401 int
402 MIDIControllable::set_state (const XMLNode& node, int /*version*/)
403 {
404         const XMLProperty* prop;
405         int xx;
406
407         if ((prop = node.property ("event")) != 0) {
408                 sscanf (prop->value().c_str(), "0x%x", &xx);
409                 control_type = (MIDI::eventType) xx;
410         } else {
411                 return -1;
412         }
413
414         if ((prop = node.property ("channel")) != 0) {
415                 sscanf (prop->value().c_str(), "%d", &xx);
416                 control_channel = (MIDI::channel_t) xx;
417         } else {
418                 return -1;
419         }
420
421         if ((prop = node.property ("additional")) != 0) {
422                 sscanf (prop->value().c_str(), "0x%x", &xx);
423                 control_additional = (MIDI::byte) xx;
424         } else {
425                 return -1;
426         }
427
428         if ((prop = node.property ("feedback")) != 0) {
429                 feedback = (prop->value() == "yes");
430         } else {
431                 feedback = true; // default
432         }
433
434         bind_midi (control_channel, control_type, control_additional);
435
436         return 0;
437 }
438
439 XMLNode&
440 MIDIControllable::get_state ()
441 {
442         char buf[32];
443
444         XMLNode* node = new XMLNode ("MIDIControllable");
445
446         if (controllable) {
447                 node->add_property ("uri", controllable->uri());
448                 snprintf (buf, sizeof(buf), "0x%x", (int) control_type);
449                 node->add_property ("event", buf);
450                 snprintf (buf, sizeof(buf), "%d", (int) control_channel);
451                 node->add_property ("channel", buf);
452                 snprintf (buf, sizeof(buf), "0x%x", (int) control_additional);
453                 node->add_property ("additional", buf);
454                 node->add_property ("feedback", (feedback ? "yes" : "no"));
455         }
456
457         return *node;
458 }
459