
#define TEMPLATE "Names/M,OutDir/K"
#define BUFFERS 64

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/mpega.h>

#include <exec/memory.h>

char Version[]="\0$VER: mpdecode 1.0 (30.10.99) by Thomas Wenzel\0";

struct Library	*MPEGABase;

MPEGA_CTRL        mctrl;
MPEGA_STREAM      *mstream;

struct RDArgs     *rdargs;
struct AnchorPath *APath;
char              *CurrentName;
ULONG             NameCount;
ULONG             Signals;

WORD              *Buffer[MPEGA_MAX_CHANNELS];
WORD              *OutBuffer;

char              OutDir[1024];

struct { /* Args structure */
	char	**Names;
	char	*OutDir;
} Args = {
	NULL,
	NULL
};

struct {
	ULONG	FORMid;
	ULONG	FORMsize;
	ULONG	AIFFid;

  ULONG	FVERid;
  ULONG	FVERsize;
  ULONG	FVERtime;

	ULONG	COMMid;
	ULONG	COMMsize;

	UWORD	nTracks;
	ULONG	nFrames;
	UWORD	bpSmpl;
	char	freq[10];
	ULONG	compType;
	char	compressionName[(sizeof("not compressed")+1)&(~1)];

	ULONG	SSNDid;
	ULONG	SSNDsize;
	ULONG	offset;
	ULONG	blockSize;
} AIFFheader = {
	0x464F524D, // FORM
	NULL,
	0x41494643, // AIFC

	0x46564552, // FVER
	4,
	0xA2805140, // file format version #1

	0x434F4D4D, // COMM
	38,
	2,
	0,
	16,
	0,0,0,0,0,0,0,0,0,0,
	0x4E4F4E45, // NONE
	sizeof("not compressed")-1,'n','o','t',' ','c','o','m','p','r','e','s','s','e','d',0,
	0x53534E44,
	0,
	0,
	0
};


BOOL Init(void);
void Free(void);
void Decode(char *Name);
void ConvertToIeeeExtended(double, char *);

int main(int argc, char **argv) {
	char c;

	printf("\n");
	printf("mpdecode 1.0 by Thomas Wenzel\n");
	printf("-----------------------------\n");
	printf("This program decodes multiple MPEG audio files to AIFF-C.\n");
	printf("Freeware. Provided 'as is'. For non-commercial use only.\n");
	printf("\n");

	if(!Init()) {
		Free();
		return(10);
	}

	if((rdargs = ReadArgs(TEMPLATE, (LONG *)&Args, NULL)) == NULL) {
		PrintFault(IoErr(), "mpdecode");
		Free();
		return(10);
	}

	if(!Args.Names) {
		printf("Usage: mpdecode %s\n", TEMPLATE);
		Free();
		return(5);
	}

	if(Args.OutDir) {
		strcpy(OutDir, Args.OutDir);
		c=OutDir[strlen(OutDir)-1];
		if((c != ':') && (c != '/')) strcat(OutDir, "/");
	}
	else {
		OutDir[0]=0;
	}

	NameCount=0;
	while(Args.Names[NameCount]!=NULL) {
		CurrentName=Args.Names[NameCount];
		if(MatchFirst(CurrentName,APath)==0) {
			do {
				Signals=CheckSignal(SIGBREAKF_CTRL_C);
				if(Signals) break;
				Decode(APath->ap_Buf);
			} while (MatchNext(APath)==0);
			MatchEnd(APath);
			if(Signals) break;
		}
		NameCount++;
	}

	if(Signals) printf("mpdecode: BREAK\n");
	Free();
	return(0);
}

