#import <stdio.h>
#import <soundkit/Sound.h>

/* Shorthand macros to handle SNDSoundStructs.
 * NSAMPS(s) = number of samples in fragment s
 * DATA(s) = Pointer to samples in fragment s
 */
#define NSAMPS(s)	(s->dataSize / 2)
#define DATA(s)		((char *)s + s->dataLocation)

#define MIN(a,b)	((a > b) ? b : a)

/* mixSounds()
 *	Mixes part of one sound onto another sound.
 *	This function will work with fragmented Sound objects.
 *	Returns -1 on error, 1 if OK.
 *	BUG: Only works with mono, short files.
 * jwp@silvertone.Princeton.EDU, 2/23/90
 */

int mixSounds(inSound,outSound,inskip,outskip,nsamps)
id inSound;			/* Input Sound object */
id outSound;			/* Output Sound (the one we mix to) */
int inskip;			/* First sample from input sound */
int outskip;			/* First sample from output sound */
int nsamps;			/* Total number of samples to mix */
{
	SNDSoundStruct **inList, **outList;		/* Pointers to the
							 * lists of Sound
							 * fragments
							 */
	SNDSoundStruct *fakein[2], *fakeout[2];		/* Phoney fragment
							 * lists for un-
							 * fragmented Sounds
							 */
	SNDSoundStruct *inStruct, *outStruct;		/* Pointers to the
							 * actual fragments
							 */
	short *in, *out;		/* Pointers to data */
	int insamps,outsamps;		/* Number of samples in fragments */
	int n, blocksize;		/* For loop control */
int i = 0;

//fprintf(stderr,"mixSounds: inskip:%d outskip:%d nsamps:%d\n",inskip,outskip,nsamps);

/* Check the format of the Sound objects.  If they're fragmented,
 * then set inStruct to point to the fragment list.  Otherwise,
 * make a fake fragment list and shove the SNDSoundStruct pointer
 * into it (this simplifies things later on).
 */
	if ([inSound needsCompacting])
		inList = (SNDSoundStruct **)[inSound data];
	else {
		fakein[0] = [inSound soundStruct];
		fakein[1] = NULL;
		inList = fakein;
	}
	if ([outSound needsCompacting])
		outList = (SNDSoundStruct **)[outSound data];
	else {
		fakeout[0] = [outSound soundStruct];
		fakeout[1] = NULL;
		outList = fakeout;
	}

	inStruct = *inList++;
	outStruct = *outList++;

/* Get a starting sample pointer for both sounds by going
 * through their fragment lists until we hit the right spot.
 * Set up insamps and outsamps to give the number of samples left
 * in the first fragment.
 */
	while (inskip) {
//fprintf(stderr,"mixSounds: inSound fragment %d\n",i++);
		if (NSAMPS(inStruct) > inskip)
			break;
//fprintf(stderr,"\t\t... going to next fragment\n");
		inskip -= NSAMPS(inStruct);
		if ((inStruct = *inList++) == NULL) {
			fprintf(stderr,"mixSounds: inskip too large\n");
			return -1;
		}
	}
	in = (short *)DATA(inStruct) + inskip;
	insamps = NSAMPS(inStruct) - inskip;	/* # of samples left in frag */

i=0;
	while (outskip) {
//fprintf(stderr,"mixSounds: outSound fragment %d\n",i++);
		if (NSAMPS(outStruct) > outskip)
			break;
//fprintf(stderr,"\t\t... going to next fragment\n");
		outskip -= NSAMPS(outStruct);
		if ((outStruct = *outList++) == NULL) {
			fprintf(stderr,"mixSounds: outskip too large\n");
			return -1;
		}
	}
	out = (short *)DATA(outStruct) + outskip;
	outsamps = NSAMPS(outStruct) - outskip;	/* # of samples left in frag */

/* Main loop:
 * Here we take the smallest of three numbers:  the number of samples in
 * the input fragment, the number of samples in the output fragment,
 * or the number of samples left to mix.  We then mix this many samples.
 * Then reset all the numbers for the next pass.  Each block of samples
 * is guaranteed continguous, so that we can do the mixing as fast as
 * possible.
 */

	while (nsamps > 0) {
		blocksize = MIN(insamps,outsamps);

fprintf(stderr,"mixSounds:  blocksize:%d nsamps:%d n:%d\n",
	blocksize,nsamps,MIN(blocksize,nsamps));

		for (n = MIN(blocksize,nsamps); n; n--, out++, in++)
			*out += *in;
		if (!(insamps -= blocksize)) {
			if ((inStruct = *inList++) == NULL)
				break;
			insamps = NSAMPS(inStruct);
			in = (short *)DATA(inStruct);
		}
		if (!(outsamps -= blocksize)) {
			if ((outStruct = *outList++) == NULL)
				break;
			outsamps = NSAMPS(outStruct);
			out = (short *)DATA(outStruct);
		}
		nsamps -= blocksize;
	}
	return 1;
}
