f17782f3c4a2bcd8a36c760a8049a715f221c315
[ardour.git] / libs / midi++2 / midicontrollable.cc
1 /*
2     Copyright (C) 1998-99 Paul Barton-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     $Id$
19 */
20
21 #include <cstdio> /* for sprintf, sigh */
22 #include <pbd/error.h>
23 #include <midi++/port.h>
24 #include <midi++/channel.h>
25 #include <midi++/controllable.h>
26
27 using namespace sigc;
28 using namespace MIDI;
29
30 Controllable::Controllable (Port *p, bool is_bistate)
31 {
32         control_type = none;
33         _control_description = "MIDI Control: none";
34         control_additional = (byte) -1;
35         bistate = is_bistate;
36         connections = 0;
37         feedback = true; // for now
38         
39         /* use channel 0 ("1") as the initial channel */
40
41         midi_rebind (p, 0);
42 }
43
44 Controllable::~Controllable ()
45 {
46         drop_external_control ();
47 }
48
49 void
50 Controllable::midi_forget ()
51 {
52         /* stop listening for incoming messages, but retain
53            our existing event + type information.
54         */
55         
56         if (connections > 0) {
57                 midi_sense_connection[0].disconnect ();
58         } 
59         
60         if (connections > 1) {
61                 midi_sense_connection[1].disconnect ();
62         }
63         
64         connections = 0;
65         midi_learn_connection.disconnect ();
66         
67 }
68
69 void
70 Controllable::midi_rebind (Port *p, channel_t c)
71 {
72         if ((port = p) == 0) {
73                 midi_forget ();
74         } else {
75                 if (c >= 0) {
76                         bind_midi (c, control_type, control_additional);
77                 } else {
78                         midi_forget ();
79                 }
80         }
81 }
82
83 void
84 Controllable::learn_about_external_control ()
85 {
86         drop_external_control ();
87
88         if (port) {
89                 midi_learn_connection = port->input()->any.connect (mem_fun (*this, &Controllable::midi_receiver));
90                 learning_started ();
91
92         } else {
93                 info << "No MIDI port specified - external control disabled" << endmsg;
94         }
95 }
96
97 void
98 Controllable::stop_learning ()
99 {
100         midi_learn_connection.disconnect ();
101 }
102
103 void
104 Controllable::drop_external_control ()
105 {
106         if (connections > 0) {
107                 midi_sense_connection[0].disconnect ();
108         } 
109         if (connections > 1) {
110                 midi_sense_connection[1].disconnect ();
111         }
112
113         connections = 0;
114         midi_learn_connection.disconnect ();
115
116         control_type = none;
117         control_additional = (byte) -1;
118 }
119
120 void 
121 Controllable::midi_sense_note_on (Parser &p, EventTwoBytes *tb) 
122 {
123         midi_sense_note (p, tb, true);
124 }
125
126 void 
127 Controllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb) 
128 {
129         midi_sense_note (p, tb, false);
130 }
131
132 void
133 Controllable::midi_sense_note (Parser &p, EventTwoBytes *msg, bool is_on)
134 {
135         if (!bistate) {
136                 set_value (msg->note_number/127.0);
137         } else {
138
139                 /* Note: parser handles the use of zero velocity to
140                    mean note off. if we get called with is_on=true, then we
141                    got a *real* note on.  
142                 */
143
144                 if (msg->note_number == control_additional) {
145                         set_value (is_on ? 1 : 0);
146                 }
147         }
148 }
149
150 void
151 Controllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
152 {
153         if (control_additional == msg->controller_number) {
154                 if (!bistate) {
155                         set_value (msg->value/127.0);
156                 } else {
157                         if (msg->value > 64.0) {
158                                 set_value (1);
159                         } else {
160                                 set_value (0);
161                         }
162                 }
163         }
164 }
165
166 void
167 Controllable::midi_sense_program_change (Parser &p, byte msg)
168 {
169         /* XXX program change messages make no sense for bistates */
170
171         if (!bistate) {
172                 set_value (msg/127.0);
173         } 
174 }
175
176 void
177 Controllable::midi_sense_pitchbend (Parser &p, pitchbend_t pb)
178 {
179         /* pitchbend messages make no sense for bistates */
180
181         /* XXX gack - get rid of assumption about typeof pitchbend_t */
182
183         set_value ((pb/(float) SHRT_MAX));
184 }                       
185
186 void
187 Controllable::midi_receiver (Parser &p, byte *msg, size_t len)
188 {
189         /* we only respond to channel messages */
190
191         if ((msg[0] & 0xF0) < 0x80 || (msg[0] & 0xF0) > 0xE0) {
192                 return;
193         }
194
195         /* if the our port doesn't do input anymore, forget it ... */
196
197         if (!port->input()) {
198                 return;
199         }
200
201         bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
202
203         learning_stopped ();
204 }
205
206 void
207 Controllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
208 {
209         char buf[64];
210
211         drop_external_control ();
212
213         control_type = ev;
214         control_channel = chn;
215         control_additional = additional;
216
217         if (port == 0 || port->input() == 0) {
218                 return;
219         }
220         
221         Parser& p = *port->input();
222
223         int chn_i = chn;
224         switch (ev) {
225         case MIDI::off:
226                 midi_sense_connection[0] = p.channel_note_off[chn_i].connect
227                         (mem_fun (*this, &Controllable::midi_sense_note_off));
228
229                 /* if this is a bistate, connect to noteOn as well,
230                    and we'll toggle back and forth between the two.
231                 */
232
233                 if (bistate) {
234                         midi_sense_connection[1] = p.channel_note_on[chn_i].connect
235                                 (mem_fun (*this, &Controllable::midi_sense_note_on));
236                         connections = 2;
237                 } else {
238                         connections = 1;
239                 }
240                 _control_description = "MIDI control: NoteOff";
241                 break;
242
243         case MIDI::on:
244                 midi_sense_connection[0] = p.channel_note_on[chn_i].connect
245                         (mem_fun (*this, &Controllable::midi_sense_note_on));
246                 if (bistate) {
247                         midi_sense_connection[1] = p.channel_note_off[chn_i].connect 
248                                 (mem_fun (*this, &Controllable::midi_sense_note_off));
249                         connections = 2;
250                 } else {
251                         connections = 1;
252                 }
253                 _control_description = "MIDI control: NoteOn";
254                 break;
255
256         case MIDI::controller:
257                 midi_sense_connection[0] = p.channel_controller[chn_i].connect 
258                         (mem_fun (*this, &Controllable::midi_sense_controller));
259                 connections = 1;
260                 snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional);
261                 _control_description = buf;
262                 break;
263
264         case MIDI::program:
265                 if (!bistate) {
266                         midi_sense_connection[0] = p.channel_program_change[chn_i].connect
267                                 (mem_fun (*this, 
268                                        &Controllable::midi_sense_program_change));
269                         connections = 1;
270                         _control_description = "MIDI control: ProgramChange";
271                 }
272                 break;
273
274         case MIDI::pitchbend:
275                 if (!bistate) {
276                         midi_sense_connection[0] = p.channel_pitchbend[chn_i].connect
277                                 (mem_fun (*this, &Controllable::midi_sense_pitchbend));
278                         connections = 1;
279                         _control_description = "MIDI control: Pitchbend";
280                 }
281                 break;
282
283         default:
284                 break;
285         }
286 }
287
288 void
289 Controllable::set_control_type (channel_t chn, eventType ev, MIDI::byte additional)
290 {
291         bind_midi (chn, ev, additional);
292 }
293
294 bool
295 Controllable::get_control_info (channel_t& chn, eventType& ev, byte& additional)
296 {
297         if (control_type == none) {
298                 chn = -1;
299                 return false;
300         } 
301         
302         ev = control_type;
303         chn = control_channel;
304         additional = control_additional;
305
306         return true;
307 }
308
309
310 void
311 Controllable::send_midi_feedback (float val)
312 {
313         byte msg[3];
314
315         if (port == 0 || control_type == none) {
316                 return;
317         }
318         
319         msg[0] = (control_type & 0xF0) | (control_channel & 0xF); 
320         msg[1] = control_additional;
321         msg[2] = (byte) (val * 127.0f);
322
323         port->write (msg, 3);
324 }
325