/* compile with cmix.o -lNeXT_s -lsys_s */
/* bugs so far.  Dont seem to be able to write 1k header, just writes
  NeXT header, have to write IRCAM header over it, destroying about
  1000 samples 
  mixsnd and deletesnd seem incompatible
  */

#import "/musr/H/ugens.h"
#import <objc/objc.h>
#import <soundkit/soundkit.h>
#import "Transport.h"
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>

int NBYTES = 16;
#define MAXFILES 90
id inputSound[MAXFILES];
id pasteSound;
id tempSound;
id myTransport;
int Srate,nchannels;
SNDSoundStruct *Sndheader;
#define ARRAYSIZE 1024
float array[ARRAYSIZE];

double opensnd(p,n_args)
float *p;
{
/* opensnd("filename",num) where num >=0 <MAXFILES */
/* file number 0 is base file, it is opened first and must start at time 0 */

	int i,filenum,error;
	char *filename;
	i = (int) p[0];
	filename = (char *) i;
	filenum = p[1];
	printf("Opening file %s as number %d\n",filename,filenum);
	inputSound[filenum] = [Sound new];
	error = [inputSound[filenum] readSoundfile:filename];
	if(error) die(error);
	if(!filenum) {
		error = [pasteSound setDataSize:[inputSound[filenum] dataSize]  
			dataFormat:[inputSound[filenum] dataFormat]
			samplingRate:[inputSound[filenum] samplingRate]  
			channelCount:[inputSound[filenum] channelCount] infoSize:1004]; 
		if(error) die(error);

		Sndheader = [pasteSound soundStruct];
		Sndheader->dataLocation = 1024; /* to make space for IRCAM header */

		/* use file 0 as base, e.g. must start at time 0 */
		if(!filenum) error = 
	 	[pasteSound copySamples:inputSound[filenum] 
		                            at:0 count:[inputSound[filenum] sampleCount]]; 		
	 	if(error) die(error);

		Srate = (int)[pasteSound samplingRate];
		SR = Srate; /* to communicate with Cmix */
		nchannels = [pasteSound channelCount];
	}
}
double mixsnd(p,n_args)
float *p;
{
/* mixsnd(filenum,inskip,outskip,endtime) if endtime <0 it is duration, if == 0
   it means go to eof */
/* this doesnt seem to work if deletesnd is also used, dont know why */

	int i,j,ichannels,filenum,inskip,outskip,endtime,error;

	filenum = p[0];

	ichannels = [inputSound[filenum] channelCount];
	inskip = Srate * p[1] * ichannels;
	outskip = Srate * p[2] * nchannels;
	p[3] = (p[3] > 0) ? p[3] : p[1]-p[3]; // if neg == endtime
	endtime = Srate * p[3] * ichannels;
	printf("file %d: inputskip=%f, outputskip=%f, inputend=%f\n",filenum,p[1],p[2],p[3]);
	if(mixSounds(inputSound[filenum],pasteSound,inskip,outskip,endtime) < 0) {
		printf("whoops\n");
		exit(-1);
	}
}
double alter(p,n_args)
float *p;
{	
	int filenum,i,j,jcount,kcount,inskip,dur;
	unsigned char *adata;
        short *ardata;
	float tabvals[4],mult;
	id tSound;
	
	filenum = p[0];
	if(filenum) tSound = inputSound[filenum];
	else  tSound = pasteSound;
	inskip = p[1] * Srate;
	p[2] = (p[2] < 0) ? -p[2] : p[2]-p[1];
	printf("file no. %d: start at %f, end at%f\n",filenum,p[1],p[2]+p[1]);
	dur = ( p[2] * Srate + inskip) * [tSound channelCount];
	tableset(p[2],ARRAYSIZE,tabvals);
	jcount = Srate/(200/[tSound channelCount]);
	for(i=inskip; i<dur; i += jcount) {
			mult=tablei((i-inskip)/[tSound channelCount],array,tabvals);
			kcount = (jcount > (dur-i)) ? jcount : dur - i;
			alterSounds(tSound,mult,i,jcount);
		}

}

double ssetline(p,n_args)
float *p;
{
	int i;
	setline(p,n_args,ARRAYSIZE,array);
}
double compactsnd(p,n_args)
float *p;
{
	int i,error;
	i = p[0];
	printf("compacting file %d\n",i);
	if(i) {
	//	if([inputSound[i] needsCompacting])
			error=[inputSound[i] compactSamples];
		if(error) die(error);
		}
	else {
	//	if([pasteSound needsCompacting])
			error=[pasteSound compactSamples];
		if(error) die(error);
		}
}

