diff --git a/CMakeLists.txt b/CMakeLists.txt index c2a8da7cd..1372d56d4 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 0d251d957..83b1ae2ee 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 85b375454..437333d75 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 398ff000f..f641d047c 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 94324e815..16347188e 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 e642ca596..f56d636b0 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 8517f3371..a36519050 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 000000000..5c4e310fc --- /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 + +#include + +#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 */