1 // ------------------------------------------------------------------------
2 // midiio-aseq.cpp: Input and output of MIDI streams using
3 //                  ALSA Sequencer
4 // Copyright (C) 2005 Pedro Lopez-Cabanillas
5 // Copyright (C) 2005 Kai Vehmanen
6 //
7 // Attributes:
8 //     eca-style-version: 3
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
23 // ------------------------------------------------------------------------
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 #ifdef ECA_COMPILE_ALSA
29 
30 #include <cstdio>
31 #include <fcntl.h>
32 #include <unistd.h>
33 
34 #include "midiio-aseq.h"
35 #include "eca-logger.h"
36 
MIDI_IO_ASEQ(const std::string & name)37 MIDI_IO_ASEQ::MIDI_IO_ASEQ(const std::string& name) { label("alsaseq"); device_name_rep = name; }
38 
~MIDI_IO_ASEQ(void)39 MIDI_IO_ASEQ::~MIDI_IO_ASEQ(void) { if (is_open()) close(); }
40 
open(void)41 void MIDI_IO_ASEQ::open(void)
42 {
43   int open_flags = 0, port_flags = 0;
44 
45   switch(io_mode()) {
46   case io_read:
47     {
48       open_flags = SND_SEQ_OPEN_INPUT;
49       port_flags = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
50       break;
51     }
52   case io_write:
53     {
54       open_flags = SND_SEQ_OPEN_OUTPUT;
55       port_flags = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
56       break;
57     }
58   case io_readwrite:
59     {
60       open_flags = SND_SEQ_OPEN_DUPLEX;
61       port_flags = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE |
62                    SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
63       break;
64     }
65   }
66 
67   ECA_LOG_MSG(ECA_LOGGER::system_objects, "Opening ALSA sequencer");
68   int err = snd_seq_open(&seq_handle_repp, "default", open_flags, SND_SEQ_NONBLOCK);
69   if (err < 0) {
70     toggle_open_state(false);
71   }
72   else {
73     toggle_open_state(true);
74   }
75 
76   // Set client name.
77   snd_seq_set_client_name(seq_handle_repp, "ecasound");
78   // Create a simple port
79   port_rep = snd_seq_create_simple_port( seq_handle_repp, "ecasound",
80                                          port_flags,
81                                          SND_SEQ_PORT_TYPE_MIDI_GENERIC);
82   // Parse the device name, and connect it to the port when successful
83   snd_seq_addr_t subs;
84   err = snd_seq_parse_address(seq_handle_repp, &subs, device_name_rep.c_str());
85   if( err == 0) {
86     switch(io_mode()) {
87     case io_read:
88       snd_seq_connect_to(seq_handle_repp, port_rep, subs.client, subs.port);
89       break;
90     case io_write:
91       snd_seq_connect_from(seq_handle_repp, port_rep, subs.client, subs.port);
92       break;
93     case io_readwrite:
94       snd_seq_connect_to(seq_handle_repp, port_rep, subs.client, subs.port);
95       snd_seq_connect_from(seq_handle_repp, port_rep, subs.client, subs.port);
96       break;
97     }
98   }
99   // Create the encoder/decoder instance
100   err = snd_midi_event_new( buffer_size_rep = 16, &coder_repp );
101   // ...
102   finished_rep = false;
103 }
104 
close(void)105 void MIDI_IO_ASEQ::close(void)
106 {
107   // Release the xxcoder instance
108   snd_midi_event_free( coder_repp );
109   // Delete the port
110   snd_seq_delete_port( seq_handle_repp, port_rep );
111   // Close the sequencer client
112   snd_seq_close( seq_handle_repp );
113   toggle_open_state(false);
114 }
115 
poll_descriptor(void) const116 int MIDI_IO_ASEQ::poll_descriptor(void) const
117 {
118   struct pollfd *pfds;
119   int npfds;
120   npfds = snd_seq_poll_descriptors_count(seq_handle_repp, POLLIN|POLLOUT);
121   pfds = reinterpret_cast<struct pollfd*>(alloca(sizeof(*pfds) * npfds));
122   snd_seq_poll_descriptors(seq_handle_repp, pfds, npfds, POLLIN|POLLOUT);
123   return pfds->fd;
124 }
125 
126 
finished(void) const127 bool MIDI_IO_ASEQ::finished(void) const { return finished_rep; }
128 
read_bytes(void * target_buffer,long int bytes)129 long int MIDI_IO_ASEQ::read_bytes(void* target_buffer, long int bytes)
130 {
131   snd_seq_event_t *event;
132   int err = 0, position = 0;
133   if ( bytes > buffer_size_rep ) {
134      snd_midi_event_resize_buffer ( coder_repp, bytes );
135      buffer_size_rep = bytes;
136   }
137   while (true) {
138     if (snd_seq_event_input_pending(seq_handle_repp, 1) == 0) {
139     	return position;
140     }
141     err = snd_seq_event_input(seq_handle_repp, &event);
142     if (err < 0) {
143     	break;
144     }
145     if ( event->type == SND_SEQ_EVENT_CONTROLLER ||
146          event->type == SND_SEQ_EVENT_CONTROL14 ||
147          event->type == SND_SEQ_EVENT_NONREGPARAM ||
148          event->type == SND_SEQ_EVENT_REGPARAM ||
149          event->type == SND_SEQ_EVENT_SYSEX ) {
150       err = snd_midi_event_decode( coder_repp,
151                                    ((unsigned char *)target_buffer) + position,
152                                    bytes - position,
153                                    event );
154       if (err < 0) {
155       	break;
156       }
157       position += err;
158       if ( position >= bytes) return position;
159     }
160   }
161   finished_rep = true;
162   ECA_LOG_MSG(ECA_LOGGER::system_objects,
163 	      std::string("error while reading from ALSA sequencer: ") + snd_strerror(err));
164   return err;
165 }
166 
write_bytes(void * target_buffer,long int bytes)167 long int MIDI_IO_ASEQ::write_bytes(void* target_buffer, long int bytes) {
168   snd_seq_event_t ev;
169   int err = 0;
170   if ( bytes > buffer_size_rep ) {
171      snd_midi_event_resize_buffer ( coder_repp, bytes );
172      buffer_size_rep = bytes;
173   }
174   snd_seq_ev_clear(&ev);
175   snd_seq_ev_set_source(&ev, port_rep);
176   snd_seq_ev_set_subs(&ev);
177   snd_seq_ev_set_direct(&ev);
178   err = snd_midi_event_encode( coder_repp,
179                     	       (unsigned char *)target_buffer,
180                                bytes, &ev );
181   if (err == bytes) {
182      snd_seq_event_output(seq_handle_repp, &ev);
183      snd_seq_drain_output(seq_handle_repp);
184      return err;
185   }
186   finished_rep = true;
187   return err;
188 }
189 
set_parameter(int param,std::string value)190 void MIDI_IO_ASEQ::set_parameter(int param,
191 				std::string value)
192 {
193   switch (param) {
194   case 1:
195     label(value);
196     break;
197 
198   case 2:
199     device_name_rep = value;
200     break;
201   }
202 }
203 
get_parameter(int param) const204 std::string MIDI_IO_ASEQ::get_parameter(int param) const
205 {
206   switch (param) {
207   case 1:
208     return label();
209 
210   case 2:
211     return device_name_rep;
212   }
213   return "";
214 }
215 
216 /**
217  * FIXME: This is an alternative to using the poll_descriptor()
218  *        interface...
219  */
pending_messages(unsigned long timeout) const220 bool MIDI_IO_ASEQ::pending_messages(unsigned long timeout) const
221 {
222   struct pollfd *pfds;
223   int result = 0;
224   int npfds = snd_seq_poll_descriptors_count(seq_handle_repp, POLLIN);
225   pfds = (struct pollfd *)alloca(sizeof(*pfds) * npfds);
226   snd_seq_poll_descriptors(seq_handle_repp, pfds, npfds, POLLIN);
227   result = poll(pfds, npfds, timeout);
228   return (result > 0);
229 }
230 
231 #endif /* COMPILE_ALSA */
232