www.pudn.com > madlldlib-10b4_dll.zip > madlldlib.cpp


/**************************************************************************** 
 * madlldlib                                        (c) 2004 J. A. Robson   * 
 *                                                                          * 
 * madlldlib.cpp -- a library based on madlld that creates a DLL with an    * 
 * easy interface--a single function with a callback. (Also changed output  *  
 * bit order to be little-endian.)                                          * 
 *                                                                          * 
 * How to use:                                                              * 
 * 	Define a callback function matching the parameters defined in the       * 
 * 	CbMpegAudioDecoder() parameter list. Call said function passing file    * 
 * 	path/names and the callback. See inline function comments for           * 
 * 	additional details.  See 'test.cpp' for a full example.                 * 
 *                                                                          * 
 * 	Example:                                                                * 
 * 	                                                                        * 
 * 	Status=CbMpegAudioDecoder("in.mp3","out.pcm", 0, CallbackFunction);     * 
 *                                                                          * 
 *  To compile, see Makefile comments.                                      * 
 *                                                                          * 
 *                                                                          * 
 * ---Original code comment below----                                       * 
 **************************************************************************** 
 * [from madlld.c] (c) 2001--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:  $ 
 * $Date: 2004/07/12 21:41:33 $ 
 * $Revision: 1.7 $ 
 */ 
 
/**************************************************************************** 
 * Includes																	* 
 ****************************************************************************/ 
#include "madlldlib.h" 
#include  
#include  
#include  
#include   /* for fstat routine */ 
#include 		/* "" */ 
#include 			/* "" */ 
#include  
#include  
#include  /* for pow() and log10() */ 
#include "bstdfile.h" 
 
 
 
/**************************************************************************** 
 * Return an error string associated with a mad error code.					* 
 ****************************************************************************/ 
/* Mad version 0.14.2b introduced the mad_stream_errorstr() function. 
 * For previous library versions a replacement is provided below. 
 */ 
#if (MAD_VERSION_MAJOR>=1) || \ 
    ((MAD_VERSION_MAJOR==0) && \ 
     (((MAD_VERSION_MINOR==14) && \ 
       (MAD_VERSION_PATCH>=2)) || \ 
      (MAD_VERSION_MINOR>14))) 
#define MadErrorString(x) mad_stream_errorstr(x) 
#else 
static const char *MadErrorString(const struct mad_stream *Stream) 
{ 
	switch(Stream->error) 
	{ 
		/* Generic unrecoverable errors. */ 
		case MAD_ERROR_BUFLEN: 
			return("input buffer too small (or EOF)"); 
		case MAD_ERROR_BUFPTR: 
			return("invalid (null) buffer pointer"); 
		case MAD_ERROR_NOMEM: 
			return("not enough memory"); 
 
		/* Frame header related unrecoverable errors. */ 
		case MAD_ERROR_LOSTSYNC: 
			return("lost synchronization"); 
		case MAD_ERROR_BADLAYER: 
			return("reserved header layer value"); 
		case MAD_ERROR_BADBITRATE: 
			return("forbidden bitrate value"); 
		case MAD_ERROR_BADSAMPLERATE: 
			return("reserved sample frequency value"); 
		case MAD_ERROR_BADEMPHASIS: 
			return("reserved emphasis value"); 
 
		/* Recoverable errors */ 
		case MAD_ERROR_BADCRC: 
			return("CRC check failed"); 
		case MAD_ERROR_BADBITALLOC: 
			return("forbidden bit allocation value"); 
		case MAD_ERROR_BADSCALEFACTOR: 
			return("bad scalefactor index"); 
		case MAD_ERROR_BADFRAMELEN: 
			return("bad frame length"); 
		case MAD_ERROR_BADBIGVALUES: 
			return("bad big_values count"); 
		case MAD_ERROR_BADBLOCKTYPE: 
			return("reserved block_type"); 
		case MAD_ERROR_BADSCFSI: 
			return("bad scalefactor selection info"); 
		case MAD_ERROR_BADDATAPTR: 
			return("bad main_data_begin pointer"); 
		case MAD_ERROR_BADPART3LEN: 
			return("bad audio data length"); 
		case MAD_ERROR_BADHUFFTABLE: 
			return("bad Huffman table select"); 
		case MAD_ERROR_BADHUFFDATA: 
			return("Huffman data overrun"); 
		case MAD_ERROR_BADSTEREO: 
			return("incompatible block_type for JS"); 
 
		/* Unknown error. This switch may be out of sync with libmad's 
		 * defined error codes. 
		 */ 
		default: 
			return("Unknown error code"); 
	} 
} 
#endif 
 
 
/**************************************************************************** 
 * 'Borrowed' from audio.c in the madplay source 
 * NAME:	Prng() 
 * DESCRIPTION:	32-bit pseudo-random number generator 
 ****************************************************************************/ 
static inline unsigned long Prng(unsigned long state) 
{ 
  return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; 
} 
 
 
/**************************************************************************** 
 * A little more advanced scaling routine based on madplay's  
 * audio_linear_dither() function. Rather than simply rounding the 24 bit 
 * number down to 16 bits (as MadFixedToSshort() in madlld does), it performs  
 * dithering, which is the addition of a random number to the least  
 * significant bits (LSB) of the sample that targets the LSB at the 16 bit 
 * mark. CbMpegAudioDecoder() uses this to perform its scaling before output. 
 ****************************************************************************/ 
static inline signed short Scale(mad_fixed_t sample, mad_fixed_t *gRandom) 
{ 
	unsigned int scalebits; 
	mad_fixed_t output, mask, rnd; 
 
	/* bias */ 
	output = sample + (1L << (MAD_F_FRACBITS + 1 - 16 - 1));	 
	 
  	scalebits = MAD_F_FRACBITS + 1 - 16; 
  	mask = (1L << scalebits) - 1; 
 
  	/* dither */ 
	rnd = Prng(*gRandom); 
  	output += (rnd & mask) - (*gRandom & mask); 
	*gRandom = rnd; 
	 
  	/* clip */ 
	if (output >= MAD_F_ONE) 
    	output = MAD_F_ONE - 1; 
	else if (output < -MAD_F_ONE) 
    	output = -MAD_F_ONE; 
 
  	/* quantize */ 
  	output &= ~mask; 
 
	/* scale */ 
	return output >> scalebits; 
} 
 
 
/**************************************************************************** 
 * This is based on the MpegAudioDecoder() function in the madlld source. 
 * It differs in that it performs the file opens, taking the path/filenames  
 * as arguments, and passes the following information to a callback  
 * function while in the decoding loop, to be handled by the calling code  
 * (generally used for reporting--see test.cpp for an example): 
 * 
 *			Frame count			 
 *			Total input bytes read 
 *			A pointer to a mad_header struct (MP3 info) 
 * 
 * Since this is used as a library function, errors and information are  
 * reported back to a char pointer (StatMsg) passed as a parameter from the  
 * calling code. 
 *  
 * It also uses a flag (WavPad) passed from the calling code which tells it 
 * whether to add a RIFF/WAVE header (making OutFN a WAV file). If WavPad==1 
 * then 44 null bytes are written to the beginning of OutFN before decoding  
 * occurs, and the WAV header is constructed and added after decoding. 
 ****************************************************************************/ 
#define INPUT_BUFFER_SIZE	(5*8192) 
#define OUTPUT_BUFFER_SIZE	8192 /* Must be an integer multiple of 4. */ 
extern "C" __declspec(dllexport) int __stdcall CbMpegAudioDecoder( 
		const char *InFN,  
		const char *OutFN, 
	    int WavPad, 
		char *StatMsg,	 
		void (__stdcall *CbFunc)(unsigned long FrameCnt, unsigned long InByteTot, struct mad_header *Header)  
		) 
{ 
 
	FILE *InputFp;  
	FILE *OutputFp; 
	struct mad_stream	Stream; 
	struct mad_frame	Frame; 
	struct mad_synth	Synth; 
	mad_timer_t			Timer; 
	unsigned char		InputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD], 
						OutputBuffer[OUTPUT_BUFFER_SIZE], 
						*OutputPtr=OutputBuffer, 
						*GuardPtr=NULL; 
	const unsigned char	*OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE; 
	int					Status=0, 
						i; 
	unsigned long		FrameCount=0; 
	unsigned long		ByteCount=0;			/* count total input bytes */ 
	bstdfile_t			*BstdFile; 
	char 				padwav[44] = "";		/* WAV padding */ 
	wavhdr 				wavh;					/* my WAV header */		 
	int					wavflg = 1; 
 
	 
	/* Random number for generating dither.  
	 * Used by Scale() below in preparing 
	 * the samples for 16 bit output.  
	 */ 
	mad_fixed_t gRand = 0xa8b9ff7e; 
	 
 
	/* Open passed files for reading and writing */ 
	if ( !(InputFp = fopen(InFN, "rb")) ) {				/* input */ 
		sprintf(StatMsg,"%s not found!\n", InFN); 
		return(1); 
	} 
	if ( !(OutputFp = fopen(OutFN, "wb")) ) { 			/* output */ 
		sprintf(StatMsg,"Couldn't open %s for write!\n", OutFN); 
		return(1); 
	} 
 
	 
	/* Write the WAV header padding if flagged to 
	 * do so. 
	 */ 
	if (WavPad == 1) { 
		fwrite(&padwav, 1, sizeof(padwav), OutputFp); 
	} 
	 
	 
	/* First the structures used by libmad must be initialized. */ 
	mad_stream_init(&Stream); 
	mad_frame_init(&Frame); 
	mad_synth_init(&Synth); 
	mad_timer_reset(&Timer); 
 
	/* Decoding options can here be set in the options field of the 
	 * 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) 
	{ 
		sprintf(StatMsg,"Can't create a new bstdfile_t (%s).\n", strerror(errno)); 
		return(1); 
	} 
 
	/* This is the decoding loop. */ 
	do 
	{ 
		/* The input bucket must be filled if it becomes empty or if 
		 * it's the first execution of the loop. 
		 */ 
		if(Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN) 
		{ 
			size_t			ReadSize, 
							Remaining; 
			unsigned char	*ReadStart; 
 
			/* {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) 
			{ 
				Remaining=Stream.bufend-Stream.next_frame; 
				memmove(InputBuffer,Stream.next_frame,Remaining); 
				ReadStart=InputBuffer+Remaining; 
				ReadSize=INPUT_BUFFER_SIZE-Remaining; 
			} 
			else 
				ReadSize=INPUT_BUFFER_SIZE, 
					ReadStart=InputBuffer, 
					Remaining=0; 
 
			/* Fill-in the buffer. If an error occurs print a message 
			 * and leave the decoding loop. If the end of stream is 
			 * reached we also leave the loop but the return status is 
			 * left untouched. 
			 */ 
			ReadSize=BstdRead(ReadStart,1,ReadSize,BstdFile); 
			ByteCount+=ReadSize;		/* passed to CbFunc */ 
			if(ReadSize<=0) 
			{ 
				if(ferror(InputFp)) 
				{ 
					sprintf(StatMsg,"Read error on bit-stream (%s)\n",strerror(errno)); 
					Status=1; 
				} 
				if(feof(InputFp)) 
					sprintf(StatMsg,"End of input stream\n");  
				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 management 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)) 
			{ 
				GuardPtr=ReadStart+ReadSize; 
				memset(GuardPtr,0,MAD_BUFFER_GUARD); 
				ReadSize+=MAD_BUFFER_GUARD; 
			} 
 
			/* Pipe the new buffer content to libmad's stream decoder 
             * facility. 
			 */ 
			mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining); 
			Stream.error=(mad_error)0; 
		} 
 
		/* Decode the next MPEG frame. The streams is read from the 
		 * buffer, its constituents are break down and stored the the 
		 * Frame structure, ready for examination/alteration or PCM 
		 * synthesis. Decoding options are carried in the Frame 
		 * structure from the Stream structure. 
		 * 
		 * Error handling: mad_frame_decode() returns a non zero value 
		 * when an error occurs. The error condition can be checked in 
		 * the error member of the Stream structure. A mad error is 
		 * recoverable or fatal, the error status is checked with the 
		 * MAD_RECOVERABLE macro. 
		 * 
		 * {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 complete 
		 * its 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 remaining bytes must be moved 
		 * to the beginning 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 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)) 
			{ 
				/* Do not print a message if the error is a loss of 
				 * synchronization or this loss is due to the end of 
				 * stream guard bytes. (See the comments marked {3} 
				 * supra for more informations about guard bytes.) 
				 */ 
				if( Stream.error!=MAD_ERROR_LOSTSYNC || 
				    Stream.this_frame!=GuardPtr || 
					/* Suppress error if ID3 or TAG(??) 
					 * tag was cause: 
					 */ 
				   (Stream.this_frame[0] !=  'I' && 
					Stream.this_frame[1] !='D' && 
					Stream.this_frame[2] !='3') ||				    
					(Stream.this_frame[0] !='T' && 
					Stream.this_frame[1] != 'A' && 
					Stream.this_frame[2] != 'G') ) 
				{ 
					 
						sprintf(StatMsg,"Recoverable frame level error (%s)\n", 
								MadErrorString(&Stream)); 
 
				} 
				continue; 
			} 
			else 
				if(Stream.error==MAD_ERROR_BUFLEN) 
					continue; 
				else 
				{ 
					sprintf(StatMsg,"Unrecoverable frame level error (%s).\n", 
							MadErrorString(&Stream)); 
					Status=1; 
					break; 
				} 
		} 
 
 
		/* 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 
		 * be added or subtracted. The timer module provides several 
		 * functions to operate on such numbers. Be careful there, as 
		 * some functions of libmad's timer module receive some of 
		 * their mad_timer_t arguments by value! 
		 */ 
		FrameCount++; 
		mad_timer_add(&Timer,Frame.header.duration); 
 
 
		/* Send accumulated information to the callback 
		 * This gets called every loop. 
		 */ 
		CbFunc(FrameCount, ByteCount, &Frame.header);		 
 
		 
		/* Set the channel and sample rate if WAV header  
		 * Use 'wavflg' so this only fires once 
		 */ 
		if (wavflg && WavPad == 1) { 
			/* assign number of channels */ 
//			if (Frame.header.mode > MAD_MODE_SINGLE_CHANNEL) { 
				wavh.nChannels = 2; 
//			} 
//			else { 
//				wavh.nChannels = 1; 
//			} 
			wavh.nSamplesPerSec = (int)Frame.header.samplerate; 
			wavflg = NULL;		/* so this is only called once */ 
		} 
 
		 
		/* Once decoded the frame is synthesized to PCM samples. No errors 
		 * are reported by mad_synth_frame(); 
		 */ 
		mad_synth_frame(&Synth,&Frame); 
 
		/* Synthesized samples must be converted from libmad's fixed 
		 * point number to the consumer format. Here we use unsigned 
		 * 16 bit big endian integers on two channels. Integer samples 
		 * are temporarily stored in a buffer that is flushed when 
		 * full. 
		 */ 
		for(i=0;i>8;       //Originally big-endian 
			*(OutputPtr++)=Sample&0xff; 
			*/ 
			*(OutputPtr++)=((Sample>>0) & 0xff); 
			*(OutputPtr++)=((Sample>>8) & 0xff); 
			 
 
 
			/* Right channel. If the decoded stream is monophonic then 
			 * the right output channel is the same as the left one. 
			 */ 
			if(MAD_NCHANNELS(&Frame.header)==2) 
				Sample=Scale(Synth.pcm.samples[1][i], &gRand); 
 
			/* output in 16 bit little-endian */			 
			/* 
			*(OutputPtr++)=Sample>>8;		//Originally big-endian 
			*(OutputPtr++)=Sample&0xff; 
			*/ 
			*(OutputPtr++)=((Sample>>0) & 0xff);			 
			*(OutputPtr++)=((Sample>>8) & 0xff); 
 
 
			/* Flush the output buffer if it is full. */ 
			if(OutputPtr==OutputBufferEnd) 
			{ 
				if(fwrite(OutputBuffer,1,OUTPUT_BUFFER_SIZE,OutputFp)!=OUTPUT_BUFFER_SIZE) 
				{ 
					sprintf(StatMsg,"PCM write error (%s).\n", 
							strerror(errno)); 
					Status=2; 
					break; 
				} 
				OutputPtr=OutputBuffer; 
			} 
		} 
	}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. 
	 */ 
	mad_synth_finish(&Synth); 
	mad_frame_finish(&Frame); 
	mad_stream_finish(&Stream); 
 
	/* If the output buffer is not empty and no error occurred during 
     * the last write, then flush it. 
	 */ 
	if(OutputPtr!=OutputBuffer && Status!=2) 
	{ 
		size_t	BufferSize=OutputPtr-OutputBuffer; 
 
		if(fwrite(OutputBuffer,1,BufferSize,OutputFp)!=BufferSize) 
		{ 
			sprintf(StatMsg,"PCM write error (%s).\n", 
					strerror(errno)); 
			Status=2; 
		} 
	} 
 
	/* Accounting report if no error occurred. */ 
	if(!Status) 
	{ 
		char	Buffer[80]; 
 
		/* The duration timer is converted to a human readable 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 broken into several 
		 * values according some of it's arguments. The units and 
		 * fracunits arguments specify the intended conversion to be 
		 * executed. 
		 * 
		 * The conversion unit (MAD_UNIT_MINUTES in our example) also 
		 * specify the order and kind of conversion specifications 
		 * that can be used in the format string. 
		 * 
		 * It is best to examine libmad's timer.c source-code for details 
		 * of the available units, fraction of units, their meanings, 
		 * the format arguments, etc. 
		 */ 
		mad_timer_string(Timer,Buffer,"%lu:%02lu.%03u", 
						 MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0); 
		sprintf(StatMsg,"%lu frames decoded (%s).\n", 
				FrameCount,Buffer);		 
 
	} 
 
 
	/* close files */ 
	fclose(InputFp); 
	fclose(OutputFp); 
 
 
	/* Write the WAV header to the file */ 
	if (WavPad == 1) { 
		 
		/* get OutFN file size (raw PCM) */ 
		struct stat fstt; 
		int iflde = open(OutFN, O_RDONLY); 
		fstat(iflde, &fstt); 
		close(iflde); 
		 
		/* setup header */ 
		wavh.RIFF = 0x46464952;						/* 'RIFF' */ 
		wavh.size = fstt.st_size-8;	/* total size of wave file, including header */ 
		wavh.WAVE =  0x45564157;					/* 'WAVE' */ 
		wavh.fmt = 0x20746D66;						/* 'fmt ' */ 
		wavh.wBitsPerSample = 16; 
		wavh.wFormatLength = 16; 
		wavh.wFormatTag = 1; 
		wavh.data = 0x61746164;						/* 'data' */ 
		wavh.datasize = (int)fstt.st_size-sizeof(wavh); 
		wavh.nBlockAlign = (short)(wavh.wBitsPerSample / 8 * wavh.nChannels); 
		wavh.nAvgBytesPerSec = wavh.nSamplesPerSec * wavh.nBlockAlign; 
 
		/* write wav header */ 
		OutputFp = fopen(OutFN, "rb+"); 
		fwrite(&wavh, 1, sizeof(wavh), OutputFp);		 
		fclose(OutputFp); 
 
	} 
 
 
	/* That's the end of the world (in the H. G. Wells way). */ 
	return(Status); 
} 
 
/**************************************************************************** 
 * End of file madlldlib.cpp                                                * 
 ****************************************************************************/