1
0
mirror of http://mpg123.de/trunk/.git synced 2025-10-25 04:37:34 +03:00

Some audio handling rework, mainly to clean up the mess with the buffer.

The initial reason was to install safeguards against multiple ao->close() operations, which was done, too.
Now either the buffer or the main mpg123 process touch the audio devices; in the buffered case the main program querying the buffer process for audio capabilities.
That and some other potential bug(s) fixed.

This is too much movement in a release candidate, but it is necessary - we want some quality for 1.0!



git-svn-id: svn://scm.orgis.org/mpg123/trunk@1246 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
thor
2007-12-07 02:57:17 +00:00
parent c0e04d380a
commit 0eaece6663
5 changed files with 246 additions and 138 deletions

View File

@@ -21,6 +21,8 @@ audio_output_t* open_output_module( const char* name )
audio_output_t *ao = NULL; audio_output_t *ao = NULL;
int result = 0; int result = 0;
if(param.usebuffer || param.outmode != DECODE_AUDIO) return NULL;
/* Open the module */ /* Open the module */
module = open_module( "output", name ); module = open_module( "output", name );
if (module == NULL) return NULL; if (module == NULL) return NULL;
@@ -42,6 +44,7 @@ audio_output_t* open_output_module( const char* name )
/* Call the init function */ /* Call the init function */
ao->device = param.output_device; ao->device = param.output_device;
ao->flags = param.output_flags; ao->flags = param.output_flags;
ao->is_open = FALSE;
result = module->init_output(ao); result = module->init_output(ao);
if (result) { if (result) {
error1( "Module's init function failed: %d", result ); error1( "Module's init function failed: %d", result );
@@ -60,12 +63,12 @@ audio_output_t* open_output_module( const char* name )
/* Close the audio output and close the module */ /* Close the audio output and close the module */
void close_output_module( audio_output_t* ao ) void close_output_module( audio_output_t* ao )
{ {
if (!ao) return; if (!ao) return; /* That covers buffer mode, too (ao == NULL there). */
debug("closing output module"); debug("closing output module");
/* Close the audio output */ /* Close the audio output */
/* No: we do that before, twice hurts. if (ao->close) ao->close( ao );*/ if(ao->is_open && ao->close != NULL) ao->close(ao);
/* Deinitialise the audio output */ /* Deinitialise the audio output */
if (ao->deinit) ao->deinit( ao ); if (ao->deinit) ao->deinit( ao );
@@ -165,10 +168,16 @@ void print_capabilities(audio_output_t *ao, mpg123_handle *mh)
size_t num_rates; size_t num_rates;
const int *encs; const int *encs;
size_t num_encs; size_t num_encs;
const char *name = "<buffer>";
const char *dev = "<none>";
if(!param.usebuffer)
{
name = ao->module->name;
if(ao->device != NULL) dev = ao->device;
}
mpg123_rates(&rates, &num_rates); mpg123_rates(&rates, &num_rates);
mpg123_encodings(&encs, &num_encs); mpg123_encodings(&encs, &num_encs);
fprintf(stderr,"\nAudio driver: %s\nAudio device: %s\nAudio capabilities:\n(matrix of [S]tereo or [M]ono support for sample format and rate in Hz)\n |", fprintf(stderr,"\nAudio driver: %s\nAudio device: %s\nAudio capabilities:\n(matrix of [S]tereo or [M]ono support for sample format and rate in Hz)\n |", name, dev);
ao->module->name, ao->device != NULL ? ao->device : "<none>");
for(e=0;e<num_encs;e++) fprintf(stderr," %5s |",audio_encoding_name(encs[e], 0)); for(e=0;e<num_encs;e++) fprintf(stderr," %5s |",audio_encoding_name(encs[e], 0));
fprintf(stderr,"\n --------------------------------------------------------\n"); fprintf(stderr,"\n --------------------------------------------------------\n");
for(r=0; r<num_rates; ++r) capline(mh, rates[r]); for(r=0; r<num_rates; ++r) capline(mh, rates[r]);
@@ -178,13 +187,17 @@ void print_capabilities(audio_output_t *ao, mpg123_handle *mh)
fprintf(stderr,"\n"); fprintf(stderr,"\n");
} }
/* This uses the currently opened audio device, queries its caps.
In case of buffered playback, this works _once_ by querying the buffer for the caps before entering the main loop. */
void audio_capabilities(audio_output_t *ao, mpg123_handle *mh) void audio_capabilities(audio_output_t *ao, mpg123_handle *mh)
{ {
int fmts; int fmts;
int ri; int ri;
audio_output_t ao1 = *ao; /* a copy */ long rate;
int channels;
const long *rates; const long *rates;
size_t num_rates; size_t num_rates;
debug("audio_capabilities");
mpg123_rates(&rates, &num_rates); mpg123_rates(&rates, &num_rates);
if(param.outmode != DECODE_AUDIO) if(param.outmode != DECODE_AUDIO)
@@ -195,21 +208,39 @@ void audio_capabilities(audio_output_t *ao, mpg123_handle *mh)
mpg123_format_none(mh); /* Start with nothing. */ mpg123_format_none(mh); /* Start with nothing. */
/* If audio_open fails, the device is just not capable of anything... */ for(channels=1; channels<=2; channels++)
if(ao1.open(&ao1) < 0) error("failed to open audio device");
else
{
for(ao1.channels=1; ao1.channels<=2; ao1.channels++)
for(ri = param.force_rate>0 ? -1 : 0;ri<num_rates;ri++) for(ri = param.force_rate>0 ? -1 : 0;ri<num_rates;ri++)
{ {
ao1.rate = ri >= 0 ? rates[ri] : param.force_rate; rate = ri >= 0 ? rates[ri] : param.force_rate;
fmts = ao1.get_formats(&ao1); #ifndef NOXFERMEM
if(param.usebuffer)
{ /* Ask the buffer process. It is waiting for this. */
buffermem->rate = rate;
buffermem->channels = channels;
buffermem->format = 0; /* Just have it initialized safely. */
debug2("asking for formats for %liHz/%ich", rate, channels);
xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_AUDIOCAP);
xfermem_getcmd(buffermem->fd[XF_WRITER], TRUE);
fmts = buffermem->format;
}
else
#endif
{ /* Check myself. */
ao->rate = rate;
ao->channels = channels;
fmts = ao->get_formats(ao);
}
debug1("got formats: 0x%x", fmts);
if(fmts < 0) continue; if(fmts < 0) continue;
else mpg123_format(mh, ao1.rate, ao1.channels, fmts); else mpg123_format(mh, rate, channels, fmts);
}
ao1.close(&ao1);
} }
#ifndef NOXFERMEM
/* Buffer loop shall start normal operation now. */
if(param.usebuffer) xfermem_putcmd(buffermem->fd[XF_WRITER], XF_CMD_WAKEUP);
#endif
if(param.verbose > 1) print_capabilities(ao, mh); if(param.verbose > 1) print_capabilities(ao, mh);
} }
@@ -225,7 +256,7 @@ static void catch_child(void)
/* FIXME: Old output initialization code that needs updating */ /* FIXME: Old output initialization code that needs updating */
int init_output(audio_output_t *ao, mpg123_handle *mh) int init_output(audio_output_t **ao)
{ {
static int init_done = FALSE; static int init_done = FALSE;
@@ -269,56 +300,69 @@ int init_output(audio_output_t *ao, mpg123_handle *mh)
error("cannot fork!"); error("cannot fork!");
return -1; return -1;
case 0: /* child */ case 0: /* child */
/* oh, is that trouble here? well, buffer should actually be opened before loading tracks IMHO */ {
mpg123_close(mh); /* child doesn't need the input stream */ /* Buffer process handles all audio stuff itself. */
audio_output_t *bao = NULL; /* To be clear: That's the buffer's pointer. */
param.usebuffer = 0; /* The buffer doesn't use the buffer. */
/* Open audio output module */
if(param.outmode == DECODE_AUDIO)
{
bao = open_output_module(param.output_module);
if(!bao)
{
error("Failed to open audio output module.");
exit(1); /* communicate failure? */
}
}
if(open_output(bao) < 0)
{
error("Unable to open audio output.");
close_output_module(bao);
exit(2);
}
xfermem_init_reader (buffermem); xfermem_init_reader (buffermem);
buffer_loop (ao, &oldsigset); buffer_loop(bao, &oldsigset); /* Here the work happens. */
xfermem_done_reader (buffermem); xfermem_done_reader (buffermem);
xfermem_done (buffermem); xfermem_done (buffermem);
close_output(bao);
close_output_module(bao);
exit(0); exit(0);
}
default: /* parent */ default: /* parent */
xfermem_init_writer (buffermem); xfermem_init_writer (buffermem);
param.outmode = DECODE_BUFFER;
} }
} }
#endif #endif
/* Open audio if not decoding to buffer */ if(param.outmode == DECODE_AUDIO && !param.usebuffer)
switch(param.outmode) { { /* Only if I handle audio device output: Get that module. */
case DECODE_AUDIO: *ao = open_output_module(param.output_module);
if(ao->open(ao) < 0) { if(!ao)
error("failed to open audio device"); {
return 1; error("Failed to open audio output module");
return -1;
} }
break;
case DECODE_WAV:
wav_open(ao,param.filename);
break;
case DECODE_AU:
au_open(ao,param.filename);
break;
case DECODE_CDR:
cdr_open(ao,param.filename);
break;
} }
else *ao = NULL; /* That ensures we won't try to free it later... */
/* This has internal protection for buffer mode. */
if(open_output(*ao) < 0) return -1;
return 0; return 0;
} }
void flush_output(int outmode, audio_output_t *ao, unsigned char *bytes, size_t count) void flush_output(audio_output_t *ao, unsigned char *bytes, size_t count)
{ {
if(count) if(count)
{ {
switch(outmode) /* Error checks? */
if(param.usebuffer) xfermem_write(buffermem, bytes, count);
else
switch(param.outmode)
{ {
case DECODE_FILE:
write (OutputDescriptor, bytes, count);
break;
case DECODE_AUDIO: case DECODE_AUDIO:
ao->write(ao, bytes, count); ao->write(ao, bytes, count);
break; break;
case DECODE_BUFFER: case DECODE_FILE:
error("The buffer doesn't work like that... I shouldn't ever be getting here."); write(OutputDescriptor, bytes, count);
write (buffer_fd[1], bytes, count);
break; break;
case DECODE_WAV: case DECODE_WAV:
case DECODE_CDR: case DECODE_CDR:
@@ -326,21 +370,60 @@ void flush_output(int outmode, audio_output_t *ao, unsigned char *bytes, size_t
wav_write(bytes, count); wav_write(bytes, count);
break; break;
} }
count = 0;
} }
} }
/* is this used? */ int open_output(audio_output_t *ao)
void close_output(int outmode, audio_output_t *ao)
{ {
debug("closing output"); if(param.usebuffer) return 0;
switch(outmode) {
switch(param.outmode)
{
case DECODE_AUDIO: case DECODE_AUDIO:
ao->close(ao); if(ao == NULL)
/* Module frees and closes its resources, but may not reset them. */ {
ao->userptr = NULL; error("ao should not be NULL here!");
ao->fn = -1; exit(110);
}
debug3("ao=%p, ao->is_open=%i, ao->open=%p", ao, ao->is_open, ao->open);
ao->is_open = ao->open(ao) < 0 ? FALSE : TRUE;
if(!ao->is_open)
{
error("failed to open audio device");
return -1;
}
else return 0;
break; break;
case DECODE_WAV:
return wav_open(ao,param.filename);
break;
case DECODE_AU:
return au_open(ao,param.filename);
break;
case DECODE_CDR:
return cdr_open(ao,param.filename);
break;
}
return -1; /* That's an error ... unknown outmode? */
}
/* is this used? */
void close_output(audio_output_t *ao)
{
if(param.usebuffer) return;
debug("closing output");
switch(param.outmode)
{
case DECODE_AUDIO:
/* Guard that close call; could be nasty. */
if(ao->is_open)
{
ao->is_open = FALSE;
if(ao->close != NULL) ao->close(ao);
}
break;
/* These are safe to be called too often. */
case DECODE_WAV: case DECODE_WAV:
wav_close(); wav_close();
break; break;
@@ -351,7 +434,6 @@ void close_output(int outmode, audio_output_t *ao)
cdr_close(); cdr_close();
break; break;
} }
} }
/* Also for WAV decoding? */ /* Also for WAV decoding? */
@@ -359,8 +441,8 @@ int reset_output(audio_output_t *ao)
{ {
if(param.outmode == DECODE_AUDIO) if(param.outmode == DECODE_AUDIO)
{ {
close_output(param.outmode, ao); close_output(ao);
return ao->open(ao); return open_output(ao);
} }
else return 0; else return 0;
} }

View File

@@ -57,6 +57,7 @@ typedef struct audio_output_struct
long gain; /* output gain */ long gain; /* output gain */
int channels; /* number of channels */ int channels; /* number of channels */
int format; /* format flags */ int format; /* format flags */
int is_open; /* something opened? */
} audio_output_t; } audio_output_t;
struct audio_format_name { struct audio_format_name {
@@ -75,9 +76,10 @@ void audio_capabilities(audio_output_t *ao, mpg123_handle *mh);
int audio_fit_capabilities(audio_output_t *ao,int c,int r); int audio_fit_capabilities(audio_output_t *ao,int c,int r);
const char* audio_encoding_name(const int encoding, const int longer); const char* audio_encoding_name(const int encoding, const int longer);
int init_output(audio_output_t *ao, mpg123_handle *mh); int init_output(audio_output_t **ao);
void flush_output(int outmode, audio_output_t *ao, unsigned char *bytes, size_t count); void flush_output(audio_output_t *ao, unsigned char *bytes, size_t count);
void close_output(int mod, audio_output_t *ao ); int open_output(audio_output_t *ao);
void close_output(audio_output_t *ao );
int reset_output(audio_output_t *ao); int reset_output(audio_output_t *ao);
#endif #endif

View File

@@ -104,10 +104,34 @@ void buffer_loop(audio_output_t *ao, sigset_t *oldsigset)
catchsignal (SIGINT, catch_interrupt); catchsignal (SIGINT, catch_interrupt);
catchsignal (SIGUSR1, catch_usr1); catchsignal (SIGUSR1, catch_usr1);
sigprocmask (SIG_SETMASK, oldsigset, NULL); sigprocmask (SIG_SETMASK, oldsigset, NULL);
if (param.outmode == DECODE_AUDIO) {
if (ao->open(ao) < 0) { if(param.outmode == DECODE_AUDIO)
perror("audio"); {
exit(1); debug("audio output: waiting for cap requests");
/* wait for audio setup queries */
while(1)
{
int cmd;
cmd = xfermem_block(XF_READER, xf);
if(cmd == XF_CMD_AUDIOCAP)
{
ao->rate = xf->rate;
ao->channels = xf->channels;
ao->format = ao->get_formats(ao);
debug3("formats for %liHz/%ich: 0x%x", ao->rate, ao->channels, ao->format);
xf->format = ao->format;
xfermem_putcmd(my_fd, XF_CMD_AUDIOCAP);
}
else if(cmd == XF_CMD_WAKEUP)
{
debug("got wakeup... leaving config mode");
break;
}
else
{
error1("unexpected command %i", cmd);
return;
}
} }
} }
@@ -141,9 +165,9 @@ void buffer_loop(audio_output_t *ao, sigset_t *oldsigset)
*/ */
if (xf->wakeme[XF_WRITER]) if (xf->wakeme[XF_WRITER])
xfermem_putcmd(my_fd, XF_CMD_WAKEUP); xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
ao->rate = xf->buf[0]; ao->rate = xf->rate;
ao->channels = xf->buf[1]; ao->channels = xf->channels;
ao->format = xf->buf[2]; ao->format = xf->format;
if (reset_output(ao) < 0) { if (reset_output(ao) < 0) {
error1("failed to reset audio: %s", strerror(errno)); error1("failed to reset audio: %s", strerror(errno));
exit(1); exit(1);
@@ -188,10 +212,10 @@ void buffer_loop(audio_output_t *ao, sigset_t *oldsigset)
done=TRUE; done=TRUE;
break; break;
case -1: case -1:
if(errno==EINTR) if(errno==EINTR) /* Got signal, handle it at top of loop... */
continue; continue;
if(errno) if(errno)
perror("Yuck! Error in buffer handling..."); perror("Yuck! Error in buffer handling... or somewhere unexpected.");
done = TRUE; done = TRUE;
xf->readindex = xf->freeindex; xf->readindex = xf->freeindex;
xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE); xfermem_putcmd(xf->fd[XF_READER], XF_CMD_TERMINATE);
@@ -213,6 +237,7 @@ void buffer_loop(audio_output_t *ao, sigset_t *oldsigset)
if (bytes > outburst) if (bytes > outburst)
bytes = outburst; bytes = outburst;
/* Could change that to use flush_output.... need to capture return value, then. */
if (param.outmode == DECODE_FILE) if (param.outmode == DECODE_FILE)
bytes = write(OutputDescriptor, xf->data + xf->readindex, bytes); bytes = write(OutputDescriptor, xf->data + xf->readindex, bytes);
else if (param.outmode == DECODE_AUDIO) else if (param.outmode == DECODE_AUDIO)
@@ -242,9 +267,6 @@ void buffer_loop(audio_output_t *ao, sigset_t *oldsigset)
if (xf->wakeme[XF_WRITER]) if (xf->wakeme[XF_WRITER])
xfermem_putcmd(my_fd, XF_CMD_WAKEUP); xfermem_putcmd(my_fd, XF_CMD_WAKEUP);
} }
if (param.outmode == DECODE_AUDIO)
ao->close(ao);
} }
#endif #endif

