diff -u -N madlld-1.0/.cvsignore madlld-1.1/.cvsignore --- madlld-1.0/.cvsignore Thu Jan 1 01:00:00 1970 +++ madlld-1.1/.cvsignore Thu Jun 26 09:03:38 2003 @@ -0,0 +1,5 @@ +*.mp3 +*.wav +*.cdr +*.ps +*.gz diff -u -N madlld-1.0/CHANGELOG madlld-1.1/CHANGELOG --- madlld-1.0/CHANGELOG Tue Nov 13 04:27:38 2001 +++ madlld-1.1/CHANGELOG Sun Feb 22 04:31:15 2004 @@ -1,6 +1,39 @@ -*- text -*- ------------------------------------------------------------------------------ +madlld v1.1 (2004-02-22) +------------------------------------------------------------------------------ + +* Added an explanation about the need for appending MAD_BUFFER_GUARD + bytes to the input buffer at the end of streams. + +* Added simple examples on the alteration of subband domain samples + for audio filtering. + +* Added the demonstration of the use of the MAD_EMPHASIS_RESERVED + enumeration value, new in libmad v0.15.0b. + +* The fixed num to short conversion: uses signed integer numbers as it + should had been. The numbers were signed but the C data types were + unsigned. Added over range samples clipping. + +* Automated the production of the postscript documentation. + +* Fixed some typos. + +------------------------------------------------------------------------------ +madlld v1.0p1 (2002-01-08) +------------------------------------------------------------------------------ + +* Just reworked some comments to clarify the MAD_ERROR_BUFLEN error + condition and the partial frames at the end of the input buffer. + +* Added a warning about the unsatifying quality of the + MadFixedToUshort() function. + +* Fixed some typos. + +------------------------------------------------------------------------------ madlld v1.0 (2001-11-13) ------------------------------------------------------------------------------ diff -u -N madlld-1.0/Makefile madlld-1.1/Makefile --- madlld-1.0/Makefile Tue Nov 13 04:27:38 2001 +++ madlld-1.1/Makefile Sun Feb 22 04:31:15 2004 @@ -1,6 +1,6 @@ ############################################################################## # Simple Makefile the the mad low-level decoder demonstration. -# (c) 2001 Bertrand Petit +# (c) 2001--2004 Bertrand Petit #----------------------------------------------------------------------------- # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,36 +29,47 @@ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################## -# $Name: v1_0 $ -# $Date: 2001/11/13 03:27:38 $ -# $Revision: 1.3 $ +# $Name: v1_1 $ +# $Date: 2004/02/22 03:31:15 $ +# $Revision: 1.10 $ ############################################################################## ############################################################################## # Configuration section ############################################################################## -# Where are located the mad header and library. +# Where are located the mad header and library? MADINCDIR=-I/usr/local/include MADLIBDIR=-L/usr/local/lib -# Options passed to the C compiler. +# Options passed to the C compiler. ------------------------------------------ CFLAGS=$(MADINCDIR) -g LDFLAGS=$(MADLIBDIR) -g -LOADLIBES=-lmad -LDLIBS=$(LOADLIBES) # for BSD make -# Theses options are used by the author during development. Comment +# These options are used by the author during development. Comment # them if you're not using gcc. CFLAGS+=-pedantic -Wall -Wpointer-arith -Wbad-function-cast \ -Wcast-qual -Wcast-align -Wconversion -Wstrict-prototypes \ -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -# Where all of this should be installed? +# Uncomment the following assignment if your C preprocessor defines +# one of the 'unix', '__unix__, or '__unix' symbol and the system +# libraries does not provide the getopt() standard function. +#CPPFLAGS+=-DWITHOUT_GETOPT + +# Options passed to the linker. ---------------------------------------------- +# Note that the math library is only used by the wrapping code (for +# decibel handling), the mad library does not need it. +LOADLIBES=-lm -lmad +LDLIBS=$(LOADLIBES) # for BSD make + +# Where all of this should be installed? ------------------------------------- PREFIX=/usr/local BINDIR=$(PREFIX)/bin MAN1DIR=$(PREFIX)/man/man1 +# Installation-related definitions ------------------------------------------- + # Command used to create a directory hierarchy. MKDIRHIER=mkdir -p #MKDIRHIER=mkdirhier @@ -73,6 +84,12 @@ .PHONY: all all: madlld madlld.1.gz +madlld: madlld.o bstdfile.o + $(CC) $(LDFLAGS) $^ $(LOADLIBES) -o $@ + +madlld.o: madlld.c bstdfile.h +bstdfile.o: bstdfile.c bstdfile.h + ############################################################################## # tests ############################################################################## @@ -81,6 +98,10 @@ test.cdr: madlld test.mp3 ./madlld test.cdr +# This may help some peoples. +test.wav: test.cdr + sox test.cdr test.wav + # This uses the play command from the sox package .PHONY: play test play test: test.cdr @@ -95,8 +116,11 @@ # Manual page ############################################################################## -madlld.ps: madlld.1 - groff -Tps -man madlld.1 >madlld.ps +%-a4.ps: %-letter.ps + psresize -Pletter -pa4 <$^ >$@ + +madlld-man-letter.ps: madlld.1 + groff -Tps -man madlld.1 >madlld-man-letter.ps madlld.man: madlld.1 groff -Tascii -man madlld.1 | sed 's/.//g' >madlld.man @@ -108,17 +132,19 @@ # "Documentation" formating ############################################################################## -PSTITLE='Mad low-level demonstration decoder, source code version 1.0' +PSTITLE='Mad low-level demonstration decoder, source code version 1.1' A2PSFLAGS=--header=$(PSTITLE) -C -T 4 --left-title='$$D{%Y-%m-%d %H:%M:%S}' +SOURCES=madlld.c bstdfile.h bstdfile.c Makefile .PHONY:ps -ps: madlld-a4.ps madlld-letter.ps madlld.ps +ps: madlld-src-a4.ps madlld-src-letter.ps \ +madlld-man-a4.ps madlld-man-letter.ps -madlld-a4.ps: madlld.c Makefile - a2ps $(A2PSFLAGS) -M a4 -o madlld-a4.ps madlld.c Makefile +madlld-src-a4.ps: $(SOURCES) + a2ps $(A2PSFLAGS) -M a4 -o $@ $^ -madlld-letter.ps: madlld.c Makefile - a2ps $(A2PSFLAGS) -M letter -o madlld-letter.ps madlld.c Makefile +madlld-src-letter.ps: $(SOURCES) + a2ps $(A2PSFLAGS) -M letter -o $@ $^ ############################################################################## # Installation @@ -134,7 +160,7 @@ $(MKDIRHIER) $(BINDIR) # Manual page -$(MAN1DIR)/madlld.1.gz: madlld.1.gz +$(MAN1DIR)/madlld.1.gz: madlld.1.gz $(MAN1DIR) $(INSTALL) madlld.1.gz $(MAN1DIR) $(MAN1DIR): $(MKDIRHIER) $(MAN1DIR) diff -u -N madlld-1.0/README madlld-1.1/README --- madlld-1.0/README Tue Nov 13 04:27:38 2001 +++ madlld-1.1/README Sun Feb 22 04:31:15 2004 @@ -1,8 +1,8 @@ -*- text -*- ************************************ -*** madlld -- Mad low-level *** v 1.0, 2001-11-13 -*** demonstration/decoder *** (c) 2001 Bertrand Petit +*** madlld -- MAD low-level *** v 1.1, 2004-02-22 +*** demonstration/decoder *** (c) 2001--2004 Bertrand Petit ************************************ Release notes @@ -13,21 +13,22 @@ can be used. As a side effect the program can be used to decode MPEG audio streams to raw PCM samples. -Madllc was written on an unix system but as only the standard ansi C -library was used no system dependencies are expected. The hosting -operating system should only be able to provide standard streams -redirection. The program structure is very simple: it reads an audio -mpeg stream from stdin and writes raw big-endian stereo 16 bits -samples on stdout. The source code is heavily commented, you are -advised to read them as that's were this package purpose is. +Madlld was written on an unix system, dependencies on the posix +interface are handled by providing an alternate behaviour on non-unix +systems. The hosting operating system should nontheless provide +standard streams redirection. The program structure is very simple: it +reads an audio MPEG stream from stdin and writes raw big-endian stereo +16 bits signed samples on stdout. The source code is heavily +commented, you are advised to read it bottom-up as that's were this +package purpose is. ============================================================================== Release status ============================================================================== -This first release was tested against mad versions 0.14.0b, 0.14.1b, -and 0.14.2b. There is no known bug. +This second release was tested against MAD versions 0.14.0b, 0.14.1b, +0.14.2b, 0.15.0b, and 0.15.1b. There is no known bug. ============================================================================== @@ -38,46 +39,99 @@ first. Follow the instructions bellow to build and install malld on unix systems. -1. Configure the build process by editing the Makefile. Inlined +1. Be sure that the MAD suite is installed on your system. As of the + 14th of January 2003, the MAD suite version 0.15.0b can be freely + obtained from . The + suite is composed of three archives: libid3tag, libmad, and + madplay. Madlld use only libmad. + +2. Configure the build process by editing the Makefile. Inlined comments will guide you there. -2. Run "make". +3. Run "make". If you are using gcc, you can safely ignore warnings + about the use of braced-groups and the "asm" keyword. Theses are + part of macros defined by the mad.h header. -3. Test the built binaries by placing an audio MPEG stream in the +4. Test the built binaries by placing an audio MPEG stream in the build directory as a file named "test.mp3". If sox(1) is intalled on your system them type "make test". Else type "make test.cdr" and use any tools of your choice to check the PCM samples that were store in the file "test.cdr". -4. If step 3 suceeded then you're ready to install the program with +5. If step 3 suceeded then you're ready to install the program with "make install". -5. If your brand new installation required some changes to the program, +6. If your brand new installation required some changes to the program, please send unified diffs to the author (see the Contact section bellow). -6. If your brand new installation was done on an operating system not - listed in this document or against an also unlisted mad version, +7. If your brand new installation was done on an operating system not + listed in this document or against an also unlisted MAD version, please report your system details/versions to the author (see the Contact section bellow). +8. Optionally you can choose to build the postscript documentation. + You'll need the a2ps command (version 4.10 or up), and the psutils + package. You can obtain the two of then at theses locations: + + a2ps + http://www.inf.enst.fr/~demaille/a2ps/ + + psutils + http://gershwin.ens.fr/vdaniel/Doc-Locale/Outils-Gnu-Linux/PsUtils/ + + You can also use the preformated documents available at the madlld + distribution site: http://www.bsd-dk.dk/~elrond/audio/madlld/#docs + + +Users of non-unix systems wishing to compile this program are on their +own. They must arrange to compile the madlld.c and bstdfile.c modules, +then link the resulting object files against the MAD library. + +The compilation should yield no trouble as most of the program was +written in an almost pure ANSI C fashion. The only part that diverges +from the specs is the function dealing with command-line parsing. On +unix systems we use the getopt() function; a crude replacement is +provided if your system does not provide it. The use the getopt part +or the alternative is governed by the detection at pre-processing time +of several well known pre-defined macros present on unix systems. If +your preprocessor does not define theses macros and you have the +getopt() function you can still use it if you arrange to pre-define to +any value the HAVE_GETOPT macro. + +When the alternative command-line parser is used, the usage rules +listed in the manual page are not applicable. For more information +just invoque the madlld filter with any number bogus non-numeric +arguments. + ============================================================================== Contact ============================================================================== Questions, bug reports, patches, etc. are welcome. Please mail your -queries to < madlld at phoe dot frmug dot org >. Note that things such -as HTML e-mails, word documents, etc. will be blindly discarded. +queries to < madlld at phoe dot frmug dot org >. If you value your +privacy you can send OpenPGP crypted mails, the author's key is +available on public keyrings under the 0606CEF7 ID, it has the +following fingerprint: + + 79C9 10C8 9C3A 3F35 482F 06D6 150B A8BB 0606 CEF7 + +Note that things such as HTML e-mails, word documents, etc. will be +blindly discarded. + +Questions related to the MAD suite should be directed to the mad-dev +mailing list. You can subscribe to the reflector through the HTTP +interface located at . ============================================================================== Fine print ============================================================================== -This program is (c) 2001 by Bertrand Petit. It is distributed under -the terms of the license similar to the Berkeley license reproduced -bellow. +This program is (c) 2001--2004 by Bertrand Petit. It is distributed +under the terms of the license similar to the Berkeley license +reproduced bellow. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff -u -N madlld-1.0/bstdfile.c madlld-1.1/bstdfile.c --- madlld-1.0/bstdfile.c Thu Jan 1 01:00:00 1970 +++ madlld-1.1/bstdfile.c Fri Feb 13 18:58:22 2004 @@ -0,0 +1,307 @@ +/* HTAB = 4 */ +/**************************************************************************** + * bstdfile.h -- This module implements a buffered interface for the * + * fread(2) standard function that can signal an eof of file condition * + * synchronously with the transmission of the last bytes of a file. * + *--------------------------------------------------------------------------* + * (c) 2004 Bertrand Petit * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the following disclaimer. * + * * + * 2. Redistributions in binary form must reproduce the above * + * copyright notice, this list of conditions and the following * + * disclaimer in the documentation and/or other materials provided * + * with the distribution. * + * * + * 3. Neither the name of the author nor the names of its contributors * + * may be used to endorse or promote products derived from this * + * software without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR * + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * + * SUCH DAMAGE. * + * * + ****************************************************************************/ + +/* + * $Name: v1_1 $ + * $Date: 2004/02/13 17:58:22 $ + * $Revision: 1.1 $ + */ + +/**************************************************************************** + * Includes. * + ****************************************************************************/ +#include +#include +#include +#include +#include "bstdfile.h" + +/**************************************************************************** + * Preprocessor definitions * + ****************************************************************************/ +#define BFILE_BUFSIZE (8192U) + +/* We use some errno symbols that may be undefined. We supply such + * definitions, all with the 1 value, only to acheive compilation: the + * semantics of the signaled errors may thus be broken. It is the + * reponssibility of this module user to map the following symbols to + * the symbols provided by the target system. + */ +#if (!defined(unix) && !defined (__unix__) && !defined(__unix)) +/* Memory exhausted */ +# ifndef ENOMEM +# define ENOMEM (1) +# endif + +/* Bad file descriptor */ +# ifndef EBADF +# define EBADF (1) +# endif + +/* Invalid address. */ +# ifndef EFAULT +# define EFAULT (1) +# endif + +/* Invalid argument. */ +# ifndef EINVAL +# define EINVAL (1) +# endif +#endif + +/**************************************************************************** + * Datatypes definitions * + ****************************************************************************/ +struct bstdfile +{ + /* buffer is an internal buffer used by BstdRead(). live points + * inside that buffer to the data tha was not yet transmited to + * the user. live_size is the number of bytes available for direct + * conspumtion to feed the user buffer from the live pointer. + */ + char buffer[BFILE_BUFSIZE], + *live; + size_t live_size; + + /* fp is the file openned for reading associated with that bfile. */ + FILE *fp; + + /* Error managment: eof is non-zero when an end of file condition + * was detected. error is zero when no error was detected (error + * is zero when eof is 1), it stores the errno of the detected + * error. + */ + int eof, + error; +}; + +/**************************************************************************** + * Creates a new bstdfile from an already openned file. * + ****************************************************************************/ +bstdfile_t *NewBstdFile(FILE *fp) +{ + bstdfile_t *BstdFile; + + /* Allocate the bstdfile structure. */ + BstdFile=(bstdfile_t *)malloc(sizeof(bstdfile_t)); + if(BstdFile==NULL) + { + errno=ENOMEM; + return(NULL); + } + + /* Initialize the structure to safe defaults. */ + BstdFile->live=BstdFile->buffer; + BstdFile->live_size=0; + BstdFile->eof=0; + BstdFile->error=0; + BstdFile->fp=fp; + + /* Return the new bfile. */ + return(BstdFile); +} + +/**************************************************************************** + * Destroys a previously allocated BstdFile. * + ****************************************************************************/ +int BstdFileDestroy(bstdfile_t *BstdFile) +{ + if(BstdFile==NULL) + { + errno=EBADF; + return(1); + } + free(BstdFile); + return(0); +} + +/**************************************************************************** + * This predicate returns a non nul value when there is an end of file * + * condition on the BstdFile argument. * + ****************************************************************************/ +int BstdFileEofP(const bstdfile_t *BstdFile) +{ + return(BstdFile->eof); +} + +/**************************************************************************** + * This predicate returns a non nul value when there is an error condition * + * on the BstdFile argument. * + ****************************************************************************/ +int BstdFileErrorP(const bstdfile_t *BstdFile) +{ + return(BstdFile->error); +} + +/**************************************************************************** + * This works as read(2) but operates on a bfile instead of a file * + * descriptor. * + ****************************************************************************/ +size_t BstdRead(void *UserBuffer, size_t ElementSize, size_t ElementsCount, bstdfile_t *BstdFile) +{ + size_t RequestSize=ElementSize*ElementsCount, + FeededSize=0, + ReadSize, + ObtainedSize; + int OldErrno=errno; + + /* Check the validity of the arguments. */ + if(BstdFile==NULL) + { + errno=EBADF; + return((size_t)0); + } + if(UserBuffer==NULL) + { + errno=EFAULT; + return((size_t)0); + } + if(RequestSize<1) + { + errno=EINVAL; + return((size_t)0); + } + + /* Return immediately if an exceptionnal situation exists. */ + if(BstdFile->eof) + return((size_t)0); + if(BstdFile->error) + { + errno=BstdFile->error; + return((size_t)0); + } + + /* The easy case. */ + if(RequestSize==0U) + return((size_t)0); + + /* First feed the target buffer from the BstdFile buffer if it has + * some meat to be feeded on. + */ + if(BstdFile->live_size>0) + { + /* If there is more data in the buffer than requested by the + * user then we feed him directly from our buffer without a + * read operation. + */ + if(BstdFile->live_size>RequestSize) + { + memcpy(UserBuffer,BstdFile->live,RequestSize); + BstdFile->live+=RequestSize; + BstdFile->live_size-=RequestSize; + return(RequestSize); + } + /* Else we drain our buffer. */ + else + { + memcpy(UserBuffer,BstdFile->live,BstdFile->live_size); + UserBuffer=(char *)UserBuffer+BstdFile->live_size; + FeededSize=BstdFile->live_size; + BstdFile->live=BstdFile->buffer; + BstdFile->live_size=0; + } + } + + /* If the user request was not yet fulfilled we then read from the + * file the remaining data requested by the user. + */ + if(FeededSizefp); + FeededSize+=ObtainedSize; + + /* If an error occurs we return the amount of data that was + * feeded from the buffer and store the error condition for a + * later call. If our buffer was empty and we thus have + * transferred no data to the user buffer then we directly + * return the error. + */ + if(ObtainedSize==0U) + { + if(feof(BstdFile->fp)) + BstdFile->eof=1; + else + { + BstdFile->error=errno; + errno=OldErrno; + } + if(FeededSize!=0) + return(FeededSize); + else + return(0U); + } + } + + /* Fill again our buffer. In case of error, or end of file, that + * error is recorded but we still report the amount of data that + * was feeded to the user buffer. + */ + ObtainedSize=fread(BstdFile->buffer,1,BFILE_BUFSIZE,BstdFile->fp); + if(ObtainedSize==0) + { + if(feof(BstdFile->fp)) + BstdFile->eof=1; + else + { + BstdFile->error=errno; + errno=OldErrno; + } + } + else + { + BstdFile->live=BstdFile->buffer; + BstdFile->live_size=ObtainedSize; + } + + /* Eventually return the number ob bytes feeded to the user + * buffer. + */ + return(FeededSize); +} + +/* + * Local Variables: + * tab-width: 4 + * End: + */ + +/**************************************************************************** + * End of file bflle.c * + ****************************************************************************/ diff -u -N madlld-1.0/bstdfile.h madlld-1.1/bstdfile.h --- madlld-1.0/bstdfile.h Thu Jan 1 01:00:00 1970 +++ madlld-1.1/bstdfile.h Fri Feb 13 18:58:22 2004 @@ -0,0 +1,73 @@ +/* HTAB = 4 */ +/**************************************************************************** + * bstdfile.h -- This module implements a buffered interface for the * + * fread(2) standard function that can signal an eof of file condition * + * synchronously with the transmission of the last bytes of a file. * + *--------------------------------------------------------------------------* + * (c) 2004 Bertrand Petit * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the following disclaimer. * + * * + * 2. Redistributions in binary form must reproduce the above * + * copyright notice, this list of conditions and the following * + * disclaimer in the documentation and/or other materials provided * + * with the distribution. * + * * + * 3. Neither the name of the author nor the names of its contributors * + * may be used to endorse or promote products derived from this * + * software without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR * + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * + * SUCH DAMAGE. * + * * + ****************************************************************************/ + +/* + * $Name: v1_1 $ + * $Date: 2004/02/13 17:58:22 $ + * $Revision: 1.1 $ + */ + +#ifndef BSTDFILE_H +#define BSTDFILE_H + +/**************************************************************************** + * Datatypes definitions * + ****************************************************************************/ +typedef struct bstdfile bstdfile_t; + +/**************************************************************************** + * Prototypes * + ****************************************************************************/ +extern bstdfile_t *NewBstdFile(FILE *fp); +extern int BstdFileDestroy(bstdfile_t *BstdFile); +extern int BstdFileEofP(const bstdfile_t *BstdFile); +extern int BstdFileErrorP(const bstdfile_t *BstdFile); +extern size_t BstdRead(void *UserBuffer, size_t ElementSize, size_t ElementsCount, bstdfile_t *BstdFile); + +#endif /* BSTDFILE_H */ + +/* + * Local Variables: + * tab-width: 4 + * End: + */ + +/**************************************************************************** + * End of file bstdflle.h * + ****************************************************************************/ diff -u -N madlld-1.0/madlld.1 madlld-1.1/madlld.1 --- madlld-1.0/madlld.1 Tue Nov 13 04:27:38 2001 +++ madlld-1.1/madlld.1 Sun Feb 22 04:30:34 2004 @@ -1,4 +1,4 @@ -.\" (c) 2001 Bertrand Petit, all rights reserved. +.\" (c) 2001--2004 Bertrand Petit, all rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -27,28 +27,44 @@ .\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $Name: v1_0 $ -.\" $Date: 2001/11/13 03:27:38 $ -.\" $Revision: 1.1 $ +.\" $Name: v1_1 $ +.\" $Date: 2004/02/22 03:30:34 $ +.\" $Revision: 1.6 $ .\" -.Dd November 13, 2001 +.Dd February 22nd, 2004 .Dt MADLLD 1 .Sh NAME .Nm madlld .Nd Mad low-level decoder/demonstration .Sh SYNOPSIS .Nm madlld +.Op Fl p +.Op Fl a Ar level .Sh DESCRIPTION .Nm madlld reads an audio MPEG stream from stdin and outputs raw big-endian -16-bit stereo PCM samples on stdout. The decoded stream +16-bit stereo PCM samples on stdout. Both output channels are +identical when the MPEG stream is monophonic. The decoded stream characteristics are printed on stderr. On completion the number of decoded frames and the duration of the stream are also printed on stderr. -No options are available. +The following options are available: +.Bl -tag -width flag +.It Fl p +The signal is filtered in the subband-domain through a band-pass +filter as if it was transmited through a telephone line. +.It Fl a +The +.Ar level +argument is either an amplification (when positive) or attenuation +factor (when negative) applied to subband-domain samples before audio +synthesis. The +.Ar level +value is expressed in dB. +.El .Sh DIAGNOSTICS -If an error occurs, a message is printed on stderr, if that error is +If an error occurs, a message is printed on stderr; if that error is fatal then .Nm madlld exits with a non zero return value. @@ -68,6 +84,7 @@ .fi .RE .Sh SEE ALSO +.Xr madplay 1 , .Xr sox 1 , .Xr play 1 . .\" EOF diff -u -N madlld-1.0/madlld.c madlld-1.1/madlld.c --- madlld-1.0/madlld.c Tue Nov 13 04:07:02 2001 +++ madlld-1.1/madlld.c Sun Feb 22 04:27:05 2004 @@ -1,10 +1,10 @@ /* HTAB = 4 */ /**************************************************************************** - * madlld.c -- A simple program decoding an mpeg audio stream to 16 bits * + * madlld.c -- A simple program decoding an mpeg audio stream to 16-bit * * PCM from stdin to stdout. This program is just a simple sample * - * demonstration how the low-level libmad API can be used. * + * demonstrating how the low-level libmad API can be used. * *--------------------------------------------------------------------------* - * (c) 2001 Bertrand Petit * + * (c) 2001--2004 Bertrand Petit * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * @@ -38,24 +38,54 @@ ****************************************************************************/ /* - * $Name: v1_0 $ - * $Date: 2001/11/13 03:07:02 $ - * $Revision: 1.8 $ + * $Name: v1_1 $ + * $Date: 2004/02/22 03:27:05 $ + * $Revision: 1.18 $ */ /**************************************************************************** * Includes * ****************************************************************************/ +#include #include #include +#include #include +#include /* for pow() and log10() */ #include +#include "bstdfile.h" + +/* Should we use getopt() for command-line arguments parsing? */ +#if (defined(unix) || defined (__unix__) || defined(__unix) || \ + defined(HAVE_GETOPT)) \ + && !defined(WITHOUT_GETOPT) +#include +#define HAVE_GETOPT +#else +#include +#undef HAVE_GETOPT +#endif /**************************************************************************** * Global variables. * ****************************************************************************/ + +/* Keeps a pointer to the program invocation name for the error + * messages. + */ const char *ProgName; +/* This table represents the subband-domain filter characteristics. It + * is initialized by the ParseArgs() function and is used as + * coefficients against each subband samples when DoFilter is non-nul. + */ +mad_fixed_t Filter[32]; + +/* DoFilter is non-nul when the Filter table defines a filter bank to + * be applied to the decoded audio subbands. + */ +int DoFilter=0; + /**************************************************************************** * Return an error string associated with a mad error code. * ****************************************************************************/ @@ -129,10 +159,10 @@ #endif /**************************************************************************** - * Converts a sample from mad's fixed point number format to an unsigned * + * Converts a sample from mad's fixed point number format to a signed * * short (16 bits). * ****************************************************************************/ -static unsigned short MadFixedToUshort(mad_fixed_t Fixed) +static signed short MadFixedToSshort(mad_fixed_t Fixed) { /* A fixed point number is formed of the following bit pattern: * @@ -142,17 +172,27 @@ * W ==> Whole part bits * F ==> Fractional part bits * - * This pattern contains MAD_F_FRACBITS, one should alway use this - * macro when working on the bits of a fixed point number. It is - * not guaranteed to be constant over the different platforms - * supported by libmad. + * This pattern contains MAD_F_FRACBITS fractional bits, one + * should alway use this macro when working on the bits of a fixed + * point number. It is not guaranteed to be constant over the + * different platforms supported by libmad. * - * The unsigned short value is formed by the least significant - * whole part bit, followed by the 15 most significant fractional - * part bits. + * The signed short value is formed, after clipping, by the least + * significant whole part bit, followed by the 15 most significant + * fractional part bits. Warning: this is a quick and dirty way to + * compute the 16-bit number, madplay includes much better + * algorithms. */ + + /* Clipping */ + if(Fixed>=MAD_F_ONE) + return(SHRT_MAX); + if(Fixed<=-MAD_F_ONE) + return(-SHRT_MAX); + + /* Conversion. */ Fixed=Fixed>>(MAD_F_FRACBITS-15); - return((unsigned short)Fixed); + return((signed short)Fixed); } /**************************************************************************** @@ -201,7 +241,10 @@ break; } - /* Convert the emphasis to it's printed representation. */ + /* Convert the emphasis to it's printed representation. Note that + * the MAD_EMPHASIS_RESERVED enumeration value appread in libmad + * version 0.15.0b. + */ switch(Header->emphasis) { case MAD_EMPHASIS_NONE: @@ -213,6 +256,12 @@ case MAD_EMPHASIS_CCITT_J_17: Emphasis="CCITT J.17"; break; +#if (MAD_VERSION_MAJOR>=1) || \ + ((MAD_VERSION_MAJOR==0) && (MAD_VERSION_MINOR>=15)) + case MAD_EMPHASIS_RESERVED: + Emphasis="reserved(!)"; + break; +#endif default: Emphasis="(unexpected emphasis value)"; break; @@ -227,6 +276,36 @@ } /**************************************************************************** + * Applies a frequency-domain filter to audio data in the subband-domain. * + ****************************************************************************/ +static void ApplyFilter(struct mad_frame *Frame) +{ + int Channel, + Sample, + Samples, + SubBand; + + /* There is two application loops, each optimized for the number + * of audio channels to process. The first alternative is for + * two-channel frames, the second is for mono-audio. + */ + Samples=MAD_NSBSAMPLES(&Frame->header); + if(Frame->header.mode!=MAD_MODE_SINGLE_CHANNEL) + for(Channel=0;Channel<2;Channel++) + for(Sample=0;Samplesbsample[Channel][Sample][SubBand]= + mad_f_mul(Frame->sbsample[Channel][Sample][SubBand], + Filter[SubBand]); + else + for(Sample=0;Samplesbsample[0][Sample][SubBand]= + mad_f_mul(Frame->sbsample[0][Sample][SubBand], + Filter[SubBand]); +} + +/**************************************************************************** * Main decoding loop. This is where mad is used. * ****************************************************************************/ #define INPUT_BUFFER_SIZE (5*8192) @@ -237,13 +316,14 @@ struct mad_frame Frame; struct mad_synth Synth; mad_timer_t Timer; - unsigned char InputBuffer[INPUT_BUFFER_SIZE], + unsigned char InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD], OutputBuffer[OUTPUT_BUFFER_SIZE], *OutputPtr=OutputBuffer; const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE; int Status=0, i; unsigned long FrameCount=0; + bstdfile_t *BstdFile; /* First the structures used by libmad must be initialized. */ mad_stream_init(&Stream); @@ -255,6 +335,22 @@ * Stream structure. */ + /* {1} When decoding from a file we need to know when the end of + * the file is reached at the same time as the last bytes are read + * (see also the comment marked {3} bellow). Neither the standard + * C fread() function nor the posix read() system call provides + * this feature. We thus need to perform our reads through an + * interface having this feature, this is implemented here by the + * bstdfile.c module. + */ + BstdFile=NewBstdFile(InputFp); + if(BstdFile==NULL) + { + fprintf(stderr,"%s: can't create a new bstdfile_t (%s).\n", + ProgName,strerror(errno)); + return(1); + } + /* This is the decoding loop. */ do { @@ -267,15 +363,22 @@ Remaining; unsigned char *ReadStart; - /* libmad does not consume all the buffer it's given. Some - * datas, part of a truncated frame, is left unused at the - * end of the buffer. Those datas must be put back at the - * beginning of the buffer and taken in account for - * refilling the buffer. This means that the input buffer - * must be large enough to hold a complete frame at the - * highest observable bit-rate (currently 448 kb/s). XXX=XXX - * Is 2016 bytes the size of the largest frame? - * (448000*(1152/32000))/8 + /* {2} libmad may not consume all bytes of the input + * buffer. If the last frame in the buffer is not wholly + * contained by it, then that frame's start is pointed by + * the next_frame member of the Stream structure. This + * common situation occurs when mad_frame_decode() fails, + * sets the stream error code to MAD_ERROR_BUFLEN, and + * sets the next_frame pointer to a non NULL value. (See + * also the comment marked {4} bellow.) + * + * When this occurs, the remaining unused bytes must be + * put back at the beginning of the buffer and taken in + * account before refilling the buffer. This means that + * the input buffer must be large enough to hold a whole + * frame at the highest observable bit-rate (currently 448 + * kb/s). XXX=XXX Is 2016 bytes the size of the largest + * frame? (448000*(1152/32000))/8 */ if(Stream.next_frame!=NULL) { @@ -294,7 +397,7 @@ * reached we also leave the loop but the return status is * left untouched. */ - ReadSize=fread(ReadStart,1,ReadSize,InputFp); + ReadSize=BstdRead(ReadStart,1,ReadSize,BstdFile); if(ReadSize<=0) { if(ferror(InputFp)) @@ -308,6 +411,36 @@ break; } + /* {3} When decoding the last frame of a file, it must be + * followed by MAD_BUFFER_GUARD zero bytes if one wants to + * decode that last frame. When the end of file is + * detected we append that quantity of bytes at the end of + * the available data. Note that the buffer can't overflow + * as the guard size was allocated but not used the the + * buffer managment code. (See also the comment marked + * {1}.) + * + * In a message to the mad-dev mailing list on May 29th, + * 2001, Rob leslie explains the guard zone as follows: + * + * "The reason for MAD_BUFFER_GUARD has to do with the + * way decoding is performed. In Layer III, Huffman + * decoding may inadvertently read a few bytes beyond + * the end of the buffer in the case of certain invalid + * input. This is not detected until after the fact. To + * prevent this from causing problems, and also to + * ensure the next frame's main_data_begin pointer is + * always accessible, MAD requires MAD_BUFFER_GUARD + * (currently 8) bytes to be present in the buffer past + * the end of the current frame in order to decode the + * frame." + */ + if(BstdFileEofP(BstdFile)) + { + memset(ReadStart+ReadSize,0,MAD_BUFFER_GUARD); + ReadSize+=MAD_BUFFER_GUARD; + } + /* Pipe the new buffer content to libmad's stream decoder * facility. */ @@ -327,17 +460,24 @@ * recoverable or fatal, the error status is checked with the * MAD_RECOVERABLE macro. * - * When a fatal error is encountered all decoding activities - * shall be stopped, except when a MAD_ERROR_BUFLEN is - * signaled. This condition means the mad_frame_decode() - * function needs more input to achieve it's work. One shall - * refill the buffer and repeat the mad_frame_decode() call. + * {4} When a fatal error is encountered all decoding + * activities shall be stopped, except when a MAD_ERROR_BUFLEN + * is signaled. This condition means that the + * mad_frame_decode() function needs more input to achieve + * it's work. One shall refill the buffer and repeat the + * mad_frame_decode() call. Some bytes may be left unused at + * the end of the buffer if those bytes forms an incomplete + * frame. Before refilling, the remainign bytes must be moved + * to the begining of the buffer and used for input for the + * next mad_frame_decode() invocation. (See the comments marked + * {2} earlier for more details.) * - * Recoverable errors are caused by malformed bit-streams in - * this case one can call mad_frame_decode() in order to skip - * the faulty frame and re-sync to the next frame. + * Recoverable errors are caused by malformed bit-streams, in + * this case one can call again mad_frame_decode() in order to + * skip the faulty part and re-sync to the next frame. */ if(mad_frame_decode(&Frame,&Stream)) + { if(MAD_RECOVERABLE(Stream.error)) { fprintf(stderr,"%s: recoverable frame level error (%s)\n", @@ -355,6 +495,7 @@ Status=1; break; } + } /* The characteristics of the stream's first frame is printed * on stderr. The first frame is representative of the entire @@ -367,7 +508,7 @@ break; } - /* Accounting.The computed frame duration is in the frame + /* Accounting. The computed frame duration is in the frame * header structure. It is expressed as a fixed point number * whole data type is mad_timer_t. It is different from the * samples fixed point format and unlike it, it can't directly @@ -378,6 +519,14 @@ */ FrameCount++; mad_timer_add(&Timer,Frame.header.duration); + + /* Between the frame decoding and samples synthesis we can + * perform some operations on the audio data. We do this only + * if some processing was required. Detailed explanations are + * given in the ApplyFilter() function. + */ + if(DoFilter) + ApplyFilter(&Frame); /* Once decoded the frame is synthesized to PCM samples. No errors * are reported by mad_synth_frame(); @@ -392,10 +541,10 @@ */ for(i=0;i>8; *(OutputPtr++)=Sample&0xff; @@ -403,11 +552,11 @@ * the right output channel is the same as the left one. */ if(MAD_NCHANNELS(&Frame.header)==2) - Sample=MadFixedToUshort(Synth.pcm.samples[1][i]); + Sample=MadFixedToSshort(Synth.pcm.samples[1][i]); *(OutputPtr++)=Sample>>8; *(OutputPtr++)=Sample&0xff; - /* Flush the buffer if it is full. */ + /* Flush the output buffer if it is full. */ if(OutputPtr==OutputBufferEnd) { if(fwrite(OutputBuffer,1,OUTPUT_BUFFER_SIZE,OutputFp)!=OUTPUT_BUFFER_SIZE) @@ -422,6 +571,11 @@ } }while(1); + /* The input file was completely read; the memory allocated by our + * reading module must be reclaimed. + */ + BstdFileDestroy(BstdFile); + /* Mad is no longer used, the structures that were initialized must * now be cleared. */ @@ -450,9 +604,9 @@ char Buffer[80]; /* The duration timer is converted to a human readable string - * with the versatile but still constrained mad_timer_string() + * with the versatile, but still constrained mad_timer_string() * function, in a fashion not unlike strftime(). The main - * difference is that the timer is break-down into several + * difference is that the timer is broken into several * values according some of it's arguments. The units and * fracunits arguments specify the intended conversion to be * executed. @@ -476,6 +630,200 @@ } /**************************************************************************** + * Prints a message on stderr explaining the usage of the program. Two * + * versionsions of this function are provided, depending on the system * + * type. * + ****************************************************************************/ +static void PrintUsage(void) +{ +#ifdef HAVE_GETOPT /* This version is for unix systems. */ + fprintf(stderr,"usage: %s [-p] [-a ]\n" + "\t-a\tSets an amplification or attenuation factor expressed\n" + "\t\tin dB. The factor bounds are [-Inf,%f].\n" + "\t-p\tRequest that the output samples be filtered as if\n" + "\t\ttransmited through a telephone switch.\n", + ProgName, + 20.*log10(mad_f_todouble(MAD_F_MAX))); +#else /* HAVE_GETOPT */ /* This other version is for non-unix systems. */ + fprintf(stderr,"usage: %s [] [phone]\n" + "\t is a floating point number expressing an " + "amplification\n" + "\t\tor attenuation factor expressed in dB. The factor bounds\n" + "\t\tare [-Inf,%f].\n" + "\tThe \"phone\" argument requests that the output samples be " + "filtered\n" + "\t\tas if transmited through a telephone switch.\n", + ProgName, + 20.*log10(mad_f_todouble(MAD_F_MAX))); +#endif /* HAVE_GETOPT */ +} + +/**************************************************************************** + * Command-line arguments parsing. We use two methods and two command-line * + * formats depending on the system type. On unix system we apply the good * + * old getopt() method, other system are offered a really primitive options * + * interface. * + ****************************************************************************/ +static int ParseArgs(int argc, char * const argv[]) +{ + int DoPhoneFilter=0, + i; + double AmpFactor; + mad_fixed_t Amp=MAD_F_ONE; + +#ifdef HAVE_GETOPT /* This version is for unix systems. */ + int Option; + + /* Parse the command line. */ + while((Option=getopt(argc,argv,"a:p"))!=-1) + switch(Option) + { + /* {5} Set the amplification/attenuation factor, expressed + * in dB. + */ + case 'a': + /* If the current linear amplification factor is not + * one (MAD_F_ONE) then is was already set. Setting it + * again is not permited. + */ + if(Amp!=MAD_F_ONE) + { + fprintf(stderr,"%s: the amplification/attenuation factor " + "was set several times.\n",ProgName); + return(1); + } + + /* The decibel value is converted to a linear factor. + * That factor is checked against the maximum value + * that can be stored in a mad_fixed_t. The upper + * bound is MAD_F_MAX, it is converted to a double + * value with mad_f_todouble() for comparison. + */ + AmpFactor=pow(10.,atof(optarg)/20); + if(AmpFactor>mad_f_todouble(MAD_F_MAX)) + { + fprintf(stderr,"%s: amplification out of range.\n", + ProgName); + return(1); + } + + /* Eventually the amplification factor is converted + * from double to fixed point with mad_f_tofixed(). + */ + Amp=mad_f_tofixed(AmpFactor); + break; + + /* {6} The output is filtered through a telephone wire. */ + case 'p': + /* Only one occurence of the option is permited. */ + if(DoPhoneFilter) + { + fprintf(stderr,"%s: the phone-line simulation option " + "was already set.\n",ProgName); + return(1); + } + + /* The output will be filtered through a band-pass + * filter simulating a phone line transmission. + */ + DoPhoneFilter=1; + break; + + /* Print usage guide for invalid options. */ + case '?': + default: + PrintUsage(); + return(1); + } +#else /* HAVE_GETOPT */ /* This other version is for non-unix systems. */ + /* Scan all command-line arguments. */ + for(i=1;imad_f_todouble(MAD_F_MAX)) + { + fprintf(stderr,"%s: amplification out of range.\n", + ProgName); + return(1); + } + + Amp=mad_f_tofixed(AmpFactor); + } + else + /* Use the phone-like filter if the argument is the * + * 'phone' string. Look at the comment of the case marked + * {6} in the unix section for details. + */ + if(strcmp(argv[i],"phone")==0) + { + if(DoPhoneFilter) + { + fprintf(stderr,"%s: the phone-line simulation option " + "was already set.\n",ProgName); + return(1); + } + DoPhoneFilter=1; + } + else + { + /* The argument is not a recognized one. Print the + * usage guidelines and stop there. + */ + PrintUsage(); + return(1); + } + } +#endif /* HAVE_GETOPT */ + + /* Initialize the subband-domain filter coefficients to one if + * filtering is requested. + */ + if(Amp!=MAD_F_ONE || DoPhoneFilter) + for(i=0;i<32;i++) + Filter[i]=MAD_F_ONE; + + /* The amplification/attenuation is applied to the subband-domain + * filter definition. + */ + if(Amp!=MAD_F_ONE) + { + DoFilter=1; + for(i=0;i<32;i++) + Filter[i]=Amp; + } + + /* The telephone-like filter is applied to the subband-domain + * filter definition. All subbands are set to zero except bands 2 + * to 6. This programs author has no access to the MPEG audio + * specification, he does not know the frequeciens bands covered + * by the MPEG subbands. + */ + if(DoPhoneFilter) + { + DoFilter=1; + Filter[0]=MAD_F(0); + for(i=5;i<32;i++) + Filter[i]=MAD_F(0); + } + + /* Command-line arguments are okay. */ + return(0); +} + +/**************************************************************************** * Program entry point. * ****************************************************************************/ int main(int argc, char *argv[]) @@ -489,6 +837,10 @@ ProgName=argv[0]; else ProgName=cptr+1; + + /* The command-line arguments are analysed. */ + if(ParseArgs(argc,argv)) + return(1); /* Decode stdin to stdout. */ Status=MpegAudioDecoder(stdin,stdout);