252 lines
7.1 KiB
C
252 lines
7.1 KiB
C
/* 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);
|
|
}
|
|
}
|