View File

@@ -400,7 +400,7 @@ topt opts[] = {
* Change the playback sample rate. * Change the playback sample rate.
* Consider that changing it after starting playback is not covered by gapless code! * Consider that changing it after starting playback is not covered by gapless code!
*/ */
static void reset_audio(void) static void reset_audio(long rate, int channels, int format)
{ {
#ifndef NOXFERMEM #ifndef NOXFERMEM
if (param.usebuffer) { if (param.usebuffer) {
@@ -418,9 +418,9 @@ static void reset_audio(void)
buffermem->freeindex = 0; buffermem->freeindex = 0;
if (intflag) if (intflag)
return; return;
buffermem->buf[0] = ao->rate; buffermem->rate = rate;
buffermem->buf[1] = ao->channels; buffermem->channels = channels;
buffermem->buf[2] = ao->format; buffermem->format = format;
buffer_reset(); buffer_reset();
} }
else else
@@ -431,6 +431,14 @@ static void reset_audio(void)
* the device's internal buffer before * the device's internal buffer before
* changing the sample rate. [OF] * changing the sample rate. [OF]
*/ */
if(ao == NULL)
{
error("Audio handle should not be NULL here!");
safe_exit(98);
}
ao->rate = rate;
ao->channels = channels;
ao->format = format;
if(reset_output(ao) < 0) if(reset_output(ao) < 0)
{ {
error1("failed to reset audio device: %s", strerror(errno)); error1("failed to reset audio device: %s", strerror(errno));
@@ -507,16 +515,8 @@ int play_frame(void)
if(param.verbose) print_header(mh); if(param.verbose) print_header(mh);
else print_header_compact(mh); else print_header_compact(mh);
} }
#ifndef NOXFERMEM /* Normal flushing of data, includes buffer decoding. */
if(param.usebuffer) flush_output(ao, audio, bytes);
{ /* Instead of directly decoding to buffer, we copy explicitly here and handle the wrapping!
Just giving libmpg123 the buffermem is no good idea since it doesn't know about the ringbuffer. */
if(xfermem_write(buffermem, audio, bytes)) return 1;
}
else
#endif
/* Normal flushing of data. */
flush_output(param.outmode, ao, audio, bytes);
if(param.checkrange) if(param.checkrange)
{ {
long clip = mpg123_clip(mh); long clip = mpg123_clip(mh);
@@ -538,20 +538,12 @@ int play_frame(void)
} }
if(mc == MPG123_NEW_FORMAT) if(mc == MPG123_NEW_FORMAT)
{ {
int ret = 0; long rate;
mpg123_getformat(mh, &ao->rate, &ao->channels, &ao->format); int channels, format;
ret = init_output(ao, mh); /* This has to become an abstraction to defer the action to the buffer process if there! */ mpg123_getformat(mh, &rate, &channels, &format);
if(ret == 1) if(param.verbose > 2) fprintf(stderr, "Note: New output format %liHz %ich, format %i\n", rate, channels, format);
{
warning("I am not sure if my code is ready to switch audio format during playback!"); reset_audio(rate, channels, format);
reset_audio();
/* safe_exit(-1); */
}
if(ret < 0)
{
error1("init_output() returned %i!", ret);
safe_exit(1);
}
} }
} }
return 1; return 1;
@@ -582,14 +574,16 @@ int main(int argc, char *argv[])
break; break;
} }
} }
httpdata_init(&htd);
/* Need to initialize mpg123 lib here for default parameter values. */
result = mpg123_init(); result = mpg123_init();
if(result != MPG123_OK) if(result != MPG123_OK)
{ {
error1("Cannot initialize mpg123 library: %s", mpg123_plain_strerror(result)); error1("Cannot initialize mpg123 library: %s", mpg123_plain_strerror(result));
safe_exit(77); exit(77);
} }
mp = mpg123_new_pars(&result); mp = mpg123_new_pars(&result); /* This may get leaked on premature exit(), which is mainly a cosmetic issue... */
if(mp == NULL) if(mp == NULL)
{ {
error1("Crap! Cannot get mpg123 parameters: %s", mpg123_plain_strerror(result)); error1("Crap! Cannot get mpg123 parameters: %s", mpg123_plain_strerror(result));
@@ -609,7 +603,6 @@ int main(int argc, char *argv[])
#endif #endif
mpg123_getpar(mp, MPG123_FLAGS, &parr, NULL); mpg123_getpar(mp, MPG123_FLAGS, &parr, NULL);
param.flags = (int) parr; param.flags = (int) parr;
bufferblock = mpg123_safe_buffer();
#ifdef OS2 #ifdef OS2
_wildcard(&argc,&argv); _wildcard(&argc,&argv);
@@ -629,6 +622,23 @@ int main(int argc, char *argv[])
usage(1); usage(1);
} }
/* Init audio as early as possible.
If there is the buffer process to be spawned, it shouldn't carry the mpg123_handle with it. */
bufferblock = mpg123_safe_buffer(); /* Can call that before mpg123_init(), it's stateless. */
if(init_output(&ao) < 0)
{
error("Failed to initialize output, goodbye.");
mpg123_delete_pars(mp);
exit(99); /* It's safe here... nothing nasty happened yet. */
}
/* ========================================================================================================= */
/* Enterning the leaking zone... we start messing with stuff here that should be taken care of when leaving. */
/* Don't just exit() or return out... */
/* ========================================================================================================= */
httpdata_init(&htd);
if(param.list_cpu) if(param.list_cpu)
{ {
char **all_dec = mpg123_decoders(); char **all_dec = mpg123_decoders();
@@ -701,14 +711,8 @@ int main(int argc, char *argv[])
} }
mpg123_delete_pars(mp); /* Don't need the parameters anymore ,they're in the handle now. */ mpg123_delete_pars(mp); /* Don't need the parameters anymore ,they're in the handle now. */
/* Open audio output module */ /* Now either check caps myself or query buffer for that. */
ao = open_output_module( param.output_module ); audio_capabilities(ao, mh);
if (!ao) {
error("Failed to open audio output module.");
safe_exit(1);
}
audio_capabilities(ao, mh); /* Query audio output parameters, store in mpg123 handle. */
if(equalfile != NULL) if(equalfile != NULL)
{ /* tst; ThOr: not TRUE or FALSE: allocated or not... */ { /* tst; ThOr: not TRUE or FALSE: allocated or not... */
@@ -768,7 +772,7 @@ int main(int argc, char *argv[])
if(param.remote) { if(param.remote) {
int ret; int ret;
ret = control_generic(mh); ret = control_generic(mh);
close_output(param.outmode, ao); close_output(ao);
close_output_module(ao); close_output_module(ao);
safe_exit(ret); safe_exit(ret);
} }
@@ -855,8 +859,6 @@ tc_hack:
#ifndef NOXFERMEM #ifndef NOXFERMEM
if (param.verbose > 1 || !(framenum & 0x7)) if (param.verbose > 1 || !(framenum & 0x7))
print_stat(mh,0,xfermem_get_usedspace(buffermem)); print_stat(mh,0,xfermem_get_usedspace(buffermem));
if(param.verbose > 2 && param.usebuffer)
fprintf(stderr,"[%08x %08x]", (unsigned int)buffermem->readindex, (unsigned int)buffermem->freeindex);
#else #else
if(param.verbose > 1 || !(framenum & 0x7)) print_stat(mh,0,0); if(param.verbose > 1 || !(framenum & 0x7)) print_stat(mh,0,0);
#endif #endif
@@ -958,12 +960,9 @@ tc_hack:
xfermem_done (buffermem); xfermem_done (buffermem);
} }
#endif #endif
/* Close the output */ /* Close the output... doesn't matter if buffer handled it, that's taken care of. */
close_output(param.outmode, ao); close_output(ao);
close_output_module(ao);
/* Close the audio output module */
close_output_module( ao );
/* Free up memory used by playlist */ /* Free up memory used by playlist */
if(!param.remote) free_playlist(); if(!param.remote) free_playlist();
safe_exit(0); safe_exit(0);

View File

@@ -34,7 +34,9 @@ typedef struct {
byte *metadata; byte *metadata;
size_t size; size_t size;
size_t metasize; size_t metasize;
int buf[3]; long rate;
int channels;
int format;
} txfermem; } txfermem;
/* /*
* [W] -- May be written to by the writing process only! * [W] -- May be written to by the writing process only!
@@ -51,6 +53,7 @@ size_t xfermem_get_usedspace (txfermem *xf);
#define XF_CMD_WAKEUP_INFO 0x04 #define XF_CMD_WAKEUP_INFO 0x04
#define XF_CMD_WAKEUP 0x02 #define XF_CMD_WAKEUP 0x02
#define XF_CMD_TERMINATE 0x03 #define XF_CMD_TERMINATE 0x03
#define XF_CMD_AUDIOCAP 0x05
#define XF_WRITER 0 #define XF_WRITER 0
#define XF_READER 1 #define XF_READER 1
int xfermem_getcmd (int fd, int block); int xfermem_getcmd (int fd, int block);