Created
November 16, 2013 03:50
-
-
Save iamgreaser/7495699 to your computer and use it in GitHub Desktop.
JACK audio driver for SDL 1.2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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