3 * copies MIDI events from an ALSA sequencer client to a JACK MIDI client
5 * Copyright (c)2005 Sean Bolton.
6 * Copyright (c) 2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be
14 * useful, but WITHOUT ANY WARRANTY; without even the implied
15 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24 /* compile with: gcc -Wall -o alsaseq2jackmidi alsaseq2jackmidi.c -ljack -lasound */
26 /* gcc -Wall -I/tmp/jackmidi/include -o alsaseq2jackmidi alsaseq2jackmidi.c -L/tmp/jackmidi/lib -ljack -lasound */
33 #include <alsa/asoundlib.h>
34 #include <jack/jack.h>
35 #include <jack/midiport.h>
36 #include <jack/ringbuffer.h>
38 snd_seq_t
*seq_handle
;
40 snd_midi_event_t
*alsa_decoder
;
42 jack_ringbuffer_t
*jack_ringbuffer
;
44 jack_client_t
*jack_client
;
45 jack_port_t
*jack_midi_port
;
47 int jack_write_overflows
= 0,
48 old_jack_write_overflows
= 0,
50 old_events_copied
= 0;
55 midi_action(snd_seq_t
*seq_handle
)
58 static unsigned char buffer
[16];
62 snd_seq_event_input(seq_handle
, &ev
);
64 count
= snd_midi_event_decode(alsa_decoder
, buffer
+ 1, 16, ev
);
65 if (count
> 0 && count
< 16) {
66 buffer
[0] = (unsigned char)count
;
68 if (jack_ringbuffer_write(jack_ringbuffer
, (char *)buffer
, count
) != count
) {
69 fprintf(stderr
, "ringbuffer overflow!\n");
73 snd_seq_free_event(ev
);
74 } while (snd_seq_event_input_pending(seq_handle
, 0) > 0);
78 jack_callback(jack_nframes_t nframes
, void *arg
)
80 static unsigned char buffer
[16];
83 void* port_buf
= jack_port_get_buffer(jack_midi_port
, nframes
);
85 jack_midi_clear_buffer(port_buf
);
87 while (jack_ringbuffer_read_space(jack_ringbuffer
)) {
88 count
= jack_ringbuffer_peek(jack_ringbuffer
, (char*)buffer
, 1);
90 count
= (size_t)buffer
[0];
91 if (jack_ringbuffer_read(jack_ringbuffer
, (char*)buffer
, count
+ 1) != count
+ 1) {
92 fprintf(stderr
, "???? short read from ringbuffer!\n"); /* shouldn't happen */
94 /* -FIX- this should have the frame time of the event, instead of '0': */
95 p
= jack_midi_event_reserve(port_buf
, 0, count
);
97 memcpy(p
, buffer
+ 1, count
);
100 jack_write_overflows
++;
113 fprintf(stderr
, "JACK shutdown notification received.\n");
114 g_keep_walking
= false;
122 g_keep_walking
= false;
126 main(int argc
, char *argv
[])
131 const char * client_name
;
132 jack_status_t status
;
136 client_name
= argv
[1];
140 client_name
= "a2j_bridge";
143 /* Create ALSA sequencer client */
144 if (snd_seq_open(&seq_handle
, "hw", SND_SEQ_OPEN_INPUT
, 0) < 0) {
145 fprintf(stderr
, "Error opening ALSA sequencer.\n");
148 snd_seq_set_client_name(seq_handle
, client_name
);
149 if ((portid
= snd_seq_create_simple_port(seq_handle
, "playback",
150 SND_SEQ_PORT_CAP_WRITE
|SND_SEQ_PORT_CAP_SUBS_WRITE
,
151 SND_SEQ_PORT_TYPE_HARDWARE
)) < 0) {
152 fprintf(stderr
, "Error creating sequencer port.\n");
155 npfd
= snd_seq_poll_descriptors_count(seq_handle
, POLLIN
);
156 pfd
= (struct pollfd
*)alloca(npfd
* sizeof(struct pollfd
));
157 snd_seq_poll_descriptors(seq_handle
, pfd
, npfd
, POLLIN
);
159 /* Create ALSA snd_seq_event_t decoder */
160 if (snd_midi_event_new(16, &alsa_decoder
)) {
161 fprintf(stderr
, "Error initializing ALSA MIDI decoder!\n");
165 snd_midi_event_reset_decode(alsa_decoder
);
166 snd_midi_event_no_status(alsa_decoder
, 1);
168 /* Create interthread ringbuffer */
169 jack_ringbuffer
= jack_ringbuffer_create(2048);
170 if (!jack_ringbuffer
) {
171 fprintf(stderr
, "Failed to create ringbuffer!\n");
174 jack_ringbuffer_reset(jack_ringbuffer
);
176 /* Create JACK MIDI client */
177 jack_client
= jack_client_open(client_name
, 0, &status
);
178 if (jack_client
== 0) {
179 fprintf(stderr
, "Failed to connect to JACK server!\n");
183 jack_midi_port
= jack_port_register (jack_client
, "capture",
184 JACK_DEFAULT_MIDI_TYPE
,
185 JackPortIsOutput
, 0);
186 if (!jack_midi_port
) {
187 fprintf(stderr
, "Failed to create JACK MIDI port!\n");
191 jack_set_process_callback(jack_client
, jack_callback
, 0);
193 g_keep_walking
= true;
195 jack_on_shutdown(jack_client
, jack_shutdown
, NULL
);
197 signal(SIGINT
, &sigint_handler
);
198 signal(SIGTERM
, &sigint_handler
);
200 if (jack_activate(jack_client
)) {
201 fprintf(stderr
, "Failed to activate JACK client!\n");
205 while (g_keep_walking
) {
206 if (poll(pfd
, npfd
, 1000) > 0) {
207 midi_action(seq_handle
);
209 if (jack_write_overflows
!= old_jack_write_overflows
) {
210 old_jack_write_overflows
= jack_write_overflows
;
211 fprintf(stderr
, "JACK port write overflow count now %d\n", jack_write_overflows
);
213 // if (events_copied != old_events_copied) {
214 // old_events_copied = events_copied;
215 // fprintf(stderr, "%d events copied\n", events_copied);
219 /* -FIX- provide a way to cleanly exit, and clean up! */
220 jack_client_close(jack_client
);
221 jack_ringbuffer_free(jack_ringbuffer
);
222 snd_seq_close(seq_handle
);