~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to libs/surfaces/generic_midi/midicontrollable.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <stdint.h>
 
21
#include <cmath>
 
22
#include <climits>
 
23
#include <iostream>
 
24
 
 
25
#include "pbd/error.h"
 
26
#include "pbd/controllable_descriptor.h"
 
27
#include "pbd/xml++.h"
 
28
#include "pbd/stacktrace.h"
 
29
 
 
30
#include "midi++/port.h"
 
31
#include "midi++/channel.h"
 
32
 
 
33
#include "ardour/automation_control.h"
 
34
#include "ardour/midi_ui.h"
 
35
#include "ardour/utils.h"
 
36
 
 
37
#include "midicontrollable.h"
 
38
#include "generic_midi_control_protocol.h"
 
39
 
 
40
using namespace std;
 
41
using namespace MIDI;
 
42
using namespace PBD;
 
43
using namespace ARDOUR;
 
44
 
 
45
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, bool m)
 
46
        : _surface (s)
 
47
        , controllable (0)
 
48
        , _descriptor (0)
 
49
        , _port (p)
 
50
        , _momentary (m)
 
51
{
 
52
        _learned = false; /* from URI */
 
53
        setting = false;
 
54
        last_value = 0; // got a better idea ?
 
55
        last_controllable_value = 0.0f;
 
56
        control_type = none;
 
57
        _control_description = "MIDI Control: none";
 
58
        control_additional = (byte) -1;
 
59
        feedback = true; // for now
 
60
}
 
61
 
 
62
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, Controllable& c, bool m)
 
63
        : _surface (s)
 
64
        , _descriptor (0)
 
65
        , _port (p)
 
66
        , _momentary (m)
 
67
{
 
68
        set_controllable (&c);
 
69
        
 
70
        _learned = true; /* from controllable */
 
71
        setting = false;
 
72
        last_value = 0; // got a better idea ?
 
73
        last_controllable_value = 0.0f;
 
74
        control_type = none;
 
75
        _control_description = "MIDI Control: none";
 
76
        control_additional = (byte) -1;
 
77
        feedback = true; // for now
 
78
}
 
79
 
 
80
MIDIControllable::~MIDIControllable ()
 
81
{
 
82
        drop_external_control ();
 
83
}
 
84
 
 
85
int
 
86
MIDIControllable::init (const std::string& s)
 
87
{
 
88
        _current_uri = s;
 
89
        delete _descriptor;
 
90
        _descriptor = new ControllableDescriptor;
 
91
        return _descriptor->set (s);
 
92
}
 
93
 
 
94
void
 
95
MIDIControllable::midi_forget ()
 
96
{
 
97
        /* stop listening for incoming messages, but retain
 
98
           our existing event + type information.
 
99
        */
 
100
 
 
101
        midi_sense_connection[0].disconnect ();
 
102
        midi_sense_connection[1].disconnect ();
 
103
        midi_learn_connection.disconnect ();
 
104
}
 
105
 
 
106
void
 
107
MIDIControllable::drop_external_control ()
 
108
{
 
109
        midi_forget ();
 
110
        control_type = none;
 
111
        control_additional = (byte) -1;
 
112
}
 
113
 
 
114
void
 
115
MIDIControllable::set_controllable (Controllable* c)
 
116
{
 
117
        if (c == controllable) {
 
118
                return;
 
119
        }
 
120
        
 
121
        controllable_death_connection.disconnect ();
 
122
 
 
123
        controllable = c;
 
124
 
 
125
        if (controllable) {
 
126
                last_controllable_value = controllable->get_value();
 
127
        } else {
 
128
                last_controllable_value = 0.0f; // is there a better value?
 
129
        }
 
130
 
 
131
        if (controllable) {
 
132
                controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR,
 
133
                                                 boost::bind (&MIDIControllable::drop_controllable, this), 
 
134
                                                 MidiControlUI::instance());
 
135
        }
 
136
}
 
137
 
 
138
void
 
139
MIDIControllable::midi_rebind (channel_t c)
 
140
{
 
141
        if (c >= 0) {
 
142
                bind_midi (c, control_type, control_additional);
 
143
        } else {
 
144
                midi_forget ();
 
145
        }
 
146
}
 
147
 
 
148
void
 
149
MIDIControllable::learn_about_external_control ()
 
