Jack!

Hello.

Anyone looked into providing a JACK backend for SDL audio?

M

I did this once …On Mon, Jan 11, 2010 at 10:18:01AM +0000, sdl at coreland.ath.cx wrote:

Hello.

Anyone looked into providing a JACK backend for SDL audio?


@Jacob_Meuser
SDF Public Access UNIX System - http://sdf.lonestar.org

SDL_jackaudio.h>>>
/*
SDL - Simple DirectMedia Layer
Copyright © 1997-2006 Sam Lantinga

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Sam Lantinga
slouken at libsdl.org

*/
#include “SDL_config.h”

#ifndef _SDL_jackaudio_h
#define _SDL_jackaudio_h

#include <jack/jack.h>

#include “…/SDL_sysaudio.h”

/* Hidden “this” pointer for the video functions */
#define _THIS SDL_AudioDevice *this

struct SDL_PrivateAudioData {
/* The parent process id, to detect when application quits */
pid_t parent;

/* Raw mixing buffer */
Uint8 *mixbuf;
int    mixlen;

/* Support for audio timing using a timer, in addition to select() */
float frame_ticks;
float next_frame;

};
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */

/* Old variable names */
#define stream (this->hidden->stream)
#define parent (this->hidden->parent)
#define mixbuf (this->hidden->mixbuf)
#define mixlen (this->hidden->mixlen)
#define frame_ticks (this->hidden->frame_ticks)
#define next_frame (this->hidden->next_frame)

#endif /* _SDL_jackaudio_h /
<<<SDL_jackaudio.h
SDL_jackaudio.c>>>
/

SDL - Simple DirectMedia Layer
Copyright © 2008 Jacob Meuser

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Jacob Meuser
@Jacob_Meuser

*/
#include “SDL_config.h”

/* Allow access to a raw mixing buffer */

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include <unistd.h>

#include “SDL_timer.h”
#include “SDL_audio.h”
#include “…/SDL_audiomem.h”
#include “…/SDL_audio_c.h”
#include “…/SDL_audiodev_c.h”
#include “SDL_jackaudio.h”

/* The tag name used by jack audio */
#define JACK_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);

static char *client_name = “SDL”;
static jack_client_t *client;
jack_port_t **client_ports;
static const char **playback_port_names;
static char *server_name = “default”;

static int sample_rate;
static int period_size;
static int channels;
static int sample_bits;

jack_default_audio_sample_t **obuf;
ssize_t obufsize;

pthread_cond_t input_ready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t input_lock = PTHREAD_MUTEX_INITIALIZER;

/* (mostly) from jack’s OSS driver */
static void
copy_and_convert_in (jack_default_audio_sample_t *dst, void *src,
size_t nframes, int channel, int chcount, int bits)
{
int srcidx;
int dstidx;
signed char *s8src = (signed char *) src;
signed short *s16src = (signed short *) src;
jack_default_audio_sample_t scale;

srcidx = channel;
switch (bits)
{
	case 8:
		/* is this right for s8?  how to do u8? */
		scale = 1.0f / 0x7f;
		for (dstidx = 0; dstidx < nframes; dstidx++)
		{
			dst[dstidx] = (jack_default_audio_sample_t) 
				s8src[srcidx] * scale;
			srcidx += chcount;
		}
		break;
	case 16:
		scale = 1.0f / 0x7fff;
		for (dstidx = 0; dstidx < nframes; dstidx++)
		{
			dst[dstidx] = (jack_default_audio_sample_t) 
				s16src[srcidx] * scale;
			srcidx += chcount;
		}
		break;
}

}

/* Audio driver bootstrap functions */

static int Audio_Available(void)
{
jack_options_t options;
jack_status_t status;
int available = 0;

options = JackServerName | JackNoStartServer;

if ( (client = jack_client_open(client_name, options, &status,
    server_name)) != NULL ) {
	available = 1;

	jack_client_close(client);
}

return available;

}

static void Audio_DeleteDevice(SDL_AudioDevice *device)
{
SDL_free(device->hidden);
SDL_free(device);
}

static SDL_AudioDevice *Audio_CreateDevice(int devindex)
{
SDL_AudioDevice *this;

/* Initialize all variables that we clean on shutdown */

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 = {
JACK_DRIVER_NAME, “Jack Audio Connection Kit”,
Audio_Available, Audio_CreateDevice
};

/* This function waits until it is possible to write a full sound buffer */

/* but then why does SDL do PlayAudio then WaitAudio ?

  • instead, let’s wait in PlayAudio until we get a process() callback.
    */

static void JACK_WaitAudio(_THIS)
{
/* don’t wait. this is defined to keep SDL from sleeping */
// bzero(mixbuf, mixlen);
#if 0
pthread_mutex_lock(&input_lock);
pthread_cond_wait(&input_ready, &input_lock);
pthread_mutex_unlock(&input_lock);
#endif
}

static int
process(jack_nframes_t nframes, void *arg)
{
if (pthread_mutex_trylock(&input_lock) == 0) {
pthread_cond_signal(&input_ready);
pthread_mutex_unlock(&input_lock);
}

return 0;

}

static void JACK_PlayAudio(_THIS)
{
int chn;

#if 1
pthread_mutex_lock(&input_lock);
pthread_cond_wait(&input_ready, &input_lock);
#endif
for (chn = 0; chn < channels; chn++) {
obuf[chn] = jack_port_get_buffer(client_ports[chn], period_size);
copy_and_convert_in(obuf[chn], mixbuf, period_size, chn,
channels, sample_bits);
}
#if 1
pthread_mutex_unlock(&input_lock);
#endif
#if 0
/* If timer synchronization is enabled, set the next write frame */
if ( frame_ticks ) {
next_frame += frame_ticks;
}
#endif
}

static Uint8 *JACK_GetAudioBuf(_THIS)
{
return(mixbuf);
}

static void JACK_CloseAudio(_THIS)
{
int i;

if ( mixbuf != NULL ) {
	SDL_FreeAudioMem(mixbuf);
	mixbuf = NULL;
}

for (i = 0; i < channels; i++) {
	if (jack_port_unregister(client, client_ports[i])) {
		SDL_SetError("jack_port_unregister() failed");
	}
}

jack_client_close(client);
free(playback_port_names);
free(client_ports);
free(obuf);

}

static int JACK_OpenAudio(_THIS, SDL_AudioSpec *spec)
{
Uint16 test_format, format;
jack_options_t options;
jack_status_t status;
int i;

options = JackServerName | JackNoStartServer;

if ( (client = jack_client_open(client_name, options, &status,
    server_name)) == NULL ) {
	SDL_SetError("Unable to initialize jack");
	return(-1);
}
client_name = jack_get_client_name(client);
sample_rate = jack_get_sample_rate(client);
period_size = jack_get_buffer_size(client);
channels = spec->channels;

playback_port_names = jack_get_ports(client, NULL, NULL,
    JackPortIsPhysical | JackPortIsInput);

jack_set_process_callback(client, process, NULL);

if (jack_activate(client)) {
	SDL_SetError("jack_activate() failed");
}

client_ports = (jack_port_t **)malloc(sizeof(jack_port_t*) * channels);
if (client_ports == NULL) {
	printf("could not allocate ports: %s@%i\n",
	    __FILE__, __LINE__);
	return(1);
}

obufsize = period_size * sizeof(jack_default_audio_sample_t *);
obuf = (jack_default_audio_sample_t **)malloc(obufsize);

bzero(obuf, obufsize);

for (i = 0; i < channels; i++) {
	char name[64];
	snprintf(name, sizeof(name), "output%d", i + 1);
	if ((client_ports[i] = jack_port_register(client, name,
	    JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == 0) {
		printf("jack_port_register() failed: %s@%i\n",
		    __FILE__, __LINE__);
		return(1);
	}
}

for (i = 0; i < channels; i++) {
	printf("connecting output %s ", jack_port_name(client_ports[i]));
	printf("to input %s\n", playback_port_names[i]);
	if (jack_connect(client, jack_port_name(client_ports[i]),
	    playback_port_names[i])) {
		printf("jack_connect() failed: %s@%i\n",
		    __FILE__, __LINE__);
		return(1);
	}
}


/* Reset the timer synchronization flag */
frame_ticks = 0.0;

mixbuf = NULL;

/* Try for a closest match on audio format */
format = 0;
for ( test_format = SDL_FirstAudioFormat(spec->format);
					! format && test_format; ) {
	switch ( test_format ) {
		case AUDIO_S8:
			sample_bits = 8;
			format = 1;
			break;
		case AUDIO_S16SYS:
			sample_bits = 16;
			format = 1;
			break;
		default:
			format = 0;
			break;
	}
	if ( ! format ) {
		test_format = SDL_NextAudioFormat();
	}
}
if ( format == 0 ) {
	SDL_SetError("Couldn't find any hardware audio formats");
	return(-1);
}
spec->format = test_format;
spec->freq = sample_rate;
spec->samples = period_size;

/* 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 ) {
	return(-1);
}
SDL_memset(mixbuf, spec->silence, spec->size);

/* Get the parent process id (we're the parent of the audio thread) */
parent = getpid();

/* We're ready to rock and roll. :-) */
return(0);

}
<<<SDL_jackaudio.c

Nice!

Any particular reason this didn’t make it into the SDL tree?

Kind of annoying to have to close every audio program on my
system and shut down jackd just to play an SDL based game…

MOn 2010-01-11 10:56:12, Jacob Meuser wrote:

On Mon, Jan 11, 2010 at 10:18:01AM +0000, @sdl_at_coreland.ath wrote:

Hello.

Anyone looked into providing a JACK backend for SDL audio?

I did this once …

I never submitted it. it worked, but the idea of callback v.
callback is … well … many SDL applications don’t make for
"nice" jack clients anyway. jack clients should to be careful
to close their connection (like when you send it a signal,
or it crashes), reply on time, etc. jack devs don’t like
reports of misbehaving clients messing up the client graph.

anyway, there is talk in jack circles about having dbus handle
which API (alsa/pulse/jack) is being requested, to deal with
the issue you are referring to.On Mon, Jan 11, 2010 at 11:08:01AM +0000, sdl at coreland.ath.cx wrote:

On 2010-01-11 10:56:12, Jacob Meuser wrote:

On Mon, Jan 11, 2010 at 10:18:01AM +0000, sdl at coreland.ath.cx wrote:

Hello.

Anyone looked into providing a JACK backend for SDL audio?

I did this once …

Nice!

Any particular reason this didn’t make it into the SDL tree?

Kind of annoying to have to close every audio program on my
system and shut down jackd just to play an SDL based game…


@Jacob_Meuser
SDF Public Access UNIX System - http://sdf.lonestar.org

sweet!

it would be great to have SDL working with jack natively.

I imagine jack cleanup could be put in exit handlers? So when the
sound system is initialised it installs some exit handlers.

You can set up alsa plugins so that SDL goes through alsa to jack
though. I wrote about using it here:


… but here is the plugin page:
http://alsa.opensrc.org/index.php/Jack_(plugin)

However, that’s not as nice as just setting an environment variable to
use JACK :slight_smile:

cheers,On Mon, Jan 11, 2010 at 11:24 AM, Jacob Meuser wrote:

On Mon, Jan 11, 2010 at 11:08:01AM +0000, sdl at coreland.ath.cx wrote:

On 2010-01-11 10:56:12, Jacob Meuser wrote:

On Mon, Jan 11, 2010 at 10:18:01AM +0000, sdl at coreland.ath.cx wrote:

Hello.

Anyone looked into providing a JACK backend for SDL audio?

I did this once …

Nice!

Any particular reason this didn’t make it into the SDL tree?

Kind of annoying to have to close every audio program on my
system and shut down jackd just to play an SDL based game…

I never submitted it. ?it worked, but the idea of callback v.
callback is … well … many SDL applications don’t make for
"nice" jack clients anyway. ?jack clients should to be careful
to close their connection (like when you send it a signal,
or it crashes), reply on time, etc. ?jack devs don’t like
reports of misbehaving clients messing up the client graph.

anyway, there is talk in jack circles about having dbus handle
which API (alsa/pulse/jack) is being requested, to deal with
the issue you are referring to.


jakemsr at sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

'Lo,On 2010-01-11 11:41:34, Ren? Dudfield wrote:

You can set up alsa plugins so that SDL goes through alsa to jack
though. I wrote about using it here:
http://renesd.blogspot.com/2009/09/alsa-midi-timidity-fluidsynth-and-jack.html
… but here is the plugin page:
http://alsa.opensrc.org/index.php/Jack_(plugin)

Yep, I do use that setup. Unfortunately, for some ALSA programs, it
just doesn’t work. For SDL-based programs, it seems to fail consistently
(SDL claims not to be able to open the audio device) and for other programs
it works intermittently (Skype mostly works, Flash player spews errors).

Some programs appear to work flawlessly…

M

SNAKE!!!

Sorry, I’ll just step back into my MGS fan closet.

there is talk in jack circles

That is the most unfortunate term for a community, ever.

How widespread is JACK? I didn’t realize people used it directly, but
rather through ALSA, and assumed that the whole thing was being
superceded by PulseAudio anyhow.

I’m not against including an SDL audio target for it.

–ryan.

JACK and PulseAudio don’t really have the same goals. JACK is more
geared towards pro audio, precise timing and the lowest possible
latency (for recording, etc). It also provides inter-application
routing which is probably the main reason I use it.

As for widespread, it’s probably not as popular as PulseAudio.

MOn 2010-01-11 12:57:24, Ryan C. Gordon wrote:

How widespread is JACK? I didn’t realize people used it directly, but
rather through ALSA, and assumed that the whole thing was being
superceded by PulseAudio anyhow.

there is talk in jack circles

That is the most unfortunate term for a community, ever.

How widespread is JACK? I didn’t realize people used it directly, but
rather through ALSA, and assumed that the whole thing was being
superceded by PulseAudio anyhow.

ALSA is used as a backend by JACK, not the other way round. (The
ALSA->JACK plugin might be an exception, but it doesn’t seem to work
reliably).

JACK is used for very low-latency audio, intra-application audio
routing, and sample-accurate synchronization of applications
(sequencers, soft synths, etc). As far as I know, this is not quite the
domain of PulseAudio, so PulseAudio won’t supercede JACK.

I’m not against including an SDL audio target for it.

That would be cool!On Mon, 11 Jan 2010 12:57:24 -0500 “Ryan C. Gordon” wrote: