jack: Initial shot at a JACK audio target.
authorRyan C. Gordon <icculus@icculus.org>
Thu, 08 Jun 2017 13:27:58 -0400
changeset 11081eea7f98a37e4
parent 11080 1677ac34c68c
child 11082 54d6e338db26
jack: Initial shot at a JACK audio target.

http://jackaudio.org/

Fixes Bugzilla #2163.
(with several more commits following to improve this code.)
CMakeLists.txt
cmake/sdlchecks.cmake
configure
configure.in
include/SDL_config.h.cmake
include/SDL_config.h.in
src/audio/SDL_audio.c
src/audio/SDL_sysaudio.h
src/audio/jack/SDL_jackaudio.c
src/audio/jack/SDL_jackaudio.h
     1.1 --- a/CMakeLists.txt	Thu Jun 08 22:40:35 2017 +0200
     1.2 +++ b/CMakeLists.txt	Thu Jun 08 13:27:58 2017 -0400
     1.3 @@ -279,6 +279,8 @@
     1.4  set_option(OSS                 "Support the OSS audio API" ${UNIX_SYS})
     1.5  set_option(ALSA                "Support the ALSA audio API" ${UNIX_SYS})
     1.6  dep_option(ALSA_SHARED         "Dynamically load ALSA audio support" ON "ALSA" OFF)
     1.7 +set_option(JACK                "Support the JACK audio API" ${UNIX_SYS})
     1.8 +dep_option(JACK_SHARED         "Dynamically load JACK audio support" ON "JACK" OFF)
     1.9  set_option(ESD                 "Support the Enlightened Sound Daemon" ${UNIX_SYS})
    1.10  dep_option(ESD_SHARED          "Dynamically load ESD audio support" ON "ESD" OFF)
    1.11  set_option(PULSEAUDIO          "Use PulseAudio" ${UNIX_SYS})
    1.12 @@ -895,6 +897,7 @@
    1.13      endif()
    1.14      CheckOSS()
    1.15      CheckALSA()
    1.16 +    CheckJACK()
    1.17      CheckPulseAudio()
    1.18      CheckESD()
    1.19      CheckARTS()
     2.1 --- a/cmake/sdlchecks.cmake	Thu Jun 08 22:40:35 2017 +0200
     2.2 +++ b/cmake/sdlchecks.cmake	Thu Jun 08 13:27:58 2017 -0400
     2.3 @@ -161,6 +161,36 @@
     2.4  # Requires:
     2.5  # - PkgCheckModules
     2.6  # Optional:
     2.7 +# - JACK_SHARED opt
     2.8 +# - HAVE_DLOPEN opt
     2.9 +macro(CheckJACK)
    2.10 +  if(JACK)
    2.11 +    pkg_check_modules(PKG_JACK jack)
    2.12 +    if(PKG_JACK_FOUND)
    2.13 +      set(HAVE_JACK TRUE)
    2.14 +      file(GLOB JACK_SOURCES ${SDL2_SOURCE_DIR}/src/audio/jack/*.c)
    2.15 +      set(SOURCE_FILES ${SOURCE_FILES} ${JACK_SOURCES})
    2.16 +      set(SDL_AUDIO_DRIVER_JACK 1)
    2.17 +      list(APPEND EXTRA_CFLAGS ${PKG_JACK_CFLAGS})
    2.18 +      if(JACK_SHARED)
    2.19 +        if(NOT HAVE_DLOPEN)
    2.20 +          message_warn("You must have SDL_LoadObject() support for dynamic JACK audio loading")
    2.21 +        else()
    2.22 +          FindLibraryAndSONAME("jack")
    2.23 +          set(SDL_AUDIO_DRIVER_JACK_DYNAMIC "\"${JACK_LIB_SONAME}\"")
    2.24 +          set(HAVE_JACK_SHARED TRUE)
    2.25 +        endif()
    2.26 +      else()
    2.27 +        list(APPEND EXTRA_LDFLAGS ${PKG_JACK_LDFLAGS})
    2.28 +      endif()
    2.29 +      set(HAVE_SDL_AUDIO TRUE)
    2.30 +    endif()
    2.31 +  endif()
    2.32 +endmacro()
    2.33 +
    2.34 +# Requires:
    2.35 +# - PkgCheckModules
    2.36 +# Optional:
    2.37  # - ESD_SHARED opt
    2.38  # - HAVE_DLOPEN opt
    2.39  macro(CheckESD)
     3.1 --- a/configure	Thu Jun 08 22:40:35 2017 +0200
     3.2 +++ b/configure	Thu Jun 08 13:27:58 2017 -0400
     3.3 @@ -658,10 +658,10 @@
     3.4  X_CFLAGS
     3.5  XMKMF
     3.6  ARTSCONFIG
     3.7 -PKG_CONFIG
     3.8  ESD_LIBS
     3.9  ESD_CFLAGS
    3.10  ESD_CONFIG
    3.11 +PKG_CONFIG
    3.12  ALSA_LIBS
    3.13  ALSA_CFLAGS
    3.14  POW_LIB
    3.15 @@ -806,6 +806,8 @@
    3.16  with_alsa_inc_prefix
    3.17  enable_alsatest
    3.18  enable_alsa_shared
    3.19 +enable_jack
    3.20 +enable_jack_shared
    3.21  enable_esd
    3.22  with_esd_prefix
    3.23  with_esd_exec_prefix
    3.24 @@ -1535,6 +1537,8 @@
    3.25    --enable-alsa           support the ALSA audio API [[default=yes]]
    3.26    --disable-alsatest      Do not try to compile and run a test Alsa program
    3.27    --enable-alsa-shared    dynamically load ALSA audio support [[default=yes]]
    3.28 +  --enable-jack           use JACK audio [[default=yes]]
    3.29 +  --enable-jack-shared    dynamically load JACK audio support [[default=yes]]
    3.30    --enable-esd            support the Enlightened Sound Daemon [[default=yes]]
    3.31    --disable-esdtest       Do not try to compile and run a test ESD program
    3.32    --enable-esd-shared     dynamically load ESD audio support [[default=yes]]
    3.33 @@ -17870,6 +17874,119 @@
    3.34      fi
    3.35  }
    3.36  
    3.37 +CheckJACK()
    3.38 +{
    3.39 +    # Check whether --enable-jack was given.
    3.40 +if test "${enable_jack+set}" = set; then :
    3.41 +  enableval=$enable_jack;
    3.42 +else
    3.43 +  enable_jack=yes
    3.44 +fi
    3.45 +
    3.46 +    if test x$enable_audio = xyes -a x$enable_jack = xyes; then
    3.47 +        audio_jack=no
    3.48 +
    3.49 +        JACK_REQUIRED_VERSION=0.125
    3.50 +
    3.51 +        # Extract the first word of "pkg-config", so it can be a program name with args.
    3.52 +set dummy pkg-config; ac_word=$2
    3.53 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
    3.54 +$as_echo_n "checking for $ac_word... " >&6; }
    3.55 +if ${ac_cv_path_PKG_CONFIG+:} false; then :
    3.56 +  $as_echo_n "(cached) " >&6
    3.57 +else
    3.58 +  case $PKG_CONFIG in
    3.59 +  [\\/]* | ?:[\\/]*)
    3.60 +  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
    3.61 +  ;;
    3.62 +  *)
    3.63 +  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
    3.64 +for as_dir in $PATH
    3.65 +do
    3.66 +  IFS=$as_save_IFS
    3.67 +  test -z "$as_dir" && as_dir=.
    3.68 +    for ac_exec_ext in '' $ac_executable_extensions; do
    3.69 +  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
    3.70 +    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
    3.71 +    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
    3.72 +    break 2
    3.73 +  fi
    3.74 +done
    3.75 +  done
    3.76 +IFS=$as_save_IFS
    3.77 +
    3.78 +  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
    3.79 +  ;;
    3.80 +esac
    3.81 +fi
    3.82 +PKG_CONFIG=$ac_cv_path_PKG_CONFIG
    3.83 +if test -n "$PKG_CONFIG"; then
    3.84 +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
    3.85 +$as_echo "$PKG_CONFIG" >&6; }
    3.86 +else
    3.87 +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
    3.88 +$as_echo "no" >&6; }
    3.89 +fi
    3.90 +
    3.91 +
    3.92 +        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for JACK $JACK_REQUIRED_VERSION support" >&5
    3.93 +$as_echo_n "checking for JACK $JACK_REQUIRED_VERSION support... " >&6; }
    3.94 +        if test x$PKG_CONFIG != xno; then
    3.95 +        if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $JACK_REQUIRED_VERSION jack; then
    3.96 +                JACK_CFLAGS=`$PKG_CONFIG --cflags jack`
    3.97 +                JACK_LIBS=`$PKG_CONFIG --libs jack`
    3.98 +                audio_jack=yes
    3.99 +            fi
   3.100 +        fi
   3.101 +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $audio_jack" >&5
   3.102 +$as_echo "$audio_jack" >&6; }
   3.103 +
   3.104 +        if test x$audio_jack = xyes; then
   3.105 +            # Check whether --enable-jack-shared was given.
   3.106 +if test "${enable_jack_shared+set}" = set; then :
   3.107 +  enableval=$enable_jack_shared;
   3.108 +else
   3.109 +  enable_jack_shared=yes
   3.110 +fi
   3.111 +
   3.112 +            jack_lib=`find_lib "libjack.so.*" "$JACK_LIBS" | sed 's/.*\/\(.*\)/\1/; q'`
   3.113 +
   3.114 +
   3.115 +$as_echo "#define SDL_AUDIO_DRIVER_JACK 1" >>confdefs.h
   3.116 +
   3.117 +            SOURCES="$SOURCES $srcdir/src/audio/jack/*.c"
   3.118 +            EXTRA_CFLAGS="$EXTRA_CFLAGS $JACK_CFLAGS"
   3.119 +            if test x$have_loadso != xyes && \
   3.120 +               test x$enable_jack_shared = xyes; then
   3.121 +                { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You must have SDL_LoadObject() support for dynamic JACK audio loading" >&5
   3.122 +$as_echo "$as_me: WARNING: You must have SDL_LoadObject() support for dynamic JACK audio loading" >&2;}
   3.123 +            fi
   3.124 +            if test x$have_loadso = xyes && \
   3.125 +               test x$enable_jack_shared = xyes && test x$jack_lib != x; then
   3.126 +                echo "-- dynamic libjack -> $jack_lib"
   3.127 +
   3.128 +cat >>confdefs.h <<_ACEOF
   3.129 +#define SDL_AUDIO_DRIVER_JACK_DYNAMIC "$jack_lib"
   3.130 +_ACEOF
   3.131 +
   3.132 +                SUMMARY_audio="${SUMMARY_audio} jack(dynamic)"
   3.133 +
   3.134 +                case "$host" in
   3.135 +                    # On Solaris, jack must be linked deferred explicitly
   3.136 +                    # to prevent undefined symbol failures.
   3.137 +                    *-*-solaris*)
   3.138 +                        JACK_LIBS=`echo $JACK_LIBS | sed 's/\-l/-Wl,-l/g'`
   3.139 +                        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-zdeferred $JACK_LIBS -Wl,-znodeferred"
   3.140 +                esac
   3.141 +            else
   3.142 +                EXTRA_LDFLAGS="$EXTRA_LDFLAGS $JACK_LIBS"
   3.143 +                SUMMARY_audio="${SUMMARY_audio} jack"
   3.144 +            fi
   3.145 +            have_audio=yes
   3.146 +        fi
   3.147 +    fi
   3.148 +}
   3.149 +
   3.150  CheckESD()
   3.151  {
   3.152      # Check whether --enable-esd was given.
   3.153 @@ -23264,6 +23381,7 @@
   3.154          CheckOSS
   3.155          CheckALSA
   3.156          CheckPulseAudio
   3.157 +        CheckJACK
   3.158          CheckARTSC
   3.159          CheckESD
   3.160          CheckNAS
     4.1 --- a/configure.in	Thu Jun 08 22:40:35 2017 +0200
     4.2 +++ b/configure.in	Thu Jun 08 13:27:58 2017 -0400
     4.3 @@ -839,6 +839,63 @@
     4.4      fi
     4.5  }
     4.6  
     4.7 +dnl Find JACK Audio
     4.8 +CheckJACK()
     4.9 +{
    4.10 +    AC_ARG_ENABLE(jack,
    4.11 +AC_HELP_STRING([--enable-jack], [use JACK audio [[default=yes]]]),
    4.12 +                  , enable_jack=yes)
    4.13 +    if test x$enable_audio = xyes -a x$enable_jack = xyes; then
    4.14 +        audio_jack=no
    4.15 +
    4.16 +        JACK_REQUIRED_VERSION=0.125
    4.17 +
    4.18 +        AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
    4.19 +        AC_MSG_CHECKING(for JACK $JACK_REQUIRED_VERSION support)
    4.20 +        if test x$PKG_CONFIG != xno; then
    4.21 +        if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $JACK_REQUIRED_VERSION jack; then
    4.22 +                JACK_CFLAGS=`$PKG_CONFIG --cflags jack`
    4.23 +                JACK_LIBS=`$PKG_CONFIG --libs jack`
    4.24 +                audio_jack=yes
    4.25 +            fi
    4.26 +        fi
    4.27 +        AC_MSG_RESULT($audio_jack)
    4.28 +
    4.29 +        if test x$audio_jack = xyes; then
    4.30 +            AC_ARG_ENABLE(jack-shared,
    4.31 +AC_HELP_STRING([--enable-jack-shared], [dynamically load JACK audio support [[default=yes]]]),
    4.32 +                          , enable_jack_shared=yes)
    4.33 +            jack_lib=[`find_lib "libjack.so.*" "$JACK_LIBS" | sed 's/.*\/\(.*\)/\1/; q'`]
    4.34 +
    4.35 +            AC_DEFINE(SDL_AUDIO_DRIVER_JACK, 1, [ ])
    4.36 +            SOURCES="$SOURCES $srcdir/src/audio/jack/*.c"
    4.37 +            EXTRA_CFLAGS="$EXTRA_CFLAGS $JACK_CFLAGS"
    4.38 +            if test x$have_loadso != xyes && \
    4.39 +               test x$enable_jack_shared = xyes; then
    4.40 +                AC_MSG_WARN([You must have SDL_LoadObject() support for dynamic JACK audio loading])
    4.41 +            fi
    4.42 +            if test x$have_loadso = xyes && \
    4.43 +               test x$enable_jack_shared = xyes && test x$jack_lib != x; then
    4.44 +                echo "-- dynamic libjack -> $jack_lib"
    4.45 +                AC_DEFINE_UNQUOTED(SDL_AUDIO_DRIVER_JACK_DYNAMIC, "$jack_lib", [ ])
    4.46 +                SUMMARY_audio="${SUMMARY_audio} jack(dynamic)"
    4.47 +
    4.48 +                case "$host" in
    4.49 +                    # On Solaris, jack must be linked deferred explicitly
    4.50 +                    # to prevent undefined symbol failures.
    4.51 +                    *-*-solaris*)
    4.52 +                        JACK_LIBS=`echo $JACK_LIBS | sed 's/\-l/-Wl,-l/g'`
    4.53 +                        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-zdeferred $JACK_LIBS -Wl,-znodeferred"
    4.54 +                esac
    4.55 +            else
    4.56 +                EXTRA_LDFLAGS="$EXTRA_LDFLAGS $JACK_LIBS"
    4.57 +                SUMMARY_audio="${SUMMARY_audio} jack"
    4.58 +            fi
    4.59 +            have_audio=yes
    4.60 +        fi
    4.61 +    fi
    4.62 +}
    4.63 +
    4.64  dnl Find the ESD includes and libraries
    4.65  CheckESD()
    4.66  {
    4.67 @@ -3035,6 +3092,7 @@
    4.68          CheckOSS
    4.69          CheckALSA
    4.70          CheckPulseAudio
    4.71 +        CheckJACK
    4.72          CheckARTSC
    4.73          CheckESD
    4.74          CheckNAS
     5.1 --- a/include/SDL_config.h.cmake	Thu Jun 08 22:40:35 2017 +0200
     5.2 +++ b/include/SDL_config.h.cmake	Thu Jun 08 13:27:58 2017 -0400
     5.3 @@ -205,6 +205,8 @@
     5.4  #cmakedefine SDL_AUDIO_DRIVER_ANDROID @SDL_AUDIO_DRIVER_ANDROID@
     5.5  #cmakedefine SDL_AUDIO_DRIVER_ALSA @SDL_AUDIO_DRIVER_ALSA@
     5.6  #cmakedefine SDL_AUDIO_DRIVER_ALSA_DYNAMIC @SDL_AUDIO_DRIVER_ALSA_DYNAMIC@
     5.7 +#cmakedefine SDL_AUDIO_DRIVER_JACK @SDL_AUDIO_DRIVER_JACK@
     5.8 +#cmakedefine SDL_AUDIO_DRIVER_JACK_DYNAMIC @SDL_AUDIO_DRIVER_JACK_DYNAMIC@
     5.9  #cmakedefine SDL_AUDIO_DRIVER_ARTS @SDL_AUDIO_DRIVER_ARTS@
    5.10  #cmakedefine SDL_AUDIO_DRIVER_ARTS_DYNAMIC @SDL_AUDIO_DRIVER_ARTS_DYNAMIC@
    5.11  #cmakedefine SDL_AUDIO_DRIVER_PULSEAUDIO @SDL_AUDIO_DRIVER_PULSEAUDIO@
     6.1 --- a/include/SDL_config.h.in	Thu Jun 08 22:40:35 2017 +0200
     6.2 +++ b/include/SDL_config.h.in	Thu Jun 08 13:27:58 2017 -0400
     6.3 @@ -204,6 +204,8 @@
     6.4  /* Enable various audio drivers */
     6.5  #undef SDL_AUDIO_DRIVER_ALSA
     6.6  #undef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
     6.7 +#undef SDL_AUDIO_DRIVER_JACK
     6.8 +#undef SDL_AUDIO_DRIVER_JACK_DYNAMIC
     6.9  #undef SDL_AUDIO_DRIVER_ARTS
    6.10  #undef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
    6.11  #undef SDL_AUDIO_DRIVER_PULSEAUDIO
     7.1 --- a/src/audio/SDL_audio.c	Thu Jun 08 22:40:35 2017 +0200
     7.2 +++ b/src/audio/SDL_audio.c	Thu Jun 08 13:27:58 2017 -0400
     7.3 @@ -101,6 +101,9 @@
     7.4  #if SDL_AUDIO_DRIVER_EMSCRIPTEN
     7.5      &EMSCRIPTENAUDIO_bootstrap,
     7.6  #endif
     7.7 +#if SDL_AUDIO_DRIVER_JACK
     7.8 +    &JACK_bootstrap,
     7.9 +#endif
    7.10  #if SDL_AUDIO_DRIVER_DISK
    7.11      &DISKAUDIO_bootstrap,
    7.12  #endif
    7.13 @@ -723,6 +726,7 @@
    7.14      return 0;
    7.15  }
    7.16  
    7.17 +/* !!! FIXME: this needs to deal with device spec changes. */
    7.18  /* The general capture thread function */
    7.19  static int SDLCALL
    7.20  SDL_CaptureAudio(void *devicep)
     8.1 --- a/src/audio/SDL_sysaudio.h	Thu Jun 08 22:40:35 2017 +0200
     8.2 +++ b/src/audio/SDL_sysaudio.h	Thu Jun 08 13:27:58 2017 -0400
     8.3 @@ -182,6 +182,7 @@
     8.4  /* Not all of these are available in a given build. Use #ifdefs, etc. */
     8.5  extern AudioBootStrap PULSEAUDIO_bootstrap;
     8.6  extern AudioBootStrap ALSA_bootstrap;
     8.7 +extern AudioBootStrap JACK_bootstrap;
     8.8  extern AudioBootStrap SNDIO_bootstrap;
     8.9  extern AudioBootStrap NETBSDAUDIO_bootstrap;
    8.10  extern AudioBootStrap DSP_bootstrap;
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/audio/jack/SDL_jackaudio.c	Thu Jun 08 13:27:58 2017 -0400
     9.3 @@ -0,0 +1,503 @@
     9.4 +/*
     9.5 +  Simple DirectMedia Layer
     9.6 +  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     9.7 +
     9.8 +  This software is provided 'as-is', without any express or implied
     9.9 +  warranty.  In no event will the authors be held liable for any damages
    9.10 +  arising from the use of this software.
    9.11 +
    9.12 +  Permission is granted to anyone to use this software for any purpose,
    9.13 +  including commercial applications, and to alter it and redistribute it
    9.14 +  freely, subject to the following restrictions:
    9.15 +
    9.16 +  1. The origin of this software must not be misrepresented; you must not
    9.17 +     claim that you wrote the original software. If you use this software
    9.18 +     in a product, an acknowledgment in the product documentation would be
    9.19 +     appreciated but is not required.
    9.20 +  2. Altered source versions must be plainly marked as such, and must not be
    9.21 +     misrepresented as being the original software.
    9.22 +  3. This notice may not be removed or altered from any source distribution.
    9.23 +*/
    9.24 +
    9.25 +#include "../../SDL_internal.h"
    9.26 +
    9.27 +#if SDL_AUDIO_DRIVER_JACK
    9.28 +
    9.29 +#include "SDL_assert.h"
    9.30 +#include "SDL_timer.h"
    9.31 +#include "SDL_audio.h"
    9.32 +#include "../SDL_audio_c.h"
    9.33 +#include "SDL_jackaudio.h"
    9.34 +#include "SDL_loadso.h"
    9.35 +#include "../../thread/SDL_systhread.h"
    9.36 +
    9.37 +
    9.38 +/* !!! FIXME: my understanding is each JACK port is a _channel_ (like: stereo, mono...)
    9.39 +   !!! FIXME:  and not a logical device. So we'll have to figure out:
    9.40 +   !!! FIXME:   a) Can there be more than one device?
    9.41 +   !!! FIXME:   b) If so, how do you decide what port goes to what?
    9.42 +   !!! FIXME: (code in BROKEN_MULTI_DEVICE blocks was written when I assumed
    9.43 +   !!! FIXME:  enumerating ports meant listing separate devices. As such, it's
    9.44 +   !!! FIXME:  incomplete, as I discovered this as I went along writing.
    9.45 +*/
    9.46 +#define BROKEN_MULTI_DEVICE 0  /* !!! FIXME */
    9.47 +
    9.48 +
    9.49 +static jack_client_t * (*JACK_jack_client_open) (const char *, jack_options_t, jack_status_t *, ...);
    9.50 +static int (*JACK_jack_client_close) (jack_client_t *);
    9.51 +static void (*JACK_jack_on_shutdown) (jack_client_t *, JackShutdownCallback, void *);
    9.52 +static int (*JACK_jack_activate) (jack_client_t *);
    9.53 +static void * (*JACK_jack_port_get_buffer) (jack_port_t *, jack_nframes_t);
    9.54 +static int (*JACK_jack_port_unregister) (jack_client_t *, jack_port_t *);
    9.55 +static void (*JACK_jack_free) (void *);
    9.56 +static const char ** (*JACK_jack_get_ports) (jack_client_t *, const char *, const char *, unsigned long);
    9.57 +static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *);
    9.58 +static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *);
    9.59 +static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long);
    9.60 +static const char * (*JACK_jack_port_name) (const jack_port_t *);
    9.61 +static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *);
    9.62 +static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *);
    9.63 +
    9.64 +static int load_jack_syms(void);
    9.65 +
    9.66 +
    9.67 +#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
    9.68 +
    9.69 +static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
    9.70 +static void *jack_handle = NULL;
    9.71 +
    9.72 +/* !!! FIXME: this is copy/pasted in several places now */
    9.73 +static int
    9.74 +load_jack_sym(const char *fn, void **addr)
    9.75 +{
    9.76 +    *addr = SDL_LoadFunction(jack_handle, fn);
    9.77 +    if (*addr == NULL) {
    9.78 +        /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
    9.79 +        return 0;
    9.80 +    }
    9.81 +
    9.82 +    return 1;
    9.83 +}
    9.84 +
    9.85 +/* cast funcs to char* first, to please GCC's strict aliasing rules. */
    9.86 +#define SDL_JACK_SYM(x) \
    9.87 +    if (!load_jack_sym(#x, (void **) (char *) &JACK_##x)) return -1
    9.88 +
    9.89 +static void
    9.90 +UnloadJackLibrary(void)
    9.91 +{
    9.92 +    if (jack_handle != NULL) {
    9.93 +        SDL_UnloadObject(jack_handle);
    9.94 +        jack_handle = NULL;
    9.95 +    }
    9.96 +}
    9.97 +
    9.98 +static int
    9.99 +LoadJackLibrary(void)
   9.100 +{
   9.101 +    int retval = 0;
   9.102 +    if (jack_handle == NULL) {
   9.103 +        jack_handle = SDL_LoadObject(jack_library);
   9.104 +        if (jack_handle == NULL) {
   9.105 +            retval = -1;
   9.106 +            /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   9.107 +        } else {
   9.108 +            retval = load_jack_syms();
   9.109 +            if (retval < 0) {
   9.110 +                UnloadJackLibrary();
   9.111 +            }
   9.112 +        }
   9.113 +    }
   9.114 +    return retval;
   9.115 +}
   9.116 +
   9.117 +#else
   9.118 +
   9.119 +#define SDL_JACK_SYM(x) JACK_##x = x
   9.120 +
   9.121 +static void
   9.122 +UnloadJackLibrary(void)
   9.123 +{
   9.124 +}
   9.125 +
   9.126 +static int
   9.127 +LoadJackLibrary(void)
   9.128 +{
   9.129 +    load_jack_syms();
   9.130 +    return 0;
   9.131 +}
   9.132 +
   9.133 +#endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
   9.134 +
   9.135 +
   9.136 +static int
   9.137 +load_jack_syms(void)
   9.138 +{
   9.139 +    SDL_JACK_SYM(jack_client_open);
   9.140 +    SDL_JACK_SYM(jack_client_close);
   9.141 +    SDL_JACK_SYM(jack_on_shutdown);
   9.142 +    SDL_JACK_SYM(jack_activate);
   9.143 +    SDL_JACK_SYM(jack_port_get_buffer);
   9.144 +    SDL_JACK_SYM(jack_port_unregister);
   9.145 +    SDL_JACK_SYM(jack_free);
   9.146 +    SDL_JACK_SYM(jack_get_ports);
   9.147 +    SDL_JACK_SYM(jack_get_sample_rate);
   9.148 +    SDL_JACK_SYM(jack_get_buffer_size);
   9.149 +    SDL_JACK_SYM(jack_port_register);
   9.150 +    SDL_JACK_SYM(jack_port_name);
   9.151 +    SDL_JACK_SYM(jack_connect);
   9.152 +    SDL_JACK_SYM(jack_set_process_callback);
   9.153 +    return 0;
   9.154 +}
   9.155 +
   9.156 +
   9.157 +static jack_client_t *JACK_client = NULL;
   9.158 +
   9.159 +static void
   9.160 +DisconnectFromJackServer(void)
   9.161 +{
   9.162 +    if (JACK_client) {
   9.163 +        JACK_jack_client_close(JACK_client);
   9.164 +        JACK_client = NULL;
   9.165 +    }
   9.166 +}
   9.167 +
   9.168 +static void
   9.169 +jackShutdownCallback(void *arg)
   9.170 +{
   9.171 +    /* !!! FIXME: alert SDL that _every_ open device is lost here */
   9.172 +    fprintf(stderr, "SDL JACK FIXME: shutdown callback fired! All audio devices are lost!\n");
   9.173 +    fflush(stderr);
   9.174 +// !!! FIXME: need to put the client (and callback) in the SDL device    SDL_SemPost(this->hidden->iosem);  /* unblock the SDL thread. */
   9.175 +}
   9.176 +
   9.177 +static int
   9.178 +ConnectToJackServer(void)
   9.179 +{
   9.180 +    /* !!! FIXME: we _still_ need an API to specify an app name */
   9.181 +    jack_status_t status;
   9.182 +    JACK_client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
   9.183 +    if (JACK_client == NULL) {
   9.184 +        return -1;
   9.185 +    }
   9.186 +
   9.187 +    JACK_jack_on_shutdown(JACK_client, jackShutdownCallback, NULL);
   9.188 +
   9.189 +#if 0  // !!! FIXME: we need to move JACK_client into the SDL audio device.
   9.190 +    if (JACK_jack_activate(JACK_client) != 0) {
   9.191 +        DisconnectFromJackServer();
   9.192 +        return -1;
   9.193 +    }
   9.194 +#endif
   9.195 +
   9.196 +    return 0;
   9.197 +}
   9.198 +
   9.199 +
   9.200 +// !!! FIXME: implement and register these!
   9.201 +//typedef int(* JackSampleRateCallback)(jack_nframes_t nframes, void *arg)
   9.202 +//typedef int(* JackBufferSizeCallback)(jack_nframes_t nframes, void *arg)
   9.203 +
   9.204 +static int
   9.205 +jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
   9.206 +{
   9.207 +    SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
   9.208 +    jack_port_t **ports = this->hidden->sdlports;
   9.209 +    const int total_channels = this->spec.channels;
   9.210 +    const int total_frames = this->spec.samples;
   9.211 +    int channelsi;
   9.212 +
   9.213 +    if (!SDL_AtomicGet(&this->enabled)) {
   9.214 +        /* silence the buffer to avoid repeats and corruption. */
   9.215 +        SDL_memset(this->hidden->iobuffer, '\0', this->spec.size);
   9.216 +    }
   9.217 +
   9.218 +    for (channelsi = 0; channelsi < total_channels; channelsi++) {
   9.219 +        float *dst = (float *) JACK_jack_port_get_buffer(ports[channelsi], nframes);
   9.220 +        if (dst) {
   9.221 +            const float *src = ((float *) this->hidden->iobuffer) + channelsi;
   9.222 +            int framesi;
   9.223 +            for (framesi = 0; framesi < total_frames; framesi++) {
   9.224 +                *(dst++) = *src;
   9.225 +                src += total_channels;
   9.226 +            }
   9.227 +        }
   9.228 +    }
   9.229 +
   9.230 +    SDL_SemPost(this->hidden->iosem);  /* tell SDL thread we're done; refill the buffer. */
   9.231 +    return 0;  /* success */
   9.232 +}
   9.233 +
   9.234 +
   9.235 +/* This function waits until it is possible to write a full sound buffer */
   9.236 +static void
   9.237 +JACK_WaitDevice(_THIS)
   9.238 +{
   9.239 +    if (SDL_AtomicGet(&this->enabled)) {
   9.240 +        if (SDL_SemWait(this->hidden->iosem) == -1) {
   9.241 +            SDL_OpenedAudioDeviceDisconnected(this);
   9.242 +        }
   9.243 +    }
   9.244 +}
   9.245 +
   9.246 +static Uint8 *
   9.247 +JACK_GetDeviceBuf(_THIS)
   9.248 +{
   9.249 +    return (Uint8 *) this->hidden->iobuffer;
   9.250 +}
   9.251 +
   9.252 +
   9.253 +#if 0 // !!! FIXME
   9.254 +/* JACK thread calls this. */
   9.255 +static int
   9.256 +jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
   9.257 +{
   9.258 +    jack_port_get_buffer(
   9.259 +asdasd
   9.260 +}
   9.261 +
   9.262 +/* SDL thread calls this. */
   9.263 +static int
   9.264 +JACK_CaptureFromDevice(_THIS, void *buffer, int buflen)
   9.265 +{
   9.266 +    return SDL_SemWait(this->hidden->iosem) == 0) ? buflen : -1;
   9.267 +}
   9.268 +#endif
   9.269 +
   9.270 +static void
   9.271 +JACK_CloseDevice(_THIS)
   9.272 +{
   9.273 +    if (this->hidden->sdlports) {
   9.274 +        const int channels = this->spec.channels;
   9.275 +        int i;
   9.276 +        for (i = 0; i < channels; i++) {
   9.277 +            JACK_jack_port_unregister(JACK_client, this->hidden->sdlports[i]);
   9.278 +        }
   9.279 +        SDL_free(this->hidden->sdlports);
   9.280 +    }
   9.281 +
   9.282 +    if (this->hidden->iosem) {
   9.283 +        SDL_DestroySemaphore(this->hidden->iosem);
   9.284 +    }
   9.285 +
   9.286 +    if (this->hidden->devports) {
   9.287 +        JACK_jack_free(this->hidden->devports);
   9.288 +    }
   9.289 +
   9.290 +    SDL_free(this->hidden->iobuffer);
   9.291 +}
   9.292 +
   9.293 +static int
   9.294 +JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   9.295 +{
   9.296 +    /* Note that JACK uses "output" for capture devices (they output audio
   9.297 +        data to us) and "input" for playback (we input audio data to them).
   9.298 +        Likewise, SDL's playback port will be "output" (we write data out)
   9.299 +        and capture will be "input" (we read data in). */
   9.300 +    const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
   9.301 +    const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
   9.302 +    const char *sdlportstr = iscapture ? "input" : "output";
   9.303 +    const char **devports = NULL;
   9.304 +    int channels = 0;
   9.305 +    int i;
   9.306 +
   9.307 +    /* Initialize all variables that we clean on shutdown */
   9.308 +    this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof (*this->hidden));
   9.309 +    if (this->hidden == NULL) {
   9.310 +        return SDL_OutOfMemory();
   9.311 +    }
   9.312 +
   9.313 +    devports = JACK_jack_get_ports(JACK_client, NULL, NULL, JackPortIsPhysical | sysportflags);
   9.314 +    this->hidden->devports = devports;
   9.315 +    if (!devports || !devports[0]) {
   9.316 +        return SDL_SetError("No physical JACK ports available");
   9.317 +    }
   9.318 +
   9.319 +    while (devports[++channels]) {
   9.320 +        /* spin to count devports */
   9.321 +    }
   9.322 +
   9.323 +    /* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */
   9.324 +
   9.325 +    /* Jack pretty much demands what it wants. */
   9.326 +    this->spec.format = AUDIO_F32SYS;
   9.327 +    this->spec.freq = JACK_jack_get_sample_rate(JACK_client);
   9.328 +    this->spec.channels = channels;
   9.329 +    this->spec.samples = JACK_jack_get_buffer_size(JACK_client);
   9.330 +
   9.331 +    SDL_CalculateAudioSpec(&this->spec);
   9.332 +
   9.333 +    this->hidden->iosem = SDL_CreateSemaphore(0);
   9.334 +    if (!this->hidden->iosem) {
   9.335 +        return -1;  /* error was set by SDL_CreateSemaphore */
   9.336 +    }
   9.337 +
   9.338 +    this->hidden->iobuffer = (float *) SDL_calloc(1, this->spec.size);
   9.339 +    if (!this->hidden->iobuffer) {
   9.340 +        return SDL_OutOfMemory();
   9.341 +    }
   9.342 +
   9.343 +    /* Build SDL's ports, which we will connect to the device ports. */
   9.344 +    this->hidden->sdlports = (jack_port_t **) SDL_calloc(channels, sizeof (jack_port_t *));
   9.345 +    if (this->hidden->sdlports == NULL) {
   9.346 +        return SDL_OutOfMemory();
   9.347 +    }
   9.348 +
   9.349 +    if (JACK_jack_set_process_callback(JACK_client, jackProcessPlaybackCallback, this) != 0) {
   9.350 +        return SDL_SetError("JACK: Couldn't set process callback");
   9.351 +    }
   9.352 +
   9.353 +    for (i = 0; i < channels; i++) {
   9.354 +        char portname[32];
   9.355 +        SDL_snprintf(portname, sizeof (portname), "sdl_jack_%s_%d", sdlportstr, i);
   9.356 +        this->hidden->sdlports[i] = JACK_jack_port_register(JACK_client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
   9.357 +        if (this->hidden->sdlports[i] == NULL) {
   9.358 +            return SDL_SetError("jack_port_register failed");
   9.359 +        }
   9.360 +    }
   9.361 +
   9.362 +    if (JACK_jack_activate(JACK_client) != 0) {
   9.363 +        return SDL_SetError("jack_activate failed");
   9.364 +    }
   9.365 +
   9.366 +    /* once activated, we can connect all the ports. */
   9.367 +    for (i = 0; i < channels; i++) {
   9.368 +        char portname[32];
   9.369 +        SDL_snprintf(portname, sizeof (portname), "sdl_jack_%s_%d", sdlportstr, i);
   9.370 +        const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]);
   9.371 +        const char *srcport = iscapture ? devports[i] : sdlport;
   9.372 +        const char *dstport = iscapture ? sdlport : devports[i];
   9.373 +        if (JACK_jack_connect(JACK_client, srcport, dstport) != 0) {
   9.374 +            return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
   9.375 +        }
   9.376 +    }
   9.377 +
   9.378 +    /* don't need these anymore. */
   9.379 +    this->hidden->devports = NULL;
   9.380 +    JACK_jack_free(devports);
   9.381 +
   9.382 +    /* We're ready to rock and roll. :-) */
   9.383 +    return 0;
   9.384 +}
   9.385 +
   9.386 +#if BROKEN_MULTI_DEVICE  /* !!! FIXME */
   9.387 +static void
   9.388 +JackHotplugCallback(jack_port_id_t port_id, int register, void *arg)
   9.389 +{
   9.390 +    JackPortFlags flags;
   9.391 +    jack_port_t *port = JACK_jack_port_by_id(JACK_client, port_id);
   9.392 +    SDL_bool iscapture;
   9.393 +    const char *name;
   9.394 +
   9.395 +    if (!port) {
   9.396 +        return;
   9.397 +    }
   9.398 +
   9.399 +    name = JACK_jack_port_name(port);
   9.400 +    if (!name) {
   9.401 +        return;
   9.402 +    }
   9.403 +
   9.404 +    flags = JACK_jack_port_flags(port);
   9.405 +    if ((flags & JackPortIsPhysical) == 0) {
   9.406 +        return;  /* not a physical device, don't care. */
   9.407 +    }
   9.408 +
   9.409 +
   9.410 +    if ((flags & JackPortIsInput|JackPortIsOutput) == 0) {
   9.411 +        return;  /* no input OR output? Don't care...? */
   9.412 +    }
   9.413 +
   9.414 +    /* make sure it's not both, I guess. */
   9.415 +    SDL_assert((flags & JackPortIsInput|JackPortIsOutput) != (JackPortIsInput|JackPortIsOutput));
   9.416 +
   9.417 +    /* JACK uses "output" for capture devices (they output audio data to us)
   9.418 +        and "input" for playback (we input audio data to them) */
   9.419 +    iscapture = ((flags & JackPortIsOutput) != 0);
   9.420 +    if (register) {
   9.421 +        SDL_AddAudioDevice(iscapture, name, port);
   9.422 +    } else {
   9.423 +        SDL_RemoveAudioDevice(iscapture, port);
   9.424 +    }
   9.425 +}
   9.426 +
   9.427 +static void
   9.428 +JackEnumerateDevices(const SDL_bool iscapture)
   9.429 +{
   9.430 +    const JackPortFlags flags = (iscapture ? JackPortIsOutput : JackPortIsInput);
   9.431 +    const char **ports = JACK_jack_get_ports(JACK_client, NULL, NULL,
   9.432 +                                        JackPortIsPhysical | flags);
   9.433 +    const char **i;
   9.434 +
   9.435 +    if (!ports) {
   9.436 +        return;
   9.437 +    }
   9.438 +
   9.439 +    for (i = ports; *i != NULL; i++) {
   9.440 +        jack_port_t *port = JACK_jack_port_by_name(JACK_client, *i);
   9.441 +        if (port != NULL) {
   9.442 +            SDL_AddAudioDevice(iscapture, *i, port);
   9.443 +        }
   9.444 +    }
   9.445 +
   9.446 +    JACK_jack_free(ports);
   9.447 +}
   9.448 +
   9.449 +static void
   9.450 +JACK_DetectDevices()
   9.451 +{
   9.452 +    JackEnumerateDevices(SDL_FALSE);
   9.453 +    JackEnumerateDevices(SDL_TRUE);
   9.454 +
   9.455 +    /* make JACK fire this callback automatically from now on. */
   9.456 +    JACK_jack_set_port_registration_callback(JACK_client, JackHotplugCallback, NULL);
   9.457 +}
   9.458 +#endif  /* BROKEN_MULTI_DEVICE */
   9.459 +
   9.460 +
   9.461 +static void
   9.462 +JACK_Deinitialize(void)
   9.463 +{
   9.464 +    DisconnectFromJackServer();
   9.465 +    UnloadJackLibrary();
   9.466 +}
   9.467 +
   9.468 +static int
   9.469 +JACK_Init(SDL_AudioDriverImpl * impl)
   9.470 +{
   9.471 +    if (LoadJackLibrary() < 0) {
   9.472 +        return 0;
   9.473 +    }
   9.474 +
   9.475 +    if (ConnectToJackServer() < 0) {
   9.476 +        UnloadJackLibrary();
   9.477 +        return 0;
   9.478 +    }
   9.479 +
   9.480 +    /* Set the function pointers */
   9.481 +
   9.482 +    #if BROKEN_MULTI_DEVICE  /* !!! FIXME */
   9.483 +    impl->DetectDevices = JACK_DetectDevices;
   9.484 +    #else
   9.485 +    impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
   9.486 +    // !!! FIXME impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
   9.487 +    #endif
   9.488 +
   9.489 +    impl->OpenDevice = JACK_OpenDevice;
   9.490 +    impl->WaitDevice = JACK_WaitDevice;
   9.491 +    impl->GetDeviceBuf = JACK_GetDeviceBuf;
   9.492 +    impl->CloseDevice = JACK_CloseDevice;
   9.493 +    impl->Deinitialize = JACK_Deinitialize;
   9.494 +    // !!! FIXME impl->CaptureFromDevice = JACK_CaptureFromDevice;
   9.495 +    // !!! FIXME impl->HasCaptureSupport = SDL_TRUE;
   9.496 +
   9.497 +    return 1;   /* this audio target is available. */
   9.498 +}
   9.499 +
   9.500 +AudioBootStrap JACK_bootstrap = {
   9.501 +    "jack", "JACK Audio Connection Kit", JACK_Init, 0
   9.502 +};
   9.503 +
   9.504 +#endif /* SDL_AUDIO_DRIVER_JACK */
   9.505 +
   9.506 +/* vi: set ts=4 sw=4 expandtab: */
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/audio/jack/SDL_jackaudio.h	Thu Jun 08 13:27:58 2017 -0400
    10.3 @@ -0,0 +1,41 @@
    10.4 +/*
    10.5 +  Simple DirectMedia Layer
    10.6 +  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
    10.7 +
    10.8 +  This software is provided 'as-is', without any express or implied
    10.9 +  warranty.  In no event will the authors be held liable for any damages
   10.10 +  arising from the use of this software.
   10.11 +
   10.12 +  Permission is granted to anyone to use this software for any purpose,
   10.13 +  including commercial applications, and to alter it and redistribute it
   10.14 +  freely, subject to the following restrictions:
   10.15 +
   10.16 +  1. The origin of this software must not be misrepresented; you must not
   10.17 +     claim that you wrote the original software. If you use this software
   10.18 +     in a product, an acknowledgment in the product documentation would be
   10.19 +     appreciated but is not required.
   10.20 +  2. Altered source versions must be plainly marked as such, and must not be
   10.21 +     misrepresented as being the original software.
   10.22 +  3. This notice may not be removed or altered from any source distribution.
   10.23 +*/
   10.24 +#ifndef _SDL_jackaudio_h
   10.25 +#define _SDL_jackaudio_h
   10.26 +
   10.27 +#include <jack/jack.h>
   10.28 +
   10.29 +#include "../SDL_sysaudio.h"
   10.30 +
   10.31 +/* Hidden "this" pointer for the audio functions */
   10.32 +#define _THIS SDL_AudioDevice *this
   10.33 +
   10.34 +struct SDL_PrivateAudioData
   10.35 +{
   10.36 +    SDL_sem *iosem;
   10.37 +    float *iobuffer;
   10.38 +    const char **devports;
   10.39 +    jack_port_t **sdlports;
   10.40 +};
   10.41 +
   10.42 +#endif /* _SDL_jackaudio_h */
   10.43 +
   10.44 +/* vi: set ts=4 sw=4 expandtab: */