diff options
author | taw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5> | 2008-07-03 18:19:59 +0000 |
---|---|---|
committer | taw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5> | 2008-07-03 18:19:59 +0000 |
commit | e70c21ed62fe20c7e7bcfa323ead4ebc0a513340 (patch) | |
tree | fd342366429db77e202d24657e7d2e75944ebc6a /src/audio.c | |
parent | e240f6fc63cdb83fd8ea4bb3931a3ca7ef4b207f (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.c | 142 |
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); + } |