1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * ALSA SEQ < - > JACK MIDI bridge
5 * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
6 * Copyright (c) 2007,2008,2009,2011 Nedko Arnaudov <nedko@arnaudov.name>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <semaphore.h>
25 #include <alsa/asoundlib.h>
26 #include <jack/jack.h>
27 #include <jack/ringbuffer.h>
31 #include "port_hash.h"
35 extern bool g_disable_port_uniqueness
;
37 /* This should be part of JACK API */
38 #define JACK_IS_VALID_PORT_NAME_CHAR(c) \
51 a2j_alsa_connect_from(
56 snd_seq_port_subscribe_t
* sub
;
57 snd_seq_addr_t seq_addr
;
60 snd_seq_port_subscribe_alloca(&sub
);
61 seq_addr
.client
= client
;
63 snd_seq_port_subscribe_set_sender(sub
, &seq_addr
);
64 seq_addr
.client
= self
->client_id
;
65 seq_addr
.port
= self
->port_id
;
66 snd_seq_port_subscribe_set_dest(sub
, &seq_addr
);
68 snd_seq_port_subscribe_set_time_update(sub
, 1);
69 snd_seq_port_subscribe_set_queue(sub
, self
->queue
);
70 snd_seq_port_subscribe_set_time_real(sub
, 1);
72 if ((err
=snd_seq_subscribe_port(self
->seq
, sub
)))
73 a2j_error("can't subscribe to %d:%d - %s", client
, port
, snd_strerror(err
));
82 struct a2j_port
*port
= a2j_port_get(hash
, addr
);
85 port
->is_dead
= true; // see jack_process_internal
89 a2j_debug("port_setdead: not found (%d:%d)", addr
.client
, addr
.port
);
95 struct a2j_port
* port
)
97 //snd_seq_disconnect_from(self->seq, self->port_id, port->remote.client, port->remote.port);
98 //snd_seq_disconnect_to(self->seq, self->port_id, port->remote.client, port->remote.port);
99 if (port
->inbound_events
)
100 jack_ringbuffer_free(port
->inbound_events
);
101 if (port
->jack_port
!= JACK_INVALID_PORT
)
102 jack_port_unregister(port
->a2j_ptr
->jack_client
, port
->jack_port
);
109 struct a2j_port
* port_ptr
,
111 snd_seq_client_info_t
* client_info_ptr
,
112 const snd_seq_port_info_t
* port_info_ptr
,
122 g_max_jack_port_name_size
,
124 snd_seq_client_info_get_name(client_info_ptr
),
125 snd_seq_client_info_get_client(client_info_ptr
),
126 type
== A2J_PORT_CAPTURE
? "capture": "playback",
127 snd_seq_port_info_get_name(port_info_ptr
));
133 g_max_jack_port_name_size
,
135 snd_seq_client_info_get_name(client_info_ptr
),
136 type
== A2J_PORT_CAPTURE
? "capture": "playback",
137 snd_seq_port_info_get_name(port_info_ptr
));
140 // replace all offending characters with ' '
141 for (c
= port_ptr
->name
; *c
; ++c
)
142 if (!JACK_IS_VALID_PORT_NAME_CHAR(*c
))
145 if (ret
< 0 || ret
>= (int)g_max_jack_port_name_size
)
147 /* force terminating nul char because the man page does not specify whether the resulting buffer is nul terminated in this case */
148 port_ptr
->name
[g_max_jack_port_name_size
] = 0;
149 a2j_warning("port name \"%s\" was truncated because of the max size support by JACK (%zu)", port_ptr
->name
, g_max_jack_port_name_size
);
158 const snd_seq_port_info_t
* info
)
160 struct a2j_port
*port
;
163 snd_seq_client_info_t
* client_info_ptr
;
165 struct a2j_stream
* stream_ptr
;
167 stream_ptr
= &self
->stream
[type
];
169 err
= snd_seq_client_info_malloc(&client_info_ptr
);
172 a2j_error("Failed to allocate client info");
176 client
= snd_seq_port_info_get_client(info
);
178 err
= snd_seq_get_any_client_info(self
->seq
, client
, client_info_ptr
);
181 a2j_error("Failed to get client info");
182 goto fail_free_client_info
;
185 a2j_debug("client name: '%s'", snd_seq_client_info_get_name(client_info_ptr
));
186 a2j_debug("port name: '%s'", snd_seq_port_info_get_name(info
));
188 port
= calloc(1, sizeof(struct a2j_port
) + g_max_jack_port_name_size
);
191 goto fail_free_client_info
;
194 port
->a2j_ptr
= self
;
196 port
->jack_port
= JACK_INVALID_PORT
;
199 a2j_port_fill_name(port
, type
, client_info_ptr
, info
, !g_disable_port_uniqueness
);
201 /* Add port to list early, before registering to JACK, so map functionality is guaranteed to work during port registration */
202 list_add_tail(&port
->siblings
, &stream_ptr
->list
);
204 if (type
== A2J_PORT_CAPTURE
)
206 jack_caps
= JackPortIsOutput
;
210 jack_caps
= JackPortIsInput
;
213 /* mark anything that looks like a hardware port as physical&terminal */
214 if (snd_seq_port_info_get_type(info
) & (SND_SEQ_PORT_TYPE_HARDWARE
|SND_SEQ_PORT_TYPE_PORT
|SND_SEQ_PORT_TYPE_SPECIFIC
))
216 jack_caps
|= JackPortIsPhysical
|JackPortIsTerminal
;
219 port
->jack_port
= jack_port_register(self
->jack_client
, port
->name
, JACK_DEFAULT_MIDI_TYPE
, jack_caps
, 0);
220 if (port
->jack_port
== JACK_INVALID_PORT
)
222 a2j_error("jack_port_register() failed for '%s'", port
->name
);
226 if (type
== A2J_PORT_CAPTURE
)
228 err
= a2j_alsa_connect_from(self
, port
->remote
.client
, port
->remote
.port
);
232 err
= snd_seq_connect_to(self
->seq
, self
->port_id
, port
->remote
.client
, port
->remote
.port
);
235 a2j_error("snd_seq_connect_to() for %d:%d failed with error %d", (int)port
->remote
.client
, (int)port
->remote
.port
, err
);
241 a2j_info("port skipped: %s", port
->name
);
245 port
->inbound_events
= jack_ringbuffer_create(MAX_EVENT_SIZE
*16);
247 a2j_info("port created: %s", port
->name
);
248 snd_seq_client_info_free(client_info_ptr
);
252 list_del(&port
->siblings
);
256 fail_free_client_info
:
257 snd_seq_client_info_free(client_info_ptr
);