150
{
 
151
        drop_external_control ();
 
152
        _port.parser()->any.connect_same_thread (midi_learn_connection, boost::bind (&MIDIControllable::midi_receiver, this, _1, _2, _3));
 
153
}
 
154
 
 
155
void
 
156
MIDIControllable::stop_learning ()
 
157
{
 
158
        midi_learn_connection.disconnect ();
 
159
}
 
160
 
 
161
int
 
162
MIDIControllable::control_to_midi (float val)
 
163
{
 
164
        if (controllable->is_gain_like()) {
 
165
                return gain_to_slider_position (val) * max_value_for_type ();
 
166
        }
 
167
 
 
168
        float control_min = controllable->lower ();
 
169
        float control_max = controllable->upper ();
 
170
        const float control_range = control_max - control_min;
 
171
 
 
172
        if (controllable->is_toggle()) {
 
173
                if (val >= (control_min + (control_range/2.0f))) {
 
174
                        return max_value_for_type();
 
175
                } else {
 
176
                        return 0;
 
177
                }
 
178
        }
 
179
 
 
180
        return (val - control_min) / control_range * max_value_for_type ();
 
181
}
 
182
 
 
183
float
 
184
MIDIControllable::midi_to_control (int val)
 
185
{
 
186
        /* fiddle with MIDI value so that we get an odd number of integer steps
 
187
           and can thus represent "middle" precisely as 0.5. this maps to
 
188
           the range 0..+1.0
 
189
        */
 
190
 
 
191
        float fv = (val == 0 ? 0 : float (val - 1) / (max_value_for_type() - 1));
 
192
 
 
193
        if (controllable->is_gain_like()) {
 
194
                return slider_position_to_gain (fv);
 
195
        }
 
196
 
 
197
        float control_min = controllable->lower ();
 
198
        float control_max = controllable->upper ();
 
199
        const float control_range = control_max - control_min;
 
200
 
 
201
        return (fv * control_range) + control_min;
 
202
}
 
203
 
 
204
void
 
205
MIDIControllable::midi_sense_note_on (Parser &p, EventTwoBytes *tb)
 
206
{
 
207
        midi_sense_note (p, tb, true);
 
208
}
 
209
 
 
210
void
 
211
MIDIControllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
 
212
{
 
213
        midi_sense_note (p, tb, false);
 
214
}
 
215
 
 
216
int
 
217
MIDIControllable::lookup_controllable()
 
218
{
 
219
        if (!_descriptor) {
 
220
                return -1;
 
221
        }
 
222
 
 
223
        boost::shared_ptr<Controllable> c = _surface->lookup_controllable (*_descriptor);
 
224
 
 
225
        if (!c) {
 
226
                return -1;
 
227
        }
 
228
 
 
229
        set_controllable (c.get ());
 
230
 
 
231
        return 0;
 
232
}
 
233
 
 
234
void
 
235
MIDIControllable::drop_controllable ()
 
236
{
 
237
        set_controllable (0);
 
238
}
 
239
 
 
240
void
 
241
MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /*is_on*/)
 
242
{
 
243
        if (!controllable) { 
 
244
                if (lookup_controllable()) {
 
245
                        return;
 
246
                }
 
247
        }
 
248
 
 
249
        if (!controllable->is_toggle()) {
 
250
                if (control_additional == msg->note_number) {
 
251
                        controllable->set_value (midi_to_control (msg->velocity));
 
252
                }
 
253
        } else {
 
254
                if (control_additional == msg->note_number) {
 
255
                        controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
 
256
                }
 
257
        }
 
258
 
 
259
        last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
 
260
}
 
261
 
 
262
void
 
263
MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
 
