src/types.h: add some documentation, use HAVE_AUBIO_DOUBLE, not AUBIO_SINGLE_PRECISION
[aubio.git] / ext / jackio.c
index 2476adc..b63f68a 100644 (file)
@@ -1,36 +1,43 @@
 /*
-   Copyright (C) 2003 Paul Brossier
+  Copyright (C) 2003-2009 Paul Brossier <piem@aubio.org>
 
-   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 file is part of aubio.
 
-   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.
+  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.
 
-   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  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 "config.h"
-#ifdef JACK_SUPPORT
-#include <jack/jack.h>
+#include <aubio.h>
+
+#if HAVE_JACK
 #include "aubio_priv.h"
 #include "jackio.h"
 
-/*typedef jack_default_audio_sample_t jack_sample_t; */
-/* work with float, never tried in double */
-typedef smpl_t jack_sample_t;
+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 {
+struct _aubio_jack_t
+{
   /** jack client */
   jack_client_t *client;
   /** jack output ports */
@@ -41,135 +48,295 @@ struct _aubio_jack_t {
   jack_sample_t **ibufs;
   /** jack output buffer */
   jack_sample_t **obufs;
-  /** jack input channels */
+#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 channels */
+  /** 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; 
+  aubio_process_func_t callback;
 };
 
 /* static memory management */
-static aubio_jack_t * aubio_jack_alloc(uint_t ichan, uint_t ochan);
-static uint_t aubio_jack_free(aubio_jack_t * jack_setup);
+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 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, 
-    aubio_process_func_t callback) {
-  aubio_jack_t * jack_setup = aubio_jack_alloc (ichan, ochan);
+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 *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);
+    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 ports */
-  for (i = 0; i < ochan; i++) 
-  {
-    AUBIO_SPRINTF(name, "out_%d", i+1);
-    AUBIO_MSG("%s\n", name);
-    if ((jack_setup->oports[i] = 
-          jack_port_register (jack_setup->client, name, 
-            JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == 0) 
-    {
-      AUBIO_ERR("failed registering output port \"%s\"!\n", name);
-      jack_client_close (jack_setup->client);
-      AUBIO_QUIT(AUBIO_FAIL);
+  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 ports */
-  for (i = 0; i < ichan; i++) 
-  {
-    AUBIO_SPRINTF(name, "in_%d", i+1);
-    AUBIO_MSG("%s\n", name);
-    if ((jack_setup->iports[i] = 
-          jack_port_register (jack_setup->client, name, 
-            JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)) == 0)
-    {
-      AUBIO_ERR("failed registering input port \"%s\"!\n", name);
-      jack_client_close (jack_setup->client);
-      AUBIO_QUIT(AUBIO_FAIL);
+  /* 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) {
+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");
+  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) {
+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);
+  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) {
-  aubio_jack_t *jack_setup = AUBIO_NEW(aubio_jack_t);
+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->oports = AUBIO_ARRAY(jack_port_t*, ichan); 
-  jack_setup->iports = AUBIO_ARRAY(jack_port_t*, ochan); 
-  jack_setup->ibufs  = AUBIO_ARRAY(jack_sample_t*, ichan); 
-  jack_setup->obufs  = AUBIO_ARRAY(jack_sample_t*, 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) {
-  AUBIO_FREE(jack_setup->oports);
-  AUBIO_FREE(jack_setup->iports);
-  AUBIO_FREE(jack_setup->ibufs );
-  AUBIO_FREE(jack_setup->obufs );
-  AUBIO_FREE(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
+aubio_jack_shutdown (void *arg UNUSED)
+{
+  AUBIO_ERR ("jack shutdown\n");
+  AUBIO_QUIT (AUBIO_OK);
 }
 
-static int aubio_jack_process(jack_nframes_t nframes, void *arg) {
-  aubio_jack_t* dev = (aubio_jack_t *)arg;
+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++) { 
+  for (i = 0; i < dev->ichan; i++) {
     /* get readable input */
-    dev->ibufs[i] = 
-      (jack_sample_t *) jack_port_get_buffer (dev->iports[i], nframes);
+    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);
+    dev->obufs[i] =
+        (jack_sample_t *) jack_port_get_buffer (dev->oports[i], nframes);
   }
-  dev->callback(dev->ibufs,dev->obufs,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 /* JACK_SUPPORT */
+#endif /* HAVE_JACK */