fca6707efdacbbad72c06b1e3a9a8123f6ec9df4
[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 <midi++/types.h>
25 #include <midi++/alsa_sequencer.h>
26 #include <midi++/port_request.h>
27
28 //#define DOTRACE 1
29
30 #ifdef DOTRACE
31 #define TR_FN() (cerr << __FUNCTION__ << endl)
32 #define TR_VAL(v) (cerr << __FILE__  " " << __LINE__ << " " #v  "=" << v << endl)
33 #else
34 #define TR_FN()
35 #define TR_VAL(v)
36 #endif
37
38
39
40
41 using namespace std;
42 using namespace MIDI;
43
44 ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (PortRequest &req)
45         : Port (req)
46         , seq (0)
47         , decoder (0) 
48         , encoder (0) 
49 {
50         TR_FN();
51         int err;
52         if (0 <= (err = CreatePorts (req)) &&
53             0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read ()
54             0 <= (err = snd_midi_event_new (64, &encoder))) {   // Length taken from ARDOUR::Session::mmc_buffer
55                 snd_midi_event_init (decoder);
56                 snd_midi_event_init (encoder);
57                 _ok = true;
58                 req.status = PortRequest::OK;
59         } else
60                 req.status = PortRequest::Unknown;
61 }
62
63 ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort ()
64 {
65         if (decoder)
66                 snd_midi_event_free (decoder);
67         if (encoder)
68                 snd_midi_event_free (encoder);
69         if (seq)
70                 snd_seq_close (seq);
71 }
72
73 int ALSA_SequencerMidiPort::selectable () const
74 {
75         struct pollfd pfd[1];
76         if (0 <= snd_seq_poll_descriptors (seq, pfd, 1, POLLIN | POLLOUT)) {
77                 return pfd[0].fd;
78         }
79         return -1;
80 }
81
82 int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen)    
83 {
84         TR_FN ();
85         int R;
86         int totwritten = 0;
87         snd_midi_event_reset_encode (encoder);
88         int nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
89         TR_VAL (nwritten);
90         while (0 < nwritten) {
91                 if (0 <= (R = snd_seq_event_output (seq, &SEv))  &&
92                     0 <= (R = snd_seq_drain_output (seq))) {
93                         bytes_written += nwritten;
94                         totwritten += nwritten;
95                         if (output_parser) {
96                                 output_parser->raw_preparse (*output_parser, msg, nwritten);
97                                 for (int i = 0; i < nwritten; i++) {
98                                         output_parser->scanner (msg[i]);
99                                 }
100                                 output_parser->raw_postparse (*output_parser, msg, nwritten);
101                         }
102                 } else {
103                         TR_VAL(R);
104                         return R;
105                 }
106
107                 msglen -= nwritten;
108                 msg += nwritten;
109                 if (msglen > 0) {
110                         nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
111                         TR_VAL(nwritten);
112                 }
113                 else {
114                         break;
115                 }
116         }
117
118         return totwritten;
119 }
120
121 int ALSA_SequencerMidiPort::read (byte *buf, size_t max)
122 {
123         TR_FN();
124         int err;
125         snd_seq_event_t *ev;
126         if (0 <= (err = snd_seq_event_input (seq, &ev))) {
127                 TR_VAL(err);
128                 err = snd_midi_event_decode (decoder, buf, max, ev);
129         }
130
131         if (err > 0) {
132                 bytes_read += err;
133
134                 if (input_parser) {
135                         input_parser->raw_preparse (*input_parser, buf, err);
136                         for (int i = 0; i < err; i++) {
137                                 input_parser->scanner (buf[i]);
138                         }       
139                         input_parser->raw_postparse (*input_parser, buf, err);
140                 }
141         }
142         return -ENOENT == err ? 0 : err;
143 }
144
145 int ALSA_SequencerMidiPort::CreatePorts (PortRequest &req)
146 {
147         int err;
148         if (0 <= (err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 
149                                      (req.mode & O_NONBLOCK) ? SND_SEQ_NONBLOCK : 0))) {
150                 snd_seq_set_client_name (seq, req.devname);
151                 unsigned int caps = 0;
152                 if (req.mode == O_WRONLY  ||  req.mode == O_RDWR)
153                         caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
154                 if (req.mode == O_RDONLY  ||  req.mode == O_RDWR)
155                         caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
156                 err = snd_seq_create_simple_port (seq, req.tagname, caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
157                 if (err >= 0) {
158                         port_id = err;
159                         snd_seq_ev_clear (&SEv);
160                         snd_seq_ev_set_source (&SEv, port_id);
161                         snd_seq_ev_set_subs (&SEv);
162                         snd_seq_ev_set_direct (&SEv);
163                 } else 
164                         snd_seq_close (seq);
165         }
166         return err;
167 }
168