[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1011425: fluidsynth: Consider adding support for sndio



Package: fluidsynth
Version: 2.2.7-1
Severity: wishlist
Tags: patch
X-Debbugs-Cc: jobbautista9@protonmail.com

Dear Maintainer,

Since Debian packages sndio, I think it would be nice if our package of
fluidsynth includes support for this sound server. Normally it should be
upstream that should implement this, but this was rejected.[1] Upstream wants
the implementation to use the LGPLv2.1 as the license, but it uses ISC instead.

FreeBSD's port of fluidsynth includes support for sndio, and their patch doesn't
seem to be too non-trivial. I attached a source patch that is based on the GitHub
pull request branch[2] that was rejected due to licensing issues.

[1] - https://github.com/FluidSynth/fluidsynth/pull/470#issuecomment-459708372
[2] - https://github.com/FluidSynth/fluidsynth/compare/master...yurivict:implement-sndio


-- System Information:
Debian Release: bookworm/sid
  APT prefers experimental
  APT policy: (1, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 5.17.0-1-amd64 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN, TAINT_FIRMWARE_WORKAROUND
Locale: LANG=en_PH.UTF-8, LC_CTYPE=en_PH.UTF-8 (charmap=UTF-8), LANGUAGE=en_PH:en
Shell: /bin/sh linked to /bin/dash
Init: OpenRC (via /run/openrc), PID 1: init
LSM: AppArmor: enabled

Versions of packages fluidsynth depends on:
ii  init-system-helpers  1.62devuan1
ii  libc6                2.33-7
ii  libfluidsynth3       2.2.7-1
ii  libglib2.0-0         2.72.1-1
ii  libsdl2-2.0-0        2.0.22+dfsg-3

Versions of packages fluidsynth recommends:
pn  qsynth  <none>

fluidsynth suggests no packages.

-- no debconf information
From: Job Bautista <jobbautista9@protonmail.com>
Subject: Implement support for sndio, based from FreeBSD

Upstream normally should implement support for such sound servers, but
a pull request to implement said support got rejected due to licensing.

Author: Yuri Victorovich <yuri@freebsd.org>
Author: Jacob Meuser <jakemsr@sdf.lonestar.org>
Forwarded: https://github.com/FluidSynth/fluidsynth/pull/470
Last-Update: 2022-05-22

--- fluidsynth-2.2.7.orig/CMakeLists.txt
+++ fluidsynth-2.2.7/CMakeLists.txt
@@ -88,6 +88,7 @@ option ( enable-winmidi "compile Windows
 option ( enable-sdl2 "compile SDL2 audio support (if it is available)" on )
 option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on )
 option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on )
+option ( enable-sndio "compile Sndio support (if it is available)" on )
 option ( enable-readline "compile readline lib line editing (if it is available)" on )
 option ( enable-threads "enable multi-threading support (such as parallel voice synthesis)" on )
 option ( enable-openmp "enable OpenMP support (parallelization of soundfont decoding, vectorization of voice mixing, etc.)" on )
@@ -562,6 +563,17 @@ else(NOT enable-pkgconfig)
     unset_pkg_config ( PULSE )
     endif ( enable-pulseaudio )
 
+    unset ( SNDIO_SUPPORT CACHE )
+    if ( enable-sndio )
+    pkg_check_modules ( SNDIO sndio>=1.5.0 )
+    set ( SNDIO_SUPPORT ${SNDIO_FOUND} )
+    if ( SNDIO_SUPPORT )
+      list( APPEND PC_REQUIRES_PRIV "ndio")
+    endif ( SNDIO_SUPPORT )
+    else ( enable-sndio )
+    unset_pkg_config ( SNDIO )
+    endif ( enable-sndio )
+
     unset ( ALSA_SUPPORT CACHE )
     if ( enable-alsa )
     pkg_check_modules ( ALSA alsa>=0.9.1 )
--- fluidsynth-2.2.7.orig/cmake_admin/report.cmake
+++ fluidsynth-2.2.7/cmake_admin/report.cmake
@@ -73,6 +73,12 @@ else ( PULSE_SUPPORT )
     set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  PulseAudio:            no\n" )
 endif ( PULSE_SUPPORT )
 
+if ( SNDIO_SUPPORT )
+    set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  Sndio:                 yes\n" )
+else ( SNDIO_SUPPORT )
+    set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  Sndio:                 no\n" )
+endif ( SNDIO_SUPPORT )
+
 if ( SDL2_SUPPORT )
     set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  SDL2:                  yes\n" )
 else ( SDL2_SUPPORT )
--- fluidsynth-2.2.7.orig/src/CMakeLists.txt
+++ fluidsynth-2.2.7/src/CMakeLists.txt
@@ -52,6 +52,11 @@ if ( PULSE_SUPPORT )
   include_directories ( ${PULSE_INCLUDE_DIRS} )
 endif ( PULSE_SUPPORT )
 
+if ( SNDIO_SUPPORT )
+  set ( fluid_sndio_SOURCES drivers/fluid_sndio.c )
+  include_directories ( ${SNDIO_INCLUDE_DIRS} )
+endif ( SNDIO_SUPPORT )
+
 if ( ALSA_SUPPORT )
   set ( fluid_alsa_SOURCES drivers/fluid_alsa.c )
   include_directories ( ${ALSA_INCLUDE_DIRS} )
@@ -284,6 +289,7 @@ add_library ( libfluidsynth-OBJ OBJECT
     ${fluid_oss_SOURCES}
     ${fluid_portaudio_SOURCES}
     ${fluid_pulse_SOURCES}
+    ${fluid_sndio_SOURCES}
     ${fluid_dsound_SOURCES}
     ${fluid_wasapi_SOURCES}
     ${fluid_waveout_SOURCES}
@@ -361,6 +367,7 @@ target_link_libraries ( libfluidsynth
     ${JACK_LIBRARIES}
     ${ALSA_LIBRARIES}
     ${PULSE_LIBRARIES}
+    ${SNDIO_LIBRARIES}
     ${PORTAUDIO_LIBRARIES}
     ${LIBSNDFILE_LIBRARIES}
     ${SDL2_LIBRARIES}
--- fluidsynth-2.2.7.orig/src/config.cmake
+++ fluidsynth-2.2.7/src/config.cmake
@@ -202,6 +202,9 @@
 /* Define to enable Windows MIDI driver */
 #cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@
 
+/* Define to enable Sndio driver */
+#cmakedefine SNDIO_SUPPORT @SNDIO_SUPPORT@
+
 /* Define to enable SDL2 audio driver */
 #cmakedefine SDL2_SUPPORT @SDL2_SUPPORT@
 
--- fluidsynth-2.2.7.orig/src/drivers/fluid_adriver.c
+++ fluidsynth-2.2.7/src/drivers/fluid_adriver.c
@@ -70,6 +70,16 @@ static const fluid_audriver_definition_t
     },
 #endif
 
+#if SNDIO_SUPPORT
+    { 
+        "sndio",
+        new_fluid_sndio_audio_driver,
+        new_fluid_sndio_audio_driver2,
+        delete_fluid_sndio_audio_driver,
+        fluid_sndio_audio_driver_settings
+    },
+#endif
+
 #if OSS_SUPPORT
     {
         "oss",
--- fluidsynth-2.2.7.orig/src/drivers/fluid_adriver.h
+++ fluidsynth-2.2.7/src/drivers/fluid_adriver.h
@@ -52,6 +52,15 @@ void delete_fluid_pulse_audio_driver(flu
 void fluid_pulse_audio_driver_settings(fluid_settings_t *settings);
 #endif
 
+#if SNDIO_SUPPORT
+fluid_audio_driver_t* new_fluid_sndio_audio_driver(fluid_settings_t* settings,
+        fluid_synth_t* synth);
+fluid_audio_driver_t* new_fluid_sndio_audio_driver2(fluid_settings_t* settings,
+        fluid_audio_func_t func, void* data);
+void delete_fluid_sndio_audio_driver(fluid_audio_driver_t* p);
+void fluid_sndio_audio_driver_settings(fluid_settings_t* settings);
+#endif
+
 #if ALSA_SUPPORT
 fluid_audio_driver_t *new_fluid_alsa_audio_driver(fluid_settings_t *settings,
         fluid_synth_t *synth);
--- fluidsynth-2.2.7.orig/src/drivers/fluid_mdriver.c
+++ fluidsynth-2.2.7/src/drivers/fluid_mdriver.c
@@ -52,6 +52,14 @@ static const fluid_mdriver_definition_t
         fluid_alsa_rawmidi_driver_settings
     },
 #endif
+#if SNDIO_SUPPORT
+    {
+        "sndio",
+        new_fluid_sndio_midi_driver,
+        delete_fluid_sndio_midi_driver,
+        fluid_sndio_midi_driver_settings
+    },
+#endif
 #if JACK_SUPPORT
     {
         "jack",
--- fluidsynth-2.2.7.orig/src/drivers/fluid_mdriver.h
+++ fluidsynth-2.2.7/src/drivers/fluid_mdriver.h
@@ -53,6 +53,15 @@ void delete_fluid_alsa_seq_driver(fluid_
 void fluid_alsa_seq_driver_settings(fluid_settings_t *settings);
 #endif
 
+/* SNDIO */
+#if SNDIO_SUPPORT
+void fluid_sndio_midi_driver_settings(fluid_settings_t *settings);
+fluid_midi_driver_t *new_fluid_sndio_midi_driver(fluid_settings_t *settings,
+        handle_midi_event_func_t handler,
+        void *data);
+void delete_fluid_sndio_midi_driver(fluid_midi_driver_t *p);
+#endif
+
 /* JACK */
 #if JACK_SUPPORT
 void fluid_jack_midi_driver_settings(fluid_settings_t *settings);
--- /dev/null
+++ fluidsynth-2.2.7/src/drivers/fluid_sndio.c
@@ -0,0 +1,520 @@
+/* sndio backend for FluidSynth - A Software Synthesizer
+ *
+ * Copyright (c) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+/* fluid_sndio.c
+ *
+ * Driver for the sndio audio access library
+ */
+
+#include "fluid_synth.h"
+#include "fluid_adriver.h"
+#include "fluid_midi.h"
+#include "fluid_mdriver.h"
+#include "fluid_settings.h"
+
+#if SNDIO_SUPPORT
+
+#include <sndio.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <unistd.h>
+
+
+/** fluid_sndio_audio_driver_t
+ *
+ * This structure should not be accessed directly. Use audio port
+ * functions instead.
+ */
+typedef struct {
+  fluid_audio_driver_t driver;
+  fluid_synth_t* synth;
+  fluid_audio_callback_t read;
+  void* buffer;
+  pthread_t thread;
+  int cont;
+  struct sio_hdl *hdl;
+  struct sio_par par;
+  int buffer_size;
+  int buffer_byte_size;
+  fluid_audio_func_t callback;
+  void* data;
+  float* buffers[2];
+} fluid_sndio_audio_driver_t;
+
+typedef struct {
+  fluid_midi_driver_t driver;
+  struct mio_hdl *hdl;
+  pthread_t thread;
+  int status;
+  fluid_midi_parser_t *parser;
+} fluid_sndio_midi_driver_t;
+
+void delete_fluid_sndio_audio_driver(fluid_audio_driver_t* p);
+
+/* local utilities */
+static void* fluid_sndio_audio_run(void* d);
+static void* fluid_sndio_audio_run2(void* d);
+
+
+void
+fluid_sndio_audio_driver_settings(fluid_settings_t* settings)
+{
+  fluid_settings_register_str(settings, "audio.sndio.device", "default", 0);
+}
+
+/*
+ * new_fluid_sndio_audio_driver
+ */
+fluid_audio_driver_t*
+new_fluid_sndio_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
+{
+  fluid_sndio_audio_driver_t* dev = NULL;
+  double sample_rate;
+  int periods, period_size;
+  char* devname;
+  pthread_attr_t attr;
+  int err;
+
+  dev = FLUID_NEW(fluid_sndio_audio_driver_t);
+  if (dev == NULL) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    return NULL;
+  }
+  FLUID_MEMSET(dev, 0, sizeof(fluid_sndio_audio_driver_t));
+
+  fluid_settings_getint(settings, "audio.periods", &periods);
+  fluid_settings_getint(settings, "audio.period-size", &period_size);
+  fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
+
+  dev->hdl = NULL;
+  dev->synth = synth;
+  dev->callback = NULL;
+  dev->data = NULL;
+  dev->cont = 1;
+
+  if (fluid_settings_dupstr(settings, "audio.sndio.device", &devname) != FLUID_OK || !devname) {
+    devname = NULL;
+  }
+
+  dev->hdl = sio_open(devname, SIO_PLAY, 0);
+  if (dev->hdl == NULL) {
+    FLUID_LOG(FLUID_ERR, "sndio could not be opened for writing");
+    goto error_recovery;
+  }
+
+  sio_initpar(&dev->par);
+
+  if (fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
+    dev->par.bits = 16;
+    dev->par.le = SIO_LE_NATIVE;
+    dev->read = fluid_synth_write_s16;
+  } else {
+    FLUID_LOG(FLUID_ERR, "Unknown sample format");
+    goto error_recovery;
+  }
+
+  dev->par.appbufsz = period_size * periods;
+  dev->par.round = period_size;
+
+  dev->par.pchan = 2;
+  dev->par.rate = sample_rate;
+
+  if (!sio_setpar(dev->hdl, &dev->par)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't set sndio audio parameters");
+    goto error_recovery;
+  }
+
+  if (!sio_getpar(dev->hdl, &dev->par)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't get sndio audio parameters");
+    goto error_recovery;
+  } else if (dev->par.pchan != 2 || dev->par.rate != sample_rate ||
+      dev->par.bits != 16) {
+    FLUID_LOG(FLUID_ERR, "Couldn't set sndio audio parameters as desired");
+    goto error_recovery;
+  }
+
+  dev->buffer_size = dev->par.round;
+  dev->buffer_byte_size = dev->par.round * dev->par.bps * dev->par.pchan;
+
+  dev->buffer = FLUID_MALLOC(dev->buffer_byte_size);
+  if (dev->buffer == NULL) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    goto error_recovery;
+  }
+
+  if (!sio_start(dev->hdl)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't start sndio");
+    goto error_recovery;
+  }
+
+  if (pthread_attr_init(&attr)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't initialize audio thread attributes");
+    goto error_recovery;
+  }
+
+  err = pthread_create(&dev->thread, &attr, fluid_sndio_audio_run, (void*) dev);
+  if (err) {
+    FLUID_LOG(FLUID_ERR, "Couldn't create audio thread");
+    goto error_recovery;
+  }
+
+  return (fluid_audio_driver_t*) dev;
+
+error_recovery:
+  delete_fluid_sndio_audio_driver((fluid_audio_driver_t*) dev);
+  return NULL;
+}
+
+fluid_audio_driver_t*
+new_fluid_sndio_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data)
+{
+  fluid_sndio_audio_driver_t* dev = NULL;
+  double sample_rate;
+  int periods, period_size;
+  char* devname;
+  pthread_attr_t attr;
+  int err;
+
+  dev = FLUID_NEW(fluid_sndio_audio_driver_t);
+  if (dev == NULL) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    return NULL;
+  }
+  FLUID_MEMSET(dev, 0, sizeof(fluid_sndio_audio_driver_t));
+
+  fluid_settings_getint(settings, "audio.periods", &periods);
+  fluid_settings_getint(settings, "audio.period-size", &period_size);
+  fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
+
+  dev->hdl = NULL;
+  dev->synth = NULL;
+  dev->read = NULL;
+  dev->callback = func;
+  dev->data = data;
+  dev->cont = 1;
+
+  if (fluid_settings_dupstr(settings, "audio.sndio.device", &devname) != FLUID_OK || !devname) {
+    devname = NULL;
+  }
+
+  dev->hdl = sio_open(devname, SIO_PLAY, 0);
+  if (dev->hdl == NULL) {
+    FLUID_LOG(FLUID_ERR, "sndio could not be opened for writing");
+    goto error_recovery;
+  }
+
+  sio_initpar(&dev->par);
+
+  dev->par.appbufsz = period_size * periods;
+  dev->par.round = period_size;
+
+  dev->par.bits = 16;
+  dev->par.le = SIO_LE_NATIVE;
+  dev->par.pchan = 2;
+  dev->par.rate = sample_rate;
+
+  if (!sio_setpar(dev->hdl, &dev->par)){
+    FLUID_LOG(FLUID_ERR, "Can't configure sndio parameters");
+    goto error_recovery;
+  }
+
+  if (!sio_getpar(dev->hdl, &dev->par)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't get sndio audio parameters");
+    goto error_recovery;
+  } else if (dev->par.pchan != 2 || dev->par.rate != sample_rate ||
+      dev->par.bits != 16) {
+    FLUID_LOG(FLUID_ERR, "Couldn't set sndio audio parameters as desired");
+    goto error_recovery;
+  }
+
+  dev->buffer_size = dev->par.round;
+  dev->buffer_byte_size = dev->par.round * dev->par.bps * dev->par.pchan;
+
+  /* allocate the buffers. FIXME!!! don't use interleaved samples */
+  dev->buffer = FLUID_MALLOC(dev->buffer_byte_size);
+  if (dev->buffer == NULL) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    goto error_recovery;
+  }
+  dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size);
+  dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size);
+  if ((dev->buffer == NULL) || (dev->buffers[0] == NULL) || (dev->buffers[1] == NULL)) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    goto error_recovery;
+  }
+
+  if (!sio_start(dev->hdl)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't start sndio");
+    goto error_recovery;
+  }
+
+  if (pthread_attr_init(&attr)) {
+    FLUID_LOG(FLUID_ERR, "Couldn't initialize audio thread attributes");
+    goto error_recovery;
+  }
+
+  err = pthread_create(&dev->thread, &attr, fluid_sndio_audio_run2, (void*) dev);
+  if (err) {
+    FLUID_LOG(FLUID_ERR, "Couldn't create audio2 thread");
+    goto error_recovery;
+  }
+
+  return (fluid_audio_driver_t*) dev;
+
+error_recovery:
+  delete_fluid_sndio_audio_driver((fluid_audio_driver_t*) dev);
+  return NULL;
+}
+
+/*
+ * delete_fluid_sndio_audio_driver
+ */
+void
+delete_fluid_sndio_audio_driver(fluid_audio_driver_t* p)
+{
+  fluid_sndio_audio_driver_t* dev = (fluid_sndio_audio_driver_t*) p;
+
+  if (dev == NULL) {
+    return FLUID_OK;
+  }
+  dev->cont = 0;
+  if (dev->thread) {
+    if (pthread_join(dev->thread, NULL)) {
+      FLUID_LOG(FLUID_ERR, "Failed to join the audio thread");
+      return FLUID_FAILED;
+    }
+  }
+  if (dev->hdl) {
+    sio_close(dev->hdl);
+  }
+  if (dev->buffer != NULL) {
+    FLUID_FREE(dev->buffer);
+  }
+  FLUID_FREE(dev);
+  return FLUID_OK;
+}
+
+/*
+ * fluid_sndio_audio_run
+ */
+void*
+fluid_sndio_audio_run(void* d)
+{
+  fluid_sndio_audio_driver_t* dev = (fluid_sndio_audio_driver_t*) d;
+  fluid_synth_t* synth = dev->synth;
+  void* buffer = dev->buffer;
+  int len = dev->buffer_size;
+
+  /* it's as simple as that: */
+  while (dev->cont)
+  {
+    dev->read (synth, len, buffer, 0, 2, buffer, 1, 2);
+    sio_write (dev->hdl, buffer, dev->buffer_byte_size);
+  }
+
+  FLUID_LOG(FLUID_DBG, "Audio thread finished");
+
+  pthread_exit(NULL);
+
+  return 0; /* not reached */
+}
+
+
+/*
+ * fluid_sndio_audio_run
+ */
+void*
+fluid_sndio_audio_run2(void* d)
+{
+  fluid_sndio_audio_driver_t* dev = (fluid_sndio_audio_driver_t*) d;
+  short* buffer = (short*) dev->buffer;
+  float* left = dev->buffers[0];
+  float* right = dev->buffers[1];
+  int buffer_size = dev->buffer_size;
+  int dither_index = 0;
+
+  FLUID_LOG(FLUID_DBG, "Audio thread running");
+
+  /* it's as simple as that: */
+  while (dev->cont)
+  {
+    (*dev->callback)(dev->data, buffer_size, 0, NULL, 2, dev->buffers);
+
+    fluid_synth_dither_s16 (&dither_index, buffer_size, left, right,
+			    buffer, 0, 2, buffer, 1, 2);
+
+    sio_write (dev->hdl, buffer, dev->buffer_byte_size);
+  }
+
+  FLUID_LOG(FLUID_DBG, "Audio thread finished");
+
+  pthread_exit(NULL);
+
+  return 0; /* not reached */
+}
+
+void fluid_sndio_midi_driver_settings(fluid_settings_t* settings)
+{
+  fluid_settings_register_str(settings, "midi.sndio.device", "default", 0);
+}
+
+void
+delete_fluid_sndio_midi_driver(fluid_midi_driver_t *addr)
+{
+  int err;
+  fluid_sndio_midi_driver_t *dev = (fluid_sndio_midi_driver_t *)addr;
+
+  if (dev == NULL) {
+    return;
+  }
+  dev->status = FLUID_MIDI_DONE;
+
+  /* cancel the thread and wait for it before cleaning up */
+  if (dev->thread) {
+    err = pthread_cancel(dev->thread);
+    if (err) {
+      FLUID_LOG(FLUID_ERR, "Failed to cancel the midi thread");
+      return;
+    }
+    if (pthread_join(dev->thread, NULL)) {
+      FLUID_LOG(FLUID_ERR, "Failed to join the midi thread");
+      return;
+    }
+  }
+  if (dev->hdl != NULL) {
+    mio_close(dev->hdl);
+  }
+  if (dev->parser != NULL) {
+    delete_fluid_midi_parser(dev->parser);
+  }
+  FLUID_FREE(dev);
+}
+
+void *
+fluid_sndio_midi_run(void *addr)
+{
+  int n, i;
+  fluid_midi_event_t* evt;
+  fluid_sndio_midi_driver_t *dev = (fluid_sndio_midi_driver_t *)addr;
+#define MIDI_BUFLEN (3125 / 10)
+  unsigned char buffer[MIDI_BUFLEN];
+
+  /* make sure the other threads can cancel this thread any time */
+  if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) {
+    FLUID_LOG(FLUID_ERR, "Failed to set the cancel state of the midi thread");
+    pthread_exit(NULL);
+  }
+  if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
+    FLUID_LOG(FLUID_ERR, "Failed to set the cancel state of the midi thread");
+    pthread_exit(NULL);
+  }
+
+  /* go into a loop until someone tells us to stop */
+  dev->status = FLUID_MIDI_LISTENING;
+
+  while (dev->status == FLUID_MIDI_LISTENING) {
+
+    /* read new data */
+    n = mio_read(dev->hdl, buffer, MIDI_BUFLEN);
+    if (n == 0 && mio_eof(dev->hdl)) {
+      FLUID_LOG(FLUID_ERR, "Failed to read the midi input");
+      dev->status = FLUID_MIDI_DONE;
+    }
+
+    /* let the parser convert the data into events */
+    for (i = 0; i < n; i++) {
+      evt = fluid_midi_parser_parse(dev->parser, buffer[i]);
+      if (evt != NULL) {
+	/* send the event to the next link in the chain */
+	(*dev->driver.handler)(dev->driver.data, evt);
+      }
+    }
+  }
+  pthread_exit(NULL);
+}
+
+int
+fluid_sndio_midi_driver_status(fluid_midi_driver_t *addr)
+{
+  fluid_sndio_midi_driver_t *dev = (fluid_sndio_midi_driver_t *)addr;
+  return dev->status;
+}
+
+
+fluid_midi_driver_t *
+new_fluid_sndio_midi_driver(fluid_settings_t *settings,
+			       handle_midi_event_func_t handler, void *data)
+{
+  int err;
+  fluid_sndio_midi_driver_t *dev;
+  char *device;
+
+  /* not much use doing anything */
+  if (handler == NULL) {
+    FLUID_LOG(FLUID_ERR, "Invalid argument");
+    return NULL;
+  }
+
+  /* allocate the device */
+  dev = FLUID_NEW(fluid_sndio_midi_driver_t);
+  if (dev == NULL) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    return NULL;
+  }
+  FLUID_MEMSET(dev, 0, sizeof(fluid_sndio_midi_driver_t));
+  dev->hdl = NULL;
+
+  dev->driver.handler = handler;
+  dev->driver.data = data;
+
+  /* allocate one event to store the input data */
+  dev->parser = new_fluid_midi_parser();
+  if (dev->parser == NULL) {
+    FLUID_LOG(FLUID_ERR, "Out of memory");
+    goto error_recovery;
+  }
+
+  /* get the device name. if none is specified, use the default device. */
+  if (fluid_settings_dupstr(settings, "midi.sndio.device", &device) != FLUID_OK || !device) {
+	device = NULL;
+  }
+
+  /* open the default hardware device. only use midi in. */
+  dev->hdl = mio_open(device, MIO_IN, 0);
+  if (dev->hdl == NULL) {
+    FLUID_LOG(FLUID_ERR, "Couldn't open sndio midi device");
+    goto error_recovery;
+  }
+
+  dev->status = FLUID_MIDI_READY;
+
+  err = pthread_create(&dev->thread, NULL, fluid_sndio_midi_run, (void *)dev);
+  if (err) {
+    FLUID_LOG(FLUID_PANIC, "Couldn't create the midi thread.");
+    goto error_recovery;
+  }
+  return (fluid_midi_driver_t *) dev;
+
+ error_recovery:
+  delete_fluid_sndio_midi_driver((fluid_midi_driver_t *)dev);
+  return NULL;
+}
+
+#endif /*#if SNDIO_SUPPORT */

Reply to: