Skip to content

Instantly share code, notes, and snippets.

@iamgreaser
Created November 16, 2013 03:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamgreaser/7495699 to your computer and use it in GitHub Desktop.
Save iamgreaser/7495699 to your computer and use it in GitHub Desktop.
JACK audio driver for SDL 1.2
/*
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, getenv("_"), 127);
strncat(client_name, "_sdljack", 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);
}
/*
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_PCM_audio_h
#define _JACK_PCM_audio_h
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include "../SDL_sysaudio.h"
/* Hidden "this" pointer for the video functions */
#define _THIS SDL_AudioDevice *this
struct SDL_PrivateAudioData {
/* */
jack_client_t *client;
jack_port_t **portlist;
int jack_freq;
int portcount;
/* Mixing buffers */
Uint8 *mixbuf;
int mixlen;
jack_default_audio_sample_t **outbufs;
jack_default_audio_sample_t **postbufs;
jack_ringbuffer_t **postrings;
int *postptrs;
int postbuflen;
};
/* Old variable names */
#define portlist (this->hidden->portlist)
#define portcount (this->hidden->portcount)
#define client (this->hidden->client)
#define mixbuf (this->hidden->mixbuf)
#define mixlen (this->hidden->mixlen)
#define postbufs (this->hidden->postbufs)
#define postrings (this->hidden->postrings)
#define postptrs (this->hidden->postptrs)
#define outbufs (this->hidden->outbufs)
#define postbuflen (this->hidden->postbuflen)
#endif /* _JACK_PCM_audio_h */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment