From c7a42a0f2c59cefa04ed1e6c69fd330ed22ea8dd Mon Sep 17 00:00:00 2001 From: thor Date: Sun, 17 Mar 2013 14:01:07 +0000 Subject: [PATCH] Another go at the wretched WAV writing. git-svn-id: svn://scm.orgis.org/mpg123/trunk@3296 35dc7657-300d-0410-a2e5-dc2837fedb53 --- NEWS | 5 +++++ configure.ac | 2 +- man1/mpg123.1 | 4 ++-- src/wav.c | 44 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index e824cfe1..bedf3dd3 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +1.16.0 +--- +- upcoming: kickass optimizations by Taihei +- Fix WAV writing. AGAIN. People love to decode WAV to stdout in various ways that started to get broken with me starting to care for people who like to write to full disks. I frikkin' HATE this rat race! I'm even starting to SHOUT. Now, the code prefers to write no header at all (when there is no actual output) instead of possibly writing too many misleading ones. Getting the blame for breaking dir2ogg repeatedly while trying to cope with one fatal situation that you cannot really cope with sucks. Big time. I hope that's it now! + 1.15.2 --- - build fix with older shells (== in configure) diff --git a/configure.ac b/configure.ac index 799c8acf..81be597d 100644 --- a/configure.ac +++ b/configure.ac @@ -8,7 +8,7 @@ dnl Require autoconf version >= 2.57 AC_PREREQ(2.57) dnl ############# Initialisation -AC_INIT([mpg123], [1.15.2], [mpg123-devel@lists.sourceforge.net]) +AC_INIT([mpg123], [1.16.0], [mpg123-devel@lists.sourceforge.net]) dnl Increment API_VERSION when the API gets changes (new functions). API_VERSION=37 LIB_PATCHLEVEL=1 diff --git a/man1/mpg123.1 b/man1/mpg123.1 index 6fdb1012..90cb8f19 100644 --- a/man1/mpg123.1 +++ b/man1/mpg123.1 @@ -176,13 +176,13 @@ is used as file name. You can also use .I --au and .I --cdr -for AU and CDR format, respectively. +for AU and CDR format, respectively. Note that WAV/AU writing to non-seekable files, or redirected stdout, needs some thought. Since 1.16.0, the logic changed to writing the header with the first actual data. This avoids spurious WAV headers in a pipe, for example. The result of decoding nothing to WAV/AU is a file consisting just of the header when it is seekable and really nothing when not (not even a header). Correctly writing data with prophetic headers to stdout is no easy business. .TP \fB\-\^\-au \fIfile Does not play the MPEG file but writes it to .I file in SUN audio format. If \- is used as the filename, the AU file is -written to stdout. +written to stdout. See paragraph about WAV writing for header fun with non-seekable streams. .TP \fB\-\^\-cdr \fIfile Does not play the MPEG file but writes it to diff --git a/src/wav.c b/src/wav.c index f43abc0e..c19fc838 100644 --- a/src/wav.c +++ b/src/wav.c @@ -58,6 +58,13 @@ static int flipendian=0; int bytes_per_sample = -1; int floatwav = 0; /* If we write a floating point WAV file. */ +/* Open routines only prepare a header, stored here and written on first actual + data write. If no data is written at all, proper files will still get a + header via the update at closing; non-seekable streams will just have no + no header if there is no data. */ +void *the_header = NULL; +size_t the_header_size = 0; + /* Convertfunctions: */ /* always little endian */ @@ -114,7 +121,11 @@ static int open_file(char *filename) #endif if(!strcmp("-",filename)) { wavfp = stdout; - return 0; + /* If stdout is redirected to a file, seeks suddenly can work. + Doing one here to ensure that such a file has the same output + it had when opening directly as such. */ + fseek(wavfp, 0L, SEEK_SET); + return 0; } else { #ifdef WANT_WIN32_UNICODE @@ -152,7 +163,6 @@ static int close_file() return 0; } -/* Wrapper over header writing; ensure that stdout doesn't get multiple headers. */ static int write_header(const void*ptr, size_t size) { if(fwrite(ptr, size, 1, wavfp) != 1 || fflush(wavfp)) @@ -206,7 +216,10 @@ int au_open(audio_output_t *ao) datalen = 0; - return write_header(&auhead, sizeof(auhead)); + the_header = &auhead; + the_header_size = sizeof(auhead); + + return 0; } int cdr_open(audio_output_t *ao) @@ -229,6 +242,9 @@ int cdr_open(audio_output_t *ao) if(open_file(ao->device) < 0) return -1; + the_header = NULL; + the_header_size = 0; + return 0; } @@ -306,10 +322,15 @@ int wav_open(audio_output_t *ao) long2littleendian(datalen+sizeof(RIFF.WAVE),RIFF.WAVElen,sizeof(RIFF.WAVElen)); } - if(!( ( floatwav && !write_header(&RIFF_FLOAT, sizeof(RIFF_FLOAT))) - || (!floatwav && !write_header(&RIFF, sizeof(RIFF))) )) + if(floatwav) { - return -1; + the_header = &RIFF_FLOAT; + the_header_size = sizeof(RIFF_FLOAT); + } + else + { + the_header = &RIFF; + the_header_size = sizeof(RIFF); } datalen = 0; @@ -326,6 +347,11 @@ int wav_write(unsigned char *buf,int len) if(!wavfp) return 0; + if(datalen == 0) + { + if(write_header(the_header, the_header_size) < 0) return 0; + } + if(flipendian) { if(bytes_per_sample == 4) /* 32 bit */ @@ -396,14 +422,14 @@ int wav_close(void) long2littleendian(datalen/(from_little(RIFF_FLOAT.WAVE.fmt.Channels,2)*from_little(RIFF_FLOAT.WAVE.fmt.BitsPerSample,2)/8), RIFF_FLOAT.WAVE.fact.samplelen,sizeof(RIFF_FLOAT.WAVE.fact.samplelen)); /* Always (over)writing the header here; also for stdout, when fseek worked, this overwrite works. */ - fwrite(&RIFF_FLOAT, sizeof(RIFF_FLOAT),1,wavfp); + write_header(&RIFF_FLOAT, sizeof(RIFF_FLOAT)); } else { long2littleendian(datalen,RIFF.WAVE.data.datalen,sizeof(RIFF.WAVE.data.datalen)); long2littleendian(datalen+sizeof(RIFF.WAVE),RIFF.WAVElen,sizeof(RIFF.WAVElen)); /* Always (over)writing the header here; also for stdout, when fseek worked, this overwrite works. */ - fwrite(&RIFF, sizeof(RIFF),1,wavfp); + write_header(&RIFF, sizeof(RIFF)); } } else @@ -427,7 +453,7 @@ int au_close(void) if(fseek(wavfp, 0L, SEEK_SET) >= 0) { long2bigendian(datalen,auhead.datalen,sizeof(auhead.datalen)); /* Always (over)writing the header here; also for stdout, when fseek worked, this overwrite works. */ - fwrite(&auhead, sizeof(auhead),1,wavfp); + write_header(&auhead, sizeof(auhead)); } else warning("Cannot rewind AU file. File-format isn't fully conform now.");