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