diff options
author | taw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5> | 2008-07-22 17:49:31 +0000 |
---|---|---|
committer | taw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5> | 2008-07-22 17:49:31 +0000 |
commit | 8c2f7a4c43d8fca38beb9a2adc68afd662946b12 (patch) | |
tree | 08037ee180197d4fba8cc7f6794c03e8258c3fc4 /src/audio.c | |
parent | 91a39241d1a2e5eadd2f84075a90962420a81064 (diff) |
Progressive Vorbis decoding
git-svn-id: svn://cook.msm.cam.ac.uk:745/thrust3d/thrust3d@153 84d2e878-0bd5-11dd-ad15-13eda11d74c5
Diffstat (limited to 'src/audio.c')
-rw-r--r-- | src/audio.c | 114 |
1 files changed, 76 insertions, 38 deletions
diff --git a/src/audio.c b/src/audio.c index f0c0e4f..bc66d83 100644 --- a/src/audio.c +++ b/src/audio.c @@ -21,6 +21,7 @@ #include <vorbis/vorbisfile.h> #include <vorbis/codec.h> #include <pthread.h> +#include <unistd.h> #include "types.h" @@ -171,7 +172,7 @@ static void *audio_play_vorbis(void *add_void) { int len; OggVorbis_File vf; vorbis_info *vi; - int finished, err, current_section; + int finished, err, current_section, decode_done, decode_samples_done; size_t offs; SDL_AudioCVT cvt; @@ -200,55 +201,90 @@ static void *audio_play_vorbis(void *add_void) { if ( a->debug ) printf("AU: Channel %i: Length is %i samples 'per channel'\n", idx, len); 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 */ + + /* Decode ten seconds of audio every 8 seconds until the job's done */ + if ( a->debug ) printf("AU: Channel %i: Started decoding Vorbis stream\n", idx); + /* Do this now because cvt.len_mult is needed to work out the size of 'data' */ + SDL_BuildAudioCVT(&cvt, AUDIO_S16, vi->channels, vi->rate, AUDIO_S16, 2, 44100); + data = malloc(10*vi->rate*2*vi->channels*cvt.len_mult); if ( data == NULL ) { fprintf(stderr, "Not enough memory to decode Vorbis stream (channel %i)\n", idx); a->sounds[idx].inuse = 0; ov_clear(&vf); goto out; } - - offs = 0; - finished = 0; - if ( a->debug ) printf("AU: Channel %i: Decoding Vorbis stream...\n", idx); - while ( finished == 0 ) { - long rval; - rval = ov_read(&vf, data+offs, 2*2*len, 0, 2, 1, ¤t_section); - if ( rval == 0 ) { - finished = 1; - } else if ( rval < 0 ) { - fprintf(stderr, "Vorbis stream error (channel %i)\n", idx); - } else { - offs += rval; + decode_done = 0; + decode_samples_done = 0; + a->sounds[idx].decode_pos = 0; /* Position (in sounds[idx].data) at which to write the next block */ + while ( !decode_done ) { + + long decode_block_samples; + long decode_block_length; + int w; + + /* Decide how much data to shovel this time */ + decode_block_samples = 10*vi->rate; /* 10 seconds in samples */ + if ( decode_samples_done + decode_block_samples > len ) { + /* This is the last block. Yay! */ + decode_block_samples = len - decode_samples_done; + decode_done = 1; } + if ( a->debug ) printf("AU: Channel %i: Decoding %li samples this time\n", idx, decode_block_samples); + decode_block_length = vi->channels*2*decode_block_samples; + decode_samples_done += decode_block_samples; + + /* Read the chosen amount of data */ + offs = 0; finished = 0; + while ( finished == 0 ) { + long rval; + rval = ov_read(&vf, data+offs, decode_block_length-offs, 0, 2, 1, ¤t_section); + if ( rval < 0 ) { + fprintf(stderr, "Vorbis stream error (channel %i)\n", idx); + } else { + offs += rval; + } + if ( offs == decode_block_length ) finished = 1; + } + + /* Convert to 44.1 kHz */ + cvt.buf = (Uint8 *)data; + cvt.len = decode_block_length; + SDL_ConvertAudio(&cvt); + + /* Paste this seamlessly into the playing thread's buffer */ + if ( !a->sounds[idx].playing ) { + a->sounds[idx].data = malloc(len*vi->channels*2); + } + memcpy(a->sounds[idx].data+a->sounds[idx].decode_pos, cvt.buf, cvt.len_cvt); + /* I have ABSOLUTELY NO IDEA why the division by two is needed. Anyone? */ + a->sounds[idx].decode_pos += cvt.len_cvt / 2; + + /* It's safe to start playing at this point */ + if ( !a->sounds[idx].playing ) { + a->sounds[idx].dlen = vi->channels*(len-1); + a->sounds[idx].dpos = 0; + 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 */ + } + + /* Sleep for eight seconds while periodically checking for shutdown */ + for ( w=0; w<80; w++ ) { + if ( a->shutdown ) { + ov_clear(&vf); + free(data); + goto out; + } + usleep(100000); /* 0.1 seconds */ + } + } - - /* 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); - free(cvt.buf); - a->sounds[idx].inuse = 0; - goto out; - } - memcpy(cvt.buf, data, vi->channels*2*len); - cvt.len = vi->channels*2*len; - SDL_ConvertAudio(&cvt); + if ( a->debug ) printf("AU: Channel %i: Finished decoding the Vorbis stream.\n", idx); free(data); /* Needed until now */ ov_clear(&vf); - - /* Put the sound data in the slot */ - a->sounds[idx].data = (Sint16 *)cvt.buf; - /* Assuming that the same dud last (stereo) sample occurs here. This may not be the case. */ - a->sounds[idx].dlen = cvt.len_cvt / 2 - 2; - a->sounds[idx].dpos = 0; - 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 */ - + out: free(add->filename); if ( a->debug ) printf("AU: Channel %i: Vorbis dispatch thread completed.\n", idx); @@ -383,6 +419,7 @@ AudioContext *audio_setup(int debug, int no_music) { a->startup = 1; a->startup_volume = 0.0; a->paused = 1; + a->shutdown = 0; pthread_mutex_init(&a->sounds_mutex, NULL); for ( i=0; i<AUDIO_MAX_SOUNDS; i++ ) { a->sounds[i].inuse = 0; @@ -424,6 +461,7 @@ void audio_shutdown(AudioContext *a) { if ( a == NULL ) return; + a->shutdown = 1; SDL_CloseAudio(); /* Wait for dispatch threads */ |