-
Notifications
You must be signed in to change notification settings - Fork 0
/
j2amidi_bridge.c
251 lines (203 loc) · 7.06 KB
/
j2amidi_bridge.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/* jackmidi2alsaseq.c
*
* copies MIDI events from a JACK MIDI client to an ALSA sequencer client
*
* Copyright (C) 2005 Lars Luthman, based on alsaseq2jackmidi.c by Sean Bolton.
* Copyright (c) 2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307, USA.
*/
/* compile with LASH:
gcc -ansi -pedantic -O2 -Wall -o jackmidi2alsaseq jackmidi2alsaseq.c `pkg-config --cflags --libs jack alsa lash-1.0`
or without LASH:
gcc -ansi -pedantic -O2 -Wall -o jackmidi2alsaseq jackmidi2alsaseq.c `pkg-config --cflags --libs jack alsa` -DNO_LASH
*/
#include <sched.h>
#include <signal.h>
#if !defined(__USE_BSD)
#define __USE_BSD /* to get snprintf() */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#if !defined(__USE_POSIX199309)
#define __USE_POSIX199309
#endif
#include <time.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include <jack/jack.h>
#include <jack/midiport.h>
#include <jack/ringbuffer.h>
jack_client_t *jack_client;
jack_port_t *jack_midi_port;
int keep_running = 1;
unsigned long int ringbuffer_overflows = 0;
snd_seq_t *seq_handle;
snd_midi_event_t *alsa_encoder;
jack_ringbuffer_t *jack_ringbuffer;
char* jack_name = NULL;
int portid;
int queue_id;
int init_alsa(const char * client_name);
int init_jack(const char * client_name);
void sigint_handler(int i);
int jack_callback(jack_nframes_t nframes, void *arg);
void output_event(void);
int main(int argc, char **argv) {
unsigned long old_ringbuffer_overflows = 0;
struct timespec sleep_time = { 0, 1e6 };
const char * client_name;
if (argc == 2)
{
client_name = argv[1];
}
else
{
client_name = "j2a_bridge";
}
/* Initialise connections and signal handlers */
if (!(init_alsa(client_name) && init_jack(client_name)))
exit(1);
signal(SIGINT, &sigint_handler);
signal(SIGTERM, &sigint_handler);
/* Loop until we get a SIGINT or SIGTERM */
while (keep_running) {
/* Report overflows */
if (old_ringbuffer_overflows != ringbuffer_overflows) {
fprintf(stderr, "Overflow, MIDI events are coming in too fast!\n");
old_ringbuffer_overflows = ringbuffer_overflows;
}
/* Write MIDI events to the ALSA sequencer port */
while (jack_ringbuffer_read_space(jack_ringbuffer) >= sizeof(size_t) &&
keep_running) {
output_event();
}
nanosleep(&sleep_time, NULL);
}
/* Clean up */
jack_client_close(jack_client);
jack_ringbuffer_free(jack_ringbuffer);
snd_seq_close(seq_handle);
return 0;
}
int init_alsa(const char * client_name) {
/* Get a sequencer handle */
if (snd_seq_open(&seq_handle, "hw", SND_SEQ_OPEN_OUTPUT, 0) < 0) {
fprintf(stderr, "Error opening ALSA sequencer.\n");
return 0;
}
snd_seq_set_client_name(seq_handle, client_name);
/* Create an output port */
if ((portid = snd_seq_create_simple_port(seq_handle, "capture",
SND_SEQ_PORT_CAP_READ |
SND_SEQ_PORT_CAP_SUBS_READ,
SND_SEQ_PORT_TYPE_HARDWARE)) < 0){
fprintf(stderr, "Error creating sequencer port.\n");
return 0;
}
/* Initialise miscellaneous other stuff */
queue_id = snd_seq_alloc_queue(seq_handle);
snd_midi_event_new(1024, &alsa_encoder);
snd_seq_start_queue(seq_handle, queue_id, NULL);
return 1;
}
int init_jack(const char * client_name) {
jack_status_t status;
/* Create a JACK client */
jack_client = jack_client_open(client_name, 0, &status);
if (jack_client == 0) {
fprintf(stderr, "Failed to connect to JACK server!\n");
return 0;
}
/* Create a MIDI input port */
jack_midi_port = jack_port_register(jack_client, "playback",
JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput, 0);
if (!jack_midi_port) {
fprintf(stderr, "Failed to create JACK MIDI port!\n");
return 0;
}
/* Initialise the ringbuffer */
jack_ringbuffer = jack_ringbuffer_create(2048);
if (!jack_ringbuffer) {
fprintf(stderr, "Failed to create ringbuffer!\n");
return 0;
}
jack_ringbuffer_reset(jack_ringbuffer);
/* Set process callback function and activate */
jack_set_process_callback(jack_client, jack_callback, jack_ringbuffer);
if (jack_activate(jack_client)) {
fprintf(stderr, "Failed to activate JACK client!\n");
return 0;
}
return 1;
}
/* This is just so we can clean up if the user presses Ctrl-C in the shell */
void sigint_handler(int i) {
((void)(i)); /* unreferenced parameter */
keep_running = 0;
}
int jack_callback(jack_nframes_t nframes, void *arg) {
jack_ringbuffer_t* jack_ringbuffer = arg;
void* midi_port_buf = jack_port_get_buffer(jack_midi_port, nframes);
jack_midi_event_t jack_midi_event;
jack_nframes_t jack_midi_event_index = 0;
jack_nframes_t jack_midi_event_count = jack_midi_get_event_count(midi_port_buf);
/* Loop while there are MIDI events in the input buffer */
while (jack_midi_event_index < jack_midi_event_count) {
jack_midi_event_get(&jack_midi_event, midi_port_buf,
jack_midi_event_index);
jack_midi_event_index++;
/* Check if we have enough space in the ringbuffer for the event */
if (jack_ringbuffer_write_space(jack_ringbuffer) <
jack_midi_event.size + sizeof(size_t)) {
++ringbuffer_overflows;
}
/* Write the event to the ringbuffer */
else {
jack_ringbuffer_write(jack_ringbuffer,
(char*)&jack_midi_event.size,
sizeof(size_t));
jack_ringbuffer_write(jack_ringbuffer,
(char*)jack_midi_event.buffer,
jack_midi_event.size);
}
}
return 0;
}
void output_event(void) {
size_t event_size;
static char event_buffer[1024];
snd_seq_event_t alsa_event;
static struct timespec sleep_time = { 0, 1e4 };
/* Read the size of the MIDI data and wait until we have that much
data to read on the ringbuffer */
jack_ringbuffer_read(jack_ringbuffer, (char*)&event_size, sizeof(size_t));
while (jack_ringbuffer_read_space(jack_ringbuffer) < event_size &&
keep_running)
nanosleep(&sleep_time, NULL);
/* Read the MIDI data and make an ALSA MIDI event from it */
jack_ringbuffer_read(jack_ringbuffer, event_buffer, event_size);
snd_seq_ev_clear(&alsa_event);
if (snd_midi_event_encode(alsa_encoder, (unsigned char*)event_buffer,
event_size, &alsa_event)) {
snd_seq_ev_set_source(&alsa_event, portid);
snd_seq_ev_set_subs(&alsa_event);
snd_seq_ev_schedule_tick(&alsa_event, queue_id, 1, 0);
snd_seq_event_output_direct(seq_handle, &alsa_event);
}
}