ref: 577cb497de471e95b162a32d03d62472563ba1a0
dir: /examples/jackio.c/
/*
Copyright (C) 2003-2009 Paul Brossier <piem@aubio.org>
This file is part of aubio.
aubio 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 3 of the License, or
(at your option) any later version.
aubio 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 aubio. If not, see <http://www.gnu.org/licenses/>.
*/
#include <aubio.h>
#include "config.h"
#if HAVE_JACK
#include "aubio_priv.h"
#include "jackio.h"
typedef jack_default_audio_sample_t jack_sample_t;
#if HAVE_AUBIO_DOUBLE
#define AUBIO_JACK_MAX_FRAMES 4096
#define AUBIO_JACK_NEEDS_CONVERSION
#endif
#define RINGBUFFER_SIZE 1024*sizeof(jack_midi_event_t)
/**
* jack device structure
*/
struct _aubio_jack_t
{
/** jack client */
jack_client_t *client;
/** jack output ports */
jack_port_t **oports;
/** jack input ports */
jack_port_t **iports;
/** jack input buffer */
jack_sample_t **ibufs;
/** jack output buffer */
jack_sample_t **obufs;
#ifdef AUBIO_JACK_NEEDS_CONVERSION
/** converted jack input buffer */
smpl_t **sibufs;
/** converted jack output buffer */
smpl_t **sobufs;
#endif
/** jack input audio channels */
uint_t ichan;
/** jack output audio channels */
uint_t ochan;
/** jack input midi channels */
uint_t imidichan;
/** jack output midi channels */
uint_t omidichan;
/** midi output ringbuffer */
jack_ringbuffer_t *midi_out_ring;
/** jack samplerate (Hz) */
uint_t samplerate;
/** jack processing function */
aubio_process_func_t callback;
};
/* static memory management */
static aubio_jack_t *aubio_jack_alloc (uint_t ichan, uint_t ochan,
uint_t imidichan, uint_t omidichan);
static uint_t aubio_jack_free (aubio_jack_t * jack_setup);
/* jack callback functions */
static int aubio_jack_process (jack_nframes_t nframes, void *arg);
static void aubio_jack_shutdown (void *arg);
aubio_jack_t *
new_aubio_jack (uint_t ichan, uint_t ochan,
uint_t imidichan, uint_t omidichan, aubio_process_func_t callback)
{
aubio_jack_t *jack_setup = aubio_jack_alloc (ichan, ochan,
imidichan, omidichan);
uint_t i;
char *client_name = "aubio";
char *jack_port_type;
char name[64];
/* initial jack client setup */
if ((jack_setup->client = jack_client_new (client_name)) == 0) {
AUBIO_ERR ("jack server not running?\n");
AUBIO_QUIT (AUBIO_FAIL);
}
if (jack_setup->omidichan) {
jack_setup->midi_out_ring = jack_ringbuffer_create (RINGBUFFER_SIZE);
if (jack_setup->midi_out_ring == NULL) {
AUBIO_ERR ("Failed creating jack midi output ringbuffer.");
AUBIO_QUIT (AUBIO_FAIL);
}
jack_ringbuffer_mlock (jack_setup->midi_out_ring);
}
/* set callbacks */
jack_set_process_callback (jack_setup->client, aubio_jack_process,
(void *) jack_setup);
jack_on_shutdown (jack_setup->client, aubio_jack_shutdown,
(void *) jack_setup);
/* register jack output audio and midi ports */
for (i = 0; i < ochan + omidichan; i++) {
if (i < ochan) {
jack_port_type = JACK_DEFAULT_AUDIO_TYPE;
AUBIO_SPRINTF (name, "out_%d", i + 1);
} else {
jack_port_type = JACK_DEFAULT_MIDI_TYPE;
AUBIO_SPRINTF (name, "midi_out_%d", i - ochan + 1);
}
if ((jack_setup->oports[i] =
jack_port_register (jack_setup->client, name,
jack_port_type, JackPortIsOutput, 0)) == 0) {
goto beach;
}
AUBIO_DBG ("%s:%s\n", client_name, name);
}
/* register jack input audio ports */
for (i = 0; i < ichan + imidichan; i++) {
if (i < ichan) {
jack_port_type = JACK_DEFAULT_AUDIO_TYPE;
AUBIO_SPRINTF (name, "in_%d", i + 1);
} else {
jack_port_type = JACK_DEFAULT_MIDI_TYPE;
AUBIO_SPRINTF (name, "midi_in_%d", i - ichan + 1);
}
if ((jack_setup->iports[i] =
jack_port_register (jack_setup->client, name,
jack_port_type, JackPortIsInput, 0)) == 0) {
goto beach;
}
AUBIO_DBG ("%s:%s\n", client_name, name);
}
/* set processing callback */
jack_setup->callback = callback;
return jack_setup;
beach:
AUBIO_ERR ("failed registering port \"%s:%s\"!\n", client_name, name);
jack_client_close (jack_setup->client);
AUBIO_QUIT (AUBIO_FAIL);
}
uint_t
aubio_jack_activate (aubio_jack_t * jack_setup)
{
/* get sample rate */
jack_setup->samplerate = jack_get_sample_rate (jack_setup->client);
/* actual jack process activation */
if (jack_activate (jack_setup->client)) {
AUBIO_ERR ("jack client activation failed");
return 1;
}
return 0;
}
void
aubio_jack_close (aubio_jack_t * jack_setup)
{
/* bug : should disconnect all ports first */
jack_client_close (jack_setup->client);
aubio_jack_free (jack_setup);
}
/* memory management */
static aubio_jack_t *
aubio_jack_alloc (uint_t ichan, uint_t ochan,
uint_t imidichan, uint_t omidichan)
{
aubio_jack_t *jack_setup = AUBIO_NEW (aubio_jack_t);
jack_setup->ichan = ichan;
jack_setup->ochan = ochan;
jack_setup->imidichan = imidichan;
jack_setup->omidichan = omidichan;
jack_setup->oports = AUBIO_ARRAY (jack_port_t *, ichan + imidichan);
jack_setup->iports = AUBIO_ARRAY (jack_port_t *, ochan + omidichan);
jack_setup->ibufs = AUBIO_ARRAY (jack_sample_t *, ichan);
jack_setup->obufs = AUBIO_ARRAY (jack_sample_t *, ochan);
#ifdef AUBIO_JACK_NEEDS_CONVERSION
/* allocate arrays for data conversion */
jack_setup->sibufs = AUBIO_ARRAY (smpl_t *, ichan);
uint_t i;
for (i = 0; i < ichan; i++) {
jack_setup->sibufs[i] = AUBIO_ARRAY (smpl_t, AUBIO_JACK_MAX_FRAMES);
}
jack_setup->sobufs = AUBIO_ARRAY (smpl_t *, ochan);
for (i = 0; i < ochan; i++) {
jack_setup->sobufs[i] = AUBIO_ARRAY (smpl_t, AUBIO_JACK_MAX_FRAMES);
}
#endif
return jack_setup;
}
static uint_t
aubio_jack_free (aubio_jack_t * jack_setup)
{
if (jack_setup->omidichan && jack_setup->midi_out_ring) {
jack_ringbuffer_free (jack_setup->midi_out_ring);
}
AUBIO_FREE (jack_setup->oports);
AUBIO_FREE (jack_setup->iports);
AUBIO_FREE (jack_setup->ibufs);
AUBIO_FREE (jack_setup->obufs);
AUBIO_FREE (jack_setup);
return AUBIO_OK;
}
/* jack callback functions */
static void
aubio_jack_shutdown (void *arg UNUSED)
{
AUBIO_ERR ("jack shutdown\n");
AUBIO_QUIT (AUBIO_OK);
}
static void process_midi_output (aubio_jack_t * dev, jack_nframes_t nframes);
static int
aubio_jack_process (jack_nframes_t nframes, void *arg)
{
aubio_jack_t *dev = (aubio_jack_t *) arg;
uint_t i;
for (i = 0; i < dev->ichan; i++) {
/* get readable input */
dev->ibufs[i] =
(jack_sample_t *) jack_port_get_buffer (dev->iports[i], nframes);
}
for (i = 0; i < dev->ochan; i++) {
/* get writable output */
dev->obufs[i] =
(jack_sample_t *) jack_port_get_buffer (dev->oports[i], nframes);
}
#ifndef AUBIO_JACK_NEEDS_CONVERSION
dev->callback (dev->ibufs, dev->obufs, nframes);
#else
uint_t j;
for (j = 0; j < MIN (nframes, AUBIO_JACK_MAX_FRAMES); j++) {
for (i = 0; i < dev->ichan; i++) {
dev->sibufs[i][j] = (smpl_t) dev->ibufs[i][j];
}
}
dev->callback (dev->sibufs, dev->sobufs, nframes);
for (j = 0; j < MIN (nframes, AUBIO_JACK_MAX_FRAMES); j++) {
for (i = 0; i < dev->ochan; i++) {
dev->obufs[i][j] = (jack_sample_t) dev->sobufs[i][j];
}
}
#endif
/* now process midi stuff */
if (dev->omidichan) {
process_midi_output (dev, nframes);
}
return 0;
}
void
aubio_jack_midi_event_write (aubio_jack_t * dev, jack_midi_event_t * event)
{
int written;
if (jack_ringbuffer_write_space (dev->midi_out_ring) < sizeof (*event)) {
AUBIO_ERR ("Not enough space to write midi output, midi event lost!\n");
return;
}
written = jack_ringbuffer_write (dev->midi_out_ring,
(char *) event, sizeof (*event));
if (written != sizeof (*event)) {
AUBIO_WRN ("Call to jack_ringbuffer_write failed, midi event lost! \n");
}
}
static void
process_midi_output (aubio_jack_t * dev, jack_nframes_t nframes)
{
int read, sendtime;
jack_midi_event_t ev;
unsigned char *buffer;
jack_nframes_t last_frame_time = jack_last_frame_time (dev->client);
// TODO for each omidichan
void *port_buffer = jack_port_get_buffer (dev->oports[dev->ochan], nframes);
if (port_buffer == NULL) {
AUBIO_WRN ("Failed to get jack midi output port, will not send anything\n");
return;
}
jack_midi_clear_buffer (port_buffer);
// TODO add rate_limit
while (jack_ringbuffer_read_space (dev->midi_out_ring)) {
read = jack_ringbuffer_peek (dev->midi_out_ring, (char *) &ev, sizeof (ev));
if (read != sizeof (ev)) {
AUBIO_WRN ("Short read from the ringbuffer, possible note loss.\n");
jack_ringbuffer_read_advance (dev->midi_out_ring, read);
continue;
}
sendtime = ev.time + nframes - last_frame_time;
/* send time is after current period, will do this one later */
if (sendtime >= (int) nframes) {
break;
}
if (sendtime < 0) {
sendtime = 0;
}
jack_ringbuffer_read_advance (dev->midi_out_ring, sizeof (ev));
buffer = jack_midi_event_reserve (port_buffer, sendtime, ev.size);
if (buffer == NULL) {
AUBIO_WRN ("Call to jack_midi_event_reserve failed, note lost.\n");
break;
}
AUBIO_MEMCPY (buffer, ev.buffer, ev.size);
}
}
#endif /* HAVE_JACK */