BOOL Init(void) {
	ULONG i;

	MPEGABase=OpenLibrary("mpega.library", 2);
	if(!MPEGABase) {
		printf("Can't open mpega.library v2\n");
		return(FALSE);
	}
	APath=AllocVec(sizeof(*APath)+256, MEMF_PUBLIC|MEMF_CLEAR);
	if(!APath) {
		printf("Out of memory!\n");
		return(FALSE);
	}
	APath->ap_Strlen=255;

	for(i=0; i<MPEGA_MAX_CHANNELS; i++) {
		Buffer[i]=AllocVec(BUFFERS*MPEGA_PCM_SIZE*sizeof(WORD), MEMF_PUBLIC|MEMF_CLEAR);
		if(!Buffer[i]) {
			printf("Out of memory!\n");
			return(FALSE);
		}
	}

	OutBuffer=AllocVec(BUFFERS*MPEGA_PCM_SIZE*sizeof(WORD)*2, MEMF_PUBLIC|MEMF_CLEAR);
	if(!OutBuffer) {
		printf("Out of memory!\n");
		return(FALSE);
	}

	mctrl.bs_access                 = NULL;
	mctrl.layer_1_2.force_mono      = 0;
	mctrl.layer_1_2.mono.freq_div   = 1;
	mctrl.layer_1_2.mono.quality    = 2;
	mctrl.layer_1_2.mono.freq_max   = 48000;
	mctrl.layer_1_2.stereo.freq_div = 1;
	mctrl.layer_1_2.stereo.quality  = 2;
	mctrl.layer_1_2.stereo.freq_max = 48000;
	mctrl.layer_3.force_mono        = 0;
	mctrl.layer_3.mono.freq_div     = 1;
	mctrl.layer_3.mono.quality      = 2;
	mctrl.layer_3.mono.freq_max     = 48000;
	mctrl.layer_3.stereo.freq_div   = 1;
	mctrl.layer_3.stereo.quality    = 2;
	mctrl.layer_3.stereo.freq_max   = 48000;
	mctrl.check_mpeg                = 1;
	mctrl.stream_buffer_size        = 0;
}

void Free(void) {
	ULONG i;

	for(i=0; i<MPEGA_MAX_CHANNELS; i++) {
		if(Buffer[i]) FreeVec(Buffer[i]);
	}
	if(OutBuffer) FreeVec(OutBuffer);

	if(rdargs)    FreeArgs(rdargs);
	if(APath)     FreeVec(APath);
	if(MPEGABase) CloseLibrary(MPEGABase);
}

void Decode(char *Name) {
	ULONG i,j,ptr;
	FILE *OutFile;
	char OutFileName[1024];

	ULONG BadFrames;
	LONG Count;
	ULONG SamplesInFile;
	ULONG SamplesInBuf;
	ULONG BytesWritten;
	ULONG Length, Time;
	BOOL WriteError;
	BOOL eof;

	WriteError=FALSE;
	eof=FALSE;

	if(OutDir[0]) {
		sprintf(OutFileName, "%s%s.aiff", OutDir, FilePart(Name));
	}
	else {
		sprintf(OutFileName, "%s.aiff", Name);
	}

	printf("\nFilename: %s -> %s\n", Name, OutFileName);
	mstream=MPEGA_open(Name, &mctrl);
	if(!mstream) {
		printf("Can't open input file!\n");
		return;
	}

	OutFile=fopen(OutFileName, "wb");
	if(!OutFile) {
		printf("Can't open output file!\n");
		MPEGA_close(mstream);
		return;	
	}

	if(fwrite(&AIFFheader, sizeof(AIFFheader), 1, OutFile) != 1) WriteError=TRUE;

	Length=mstream->ms_duration/1000;

	SamplesInFile=0;
	BadFrames=0;

	while(!eof) {
		if(MPEGA_time(mstream, &Time)==0) {
			printf("Decoding: %ds/%ds (%d%%)", Time/1000, Length, Time/Length/10);
			if(BadFrames) printf(" [%d bad frames]", BadFrames);
			printf("\r");
		}
		flushall();

		ptr=0;
		SamplesInBuf=0;
		for(i=0; i<BUFFERS; i++) {
			Signals=CheckSignal(SIGBREAKF_CTRL_C);
			if(Signals || WriteError) break;
			Count=MPEGA_decode_frame(mstream, Buffer);

			if(Count==MPEGA_ERR_BADFRAME) BadFrames++;

			if(Count < 0) {
				if(Count==MPEGA_ERR_EOF) {
					eof=TRUE;
				}
				else {
					Count=0;
				}
			}

			if(Count>BUFFERS*MPEGA_PCM_SIZE) Count=BUFFERS*MPEGA_PCM_SIZE;

			if(Count>0) {
				switch(mstream->channels) {
					case 1:
						for(j=0; j<Count; j++) {
							OutBuffer[ptr++] = Buffer[0][j];
						}
					break;
					case 2:
						for(j=0; j<Count; j++) {
							OutBuffer[ptr++] = Buffer[0][j];
							OutBuffer[ptr++] = Buffer[1][j];
						}
					break;
				}
			}
			SamplesInBuf += Count;
		}

		if(Signals || WriteError) break;

		if(SamplesInBuf) {
			if(fwrite(OutBuffer, SamplesInBuf, 2*mstream->channels, OutFile) != 2*mstream->channels) WriteError=TRUE;
			SamplesInFile += SamplesInBuf;
		}
	}

	BytesWritten=SamplesInFile*2*mstream->channels;

	AIFFheader.bpSmpl   = 16;
	AIFFheader.nFrames  = SamplesInFile;
	AIFFheader.SSNDsize = BytesWritten +4+4; // +4+4: offset and blockSize
	ConvertToIeeeExtended((double)mstream->frequency, AIFFheader.freq);
	AIFFheader.nTracks  = mstream->channels;
	AIFFheader.FORMsize = BytesWritten+70 +4+4; // +4+4: offset and blockSize

	fseek(OutFile, 0, SEEK_SET);
	fwrite(&AIFFheader, sizeof(AIFFheader), 1, OutFile);

	MPEGA_close(mstream);
	fclose(OutFile);
	if(WriteError) printf("\nCan't write to output file!\n");
	if(Signals || WriteError) printf("\n");
	else                      printf("Decoding: %ds/%ds (%d%%)\n", Length, Length, 100);
}


/*
 * C O N V E R T   T O   I E E E   E X T E N D E D
 */

/* Copyright (C) 1988-1991 Apple Computer, Inc.
 * All rights reserved.
 *
 * Machine-independent I/O routines for IEEE floating-point numbers.
 *
 * NaN's and infinities are converted to HUGE_VAL or HUGE, which
 * happens to be infinity on IEEE machines.  Unfortunately, it is
 * impossible to preserve NaN's in a machine-independent way.
 * Infinities are, however, preserved on IEEE machines.
 *
 * These routines have been tested on the following machines:
 *    Apple Macintosh, MPW 3.1 C compiler
 *    Apple Macintosh, THINK C compiler
 *    Silicon Graphics IRIS, MIPS compiler
 *    Cray X/MP and Y/MP
 *    Digital Equipment VAX
 *
 *
 * Implemented by Malcolm Slaney and Ken Turkowski.
 *
 * Malcolm Slaney contributions during 1988-1990 include big- and little-
 * endian file I/O, conversion to and from Motorola's extended 80-bit
 * floating-point format, and conversions to and from IEEE single-
 * precision floating-point format.
 *
 * In 1991, Ken Turkowski implemented the conversions to and from
 * IEEE double-precision format, added more precision to the extended
 * conversions, and accommodated conversions involving +/- infinity,
 * NaN's, and denormalized numbers.
 */


# define FloatToUnsigned(f)      ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1)

void ConvertToIeeeExtended(double num, char *bytes) {
	int			sign;
	int			expon;
	double	fMant, fsMant;
	ULONG		hiMant, loMant;

	if (num < 0) {
		sign = 0x8000;
		num *= -1;
	}
	else sign = 0;

	if (num == 0) {
		expon = 0; hiMant = 0; loMant = 0;
	}
	else {
		fMant = frexp(num, &expon);
		if ((expon > 16384) || !(fMant < 1)) {    /* Infinity or NaN */
			expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */
		}
		else {    /* Finite */
			expon += 16382;
			if (expon < 0) {    /* denormalized */
				fMant = ldexp(fMant, expon);
				expon = 0;
			}
			expon |= sign;
			fMant = ldexp(fMant, 32);          
			fsMant = floor(fMant); 
			hiMant = FloatToUnsigned(fsMant);
			fMant = ldexp(fMant - fsMant, 32); 
			fsMant = floor(fMant); 
			loMant = FloatToUnsigned(fsMant);
		}
	}
	bytes[0] = expon >> 8;
	bytes[1] = expon;
	bytes[2] = hiMant >> 24;
	bytes[3] = hiMant >> 16;
	bytes[4] = hiMant >> 8;
	bytes[5] = hiMant;
	bytes[6] = loMant >> 24;
	bytes[7] = loMant >> 16;
	bytes[8] = loMant >> 8;
	bytes[9] = loMant;
}