double splicesnd(p,n_args)
float *p;
{
/* splicesnd(filenum,inskip,outskip,endtime) if endtime < 0 it is duration, if
   == 0 it means go to eof */

	int filenum,inskip,outskip,duration,error;
	filenum = p[0];
	inskip = Srate * p[1];
	outskip = Srate * p[2];
	p[3] = (p[3] >= 0) ? p[3] : p[1]-p[3]; // if neg == duration
	printf("file number %d, inskip=%f, outskip=%f, end inputtime=%f\n",filenum,p[1],p[2],p[3]);
	duration = Srate * p[3];
	if(!duration) duration = [inputSound[filenum] sampleCount] - inskip;
	error = [tempSound copySamples:inputSound[filenum] at:inskip count:duration];
	if(error) die(error);
	error = [pasteSound insertSamples:tempSound at:outskip];
	if(error) die(error);
}

double copysnd(p,n_args)
float *p;
{
	int error,filein,fileout;
	/* simply replaces fileout with filein, thus destroying fileout */
	filein = p[0];
	fileout = p[1];
	printf("copy from file %d to file %d\n",filein,fileout);
	if(!filein) {
		 error = [inputSound[fileout] copySound:pasteSound];
		if(error) die(error);
		}
	else if(!fileout) {
		error = [pasteSound copySound:inputSound[filein]];
	        if(error) die(error);
		}
	else {
		error = [inputSound[fileout] copySound:inputSound[filein]];
		if(error) die(error);
		}
}

double playsnd(p,n_args)
float *p;
{

/* playsnd(start,end) plays base file, if end < 0 it is duration, if 0 it
means play to end of file.  Note in this case that it will play whole thing
including spliced ends of sections */

	id newSound;
	int inskip,duration,error;
	
	inskip = p[0] * Srate;
	p[1] = (p[1] <= 0) ? -p[1] : p[1]-p[0];
	duration = (p[1] * Srate) ? p[1] * Srate : [pasteSound sampleCount] - inskip;
	printf("play starting at time %f for %f seconds\n",p[0],(float)duration/Srate);
	if(!inskip && !duration) { 
		[myTransport play:(id)pasteSound];
		[myTransport wait];
		}
	else {
		newSound = [Sound new];
		error = [newSound copySamples:pasteSound at:inskip count:duration];
		if(error) die(error);
		[myTransport play:(id)newSound];
		[myTransport wait];
		[newSound free];
	}	
}
double deletesnd(p,n_args)
float *p;
{
/* deletesnd(filenum,inskip,end) if end < 0 it indicates duration */

	int filenum,start,dur,error;
	filenum = p[0];
	start = p[1] * Srate;
	p[2] = (p[2] < 0.) ? -p[2] : p[2] - p[1];
	printf("file number %d, delete starting at time %f for %f seconds\n",filenum,p[1],p[2]);
	dur = p[2] * Srate;
	if(filenum) {
		if(!dur) dur = [inputSound[filenum] sampleCount] - start;
		error = [inputSound[filenum] deleteSamplesAt:start count:dur];
		if(error) die(error);
	}
	else {
		if(!dur) dur = [pasteSound sampleCount] - start;
		error = [pasteSound deleteSamplesAt:start count:dur];
		if(error) die(error);
	}
}

double writesnd(p,n_args)
float *p;
{
	int error,i,nbytes,inskip,duration,rest;
	id newSound;
	char *filename;
	i = (int) p[0];
	filename = (char *) i;
	inskip = p[1] * Srate;
	p[2] = (p[2] < 0) ? -p[2] : p[2]-p[1];
	duration = (p[2] * Srate) ? p[2] * Srate : [pasteSound sampleCount] - inskip;
	printf("create file %s, from scratch board at time %f for %f seconds\n"
	                                                     ,filename,p[1],(float)duration/Srate);
	if( inskip || duration) {
		error = [pasteSound deleteSamplesAt:0 count:inskip];
		if(error) die(error);
		rest = [pasteSound sampleCount] - duration;
		error = [pasteSound deleteSamplesAt:duration count:rest];
		if(error) die;
		}
	printf("duration and rest %d %d\n",duration,rest);
	error = [pasteSound writeSoundfile:filename];

	if(error) die(error);
	printf("done\n");
}
	
profile()
{
	UG_INTRO("opensnd",opensnd);
	UG_INTRO("splicesnd",splicesnd);
	UG_INTRO("playsnd",playsnd);
	UG_INTRO("deletesnd",deletesnd);
	UG_INTRO("writesnd",writesnd);
	UG_INTRO("mixsnd",mixsnd);
	UG_INTRO("compactsnd",compactsnd);
	UG_INTRO("setline",ssetline);
	UG_INTRO("alter",alter);
	UG_INTRO("copysnd",copysnd);
	pasteSound = [Sound new];
	tempSound = [Sound new];
	myTransport = [Transport new];
}
die(error)
{
	printf("%s\n",SNDSoundError(error));
	exit(-1);
}