264
{
 
265
        if (!controllable) { 
 
266
                if (lookup_controllable ()) {
 
267
                        return;
 
268
                }
 
269
        }
 
270
 
 
271
        assert (controllable);
 
272
 
 
273
        if (controllable->touching()) {
 
274
                return; // to prevent feedback fights when e.g. dragging a UI slider
 
275
        }
 
276
 
 
277
        if (control_additional == msg->controller_number) {
 
278
 
 
279
                if (!controllable->is_toggle()) {
 
280
 
 
281
                        float new_value = msg->value;
 
282
                        float max_value = max(last_controllable_value, new_value);
 
283
                        float min_value = min(last_controllable_value, new_value);
 
284
                        float range = max_value - min_value;
 
285
                        float threshold = (float) _surface->threshold ();
 
286
 
 
287
                        bool const in_sync = (
 
288
                                range < threshold &&
 
289
                                controllable->get_value() <= midi_to_control(max_value) &&
 
290
                                controllable->get_value() >= midi_to_control(min_value)
 
291
                                );
 
292
 
 
293
                        /* If the surface is not motorised, we try to prevent jumps when
 
294
                           the MIDI controller and controllable are out of sync.
 
295
                           There might be a better way of doing this.
 
296
                        */
 
297
 
 
298
                        if (in_sync || _surface->motorised ()) {
 
299
                                controllable->set_value (midi_to_control (new_value));
 
300
                        }
 
301
 
 
302
                        last_controllable_value = new_value;
 
303
                } else {
 
304
                        if (msg->value > 64.0f) {
 
305
                                controllable->set_value (1);
 
306
                        } else {
 
307
                                controllable->set_value (0);
 
308
                        }
 
309
                }
 
310
 
 
311
                last_value = (MIDI::byte) (control_to_midi(controllable->get_value())); // to prevent feedback fights
 
312
        }
 
313
}
 
314
 
 
315
void
 
316
MIDIControllable::midi_sense_program_change (Parser &, byte msg)
 
317
{
 
318
        if (!controllable) { 
 
319
                if (lookup_controllable ()) {
 
320
                        return;
 
321
                }
 
322
        }
 
323
 
 
324
        if (!controllable->is_toggle()) {
 
325
                controllable->set_value (midi_to_control (msg));
 
326
        } else if (msg == control_additional) {
 
327
                controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
 
328
        }
 
329
 
 
330
        last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
 
331
}
 
332
 
 
333
void
 
334
MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
 
335
{
 
336
        if (!controllable) { 
 
337
                if (lookup_controllable ()) {
 
338
                        return;
 
339
                }
 
340
        }
 
341
 
 
342
        if (!controllable->is_toggle()) {
 
343
                controllable->set_value (midi_to_control (pb));
 
344
        } else {
 
345
                controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
 
346
        }
 
347
 
 
348
        last_value = control_to_midi (controllable->get_value ());
 
349
}
 
350
 
 
351
void
 
352
MIDIControllable::midi_receiver (Parser &, byte *msg, size_t /*len*/)
 
353
{
 
354
        /* we only respond to channel messages */
 
355
 
 
356
        if ((msg[0] & 0xF0) < 0x80 || (msg[0] & 0xF0) > 0xE0) {
 
357
                return;
 
358
        }
 
359
 
 
360
        /* if the our port doesn't do input anymore, forget it ... */
 
361
 
 
362
        if (!_port.parser()) {
 
363
                return;
 
364
        }
 
365
 
 
366
        bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
 
367
 
 
368
        if (controllable) {
 
369
                controllable->LearningFinished ();
 
370
        }
 
371
}
 
372
 
 
373
void
 
374
MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
 
375
{
 
376
        char buf[64];
 
377
 
 
378
        drop_external_control ();
 
379
 
 
380
        control_type = ev;
 
381
        control_channel = chn;
 
382
        control_additional = additional;
 
383
 
 
384
        if (_port.parser() == 0) {
 
385
                return;
 
386
        }
 
387
 
 
388
        Parser& p = *_port.parser();
 
389
 
 
390
        int chn_i = chn;
 
391
        switch (ev) {
 
392
        case MIDI::off:
 
393
                p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
 
394
 
 
395
                /* if this is a togglee, connect to noteOn as well,
 
396
                   and we'll toggle back and forth between the two.
 
397
                */
 
398
 
 
399
                if (_momentary) {
 
400
                        p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
 
401
                } 
 
402
 
 
403
                _control_description = "MIDI control: NoteOff";
 
404
                break;
 
405
 
 
406
        case MIDI::on:
 
407
                p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
 
408
                if (_momentary) {
 
409
                        p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
 
410
                }
 
411
                _control_description = "MIDI control: NoteOn";
 
412
                break;
 
413
                
 
414
        case MIDI::controller:
 
415
                p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_controller, this, _1, _2));
 
416
                snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional);
 
417
                _control_description = buf;
 
418
                break;
 
419
 
 
420
        case MIDI::program:
 
421
                p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_program_change, this, _1, _2));
 
