FluidSynth / fluidsynth

Software synthesizer based on the SoundFont 2 specifications
https://www.fluidsynth.org
GNU Lesser General Public License v2.1
1.82k stars 254 forks source link

Add OS/2 KAI audio driver #1330

Closed komh closed 2 months ago

komh commented 4 months ago

Hi/2.

On OS/2, add KAI audio driver support as well as DART.

Here is the patch.

Review, please...

0001-Add-OS-2-KAI-audio-driver.patch

From 3e88cf762de80cea4565cc8fca411148a198c084 Mon Sep 17 00:00:00 2001
From: KO Myung-Hun <komh@chollian.net>
Date: Tue, 21 May 2024 21:32:13 +0900
Subject: [PATCH] Add OS/2 KAI audio driver

---
 CMakeLists.txt              |  12 +++
 FluidSynthConfig.cmake.in   |   1 +
 cmake_admin/report.cmake    |   6 ++
 src/CMakeLists.txt          |  10 +++
 src/config.cmake            |   3 +
 src/drivers/fluid_adriver.c |  10 +++
 src/drivers/fluid_adriver.h |   7 ++
 src/drivers/fluid_kai.c     | 164 ++++++++++++++++++++++++++++++++++++
 8 files changed, 213 insertions(+)
 create mode 100644 src/drivers/fluid_kai.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c2a8da7c..1372d56d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,6 +117,7 @@ endif ( CMAKE_SYSTEM MATCHES "Darwin" )

 if ( CMAKE_SYSTEM MATCHES "OS2" )
     option ( enable-dart "compile DART support (if it is available)" on )
+    option ( enable-kai "compile KAI support (if it is available)" on )
     set ( enable-ipv6 off )
 endif ( CMAKE_SYSTEM MATCHES "OS2" )

@@ -367,6 +368,9 @@ set ( LIBFLUID_LIBS ${MATH_LIBRARY} Threads::Threads )
 unset ( DART_SUPPORT CACHE )
 unset ( DART_LIBS CACHE )
 unset ( DART_INCLUDE_DIRS CACHE )
+unset ( KAI_SUPPORT CACHE )
+unset ( KAI_LIBS CACHE )
+unset ( KAI_INCLUDE_DIRS CACHE )
 if ( CMAKE_SYSTEM MATCHES "OS2" )
   set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Zbin-files" )
   set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Zbin-files" )
@@ -375,6 +379,14 @@ if ( CMAKE_SYSTEM MATCHES "OS2" )
     set ( DART_SUPPORT ${HAVE_DART_H} )
     unset ( DART_INCLUDE_DIRS CACHE )
   endif ( enable-dart )
+  if ( enable-kai )
+    check_include_files ( "kai.h" HAVE_KAI_H )
+    if ( HAVE_KAI_H )
+        set ( KAI_SUPPORT ${HAVE_KAI_H} )
+        set ( KAI_LIBS "-lkai" )
+    endif ( HAVE_KAI_H )
+    unset ( KAI_INCLUDE_DIRS CACHE )
+  endif ( enable-kai )
 endif ( CMAKE_SYSTEM MATCHES "OS2" )

 # Solaris / SunOS
diff --git a/FluidSynthConfig.cmake.in b/FluidSynthConfig.cmake.in
index 0d251d95..83b1ae2e 100644
--- a/FluidSynthConfig.cmake.in
+++ b/FluidSynthConfig.cmake.in
@@ -5,6 +5,7 @@ set(FLUIDSYNTH_SUPPORT_COREMIDI @COREMIDI_SUPPORT@)
 set(FLUIDSYNTH_SUPPORT_DART @DART_SUPPORT@)
 set(FLUIDSYNTH_SUPPORT_DSOUND @DSOUND_SUPPORT@)
 set(FLUIDSYNTH_SUPPORT_JACK @JACK_SUPPORT@)
+set(FLUIDSYNTH_SUPPORT_KAI @KAI_SUPPORT@)
 set(FLUIDSYNTH_SUPPORT_MIDISHARE @MIDISHARE_SUPPORT@)
 set(FLUIDSYNTH_SUPPORT_OBOE @OBOE_SUPPORT@)
 set(FLUIDSYNTH_SUPPORT_OPENSLES @OPENSLES_SUPPORT@)
diff --git a/cmake_admin/report.cmake b/cmake_admin/report.cmake
index 85b37545..437333d7 100644
--- a/cmake_admin/report.cmake
+++ b/cmake_admin/report.cmake
@@ -55,6 +55,12 @@ else ( DART_SUPPORT )
     set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  OS/2 DART:             no\n" )
 endif ( DART_SUPPORT )

