aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortaw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5>2008-07-22 17:49:31 +0000
committertaw27 <taw27@84d2e878-0bd5-11dd-ad15-13eda11d74c5>2008-07-22 17:49:31 +0000
commit8c2f7a4c43d8fca38beb9a2adc68afd662946b12 (patch)
tree08037ee180197d4fba8cc7f6794c03e8258c3fc4 /src
parent91a39241d1a2e5eadd2f84075a90962420a81064 (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')
-rw-r--r--src/audio.c114
-rw-r--r--src/types.h6
2 files changed, 80 insertions, 40 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, &current_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, &current_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 */
diff --git a/src/types.h b/src/types.h
index bfbe36a..950d26e 100644
--- a/src/types.h
+++ b/src/types.h
@@ -160,12 +160,13 @@ typedef struct {
struct sound {
Sint16 *data;
- int dpos;
- int dlen;
+ long dpos;
+ long dlen;
int inuse;
int playing;
int repeat;
float volume;
+ size_t decode_pos;
} sounds[AUDIO_MAX_SOUNDS];
pthread_mutex_t sounds_mutex; /* Mutex for the list of sounds being played */
@@ -176,6 +177,7 @@ typedef struct {
int startup;
float startup_volume;
int paused;
+ int shutdown;
} AudioContext;