Skip to content

Instantly share code, notes, and snippets.

@fuck-shithub
Created September 10, 2022 19:56
Show Gist options
  • Save fuck-shithub/47f60ac0ad01ab78506b9cce2011ccb2 to your computer and use it in GitHub Desktop.
Save fuck-shithub/47f60ac0ad01ab78506b9cce2011ccb2 to your computer and use it in GitHub Desktop.
SDL 1.2 JACK support
# made while HEAD at 4c3097466bdf254a262ab7ffb72633992a892801
# Make sure to add the following line to include/SDL_config.h
#define SDL_AUDIO_DRIVER_JACK 1
diff --git a/configure.ac b/configure.ac
index a654c1bd..743dfbbf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -555,6 +555,41 @@ CheckPulseAudio()
fi
}
+dnl See if JACK is supported
+CheckJACK()
+{
+ AC_ARG_ENABLE(jack,
+[AS_HELP_STRING([--enable-jack], [use JACK [default=yes]])],
+ , enable_alsa=yes)
+# if test x$enable_audio = xyes -a x$enable_jack = xyes; then
+# PKG_CHECK_MODULES([JACK], [libjack >= 0.1], audio_jack=yes, audio_jack=no)
+ have_jack=yes
+
+ if test x$have_jack = xyes; then
+# AC_ARG_ENABLE(jack-shared,
+#[AS_HELP_STRING([--enable-jack-shared], [dynamically load JACK support [default=no]])],
+# , enable_jack_shared=yes)
+ jack_lib=[`find_lib "libjack.so.*" "$JACK_LIBS" | sed 's/.*\/\(.*\)/\1/; q'`]
+
+ AC_DEFINE(SDL_AUDIO_DRIVER_JACK)
+ SOURCES="$SOURCES $srcdir/src/audio/jack/*.c"
+ EXTRA_CFLAGS="$EXTRA_CFLAGS $JACK_CFLAGS"
+ if test x$have_loadso != xyes && \
+ test x$enable_jack_shared = xyes; then
+ AC_MSG_WARN([You must have SDL_LoadObject() support for dynamic JACK loading])
+ fi
+ if test x$have_loadso = xyes && \
+ test x$enable_jack_shared = xyes && test x$jack_lib != x; then
+ echo "-- dynamic libjack -> $jack_lib"
+ AC_DEFINE_UNQUOTED(SDL_AUDIO_DRIVER_JACK_DYNAMIC, "$jack_lib")
+ else
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ljack"
+ fi
+ have_audio=yes
+ fi
+# fi
+}
+
CheckARTSC()
{
AC_ARG_ENABLE(arts,
@@ -2431,6 +2466,7 @@ case "$host" in
CheckESD
CheckSndio
CheckPulseAudio
+ CheckJACK
CheckNAS
CheckX11
CheckNANOX
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 9f0a6990..e431d572 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -30,6 +30,9 @@
/* Available audio drivers */
static AudioBootStrap *bootstrap[] = {
+#if SDL_AUDIO_DRIVER_JACK
+ &JACK_bootstrap,
+#endif
#if SDL_AUDIO_DRIVER_PULSE
&PULSE_bootstrap,
#endif
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index fa29a2bb..035e1341 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -108,6 +108,9 @@ extern AudioBootStrap SNDIO_bootstrap;
#if SDL_AUDIO_DRIVER_BSD
extern AudioBootStrap BSD_AUDIO_bootstrap;
#endif
+#if SDL_AUDIO_DRIVER_JACK
+extern AudioBootStrap JACK_bootstrap;
+#endif
#if SDL_AUDIO_DRIVER_PULSE
extern AudioBootStrap PULSE_bootstrap;
#endif
diff --git a/src/audio/jack/SDL_jack_audio.c b/src/audio/jack/SDL_jack_audio.c
new file mode 100644
index 00000000..317812f1
--- /dev/null
+++ b/src/audio/jack/SDL_jack_audio.c
@@ -0,0 +1,434 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+/*
+This driver was written by GreaseMonkey, and used the ALSA driver as a template.
+It doesn't support dynamic loading of the JACK library yet.
+*/
+#include "SDL_config.h"
+
+/* Allow access to a raw mixing buffer */
+
+#include <sys/types.h>
+#include <signal.h> /* For kill() */
+#include <unistd.h> /* For usleep() */
+
+#include "SDL_timer.h"
+#include "SDL_audio.h"
+#include "../SDL_audiomem.h"
+#include "../SDL_audio_c.h"
+#include "SDL_jack_audio.h"
+
+#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
+#include "SDL_name.h"
+#include "SDL_loadso.h"
+#else
+#define SDL_NAME(X) X
+#endif
+
+/* The tag name used by JACK audio */
+#define DRIVER_NAME "jack"
+
+/* Audio driver functions */
+static int JACK_OpenAudio(_THIS, SDL_AudioSpec *spec);
+static void JACK_WaitAudio(_THIS);
+static void JACK_PlayAudio(_THIS);
+static Uint8 *JACK_GetAudioBuf(_THIS);
+static void JACK_CloseAudio(_THIS);
+
+#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
+#error "We don't support dynamic loading of JACK yet"
+#else
+static void UnloadJACKLibrary(void) {
+ return;
+}
+
+static int LoadJACKLibrary(void) {
+ return 0;
+}
+#endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
+
+/* Audio driver bootstrap functions */
+
+static int Audio_Available(void)
+{
+ int available;
+ char cnamebuf[32];
+ jack_client_t *qclient;
+
+ available = 0;
+ if (LoadJACKLibrary() < 0) {
+ return available;
+ }
+
+ strcpy(cnamebuf, "sdlquery_a");
+ qclient = SDL_NAME(jack_client_open)(cnamebuf, JackNoStartServer, NULL);
+
+ if ( qclient != NULL ) {
+ available = 1;
+ SDL_NAME(jack_client_close)(qclient);
+ }
+ UnloadJACKLibrary();
+ printf("Attempting JACK: %i\n", available);
+ return(available);
+}
+
+static void Audio_DeleteDevice(SDL_AudioDevice *device)
+{
+ SDL_free(device->hidden);
+ SDL_free(device);
+ UnloadJACKLibrary();
+}
+
+static SDL_AudioDevice *Audio_CreateDevice(int devindex)
+{
+ SDL_AudioDevice *this;
+
+ /* Initialize all variables that we clean on shutdown */
+ LoadJACKLibrary();
+ this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
+ if ( this ) {
+ SDL_memset(this, 0, (sizeof *this));
+ this->hidden = (struct SDL_PrivateAudioData *)
+ SDL_malloc((sizeof *this->hidden));
+ }
+ if ( (this == NULL) || (this->hidden == NULL) ) {
+ SDL_OutOfMemory();
+ if ( this ) {
+ SDL_free(this);
+ }
+ return(0);
+ }
+ SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+
+ /* Set the function pointers */
+ this->OpenAudio = JACK_OpenAudio;
+ this->WaitAudio = JACK_WaitAudio;
+ this->PlayAudio = JACK_PlayAudio;
+ this->GetAudioBuf = JACK_GetAudioBuf;
+ this->CloseAudio = JACK_CloseAudio;
+
+ this->free = Audio_DeleteDevice;
+
+ return this;
+}
+
+AudioBootStrap JACK_bootstrap = {
+ DRIVER_NAME, "JACK Audio Connection Kit",
+ Audio_Available, Audio_CreateDevice
+};
+
+static int Audio_CanWrite(_THIS)
+{
+ int i;
+ const int wlen = sizeof(jack_default_audio_sample_t) * postbuflen;
+
+ /* Wait until all rings have enough space to shove in postbuflen samples. */
+ for(i = 0; i < portcount; i++) {
+ if(jack_ringbuffer_write_space(postrings[i]) < wlen) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* This function waits until it is possible to write a full sound buffer */
+static void JACK_WaitAudio(_THIS)
+{
+ /* Waiting happens in PlayAudio */
+}
+
+/*
+ * http://bugzilla.libsdl.org/show_bug.cgi?id=110
+ * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
+ * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
+ */
+/* Being aware of this we might have to rewire stuff carefully */
+
+static int j_process(jack_nframes_t nframes, void *arg)
+{
+ SDL_AudioDevice *this = (SDL_AudioDevice *)arg;
+ int i;
+
+ /* Read from our ring buffers */
+ for(i = 0; i < portcount; i++) {
+ outbufs[i] = SDL_NAME(jack_port_get_buffer)(portlist[i], nframes);
+ }
+ for(i = 0; i < portcount; i++) {
+ SDL_NAME(jack_ringbuffer_read)(postrings[i], (char *)(outbufs[i]),
+ nframes * sizeof(jack_default_audio_sample_t));
+ }
+
+ return 0;
+}
+
+static void JACK_PlayAudio(_THIS)
+{
+ int ring_left, ring_pushed;
+ int channels;
+ int chans_remain;
+ int i, j;
+ Uint8 *p_u8;
+ Sint8 *p_s8;
+ Uint16 *p_u16;
+ Sint16 *p_s16;
+ int b_counter = this->hidden->jack_freq;
+
+ channels = this->spec.channels;
+
+ /* Convert to buffer */
+ switch(this->spec.format)
+ {
+#define FBLK(AUDIO_S8, p_s8, Sint8, formula) \
+ case AUDIO_S8: \
+ p_s8 = (Sint8 *)mixbuf; \
+ for(i = 0; i < postbuflen; i++) { \
+ for(j = 0; j < channels; j++) { \
+ postbufs[j][i] = (p_s8[j]) formula; \
+ } \
+ b_counter -= this->spec.freq; \
+ while(b_counter < 0) { \
+ b_counter += this->hidden->jack_freq; \
+ p_s8 += channels; \
+ } \
+ } \
+ break; \
+
+ FBLK(AUDIO_S8, p_s8, Sint8, / 128.0f)
+ FBLK(AUDIO_U8, p_u8, Uint8, / 128.0f - 1.0f)
+ FBLK(AUDIO_S16SYS, p_s16, Sint16, / 32768.0f)
+ FBLK(AUDIO_U16SYS, p_u16, Uint16, / 32768.0f - 1.0f)
+
+ default:
+ fprintf(stderr, "PANIC: Format %i not supported by JACK driver!\n",
+ this->spec.format);
+ fflush(stderr);
+ abort();
+ }
+
+ /* Jam the audio into the ports */
+ chans_remain = 0;
+ for(i = 0; i < portcount; i++) {
+ postptrs[i] = 0;
+ chans_remain |= 1<<i;
+ }
+
+ for(;;) {
+ for(j = 0; j < 3; j++) {
+ for(i = 0; i < portcount; i++) {
+ if((chans_remain & (1<<i)) == 0) {
+ continue;
+ }
+
+ ring_left = (sizeof(jack_default_audio_sample_t) * postbuflen) - postptrs[i];
+ ring_pushed = SDL_NAME(jack_ringbuffer_write)(postrings[i],
+ &(((const char *)(postbufs[i]))[postptrs[i]]),
+ ring_left);
+
+ if(ring_left == ring_pushed) {
+ chans_remain &= ~(1<<i);
+ }
+
+ postptrs[i] += ring_pushed;
+ }
+
+ if(chans_remain == 0)
+ break;
+ }
+
+ if(chans_remain == 0)
+ break;
+
+ usleep(100); /* Give the CPU a short rest */
+ }
+}
+
+static Uint8 *JACK_GetAudioBuf(_THIS)
+{
+ return(mixbuf);
+}
+
+static void JACK_CloseAudio(_THIS)
+{
+ int i;
+
+ if ( mixbuf != NULL ) {
+ SDL_FreeAudioMem(mixbuf);
+ mixbuf = NULL;
+ }
+
+ if ( client != NULL ) {
+ if( portlist != NULL ) {
+ for(i = 0; i < portcount; i++) {
+ if( portlist[i] != NULL ) {
+ SDL_NAME(jack_port_unregister)(client, portlist[i]);
+ }
+ }
+
+ SDL_free(portlist);
+ }
+ SDL_NAME(jack_client_close)(client);
+ client = NULL;
+ }
+}
+
+static int JACK_OpenAudio(_THIS, SDL_AudioSpec *spec)
+{
+ int status;
+ char client_name[128];
+ char port_name[64];
+ Uint16 format;
+ unsigned int channels;
+ Uint16 test_format;
+ const char **ports;
+ int i;
+
+ /* Open the audio device */
+ strncpy(client_name, basename(getenv("_")), 127);
+ client = SDL_NAME(jack_client_open)(client_name, JackNoStartServer, NULL);
+
+ if ( client == NULL ) {
+ SDL_SetError("Couldn't open audio device: %s", "jack_client_open failed");
+ return(-1);
+ }
+
+ /* Figure out what the hardware is capable of */
+ /* IT'S CAPABLE OF EVERYTHING SO SHUT UP --GM */
+
+ /* SDL only uses interleaved sample output */
+ /* Wait, how do we do this? --GM */
+
+ /* Try for a closest match on audio format */
+ portcount = spec->channels;
+ portlist = SDL_malloc(sizeof(jack_port_t *) * portcount);
+ status = -1;
+ for ( test_format = SDL_FirstAudioFormat(spec->format);
+ test_format && (status < 0); ) {
+ switch ( test_format ) {
+ case AUDIO_U8:
+ case AUDIO_S8:
+ case AUDIO_S16SYS:
+ case AUDIO_U16SYS:
+ format = test_format;
+ break;
+ default:
+ format = 0;
+ break;
+ }
+ if ( format != 0 ) {
+ status = 0;
+ for(i = 0; i < portcount; i++)
+ {
+ snprintf(port_name, 63, "out_%i", i+1);
+ portlist[i] = SDL_NAME(jack_port_register)(client, port_name,
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+ if(portlist[i] == NULL) {
+ while(i-- >= 0) {
+ SDL_NAME(jack_port_unregister)(client, portlist[i]);
+ portlist[i] = NULL;
+ }
+ status = -1;
+ }
+
+ }
+ }
+ if ( status < 0 ) {
+ test_format = SDL_NextAudioFormat();
+ }
+ }
+ if ( status < 0 ) {
+ SDL_SetError("Couldn't find any hardware audio formats");
+ JACK_CloseAudio(this);
+ return(-1);
+ }
+ spec->format = test_format;
+ channels = spec->channels;
+
+ /* Get the audio rate */
+ this->hidden->jack_freq = SDL_NAME(jack_get_sample_rate)(client);
+
+ /* Set the buffer size, in samples */
+ /* Can't do that either! */
+
+ /* Calculate the final parameters for this audio specification */
+ SDL_CalculateAudioSpec(spec);
+
+ /* Allocate mixing buffer */
+ mixlen = spec->size;
+ mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
+ if ( mixbuf == NULL ) {
+ JACK_CloseAudio(this);
+ return(-1);
+ }
+ SDL_memset(mixbuf, spec->silence, spec->size);
+
+ /* Switch to blocking mode for playback */
+ /* CAN. NOT. DO. */
+
+ /* Activate JACK client */
+ SDL_NAME(jack_set_process_callback)(client, j_process, (void *)this);
+ status = SDL_NAME(jack_activate)(client);
+ if(status != 0) {
+ SDL_SetError("Couldn't open audio device: jack_activate %i", status);
+ return(-1);
+ }
+
+ /* Get length of postproc buffers */
+ //this->hidden->jack_freq = spec->freq;
+ postbuflen = (spec->samples * this->hidden->jack_freq) / spec->freq;
+
+ /* Allocate audio buffers */
+ outbufs = SDL_malloc(sizeof(jack_default_audio_sample_t *) * channels);
+ postbufs = SDL_malloc(sizeof(jack_default_audio_sample_t *) * channels);
+ postrings = SDL_malloc(sizeof(jack_ringbuffer_t *) * channels);
+ postptrs = SDL_malloc(sizeof(int) * channels);
+
+ for(i = 0; i < channels; i++) {
+ postbufs[i] = SDL_malloc(sizeof(jack_default_audio_sample_t) * postbuflen);
+ postrings[i] = SDL_NAME(jack_ringbuffer_create)(
+ sizeof(jack_default_audio_sample_t) * postbuflen * 2);
+ }
+
+ /* Wire everything up for JACK */
+ ports = SDL_NAME(jack_get_ports)(client, ".*:playback_.*", JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsInput|JackPortIsPhysical);
+
+ for(i = 0; i < (channels == 1 ? 2 : channels) && ports[i] != NULL; i++) {
+ status = SDL_NAME(jack_connect)(client,
+ SDL_NAME(jack_port_name)(portlist[i % portcount]),
+ ports[i]);
+
+ if( status != 0 ) {
+ fprintf(stderr, "Connection failed: %i %i %s %s\n", i, status,
+ jack_port_name(portlist[i % portcount]),
+ ports[i]);
+ }
+ }
+
+ SDL_NAME(jack_free)(ports);
+
+ /* We're ready to rock and roll. :-) */
+ return(0);
+}
+
diff --git a/src/audio/jack/SDL_jack_audio.h b/src/audio/jack/SDL_jack_audio.h
new file mode 100644
index 00000000..de4f9f41
--- /dev/null
+++ b/src/audio/jack/SDL_jack_audio.h
@@ -0,0 +1,63 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#ifndef _JACK_audio_h
+#define _JACK_audio_h
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <jack/types.h>
+
+#include "../SDL_sysaudio.h"
+
+/* Hidden "this" pointer for the video functions */
+#define _THIS SDL_AudioDevice *this
+
+struct SDL_PrivateAudioData {
+ /* Raw mixing buffer */
+ int postbuflen;
+ int portcount;
+ jack_ringbuffer_t **postrings;
+ jack_port_t **portlist;
+ void **outbufs;
+ jack_default_audio_sample_t **postbufs;
+ int *postptrs;
+ int jack_freq;
+ jack_client_t *client;
+ Uint8 *mixbuf;
+ int mixlen;
+};
+
+/* Old variable names */
+#define postbuflen (this->hidden->postbuflen)
+#define portcount (this->hidden->portcount)
+#define postrings (this->hidden->postrings)
+#define portlist (this->hidden->portlist)
+#define outbufs (this->hidden->outbufs)
+#define postbufs (this->hidden->postbufs)
+#define postptrs (this->hidden->postptrs)
+#define client (this->hidden->client)
+#define mixbuf (this->hidden->mixbuf)
+#define mixlen (this->hidden->mixlen)
+
+#endif /* _JACK_audio_h */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment