
/* Generated by Interface Builder */

#import "LPApp.h"
#import "SoundInfo.h"
#import "ScrollDis.h"
#import "lpsf.h"
#import "lp.h"
#import <stdio.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <sys/file.h>
#import <sound/soundstruct.h>
#import <appkit/appkit.h>
#import <streams/streams.h>

#define SNDSIZE sizeof (SNDSoundStruct)
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
#define DUMPFILE "/tmp/lpdump.tmp"

static BOOL noquery = NO, lptouched = NO;
static char AppPath[256];
SNDSoundStruct sndh;
static int fildes;
extern char **NXArgv, *getenv();
extern void pclose();
static float soundtime;
static int sample_size;
static struct stat statbuf;
static id _status, _window;

/* interface to anallpc, ptrack, lpconcat, lpdump */

char *comment;
int npoles, lpframeoffset, ptframesize, ptframeoffset;
float lpinskip, lpdur, ptframerate, ptinskip, ptdur, sr, ptlowest, pthighest;
char soundFile[256], lpFile[256], ptFile[256];
extern void lpdump(FILE *outfile);
extern int lpconcat(), anallpc(), ptrack();

static void alert (msg)
const char *msg;
{
	void NXAlert();
	NXAlert (msg, NULL, NULL, NULL);
	return;
}

static BOOL getOpenPath (buf, file, sfx)
char *buf;
char const *file, *sfx;
{
	static int init = 1;
	static char dir[512];
	id openPanel;
	int rtn;
	char *q;
	char const *fileTypes[2] = {NULL, NULL};
   
   	if (init) {
		init = 0;
		if (q = getenv ("HOME")) strcpy (dir, q);
		else strcpy (dir, ".");
		}

    [NXWait set];
    NXPing();
    openPanel = [OpenPanel new];
    [NXArrow set];
    NXPing();
    fileTypes[0] = sfx;
    [openPanel allowMultipleFiles:NO];
    if ([openPanel runModalForDirectory:dir file:file types:fileTypes]) {
	strcpy (buf, [openPanel filename]);
	strcpy (dir, buf);
	*(rindex (dir, '/')) = '\0';
	rtn = YES;
	}
    else rtn = NO;
    return rtn;
}

void report (char *s)
{
	[_status setStringValue: s];
	[_window flushWindow];
	return;
}

static void displayFileName (id form, char *s)
{
	char *pt, name[256];
	
	pt = rindex (s, '/');
	strcpy (name, pt + 1);
	strcat (name, ":");
	*pt = '\0';
	strcat (name, s);
	*pt = '/';
	[form setStringValue: name];
	return;
}

@implementation LPApp

- setMyWindow:anObject
{
	char *p;
	_window = myWindow = anObject;
	[myWindow makeKeyAndOrderFront:self];
	strcpy (AppPath, NXArgv[0]);
	if (p = rindex (AppPath, '/')) *p = '\0';
	else strcpy (AppPath, ".");
	soundFile[0] = '\0';
	lpFile[0] = '\0';
	ptFile[0] = '\0';
	return self;
}

- setStatus:anObject
{
	_status = status = anObject;
	return self;
}

- setPtFrameOffset:anObject
{
	ptFrameOffset = anObject;
	return self;
}

- setLpFrameRate:anObject
{
	lpFrameRate = anObject;
	return self;
}

- setPtLowEst:anObject
{
    ptLowEst = anObject;
    return self;
}

- setPtSkip:anObject
{
    ptSkip = anObject;
    return self;
}

- setPtDuration:anObject
{
    ptDuration = anObject;
    return self;
}

- setPtFrameSize:anObject
{
    ptFrameSize = anObject;
    return self;
}

- setLpFrameOffset:anObject
{
    lpFrameOffset = anObject;
    return self;
}

- setLpComment:anObject
{
    lpComment = anObject;
    return self;
}

- setLpPoles:anObject
{
    lpPoles = anObject;
    return self;
}

- setPtHighEst:anObject
{
    ptHighEst = anObject;
    return self;
}

- setLpDuration:anObject
{
    lpDuration = anObject;
    return self;
}

- setLpSkip:anObject
{
    lpSkip = anObject;
    return self;
}

- setLpButton:anObject
{
    lpButton = anObject;
    return self;
}

- setPtButton:anObject
{
    ptButton = anObject;
    return self;
}

- setMrgButton:anObject
{
    mrgButton = anObject;
    return self;
}

- setSoundFileName:anObject
{
	soundFileName = anObject;
	return self;
}

- setLpFileName:anObject
{
	lpFileName = anObject;
	return self;
}

- setPtFileName:anObject
{
	ptFileName = anObject;
	return self;
}

- setOffset:sender
{
	int offset;
	
	if ((offset = [lpFrameOffset intValue]) < 1) offset = 1;
	if (offset > HALFFRAMAX) offset = HALFFRAMAX;
	[lpFrameOffset setIntValue:offset];
	[ptFrameOffset setIntValue:offset];
	[lpFrameRate setFloatValue:sr / (float) offset];
	return self;
}

- setRate:sender
{
	float rate;
	int offset;
	
	if ((rate = [lpFrameRate floatValue]) <= 0.0) rate = 1.0;
	offset = sr / rate;
	if (offset > HALFFRAMAX) offset = HALFFRAMAX;
	[lpFrameRate setFloatValue:sr / (float) offset];
	[lpFrameOffset setIntValue:offset];
	[ptFrameOffset setIntValue:offset];
	return self;
}

- setPoles:sender
{
	if ([lpPoles intValue] > POLEMAX) [lpPoles setIntValue:POLEMAX];
	return self;
}

- setFrame:sender
{
	if ([ptFrameSize intValue] > PFRAMAX)
		[ptFrameSize setIntValue:PFRAMAX];
	return self;
}

- (int)openFile:(const char *)path ok:(int *)ok
{
	noquery = YES;
	strcpy (soundFile, path);	
	[self openSoundFile:self];
	noquery = NO;
	*ok = YES;
	[myWindow makeKeyAndOrderFront:self];
	return 0;
}

- (BOOL) checkLPFile:(char *)name
{
	int fd, n;
	char lpbuf[LPBUFSIZ];
	LPHEADER *lph;
	
	if((fd = open (name, O_RDONLY)) < 0) return NO;
	n = read (fd, lpbuf, LPBUFSIZ);
	close (fd);
	if (n < sizeof (LPHEADER)) return NO;
	lph = (LPHEADER *) lpbuf;
	if (lph->lpmagic != LP_MAGIC) return NO;
	[lpPoles setIntValue:lph->npoles];
	[lpFrameRate setFloatValue:lph->framrate];
	[lpFrameOffset setIntValue:lph->srate / lph->framrate];
	if (lph->headersize > sizeof (LPHEADER))
		[lpComment setStringValue:lph->text];
	return YES;
}

- openSoundFile:sender
{
	char *q;
	int fd;
	
	if (!noquery) {
		if (!getOpenPath (soundFile, NULL, "snd")) return self;
		}
	if ((fildes = open (soundFile, O_RDONLY)) < 0) {
		alert ("Can't open sound file.");
		soundFile[0] = '\0';
		return self;
		}
	if ((read (fildes, &sndh, SNDSIZE) != SNDSIZE) ||
		   (sndh.magic != SND_MAGIC)) {
		alert ("Corrupt sound file.");
		soundFile[0] = '\0';
		close (fildes);
		return self;
		}
	if (sndh.channelCount > 1) {
		alert ("Can't analyze multi-channel sound.");
		soundFile[0] = '\0';
		close (fildes);
		return self;
		}
	close (fildes);
	switch (sndh.dataFormat) {
		case SND_FORMAT_MULAW_8:
		case SND_FORMAT_LINEAR_8: sample_size = 1; break;
		case SND_FORMAT_LINEAR_16: sample_size = 2; break;
		case SND_FORMAT_LINEAR_24: sample_size = 3; break;
		case SND_FORMAT_LINEAR_32:
		case SND_FORMAT_FLOAT: sample_size = 4; break;
		case SND_FORMAT_DOUBLE: sample_size = 8; break;
		default:
			alert ("Invalid data format.");
			soundFile[0] = '\0';
			close (fildes);
			return self;		
		}
	sr = (float) sndh.samplingRate;
	soundtime =
		(float) sndh.dataSize / (float) sample_size / sr;
	close (fildes);
	displayFileName (soundFileName, soundFile);
	[lpFileName setStringValue:""];
	[ptFileName setStringValue:""];
	[lpDuration setFloatValue:soundtime];
	[ptDuration setFloatValue:soundtime];
	strcpy (lpFile, soundFile);
	*(rindex (lpFile, '.')) = '\0';
	strcat (lpFile, ".lpc");
	if ([self checkLPFile:lpFile]) displayFileName (lpFileName, lpFile);
	strcpy (ptFile, soundFile);
	*(rindex (ptFile, '.')) = '\0';
	strcat (ptFile, ".pt");
	if (stat (ptFile, &statbuf) == 0) displayFileName (ptFileName, ptFile);
	return self;
}

- openLPFile:sender
{
	char buf[256];
	
	if (getOpenPath (buf, NULL, "lpc"))
		if ([self checkLPFile:buf]) {
			strcpy (lpFile, buf);
			displayFileName (lpFileName, lpFile);
			lptouched = YES;
			}
	return self;
}

- openPTFile:sender
{
	char buf[256];
	
	if (getOpenPath (buf, NULL, "pt"))
		if (stat (buf, &statbuf) == 0) {
			strcpy (ptFile, buf);
			displayFileName (ptFileName, ptFile);
			}
	return self;
}

- help:sender
{
	char helpfile[256], command[256];
	NXRect frame = {{100.0, 100.0}, {500.0, 300.0}};
	NXStream *strm, *NXOpenFile();
	FILE *pipe, *popen();
	
	sprintf (helpfile, "%s/%s", AppPath, "LPC.man");
	if (stat (helpfile, &statbuf) != 0) {
		alert ("Can't find help file.");
		return self;
		}
	if (helpView == nil)
		helpView = [ScrollingTextDisplay newFrame:&frame
						 title:"LPC Manual"];
	sprintf (command, "nroff -man %s | tr -d '_\\010'", helpfile);
	if ((pipe = popen (command, "r")) == NULL) {
		alert ("Sorry, no help.");
		fprintf (stderr, "failed command: %s\n", command);
		return self;
		}
	strm = NXOpenFile (fileno (pipe), NX_READONLY);
	[helpView show];
	[helpView readText:strm burst:YES];
	NXClose (strm);
	pclose (pipe);
	return self;
}

- free
{
	if (helpView != nil) [helpView free];
	if (dumpView != nil) [dumpView free];
	if (soundView != nil) [soundView free];
	self = [super free];
	return self;
}

- soundInfo:sender
{
	if (soundFile[0] == '\0') {
		alert ("No soundfile open.");
		return self;
		}
	if (soundView == nil) soundView = [SoundInfo new];
	if (![soundView displaySoundFile:soundFile])
		alert ("Missing or malformed soundfile.");
	return self;
}

- doLP:sender
{
	if (soundFile[0] == '\0') {
		alert ("No soundfile open.");
		return self;
		}
	npoles = max (min ([lpPoles intValue], POLEMAX), 1);
	[lpPoles setIntValue:npoles];
	lpframeoffset = min ([lpFrameOffset intValue], HALFFRAMAX);
	[lpFrameOffset setIntValue:lpframeoffset];
	[lpFrameRate setFloatValue: sr / lpframeoffset];
	lpdur = [lpDuration floatValue];
	lpinskip = min ([lpSkip floatValue], lpdur);
	[lpSkip setFloatValue:lpinskip];
	comment = [lpComment stringValue];
	[myWindow flushWindow];
	if (anallpc ()) {
		lptouched = YES;
		displayFileName (lpFileName, lpFile);
		}
	return self;
}

- doPitch:sender
{
	if (soundFile[0] == '\0') {
		alert ("No soundfile open.");
		return self;
		}
	ptframesize = min ([ptFrameSize intValue], PFRAMAX);
	[ptFrameSize setIntValue:ptframesize];
	ptframeoffset = min ([ptFrameOffset intValue], HALFFRAMAX);
	[ptFrameOffset setIntValue:ptframeoffset];
	ptlowest = (float) [ptLowEst intValue];
	pthighest = (float) [ptHighEst intValue];
	ptdur = [ptDuration floatValue];
	ptinskip = min ([ptSkip floatValue], ptdur);
	[ptSkip setFloatValue:ptinskip];
	[myWindow flushWindow];
	if (ptrack ()) displayFileName (ptFileName, ptFile);
	return self;
}

- doMerge:sender
{
	int frameSize;

	if (![self checkLPFile:lpFile]) {
		alert ("LP file missing or corrupt.");
		return self;
		}
	if (stat (ptFile, &statbuf) != 0) {
		alert ("Pitch file missing.");
		return self;
		}
	if ((frameSize = [ptFrameOffset intValue]) == 0) frameSize = 1;
	ptframerate = sr / frameSize;
	if (lpconcat ()) lptouched = YES;
	return self;
}

- doAuto:sender
{
	[lpButton performClick:self];
	[ptButton performClick:self];
	[mrgButton performClick:self];
	return self;
}

- lpDump:sender
{
	FILE *tmpfile, *fopen();
	NXRect frame = {{100.0, 50.0}, {500.0, 300.0}};
	NXStream *strm, *NXOpenFile();
	
	if (stat (lpFile, &statbuf) != 0) {
		alert ("No LP file.");
		return;
		}
	if (dumpView == nil)
		dumpView = [ScrollingTextDisplay newFrame:&frame
						 title:"LP Dump"];
	[dumpView show];
	if (!lptouched) return nil;
	tmpfile = fopen (DUMPFILE, "w");
	lpdump (tmpfile);
	fclose (tmpfile);
	tmpfile = fopen (DUMPFILE, "r");
	strm = NXOpenFile (fileno (tmpfile), NX_READONLY);
	[dumpView readText:strm burst:YES];
	NXClose (strm);
	fclose (tmpfile);
	unlink (DUMPFILE);
	lptouched = NO;
	return self;
}

@end