422
                _control_description = "MIDI control: ProgramChange";
 
423
                break;
 
424
 
 
425
        case MIDI::pitchbend:
 
426
                p.channel_pitchbend[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_pitchbend, this, _1, _2));
 
427
                _control_description = "MIDI control: Pitchbend";
 
428
                break;
 
429
 
 
430
        default:
 
431
                break;
 
432
        }
 
433
}
 
434
 
 
435
MIDI::byte*
 
436
MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/)
 
437
{
 
438
        if (!controllable || control_type == none || !feedback || bufsize <= 2) {
 
439
                return buf;
 
440
        }
 
441
        
 
442
        int const gm = control_to_midi (controllable->get_value());
 
443
 
 
444
        if (gm == last_value) {
 
445
                return buf;
 
446
        }
 
447
 
 
448
        *buf++ = (0xF0 & control_type) | (0xF & control_channel);
 
449
        
 
450
        switch (control_type) {
 
451
        case MIDI::pitchbend:
 
452
                *buf++ = int (gm) & 127;
 
453
                *buf++ = (int (gm) >> 7) & 127;
 
454
                break;
 
455
        default:
 
456
                *buf++ = control_additional; /* controller number */
 
457
                *buf++ = gm;
 
458
                break;
 
459
        }
 
460
 
 
461
        last_value = gm;
 
462
        bufsize -= 3;
 
463
 
 
464
        return buf;
 
465
}
 
466
 
 
467
int
 
468
MIDIControllable::set_state (const XMLNode& node, int /*version*/)
 
469
{
 
470
        const XMLProperty* prop;
 
471
        int xx;
 
472
 
 
473
        if ((prop = node.property ("event")) != 0) {
 
474
                sscanf (prop->value().c_str(), "0x%x", &xx);
 
475
                control_type = (MIDI::eventType) xx;
 
476
        } else {
 
477
                return -1;
 
478
        }
 
479
 
 
480
        if ((prop = node.property ("channel")) != 0) {
 
481
                sscanf (prop->value().c_str(), "%d", &xx);
 
482
                control_channel = (MIDI::channel_t) xx;
 
483
        } else {
 
484
                return -1;
 
485
        }
 
486
 
 
487
        if ((prop = node.property ("additional")) != 0) {
 
488
                sscanf (prop->value().c_str(), "0x%x", &xx);
 
489
                control_additional = (MIDI::byte) xx;
 
490
        } else {
 
491
                return -1;
 
492
        }
 
493
 
 
494
        if ((prop = node.property ("feedback")) != 0) {
 
495
                feedback = (prop->value() == "yes");
 
496
        } else {
 
497
                feedback = true; // default
 
498
        }
 
499
 
 
500
        bind_midi (control_channel, control_type, control_additional);
 
501
 
 
502
        return 0;
 
503
}
 
504
 
 
505
XMLNode&
 
506
MIDIControllable::get_state ()
 
507
{
 
508
        char buf[32];
 
509
 
 
510
        XMLNode* node = new XMLNode ("MIDIControllable");
 
511
 
 
512
        if (_current_uri.empty()) {
 
513
                node->add_property ("id", controllable->id().to_s());
 
514
        } else {
 
515
                node->add_property ("uri", _current_uri);
 
516
        }
 
517
 
 
518
        if (controllable) {
 
519
                snprintf (buf, sizeof(buf), "0x%x", (int) control_type);
 
520
                node->add_property ("event", buf);
 
521
                snprintf (buf, sizeof(buf), "%d", (int) control_channel);
 
522
                node->add_property ("channel", buf);
 
523
                snprintf (buf, sizeof(buf), "0x%x", (int) control_additional);
 
524
                node->add_property ("additional", buf);
 
525
                node->add_property ("feedback", (feedback ? "yes" : "no"));
 
526
        }
 
527
 
 
528
        return *node;
 
529
}
 
530
 
 
531
/** @return the maximum value for a control value transmitted
 
532
 *  using a given MIDI::eventType.
 
533
 */
 
534
int
 
535
MIDIControllable::max_value_for_type () const
 
536
{
 
537
        /* XXX: this is not complete */
 
538
        
 
539
        if (control_type == MIDI::pitchbend) {
 
540
                return 16383;
 
541
        }
 
542
 
 
543
        return 127;
 
544
}