Fix unused parameter warnings since GCC apparently doesn't feel like listening to...
[ardour.git] / libs / midi++2 / alsa_sequencer_midiport.cc
1 /*
2   Copyright (C) 2004 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   $Id$
19 */
20
21 #include <fcntl.h>
22 #include <cerrno>
23
24 #include "pbd/failed_constructor.h"
25 #include "pbd/error.h"
26 #include "pbd/xml++.h"
27
28 #include "midi++/types.h"
29 #include "midi++/alsa_sequencer.h"
30
31 #include "i18n.h"
32
33 //#define DOTRACE 1
34
35 #ifdef DOTRACE
36 #define TR_FN() (cerr << __FUNCTION__ << endl)
37 #define TR_VAL(v) (cerr << __FILE__  " " << __LINE__ << " " #v  "=" << v << endl)
38 #else
39 #define TR_FN()
40 #define TR_VAL(v)
41 #endif
42
43 using namespace std;
44 using namespace MIDI;
45 using namespace PBD;
46
47 snd_seq_t* ALSA_SequencerMidiPort::seq = 0;
48
49 ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (const XMLNode& node)
50         : Port (node)
51         , decoder (0) 
52         , encoder (0) 
53         , port_id (-1)
54 {
55         TR_FN();
56         int err;
57         Descriptor desc (node);
58
59         if (!seq && init_client (desc.device) < 0) {
60                 _ok = false; 
61
62         } else {
63                 
64                 if (0 <= (err = create_ports (desc)) &&
65                     0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read ()
66                     0 <= (err = snd_midi_event_new (64, &encoder))) {   // Length taken from ARDOUR::Session::mmc_buffer
67                         snd_midi_event_init (decoder);
68                         snd_midi_event_init (encoder);
69                         _ok = true;
70                 } 
71         }
72
73         set_state (node);
74 }
75
76 ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort ()
77 {
78         if (decoder) {
79                 snd_midi_event_free (decoder);
80         }
81         if (encoder) {
82                 snd_midi_event_free (encoder);
83         }
84         if (port_id >= 0) {
85                 snd_seq_delete_port (seq, port_id);
86         }
87 }
88
89 int 
90 ALSA_SequencerMidiPort::selectable () const
91 {
92         struct pollfd pfd[1];
93         if (0 <= snd_seq_poll_descriptors (seq, pfd, 1, POLLIN | POLLOUT)) {
94                 return pfd[0].fd;
95         }
96         return -1;
97 }
98
99 int 
100 ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t ignored)   
101 {
102         TR_FN ();
103         int R;
104         int totwritten = 0;
105         snd_midi_event_reset_encode (encoder);
106         int nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
107         TR_VAL (nwritten);
108         while (0 < nwritten) {
109                 if (0 <= (R = snd_seq_event_output (seq, &SEv))  &&
110                     0 <= (R = snd_seq_drain_output (seq))) {
111                         bytes_written += nwritten;
112                         totwritten += nwritten;
113                         if (output_parser) {
114                                 output_parser->raw_preparse (*output_parser, msg, nwritten);
115                                 for (int i = 0; i < nwritten; i++) {
116                                         output_parser->scanner (msg[i]);
117                                 }
118                                 output_parser->raw_postparse (*output_parser, msg, nwritten);
119                         }
120                 } else {
121                         TR_VAL(R);
122                         return R;
123                 }
124
125                 msglen -= nwritten;
126                 msg += nwritten;
127                 if (msglen > 0) {
128                         nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
129                         TR_VAL(nwritten);
130                 }
131                 else {
132                         break;
133                 }
134         }
135
136         return totwritten;
137 }
138
139 int 
140 ALSA_SequencerMidiPort::read (byte *buf, size_t max)
141 {
142         TR_FN();
143         int err;
144         snd_seq_event_t *ev;
145         if (0 <= (err = snd_seq_event_input (seq, &ev))) {
146                 TR_VAL(err);
147                 err = snd_midi_event_decode (decoder, buf, max, ev);
148         }
149
150         if (err > 0) {
151                 bytes_read += err;
152
153                 if (input_parser) {
154                         input_parser->raw_preparse (*input_parser, buf, err);
155                         for (int i = 0; i < err; i++) {
156                                 input_parser->scanner (buf[i]);
157                         }       
158                         input_parser->raw_postparse (*input_parser, buf, err);
159                 }
160         }
161         return -ENOENT == err ? 0 : err;
162 }
163
164 int 
165 ALSA_SequencerMidiPort::create_ports (const Port::Descriptor& desc)
166 {
167         int err;
168         unsigned int caps = 0;
169
170         if (desc.mode == O_WRONLY  ||  desc.mode == O_RDWR)
171                 caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
172         if (desc.mode == O_RDONLY  ||  desc.mode == O_RDWR)
173                 caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
174
175         if (0 <= (err = snd_seq_create_simple_port (seq, desc.tag.c_str(), caps, 
176                                                     (SND_SEQ_PORT_TYPE_MIDI_GENERIC|
177                                                      SND_SEQ_PORT_TYPE_SOFTWARE|
178                                                      SND_SEQ_PORT_TYPE_APPLICATION)))) {
179                 
180                 port_id = err;
181
182                 snd_seq_ev_clear (&SEv);
183                 snd_seq_ev_set_source (&SEv, port_id);
184                 snd_seq_ev_set_subs (&SEv);
185                 snd_seq_ev_set_direct (&SEv);
186                 
187                 return 0;
188         }
189
190         return err;
191 }
192
193 int
194 ALSA_SequencerMidiPort::init_client (std::string name)
195 {
196         static bool called = false;
197
198         if (called) {
199                 return -1;
200         }
201
202         called = true;
203
204         if (snd_seq_open (&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) >= 0) {
205                 snd_seq_set_client_name (seq, name.c_str());
206                 return 0;
207         } else {
208                 warning << "The ALSA MIDI system is not available. No ports based on it will be created"
209                         << endmsg;
210                 return -1;
211         }
212 }
213
214 int
215 ALSA_SequencerMidiPort::discover (vector<PortSet>& ports)
216 {
217          int n = 0;
218
219          snd_seq_client_info_t *client_info;
220          snd_seq_port_info_t   *port_info;
221
222          snd_seq_client_info_alloca (&client_info);
223          snd_seq_port_info_alloca (&port_info);
224          snd_seq_client_info_set_client (client_info, -1);
225
226          while (snd_seq_query_next_client(seq, client_info) >= 0) {
227
228                  int alsa_client;
229
230                  if ((alsa_client = snd_seq_client_info_get_client(client_info)) <= 0) {
231                          break;
232                  }
233
234                  snd_seq_port_info_set_client(port_info, alsa_client);
235                  snd_seq_port_info_set_port(port_info, -1);
236
237                  char client[256];
238                  snprintf (client, sizeof (client), "%d:%s", alsa_client, snd_seq_client_info_get_name(client_info));
239
240                  ports.push_back (PortSet (client));
241
242                  while (snd_seq_query_next_port(seq, port_info) >= 0) {
243
244 #if 0
245                          int type = snd_seq_port_info_get_type(pinfo);
246                          if (!(type & SND_SEQ_PORT_TYPE_PORT)) {
247                                  continue;
248                          }
249 #endif
250
251                          unsigned int port_capability = snd_seq_port_info_get_capability(port_info);
252
253                          if ((port_capability & SND_SEQ_PORT_CAP_NO_EXPORT) == 0) {
254
255                                  int alsa_port = snd_seq_port_info_get_port(port_info);
256
257                                  char port[256];
258                                  snprintf (port, sizeof (port), "%d:%s", alsa_port, snd_seq_port_info_get_name(port_info));
259
260                                  std::string mode;
261
262                                  if (port_capability & SND_SEQ_PORT_CAP_READ) {
263                                          if (port_capability & SND_SEQ_PORT_CAP_WRITE) {
264                                                  mode = "duplex";
265                                          } else {
266                                                  mode = "output";
267                                          } 
268                                  } else if (port_capability & SND_SEQ_PORT_CAP_WRITE) {
269                                          if (port_capability & SND_SEQ_PORT_CAP_READ) {
270                                                  mode = "duplex";
271                                          } else {
272                                                  mode = "input";
273                                          } 
274                                  }
275
276                                  XMLNode node (X_("MIDI-port"));
277                                  node.add_property ("device", client);
278                                  node.add_property ("tag", port);
279                                  node.add_property ("mode", mode);
280                                  node.add_property ("type", "alsa/sequencer");
281                                  
282                                  ports.back().ports.push_back (node);
283                                  ++n;
284                          }
285                  }
286          }
287          
288          return n;
289 }
290
291 void
292 ALSA_SequencerMidiPort::get_connections (vector<SequencerPortAddress>& connections, int dir) const
293 {
294         snd_seq_query_subscribe_t *subs;
295         snd_seq_addr_t seq_addr;
296
297         snd_seq_query_subscribe_alloca (&subs);
298
299         // Get port connections...
300         
301         if (dir) {
302                 snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_WRITE);
303         } else {
304                 snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_READ);
305         }
306
307         snd_seq_query_subscribe_set_index(subs, 0);
308         seq_addr.client = snd_seq_client_id (seq);
309         seq_addr.port   = port_id;
310         snd_seq_query_subscribe_set_root(subs, &seq_addr);
311
312         while (snd_seq_query_port_subscribers(seq, subs) >= 0) {
313
314                 if (snd_seq_query_subscribe_get_time_real (subs)) {
315                         /* interesting connection */
316
317                         seq_addr = *snd_seq_query_subscribe_get_addr (subs);
318                         
319                         connections.push_back (SequencerPortAddress (seq_addr.client,
320                                                                      seq_addr.port));
321                 }
322
323                 snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1);
324         }
325 }
326
327 XMLNode&
328 ALSA_SequencerMidiPort::get_state () const
329 {
330         XMLNode& root (Port::get_state ());
331         vector<SequencerPortAddress> connections;
332         XMLNode* sub = 0;
333         char buf[256];
334
335         get_connections (connections, 1);
336
337         if (!connections.empty()) {
338                 if (!sub) {
339                         sub = new XMLNode (X_("connections"));
340                 }
341                 for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) {
342                         XMLNode* cnode = new XMLNode (X_("read"));
343                         snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second);
344                         cnode->add_property ("dest", buf);
345                         sub->add_child_nocopy (*cnode);
346                 }
347         }
348         
349         connections.clear ();
350         get_connections (connections, 0);
351
352         if (!connections.empty()) {
353                 if (!sub) {
354                         sub = new XMLNode (X_("connections"));
355                 }
356                 for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) {
357                         XMLNode* cnode = new XMLNode (X_("write"));
358                         snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second);
359                         cnode->add_property ("dest", buf);
360                         sub->add_child_nocopy (*cnode);
361                 }
362         }
363
364         if (sub) {
365                 root.add_child_nocopy (*sub);
366         }
367
368         return root;
369 }
370
371 void
372 ALSA_SequencerMidiPort::set_state (const XMLNode& node)
373 {
374         Port::set_state (node);
375
376         XMLNodeList children (node.children());
377         XMLNodeIterator iter;
378
379         for (iter = children.begin(); iter != children.end(); ++iter) {
380
381                 if ((*iter)->name() == X_("connections")) {
382
383                         XMLNodeList gchildren ((*iter)->children());
384                         XMLNodeIterator gciter;
385
386                         for (gciter = gchildren.begin(); gciter != gchildren.end(); ++gciter) {
387                                 XMLProperty* prop;
388
389                                 if ((prop = (*gciter)->property ("dest")) != 0) {
390                                         int client;
391                                         int port;
392
393                                         if (sscanf (prop->value().c_str(), "%d:%d", &client, &port) == 2) {
394
395                                                 snd_seq_port_subscribe_t *sub;
396                                                 snd_seq_addr_t seq_addr;
397                                                 
398                                                 snd_seq_port_subscribe_alloca(&sub);
399
400                                                 if ((*gciter)->name() == X_("write")) {
401                                                         
402                                                         seq_addr.client = snd_seq_client_id (seq);
403                                                         seq_addr.port   = port_id;
404                                                         snd_seq_port_subscribe_set_sender(sub, &seq_addr);
405                                                         
406                                                         seq_addr.client = client;
407                                                         seq_addr.port   = port;
408                                                         snd_seq_port_subscribe_set_dest(sub, &seq_addr);
409
410                                                 } else {
411                                                         
412                                                         seq_addr.client = snd_seq_client_id (seq);
413                                                         seq_addr.port   = port_id;
414                                                         snd_seq_port_subscribe_set_dest(sub, &seq_addr);
415                                                         
416                                                         seq_addr.client = client;
417                                                         seq_addr.port   = port;
418                                                         snd_seq_port_subscribe_set_sender(sub, &seq_addr);
419                                                 }
420
421                                                 snd_seq_subscribe_port (seq, sub);
422                                         }
423                                 }
424                         }
425
426                         break;
427                 }
428         }
429 }