c748bbece0dfd2fecf2149303f969557a78c8330
[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
27 #include <midi++/types.h>
28 #include <midi++/alsa_sequencer.h>
29 #include <midi++/port_request.h>
30
31 //#define DOTRACE 1
32
33 #ifdef DOTRACE
34 #define TR_FN() (cerr << __FUNCTION__ << endl)
35 #define TR_VAL(v) (cerr << __FILE__  " " << __LINE__ << " " #v  "=" << v << endl)
36 #else
37 #define TR_FN()
38 #define TR_VAL(v)
39 #endif
40
41 using namespace std;
42 using namespace MIDI;
43 using namespace PBD;
44
45 snd_seq_t* ALSA_SequencerMidiPort::seq = 0;
46
47 ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (PortRequest &req)
48         : Port (req)
49         , decoder (0) 
50         , encoder (0) 
51         , port_id (-1)
52 {
53         TR_FN();
54         int err;
55
56         if (!seq && init_client (req.devname) < 0) {
57                 _ok = false; 
58
59         } else {
60                 
61                 if (0 <= (err = CreatePorts (req)) &&
62                     0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read ()
63                     0 <= (err = snd_midi_event_new (64, &encoder))) {   // Length taken from ARDOUR::Session::mmc_buffer
64                         snd_midi_event_init (decoder);
65                         snd_midi_event_init (encoder);
66                         _ok = true;
67                         req.status = PortRequest::OK;
68                 } else {
69                         req.status = PortRequest::Unknown;
70                 }
71         }
72 }
73
74 ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort ()
75 {
76         if (decoder) {
77                 snd_midi_event_free (decoder);
78         }
79         if (encoder) {
80                 snd_midi_event_free (encoder);
81         }
82         if (port_id >= 0) {
83                 snd_seq_delete_port (seq, port_id);
84         }
85 }
86
87 int 
88 ALSA_SequencerMidiPort::selectable () const
89 {
90         struct pollfd pfd[1];
91         if (0 <= snd_seq_poll_descriptors (seq, pfd, 1, POLLIN | POLLOUT)) {
92                 return pfd[0].fd;
93         }
94         return -1;
95 }
96
97 int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp)     
98 {
99         TR_FN ();
100         int R;
101         int totwritten = 0;
102         snd_midi_event_reset_encode (encoder);
103         int nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
104         TR_VAL (nwritten);
105         while (0 < nwritten) {
106                 if (0 <= (R = snd_seq_event_output (seq, &SEv))  &&
107                     0 <= (R = snd_seq_drain_output (seq))) {
108                         bytes_written += nwritten;
109                         totwritten += nwritten;
110                         if (output_parser) {
111                                 output_parser->raw_preparse (*output_parser, msg, nwritten);
112                                 for (int i = 0; i < nwritten; i++) {
113                                         output_parser->scanner (msg[i]);
114                                 }
115                                 output_parser->raw_postparse (*output_parser, msg, nwritten);
116                         }
117                 } else {
118                         TR_VAL(R);
119                         return R;
120                 }
121
122                 msglen -= nwritten;
123                 msg += nwritten;
124                 if (msglen > 0) {
125                         nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
126                         TR_VAL(nwritten);
127                 }
128                 else {
129                         break;
130                 }
131         }
132
133         return totwritten;
134 }
135
136 <<<<<<< .working
137 int ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t timestamp)
138 =======
139 int 
140 ALSA_SequencerMidiPort::read (byte *buf, size_t max)
141 >>>>>>> .merge-right.r1393
142 {
143         TR_FN();
144         int err;
145         snd_seq_event_t *ev;
146         if (0 <= (err = snd_seq_event_input (seq, &ev))) {
147                 TR_VAL(err);
148                 err = snd_midi_event_decode (decoder, buf, max, ev);
149         }
150
151         if (err > 0) {
152                 bytes_read += err;
153
154                 if (input_parser) {
155                         input_parser->raw_preparse (*input_parser, buf, err);
156                         for (int i = 0; i < err; i++) {
157                                 input_parser->scanner (buf[i]);
158                         }       
159                         input_parser->raw_postparse (*input_parser, buf, err);
160                 }
161         }
162         return -ENOENT == err ? 0 : err;
163 }
164
165 int 
166 ALSA_SequencerMidiPort::CreatePorts (PortRequest &req)
167 {
168         int err;
169         unsigned int caps = 0;
170
171         if (req.mode == O_WRONLY  ||  req.mode == O_RDWR)
172                 caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
173         if (req.mode == O_RDONLY  ||  req.mode == O_RDWR)
174                 caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
175         
176         if (0 <= (err = snd_seq_create_simple_port (seq, req.tagname, caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC))) {
177                 
178                 port_id = err;
179
180                 snd_seq_ev_clear (&SEv);
181                 snd_seq_ev_set_source (&SEv, port_id);
182                 snd_seq_ev_set_subs (&SEv);
183                 snd_seq_ev_set_direct (&SEv);
184                 
185                 return 0;
186         }
187
188         return err;
189 }
190
191 int
192 ALSA_SequencerMidiPort::init_client (std::string name)
193 {
194         static bool called = false;
195
196         if (called) {
197                 return -1;
198         }
199
200         called = true;
201
202         if (snd_seq_open (&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) >= 0) {
203                 snd_seq_set_client_name (seq, name.c_str());
204                 return 0;
205         } else {
206                 warning << "The ALSA MIDI system is not available. No ports based on it will be created"
207                         << endmsg;
208                 return -1;
209         }
210 }