stop metering thread as we disconnect a session from audioengine; explicitly drop...
[ardour.git] / libs / surfaces / generic_midi / midifunction.cc
1 /*
2     Copyright (C) 2009 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 #include <cstring>
20
21 #include "midi++/port.h"
22
23 #include "midifunction.h"
24 #include "generic_midi_control_protocol.h"
25
26 using namespace MIDI;
27
28 MIDIFunction::MIDIFunction (MIDI::Port& p)
29         : _port (p)
30 {
31         sysex_size = 0;
32         sysex = 0;
33 }
34
35 MIDIFunction::~MIDIFunction ()
36 {
37         delete [] sysex;
38 }
39
40 int
41 MIDIFunction::init (GenericMidiControlProtocol& ui, const std::string& function_name, MIDI::byte* sysex_data, size_t sysex_sz)
42 {
43         if (strcasecmp (function_name.c_str(), "transport-stop") == 0) {
44                 _function = TransportStop;
45         } else if (strcasecmp (function_name.c_str(), "transport-roll") == 0) {
46                 _function = TransportRoll;
47         } else if (strcasecmp (function_name.c_str(), "transport-zero") == 0) {
48                 _function = TransportZero;
49         } else if (strcasecmp (function_name.c_str(), "transport-start") == 0) {
50                 _function = TransportStart;
51         } else if (strcasecmp (function_name.c_str(), "transport-end") == 0) {
52                 _function = TransportEnd;
53         } else if (strcasecmp (function_name.c_str(), "loop-toggle") == 0) {
54                 _function = TransportLoopToggle;
55         } else if (strcasecmp (function_name.c_str(), "rec-enable") == 0) {
56                 _function = TransportRecordEnable;
57         } else if (strcasecmp (function_name.c_str(), "rec-disable") == 0) {
58                 _function = TransportRecordDisable;
59         } else if (strcasecmp (function_name.c_str(), "next-bank") == 0) {
60                 _function = NextBank;
61         } else if (strcasecmp (function_name.c_str(), "prev-bank") == 0) {
62                 _function = PrevBank;
63         } else {
64                 return -1;
65         }
66
67         _ui = &ui;
68
69         if (sysex_sz) {
70                 /* we take ownership of the sysex data */
71                 sysex = sysex_data;
72                 sysex_size = sysex_sz;
73         }
74
75         return 0;
76 }
77
78 void
79 MIDIFunction::execute ()
80 {
81         switch (_function) {
82         case NextBank:
83                 _ui->next_bank();
84                 break;
85
86         case PrevBank:
87                 _ui->prev_bank();
88                 break;
89
90         case TransportStop:
91                 _ui->transport_stop ();
92                 break;
93
94         case TransportRoll:
95                 _ui->transport_play ();
96                 break;
97
98         case TransportStart:
99                 _ui->goto_start ();
100                 break;
101
102         case TransportZero:
103                 // need this in BasicUI
104                 break;
105
106         case TransportEnd:
107                 _ui->goto_end ();
108                 break;
109
110         case TransportLoopToggle:
111                 _ui->loop_toggle ();
112                 break;
113
114         case TransportRecordEnable:
115                 _ui->set_record_enable (true);
116                 break;
117
118         case TransportRecordDisable:
119                 _ui->set_record_enable (false);
120                 break;
121         }
122 }
123
124 void
125 MIDIFunction::midi_sense_note_on (Parser &p, EventTwoBytes *tb)
126 {
127         midi_sense_note (p, tb, true);
128 }
129
130 void
131 MIDIFunction::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
132 {
133         midi_sense_note (p, tb, false);
134 }
135
136 void
137 MIDIFunction::midi_sense_note (Parser &, EventTwoBytes *msg, bool /* is_on */)
138 {
139         if (msg->note_number == control_additional) {
140                 execute ();
141         }
142 }
143
144 void
145 MIDIFunction::midi_sense_controller (Parser &, EventTwoBytes *msg)
146 {
147         if (control_additional == msg->controller_number) {
148                 execute ();
149         }
150 }
151
152 void
153 MIDIFunction::midi_sense_program_change (Parser &, byte msg)
154 {
155         if (msg == control_additional) {
156                 execute ();
157         }
158 }
159
160 void
161 MIDIFunction::midi_sense_sysex (Parser &, byte* msg, size_t sz)
162 {
163         if (sz != sysex_size) {
164                 return;
165         }
166
167         if (memcmp (msg, sysex, sysex_size) != 0) {
168                 return;
169         }
170
171         execute ();
172 }
173
174 void
175 MIDIFunction::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
176 {
177         midi_sense_connection[0].disconnect ();
178         midi_sense_connection[1].disconnect ();
179
180         control_type = ev;
181         control_channel = chn;
182         control_additional = additional;
183
184         if (_port.input() == 0) {
185                 return;
186         }
187
188         Parser& p = *_port.input();
189
190         int chn_i = chn;
191
192         /* incoming MIDI is parsed by Ardour' MidiUI event loop/thread, and we want our handlers to execute in that context, so we use
193            Signal::connect_same_thread() here.
194         */
195
196         switch (ev) {
197         case MIDI::off:
198                 p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_note_off, this, _1, _2));
199                 break;
200
201         case MIDI::on:
202                 p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_note_on, this, _1, _2));
203                 break;
204                 
205         case MIDI::controller:
206                 p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_controller, this, _1, _2));
207                 break;
208
209         case MIDI::program:
210                 p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_program_change, this, _1, _2));
211                 break;
212
213         case MIDI::sysex:
214                 p.sysex.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_sysex, this, _1, _2, _3));
215                 break;
216
217         default:
218                 break;
219         }
220 }
221
222 XMLNode&
223 MIDIFunction::get_state ()
224 {
225         XMLNode* node = new XMLNode ("MIDIFunction");
226         return *node;
227 }
228
229 int
230 MIDIFunction::set_state (const XMLNode& node, int version)
231 {
232         return 0;
233 }
234