+if ( KAI_SUPPORT )
+    set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  OS/2 KAI:              yes\n" )
+else ( KAI_SUPPORT )
+    set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  OS/2 KAI:              no\n" )
+endif ( KAI_SUPPORT )
+
 if ( OSS_SUPPORT )
     set ( AUDIO_MIDI_REPORT "${AUDIO_MIDI_REPORT}  OSS:                   yes\n" )
 else ( OSS_SUPPORT )
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 398ff000..f641d047 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -81,6 +81,10 @@ if ( DART_SUPPORT )
   set ( fluid_dart_SOURCES drivers/fluid_dart.c )
 endif ( DART_SUPPORT )

+if ( KAI_SUPPORT )
+  set ( fluid_kai_SOURCES drivers/fluid_kai.c )
+endif ( KAI_SUPPORT )
+
 if ( MIDISHARE_SUPPORT )
   set ( fluid_midishare_SOURCES drivers/fluid_midishare.c )
 endif ( MIDISHARE_SUPPORT )
@@ -232,6 +236,7 @@ add_library ( libfluidsynth-OBJ OBJECT
     ${fluid_dart_SOURCES}
     ${fluid_dbus_SOURCES}
     ${fluid_jack_SOURCES}
+    ${fluid_kai_SOURCES}
     ${fluid_pipewire_SOURCES}
     ${fluid_midishare_SOURCES}
     ${fluid_opensles_SOURCES}
@@ -347,6 +352,7 @@ endif ( MACOSX_FRAMEWORK )
 # but since they do not have a link step nothing is done with their object files.
 target_link_libraries ( libfluidsynth-OBJ PUBLIC
     ${DART_LIBS}
+    ${KAI_LIBS}
     ${COREAUDIO_LIBS}
     ${COREMIDI_LIBS}
     ${WINDOWS_LIBS}
@@ -414,6 +420,10 @@ if ( DART_SUPPORT )
     target_include_directories ( libfluidsynth-OBJ PUBLIC ${DART_INCLUDE_DIRS} )
 endif ( DART_SUPPORT )

+if ( KAI_SUPPORT )
+    target_include_directories ( libfluidsynth-OBJ PUBLIC ${KAI_INCLUDE_DIRS} )
+endif ( KAI_SUPPORT )
+
 if ( MIDISHARE_SUPPORT )
     target_link_libraries ( libfluidsynth-OBJ PUBLIC MidiShare::MidiShare )
 endif ( MIDISHARE_SUPPORT )
diff --git a/src/config.cmake b/src/config.cmake
index 94324e81..16347188 100644
--- a/src/config.cmake
+++ b/src/config.cmake
@@ -127,6 +127,9 @@
 /* Define to enable JACK driver */
 #cmakedefine JACK_SUPPORT @JACK_SUPPORT@

+/* Define to enable KAI driver */
+#cmakedefine KAI_SUPPORT @KAI_SUPPORT@
+
 /* Define to enable PipeWire driver */
 #cmakedefine PIPEWIRE_SUPPORT @PIPEWIRE_SUPPORT@

diff --git a/src/drivers/fluid_adriver.c b/src/drivers/fluid_adriver.c
index e642ca59..f56d636b 100644
--- a/src/drivers/fluid_adriver.c
+++ b/src/drivers/fluid_adriver.c
@@ -170,6 +170,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
     },
 #endif

+#if KAI_SUPPORT
+    {
+        "kai",
+        new_fluid_kai_audio_driver,
+        NULL,
+        delete_fluid_kai_audio_driver,
+        fluid_kai_audio_driver_settings
+    },
+#endif
+
 #if DART_SUPPORT
     {
         "dart",
diff --git a/src/drivers/fluid_adriver.h b/src/drivers/fluid_adriver.h
index 8517f337..a3651905 100644
--- a/src/drivers/fluid_adriver.h
+++ b/src/drivers/fluid_adriver.h
@@ -159,6 +159,13 @@ fluid_audio_driver_t *new_fluid_sndmgr_audio_driver2(fluid_settings_t *settings,
 void delete_fluid_sndmgr_audio_driver(fluid_audio_driver_t *p);
 #endif

+#if KAI_SUPPORT
+fluid_audio_driver_t *new_fluid_kai_audio_driver(fluid_settings_t *settings,
+        fluid_synth_t *synth);
+void delete_fluid_kai_audio_driver(fluid_audio_driver_t *p);
+void fluid_kai_audio_driver_settings(fluid_settings_t *settings);
+#endif
+
 #if DART_SUPPORT
 fluid_audio_driver_t *new_fluid_dart_audio_driver(fluid_settings_t *settings,
         fluid_synth_t *synth);
diff --git a/src/drivers/fluid_kai.c b/src/drivers/fluid_kai.c
new file mode 100644
index 00000000..5c4e310f
--- /dev/null
+++ b/src/drivers/fluid_kai.c
@@ -0,0 +1,164 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003  Peter Hanappe and others.
+ * Copyright (C) 2024  KO Myung-Hun
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/* fluid_kai.c
+ *
+ * Driver for OS/2 KAI
+ *
+ */
+
+#include "fluid_adriver.h"
+#include "fluid_settings.h"
+#include "fluid_sys.h"
+
+#if KAI_SUPPORT
+
+#define INCL_DOS
+#include <os2.h>
+
+#include <kai.h>
+
+#define NUM_MIX_BUFS    2
+
+/** fluid_kai_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;
+    int frame_size;
+    HKAI hkai;                                  /* KAI handle              */
+} fluid_kai_audio_driver_t;
+
+static APIRET APIENTRY
+fluid_kai_callback(PVOID pCBData, PVOID pBuffer, ULONG ulSize);
+
+/**************************************************************
+ *
+ *        KAI audio driver
+ *
+ */
+
+void fluid_kai_audio_driver_settings(fluid_settings_t *settings)
+{
+    fluid_settings_register_str(settings, "audio.kai.device", "default", 0);
+}
+
+fluid_audio_driver_t *
+new_fluid_kai_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
+{
+    fluid_kai_audio_driver_t *dev;
+    KAISPEC wanted, obtained;
+    double sample_rate;
+    int periods, period_size;
+    ULONG rc;
+
+    dev = FLUID_NEW(fluid_kai_audio_driver_t);
+    if (dev == NULL) {
+        FLUID_LOG(FLUID_ERR, "Out of memory");
+        return NULL;
+    }
+
+    FLUID_MEMSET(dev, 0, sizeof(fluid_kai_audio_driver_t));
+
+    fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
+    fluid_settings_getint(settings, "audio.periods", &periods);
+    fluid_settings_getint(settings, "audio.period-size", &period_size);
+
+    /* check the format */
+    if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
+        FLUID_LOG(FLUID_ERR, "Unhandled sample format");
+        goto error_recovery;
+    }
+
+    dev->synth = synth;
+    dev->frame_size = 2/* channels */ * sizeof(short)/* 16bits sample */;
+
+    /* Initialize KAI */
+    rc = kaiInit(KAIM_AUTO);
+    if (rc != KAIE_NO_ERROR) {
+        FLUID_LOG(FLUID_ERR, "Cannot initialize KAI, rc = %lu", rc);
+        goto error_recovery;
+    }
+
+    /* Open KAI */
+    wanted.usDeviceIndex    = 0;        /* default device */
+    wanted.ulType           = KAIT_PLAY;
+    wanted.ulBitsPerSample  = BPS_16;   /* only 16bits audio */
+    wanted.ulSamplingRate   = sample_rate;
+    wanted.ulDataFormat     = MCI_WAVE_FORMAT_PCM;
+    wanted.ulChannels       = 2;        /* only 2 channels */
+    wanted.ulNumBuffers     = NUM_MIX_BUFS;
+    wanted.ulBufferSize     = periods * period_size * dev->frame_size;
+    wanted.fShareable       = TRUE;
+    wanted.pfnCallBack      = fluid_kai_callback;
+    wanted.pCallBackData    = dev;
+
+    rc = kaiOpen(&wanted, &obtained, &dev->hkai);
+    if (rc != KAIE_NO_ERROR) {
+        FLUID_LOG(FLUID_ERR, "Cannot open KAI, rc = %lu", rc);
+        goto error_recovery;
+    }
+
+    /* Start to play */
+    kaiPlay(dev->hkai);
+
+    return (fluid_audio_driver_t *) dev;
+
+error_recovery:
+
+    delete_fluid_kai_audio_driver((fluid_audio_driver_t *) dev);
+    return NULL;
+}
+
+void delete_fluid_kai_audio_driver(fluid_audio_driver_t *p)
+{
+    fluid_kai_audio_driver_t *dev = (fluid_kai_audio_driver_t *) p;
+
+    fluid_return_if_fail(dev != NULL);
+
+    /* Stop playing */
+    kaiStop(dev->hkai);
+
+    /* Close KAI */
+    kaiClose(dev->hkai);
+
+    /* Terminate KAI */
+    kaiDone();
+
+    FLUID_FREE(dev);
+}
+
+static APIRET APIENTRY
+fluid_kai_callback(PVOID pCBData, PVOID pBuffer, ULONG ulSize)
+{
+    fluid_kai_audio_driver_t *dev = (fluid_kai_audio_driver_t *) pCBData;
+
+    FLUID_MEMSET(pBuffer, 0, ulSize);
+    fluid_synth_write_s16(dev->synth, ulSize / dev->frame_size,
+                          pBuffer, 0, 2, pBuffer, 1, 2 );
+
+    return ulSize;
+}
+
+#endif /* #if KAI_SUPPORT */
-- 
2.42.0