aboutsummaryrefslogtreecommitdiff
path: root/src/audio.c
diff options
context:
space:
mode:
authortaw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5>2008-07-03 18:19:59 +0000
committertaw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5>2008-07-03 18:19:59 +0000
commite70c21ed62fe20c7e7bcfa323ead4ebc0a513340 (patch)
treefd342366429db77e202d24657e7d2e75944ebc6a /src/audio.c
parente240f6fc63cdb83fd8ea4bb3931a3ca7ef4b207f (diff)
Thread safety stuff
git-svn-id: svn://cook.msm.cam.ac.uk:745/thrust3d/thrust3d@126 84d2e878-0bd5-11dd-ad15-13eda11d74c5
Diffstat (limited to 'src/audio.c')
-rw-r--r--src/audio.c142
1 files changed, 124 insertions, 18 deletions
diff --git a/src/audio.c b/src/audio.c
index bf8343b..d252086 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -95,7 +95,11 @@ static void audio_mix(void *data, Uint8 *stream8, int len) {
}
-static void audio_play_wav(AudioContext *a, char *filename, float volume, int repeat) {
+static void *audio_play_wav(void *add_void) {
+
+ AudioDispatchData *add = add_void;
+ AudioContext *a = add->audiocontext;
+ char *filename = add->filename;
int idx;
SDL_AudioSpec wave;
@@ -104,37 +108,47 @@ static void audio_play_wav(AudioContext *a, char *filename, float volume, int re
SDL_AudioCVT cvt;
/* Look for an empty sound slot */
+ pthread_mutex_lock(&a->sounds_mutex);
for ( idx=0; idx<AUDIO_MAX_SOUNDS; idx++ ) {
if ( !a->sounds[idx].inuse ) break;
}
if ( idx == AUDIO_MAX_SOUNDS ) {
fprintf(stderr, "Not enough audio channels to play '%s'\n", filename);
- return;
+ return NULL;
}
a->sounds[idx].inuse = 1;
+ pthread_mutex_unlock(&a->sounds_mutex);
if ( a->debug ) printf("AU: Channel %i: Selected this channel for sound '%s'\n", idx, filename);
/* Load the sound file and convert it to 16-bit stereo at 44.1 kHz */
if ( SDL_LoadWAV(filename, &wave, &data, &dlen) == NULL ) {
fprintf(stderr, "Couldn't load %s: (channel %i) %s\n", filename, idx, SDL_GetError());
- return;
+ return NULL;
}
SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16, 2, 44100);
cvt.buf = malloc(dlen*cvt.len_mult);
+ if ( cvt.buf == NULL ) {
+ fprintf(stderr, "Not enough memory to convert audio (channel %i)\n", idx);
+ }
memcpy(cvt.buf, data, dlen);
cvt.len = dlen;
SDL_ConvertAudio(&cvt);
SDL_FreeWAV(data);
/* Put the sound data in the slot */
- SDL_LockAudio();
a->sounds[idx].data = (Sint16 *)cvt.buf;
a->sounds[idx].dlen = cvt.len_cvt / 2;
a->sounds[idx].dpos = 0;
- a->sounds[idx].playing = 1;
- a->sounds[idx].repeat = repeat;
- a->sounds[idx].volume = volume;
- SDL_UnlockAudio();
+ a->sounds[idx].repeat = add->repeat;
+ a->sounds[idx].volume = add->volume;
+ a->sounds[idx].playing = 1; /* Must be done last - tell the mixer thread it can use this */
+
+ free(add->filename);
+ if ( a->debug ) printf("AU: Channel %i: WAV dispatch thread completed.\n", idx);
+ a->dispatch_threads_inuse[add->idx] = 0;
+ free(add);
+
+ return NULL;
}
@@ -154,6 +168,7 @@ static void *audio_play_vorbis(void *add_void) {
SDL_AudioCVT cvt;
/* Look for an empty sound slot */
+ pthread_mutex_lock(&a->sounds_mutex);
for ( idx=0; idx<AUDIO_MAX_SOUNDS; idx++ ) {
if ( !a->sounds[idx].inuse ) break;
}
@@ -162,6 +177,7 @@ static void *audio_play_vorbis(void *add_void) {
return NULL;
}
a->sounds[idx].inuse = 1;
+ pthread_mutex_unlock(&a->sounds_mutex);
if ( a->debug ) printf("AU: Channel %i: Selected this channel for sound '%s'\n", idx, filename);
err = ov_fopen(filename, &vf);
@@ -175,6 +191,9 @@ static void *audio_play_vorbis(void *add_void) {
vi = ov_info(&vf,-1);
if ( a->debug ) printf("AU: Channel %i: %i channels, %li Hz\n", idx, vi->channels, vi->rate);
data = malloc(vi->channels*2*len); /* Two bytes per sample per channel */
+ if ( data == NULL ) {
+ fprintf(stderr, "Not enough memory to decode Vorbis stream (channel %i)\n", idx);
+ }
offs = 0;
finished = 0;
@@ -194,25 +213,29 @@ static void *audio_play_vorbis(void *add_void) {
/* Convert to 44.1 kHz */
SDL_BuildAudioCVT(&cvt, AUDIO_S16, vi->channels, vi->rate, AUDIO_S16, 2, 44100);
cvt.buf = malloc(vi->channels*2*len*cvt.len_mult);
+ if ( cvt.buf == NULL ) {
+ fprintf(stderr, "Not enough memory to convert audio (channel %i)\n", idx);
+ }
memcpy(cvt.buf, data, vi->channels*2*len);
cvt.len = vi->channels*2*len;
SDL_ConvertAudio(&cvt);
+ free(data);
ov_clear(&vf);
/* Put the sound data in the slot */
- SDL_LockAudio();
a->sounds[idx].data = (Sint16 *)cvt.buf;
a->sounds[idx].dlen = cvt.len_cvt / 2;
a->sounds[idx].dpos = 0;
- a->sounds[idx].playing = 1;
a->sounds[idx].repeat = add->repeat;
a->sounds[idx].volume = add->volume;
- SDL_UnlockAudio();
+ a->sounds[idx].playing = 1; /* Must be done last - tell the mixer thread it can use this */
free(add->filename);
+ if ( a->debug ) printf("AU: Channel %i: Vorbis dispatch thread completed.\n", idx);
+ a->dispatch_threads_inuse[add->idx] = 0;
free(add);
- if ( a->debug ) printf("AU: Channel %i: audio_play_vorbis dispatch thread completed.\n", idx);
+
return NULL;
}
@@ -226,18 +249,40 @@ void audio_play(AudioContext *a, char *name, float volume, int repeat) {
snprintf(filename, 127, "%s%s.ogg", DATADIR"/sound/", name);
if ( stat(filename, &statbuf) == 0 ) {
- int rval;
+ int rval, idx;
AudioDispatchData *add;
- pthread_t thread;
+ pthread_attr_t attr;
add = malloc(sizeof(AudioDispatchData));
add->audiocontext = a;
add->filename = strdup(filename);
add->volume = volume;
add->repeat = repeat;
- rval = pthread_create(&thread, NULL, audio_play_vorbis, add);
+
+ /* Claim a dispatch thread slot */
+ for ( idx=0; idx<AUDIO_MAX_SOUNDS; idx++ ) {
+ if ( !a->dispatch_threads_inuse[idx] ) break;
+ }
+ if ( idx == AUDIO_MAX_SOUNDS ) {
+ fprintf(stderr, "Not enough dispatch thread space to play '%s'\n", filename);
+ free(add->filename);
+ free(add);
+ return;
+ }
+ a->dispatch_threads_inuse[idx] = 1;
+ add->idx = idx;
+
+ /* Start the dispatch thread */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ rval = pthread_create(&a->dispatch_threads[idx], &attr, audio_play_vorbis, add);
+ pthread_attr_destroy(&attr);
if ( rval != 0 ) {
fprintf(stderr, "Couldn't create audio dispatch thread (%i)\n", rval);
+ free(add->filename);
+ free(add);
+ a->dispatch_threads_inuse[idx] = 0;
+ return;
}
goto done;
@@ -247,8 +292,45 @@ void audio_play(AudioContext *a, char *name, float volume, int repeat) {
/* Try to find a WAV file */
snprintf(filename, 127, "%s%s.wav", DATADIR"/sound/", name);
if ( stat(filename, &statbuf) == 0 ) {
- audio_play_wav(a, filename, volume, repeat);
+
+ int rval, idx;
+ AudioDispatchData *add;
+ pthread_attr_t attr;
+
+ add = malloc(sizeof(AudioDispatchData));
+ add->audiocontext = a;
+ add->filename = strdup(filename);
+ add->volume = volume;
+ add->repeat = repeat;
+
+ /* Claim a dispatch thread slot */
+ for ( idx=0; idx<AUDIO_MAX_SOUNDS; idx++ ) {
+ if ( !a->dispatch_threads_inuse[idx] ) break;
+ }
+ if ( idx == AUDIO_MAX_SOUNDS ) {
+ fprintf(stderr, "Not enough dispatch thread space to play '%s'\n", filename);
+ free(add->filename);
+ free(add);
+ return;
+ }
+ a->dispatch_threads_inuse[idx] = 1;
+ add->idx = idx;
+
+ /* Start the dispatch thread */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ rval = pthread_create(&a->dispatch_threads[idx], &attr, audio_play_wav, add);
+ pthread_attr_destroy(&attr);
+ if ( rval != 0 ) {
+ fprintf(stderr, "Couldn't create audio dispatch thread (%i)\n", rval);
+ free(add->filename);
+ free(add);
+ a->dispatch_threads_inuse[idx] = 0;
+ return;
+ }
+
goto done;
+
}
/* Still not found */
@@ -280,9 +362,11 @@ AudioContext *audio_setup(int debug) {
a->startup = 1;
a->startup_volume = 0.0;
a->paused = 1;
+ pthread_mutex_init(&a->sounds_mutex, NULL);
for ( i=0; i<AUDIO_MAX_SOUNDS; i++ ) {
a->sounds[i].inuse = 0;
a->sounds[i].playing = 0;
+ a->dispatch_threads[i] = 0;
}
/* 16-bit stereo audio at 44.1 kHz */
@@ -307,8 +391,30 @@ AudioContext *audio_setup(int debug) {
}
-void audio_shutdown(AudioContext *ctx) {
+void audio_shutdown(AudioContext *a) {
+
+ int i;
+
SDL_CloseAudio();
- free(ctx);
+
+ /* Wait for dispatch threads */
+ for ( i=0; i<AUDIO_MAX_SOUNDS; i++ ) {
+ if ( a->dispatch_threads_inuse[i] ) {
+ pthread_join(a->dispatch_threads[i], NULL);
+ }
+ }
+
+ /* Now that all the dispatch threads are done, and the mixer callback
+ is no longer getting called, the sound system is in a consistent
+ state. i.e. 'inuse' means memory is allocated and can be freed */
+ for ( i=0; i<AUDIO_MAX_SOUNDS; i++ ) {
+ if ( a->sounds[i].inuse ) {
+ free(a->sounds[i].data);
+ }
+ }
+
+ /* Now this can be freed */
+ free(a);
+
}