#!/usr/bin/perl

# smogrify - Copyright G. Finch 2003
# Released under the GPL 2.0 - see file COPYING for details


##################################################################

# these MUST match with the definition in the GUI
$rc_filename=".lives";
$gui_bootstrap_file="/tmp/.smogrify";


# default temporary directory (can be overridden from the GUI)
$default_tmpdir="/tmp/livestmp/";

$umask=0177;

###################################################################

# default values
$video_player="internal";
$encoder="mencoder";

    
###################################################################
# Do not change these except for testing

$background=1;
$img_ext=".jpg";

$version="0.4.5";

###################################################################


$uid=$>;
$gid=$);
$gid=~ s/\W.*//;


$gui_bootstrap_file.=".".$uid.".".$gid;

if (defined($ARGV[0])) {
    $command=$ARGV[0];


## blocking calls

    if (!($command eq "report")) {
	$home=&get_home_dir;
	$tmpdir=&rc_get("tempdir");
    }

    if ($command eq "cancel" ) {
	$handle=$ARGV[1];
	$pidfile="$tmpdir/$handle/.pid";
	$statusfile="$tmpdir/$handle/.status";
	$wait_for_pid=1;
	if (defined $ARGV[2]) {
	    $wait_for_pid=-1;
	}


	while ($wait_for_pid) {
	    if (defined(open IN,"< $pidfile")) {
		read IN,$target_pid,8;
		close IN;
		if ($target_pid eq NULL) {
		    exit 1;
		}
		# make sure pidfile has same group and owner, otherwise exit
		@stat=stat($pidfile);
		if (!($uid==@stat[4]&&$gid==@stat[5])) {
		    die "Unable to stop process $target_pid.\n";
		}
		#print "Attempting to stop process ".$target_pid."\n";
		`kill -9 $target_pid`;
		unlink $pidfile;
		unlink $statusfile;
		exit 0;
	    }

	    if (defined(open IN,"< $statusfile")) {
		read IN,$status,9;
		close IN;
		# note: $status could also be "error", "video_ended"
		# or "audio_ended" in which case we leave it there
		if ($status eq "completed") {
		    #print "Process finished !\n";
		    unlink $statusfile;
		}
		exit 1;
	    }
	 
	    if ($wait_for_pid==-1) {
		exit 1;
	    }
	    
	}
    }


    if ($command eq "pause_play") {
	`kill -STOP mplayer`;
	`kill -STOP sox`;
	exit 0;
    }

    if ($command eq "resume_play") {
	`kill -CONT mplayer`;
	`kill -CONT sox`;
	exit 0;
    }


    if ($command eq "stop") {
	&stop;
	exit 0;
    }


    if ($command eq "new") {

	# write mini-info file
	$info_handle=int(rand 255)+1;
	$infofile=$tmpdir."/.info.".$info_handle;
	
	do {
	    $handle=int(rand 1000000000)+65536;
	    $curtmpdir=$tmpdir . "/" . $handle;
	} while (-d $curtmpdir);
	
	umask 0;
	mkdir $curtmpdir,0700;
	umask $umask;
	
	if (!defined(open OUT,"> $infofile")) {
	    exit 0;
	}
	
	print OUT $handle;
	close OUT;
	exit $info_handle;
    }
    

    if ($command eq "report") {
	if (defined($ARGV[1])&&!($ARGV[1] eq "")) {
	    $home=$ARGV[1];
	    $rcfile=$home."/.lives";

	    $video_open_command=&location("mplayer");
	    $video_play_command="- internal -";
	    $mencoder_command=&location("mencoder");
	    $mogrify_command=&location("mogrify");
	    $cdda2wav_command=&location("cdda2wav");

	    $audio_player="sox";
	    $audio_play_command=&rc_get_default("sox_command");
	    
	    $mog_auto_bak="true";

	    $mogrify_version=`$mogrify_command | grep -i version`;
	    @mogrify_version=split(" ",$mogrify_version);
	    $mogrify_version=@mogrify_version[2];
	    ($mog_maj,$mog_min,$mog_mic)=split (/[.]/, $mogrify_version,3);
	    $mogrify_version_hash=($mog_maj*1000+$mog_min)*1000+$mog_mic;

	    if ($mogrify_version_hash >= 5004008) {
		$mog_auto_bak="false";
	    }
	    
	    if ($audio_play_command eq "") {
		$audio_player="mplayer";
		$audio_play_command=&rc_get_default("mplayer_audio_command");;
	    }

	    $xmms_command=&location("xmms");

	    # get $tmpdir from .rc file, if no .rc file, create it
	    if (! -e $rcfile) {
		# set comment at start of file
		&rc_set("","");
		&rc_set("version",$version);
	        &rc_set("tempdir",$default_tmpdir);
		&rc_set("video_open_command",$video_open_command);
	        &rc_set("video_player",$video_player);
	        &rc_set("video_play_command",$video_play_command);
	        &rc_set("encoder",$encoder);
	        &rc_set("encoder_command",$mencoder_command);
	        &rc_set("output_type","MJPG");
	        &rc_set("effects_command",$mogrify_command);
		&rc_set("mog_auto_bak",$mog_auto_back);
	        &rc_set("audio_play_command",$audio_play_command);
		&rc_set("audio_player",$audio_player);
	        &rc_set("xmms_command",$xmms_command);
	        &rc_set("mplayer_extra_frame",1);
	        &rc_set("default_fps",25);
	        &rc_set("bgcolour","black");
		&rc_set("vid_load_dir","/usr/local/");
		&rc_set("vid_save_dir","/usr/local/");
		&rc_set("audio_dir","/usr/local/");
		&rc_set("stop_screensaver","true");
		&rc_set("open_maximised","true");
		&rc_set("frontend","gtk+");
	    }

	    $tmpdir=&rc_get("tempdir");
	    &save_home_dir;
	    
	    $mogrify_old_version=&rc_get("mogrify_version");
	    if (!($mogrify_old_version eq $mogrify_version)) {
		&rc_set("mog_auto_bak",$mog_auto_bak);
		&rc_set("mogrify_version",$mogrify_version);
	    }
	    &version_check_and_upgrade;
	}
	
	else {
	    # we didn't get the home directory...that's baaaaaddd....
	    $tmpdir=$default_tempdir;
	    unlink $gui_bootstrap_file;
	    open OUT,">> $gui_bootstrap_file" or exit 4;
	    print OUT "unknown\n$version|$tmpdir|";
	    close OUT;
	}

	open OUT,">> $gui_bootstrap_file" or exit 4;
	print OUT "$video_play_command|$mencoder_command|$mogrify_command|$play_command|$xmms_command|$msg|$msg2|$cdda2wav_command";
	close OUT;

	#make sure we can write to tempdir
	open OUT,"> $tmpdir/test" or exit 5;
	close OUT;
	unlink "$tmpdir/test";

	exit 0;
    }

    if ($command eq "xmmsplay") {
	$xmms_command=&location("xmms");
	if (!($xmms_command eq "")) {
	    $syscom=$xmms_command." -p \"".$ARGV[1]."\"";
	    system($syscom);
	}
	exit 0;
    }

    if ($command eq "xmmsstop") {
	$xmms_command=&location("xmms");
	if (!($xmms_command eq "")) {
	    $syscom=$xmms_command." -s";
	    system($syscom);
	}
	exit 0;
    }

    if ($command eq "set_pref") {
	$key=$ARGV[1];
	shift(@ARGV);
	shift(@ARGV);

	$value=join(" ",@ARGV);
	&rc_set($key,$value);
	exit 0;
    }
	
    if ($command eq "get_pref") {
	$key=$ARGV[1];
	$value=&rc_get($key,$home);
	open OUT,"> $tmpdir/.smogval.$uid.$gid";
	print OUT $value;
	close OUT;
	exit 0;
    }

    if ($command eq "get_pref_default") {
	$key=$ARGV[1];
	$value=&rc_get_default($key);
	open OUT,"> $tmpdir/.smogval.$uid.$gid";
	print OUT $value;
	close OUT;
	exit 0;
    }

    if ($command eq "version") {
	print "smogrify $version\n";
	exit 0;
    }
	
    if ($command eq "pause") {
	$syscom="touch $tmpdir/$ARGV[1]/pause";
	system($syscom);
	exit 0;
    }


    if ($command eq "resume") {
	unlink "$tmpdir/$ARGV[1]/pause";
	exit 0;
    }

    if ($command eq "commit_audio") {
	# commit the audio file in audio.wav as new audio file
	$handle=$ARGV[1];
	$curtmpdir=$tmpdir."/".$handle;
	$audiofile=$curtmpdir."/audio.wav";

	$f_size=-s $audiofile;
	if ($f_size>0) {
	    rename $audiofile,"$curtmpdir/audio";
	}

	&sig_complete(44100,2,16,$f_size);
	exit 0;
    }

    if ($command eq "cancel_audio") {
	# remove the audio file in audio.wav
	$handle=$ARGV[1];
	$curtmpdir=$tmpdir."/".$handle;
	$audiofile=$curtmpdir."/audio.wav";

	if (-f $audiofile) {
	    unlink $audiofile;
	}

	&sig_complete;
	exit 0;
    }


    if ($command eq "cleanup") {
	`rm /tmp/liveswin.*`;
	`rm /tmp/.smogrify*`;
	chdir $tmpdir or die "Couldn't cd to ".$tmpdir."\n";
	`rm -r *`;
	`rm .*`;
	exit 0;
    }
	
    if (!defined($ARGV[1])) {
	&usage;
	exit 1;
    }

    
####################################################################    
    # run the rest in background
    if ($background==1) {
	if (fork()) {
	    exit 0;
	}
    }
####################################################################
    
    umask $umask;

    if (!defined($ARGV[1]) || $ARGV[1] eq "") {
	&usage;
	exit 1;
    }

    $handle=$ARGV[1];

    $curtmpdir=$tmpdir."/".$handle;
    $statusfile=$curtmpdir."/.status";
    $pidfile=$tmpdir."/".$handle."/.pid";

    if ($command eq "close") {
	if (defined(open IN,"< $pidfile")) {
	    close IN;
	    # cancel any processing
	    $syscom="smogrify cancel ". $handle;
	    system($syscom);
	}

	if (chdir $curtmpdir) {
	    unlink <* .*>;
	    chdir $tmpdir;
	    rmdir $curtmpdir;
	}
	exit 0;
    }
    

    if ($command eq "keep") {
	$syscom="smogrify cancel $handle";
	system($syscom);
	unlink "$curtmpdir/pause";
	$start=$ARGV[2];
	$end=$ARGV[3];
	&mv_mgk;
	&sig_complete;
	exit 0;
    }


    # tell the world our pid
    if (!($command eq "play_preview" )) {
	&sig_pid;
    }


    if ($command eq "get_details") {
	unlink "$curtmpdir/pause";

	#attempt to get file details
	$file=$ARGV[2];

	$frames=0;
	
	umask 0;
	mkdir $curtmpdir,0700;
	umask $umask;

	$f_size= -s $file;

	&sig_complete($f_size,$frames);
	exit 0;
    }



    if ($command eq "open") {
	$file=$ARGV[2];
	$fps=" -fps 10000 ";
	$ss="";
	if (defined($ARGV[3]) && $ARGV[3] > 0) {
	    $ss=" -ss $ARGV[3] ";
	    $fps="";
	}
	$frames="";
	if (defined($ARGV[4]) && $ARGV[4] > 0 ) {
	    $frames=" -frames $ARGV[4] ";
	    $fps="";
	}
	unlink "$curtmpdir/pause";

	chdir $curtmpdir;

	$mplay_command=&rc_get("video_open_command");

	# process video

	# 2 other files we will use
	$curtmpfile=$curtmpdir . "/.temp";
	$audio_out=$curtmpdir . "/audio";

	$syscom=$mplay_command . " -quiet -slave -vo jpeg $fps $ss $frames -ao null \"" . $file . "\">" . $curtmpfile . " 2>/dev/null";
	
	system($syscom);

	@info=split /  /, `grep VIDEO: $curtmpfile`;
	
	$audio_in=$curtmpdir . "/audiox";
	$syscom=$mplay_command . " -quiet -slave -ao pcm $ss $frames -vo null -mc 0 -aofile ".$audio_in." \"" . $file . "\">" . $curtmpfile . " 2>/dev/null";

	system($syscom);

	@info2=split / /, `grep AUDIO: $curtmpfile`;
	unlink $curtmpfile;
	$type=$info[1];
	if ($type=~ m/^\[/) {
	    $type=substr($type,1,-1);
	}

	$size=$info[2];
	$hsize=(split /x/,$size)[0];
	$vsize=(split /x/,$size)[1];
	$bpp=$info[3];
	$fps=(split / /,$info[4])[0];


	$arate=$info2[1];
	$achans=$info2[3];

	$asamps=$info2[5];

	# get file size
	$f_size= -s $file;

	# get number of frames
	$count=-3;
	if (-f $audio_in) {
	    $count--;
	}
	opendir DIR,$curtmpdir;
	while (readdir(DIR)) {
	    $count++;
	}
	closedir DIR;
	
	if (!$count) {
	    &sig_error("This does not appear to be a valid video file","LiVES was unable to open it.");
	    exit 1;
	}

	# mplayer seems to output one extra frame

	if (&rc_get("mplayer_extra_frame")) {
	    $name=&mkname($count);
	    unlink $curtmpdir."/".$name.$img_ext;
	    $count--;
	}


	# convert to raw audio
	if (-f $audio_in) {
	    &convert_audio_to_raw;
	    unlink $audio_in;
	}

	$af_size= -s $audio_out;

	#if mplayer supports the -identify command, use that
	$file_ident=$curtmpdir."/file_info";
	$syscom=$mplay_command . " -identify -slave \"" . $file . "\">" . $file_ident." 2>/dev/null";
	system($syscom);

	$id_vid_form=`grep ID_VIDEO_FORMAT $file_ident`;
	@tmp=split("=",$id_vid_form);
	$id_vid_form=@tmp[1];
	chomp($id_vid_form);

	if (!($id_vid_form eq "")) {
	    $type=$id_vid_form;

	    $hsize=`grep ID_VIDEO_WIDTH $file_ident`;
	    @tmp=split("=",$hsize);
	    $hsize=@tmp[1];
	    chomp($hsize);

	    $vsize=`grep ID_VIDEO_HEIGHT $file_ident`;
	    @tmp=split("=",$vsize);
	    $vsize=@tmp[1];
	    chomp($vsize);

	    $fps=`grep ID_VIDEO_FPS $file_ident`;
	    @tmp=split("=",$fps);
	    $fps=@tmp[1];
	    chomp($fps);

	    $arate=`grep ID_AUDIO_RATE $file_ident`;
	    @tmp=split("=",$arate);
	    $arate=@tmp[1];
	    chomp($arate);
	    
	    $achans=`grep ID_AUDIO_NCH $file_ident`;
	    @tmp=split("=",$achans);
	    $achans=@tmp[1];
	    chomp($achans);

	    $abitrate=`grep ID_AUDIO_BITRATE $file_ident`;
	    @tmp=split("=",$abitrate);
	    $abitrate=@tmp[1];
	    chomp($abitrate);
	    $abitrate;

	    if ($arate*$achans>0) {
		$asamps=$abitrate/$arate/$achans;
	    }
	    else {
		$asamps=0;
	    }
	}

	unlink $file_ident;

	if ($asamps<3&&$asamps>0) {
	    $asamps=16;
	}

	# mplayer may force the channs to 2 regardless
	if ($achans>0) {
	    $achans=2;
	}

	&sig_complete($handle,$count,$type,$hsize,$vsize,$bpp,$fps,$f_size,$arate,$achans,$asamps,$af_size);
	exit 0;
    }



    if ($command eq "play_preview") {
	# play_preview is exactly like play, but we play .mgk files
	$img_ext=".mgk";
	
	# take the opportunity to sync the disk
	`sync;sync;sync`;
	$command="play";
    }


    if ($command eq "play") {

	$play_vid=1;

	$fps=$ARGV[2];
	if ($fps==0) {
	    $fps=&rc_get("default_fps");
	}
	if ($fps<0) {
	    $fps=-$fps;
	    $play_vid=0;
	}
	$start=$ARGV[3];
	$end=$ARGV[4];
	$fs=" ";
	if (defined $ARGV[5]) {
	    if ($ARGV[5]!="0") {
		$fs=" -fs ";
		}
	}
	$loop=0;
	$loops=" ";
	if ($ARGV[6]==1) {
	    $loop=1;
	    $loops=" -loop ";
	    if ($start!=1) {
		$loop=2;
		$loops=" ";
	    }
	    
	}
	$wid=" ";
	if (defined $ARGV[7]) {
	    if ($ARGV[7]!=0) {
		$wid=" -wid ".$ARGV[7]." ";
	    }
	}
	if (!defined $ARGV[8]||$ARGV[8]==0||!($fs eq " ")) {
	    $height="";
	}
	else {
	    $height=$ARGV[8];
	    $height="-y $height ";
	}
	if (!defined $ARGV[9]||$ARGV[9]==0||!($fs eq " ")) {
	    $width="";
	}
	else {
	    $width=$ARGV[9];
	    $width="-x $width ";
	}
	if (!defined $ARGV[10]||$ARGV[10]==0) {
	    $arate=44100;
	}
	else {
	    $arate=$ARGV[10];
	}
	if (!defined $ARGV[11]||$ARGV[11]==0) {
	    $achans=2;
	}
	else {
	    $achans=$ARGV[11];
	}
	if (!defined $ARGV[12]||$ARGV[12]==0) {
	    $asamps=2;
	    $stype="w";
	}
	else {
	    $asamps=$ARGV[12];
	    if ($asamps>7) {
		$asamps/=8;
	    }
	    if ($asamps==1) {
		$stype="b";
	    }
	    if ($asamps==2) {
		$stype="w";
	    }
	}

	$frames_played=0;
       	$thread=1;

	$vid_start=($start-1)/$fps;
	$audiofile=$curtmpdir."/audio";

	$video_player=&rc_get("video_player");
	$mplay_command=&rc_get("video_play_command");
	$audio_player=&rc_get("audio_player");

	# if playing a preview, we want to play *all* the .mgk files
	if ($img_ext eq ".mgk") {
	    $vid_start="0";
	}

	$frames=" -frames ".($end-$start+1)." ";
	if ($end==0) {
	    $frames=" ";
	}

	$syscom=$mplay_command . $fs . $loops . " -quiet -slave -mf on:type=jpeg:fps=" . $fps . $wid . " -ao null " . $height . $width . "-ss " . $vid_start . $frames . $curtmpdir . "/\\*" . $img_ext . " >/dev/null 2>&1";

	if (-f $audiofile && $arate) {

	    $audio_start=($start-1)/$fps;
	    $audio_end=$end/$fps;

	    $mute="";
	    if ($arate<0) {
		$mute=" -ao null  ";
		$arate=-$arate;
		if ($audio_player eq "sox") {
		    $mute=" -v 0 ";
		}
	    }
	    
	    $audio_play_command=&rc_get("audio_play_command");

	    if ($audio_player eq "sox") {
		if ($loop) {
		    if ($video_player eq "internal") {
			$trimcom=" trim $audio_start";
		    }
		    else {
			$trimcom="";
		    }
		} else {
		    $trimcom=" trim $audio_start " . ($audio_end-$audio_start);
		}
		
		$syscom2=$audio_play_command." -c " . $achans . " -r " . $arate . " -f s -t raw -s " . $stype . " " . $audiofile . $mute . $trimcom . " >/dev/null 2>&1";
	    } else {
		$syscom2=$audio_play_command . " -slave -rawaudio on:rate=" . $arate . ":channels=" . $achans . ":samplesize=" . $asamps . " " . $mute . " " . $audiofile . " -ss ".($audio_start-1)." >/dev/null 2>&1";
	    }

	#fork into 2, one for video, one for audio
	$thread=fork();

	}
	else {
	    &sig_complete_audio;
	    if ($loop) {
		&sig_complete_video(0);
		exit 0;
	    }
	}
	

	if ($thread) {
	    if ($play_vid) {
		system($syscom);
		&sig_complete_video($frames_played);
		exit 0;
	    }
	    else {
		# if using internal player, just exit
		exit 0;
	    }
	    
	} else {
	    system($syscom2);
	    &sig_complete_audio;
	    if ($loop && $play_vid) {
		&stop;
	    }
	    exit 0;
	}
    }



    if ($command eq "save") {
	unlink "$curtmpdir/pause";

	$fps=$ARGV[2];
	$nfile=$ARGV[3];
	$start=$ARGV[4];
	$end=$ARGV[5];
	$arate=$ARGV[6];
	$achans=$ARGV[7];
	$asamps=$ARGV[8];
	$otype=&rc_get("output_type");

	if ($otype eq "") {
	    $otype="MJPG";
	    &rc_set("output_type",$otype);
	}
	
	$encoder=&rc_get("encoder");
	$mencoder_command=&rc_get("encoder_command");

	#mplayer assumes 25fps
	$vid_start=($start-1)/25;

	# check the file is writable
	# gtk f/s kindly creates a temp file for us to unlink
	unless (unlink $nfile) {
	    &sig_error("Unable to open output file !","LiVES could not write to where you asked.");
	    exit 1;
	}

	$tmpvid=$curtmpdir . "/tmpvid";
	
	#prepare video stream
	$syscom=$mencoder_command . " -mf on -o \"" . $tmpvid . "\" -ovc lavc -lavcopts vcodec=mjpeg -noskip -ofps " . $fps . " " . $curtmpdir . "/\\*" . $img_ext . " -ss " . $vid_start . " -frames " . ($end-$start+1) . " >/dev/null 2>&1";
	system($syscom);

	#prepare audio stream
	$audiofile=$curtmpdir."/audio";
	if (-f $audiofile&&$arate>0) {

	    $aud_start=($start-1)/$fps;
	    $aud_end=$end/$fps;
	    
	    if ($encoder eq "mencoder") {
		#mencoder encodes sound up to the next nearest second
		# so we must too...
		$aud_end=$aud_start+int($aud_end-$aud_start+1.001);
	    }

	    # clip the audio and convert it to wav
	    $audio_in=&clip_audio($aud_start,$aud_end);
	    $audio_out=$curtmpdir . "/audiodump.wav";

	    &convert_audio_to_wav;
	    if (-f $audio_out) {
		$audio_com=" -oac pcm -audiofile " . $audio_out;
	    }
	}

	# get the codec
	$vcodec="mjpeg";

	if ($otype eq "mpg1") {
	    $vcodec="mpeg1video";
	}

	$syscom=$mencoder_command . " -mc 0 -o \"$nfile\" $audio_com -ovc lavc -lavcopts vcodec=$vcodec -noskip -ofps $fps $tmpvid >/dev/null 2>&1";

	system($syscom);

	unlink $tmpvid;
	unlink $audio_out;
	&sig_complete;
	exit 0;
	}



    if ($command eq "cut") {
	unlink "$curtmpdir/pause";

	$start=$ARGV[2];
	$end=$ARGV[3];
	$cut_audio=$ARGV[4];
	$frames=$ARGV[5];
	&cut($start,$end);
	&sig_complete;
	exit 0;
    }

    if ($command eq "reverse") {
	unlink "$curtmpdir/pause";

	$start=$ARGV[2];
	$end=$ARGV[3];
	&reverse;
	&sig_complete;
	exit 0;
    }


    if ($command eq "undo") {
	unlink "$curtmpdir/pause";

	$start=$ARGV[2];
	$end=$ARGV[3];
	&undo;
	&sig_complete;
	exit 0;
    }


    if ($command eq "redo") {
	unlink "$curtmpdir/pause";

	$start=$ARGV[2];
	$end=$ARGV[3];

	&mv_mgk(1);
	&sig_complete;
	exit 0;
    }

    if ($command eq "get_window_id") {
	unlink "$curtmpdir/pause";
	$syscom="xwininfo > $curtmpdir/tmpinfo";
	system($syscom);

	$syscom="grep \"Window id:\" $curtmpdir/tmpinfo > $curtmpdir/tmpinfo2";
	system ($syscom);
	if (defined(open IN,"< $curtmpdir/tmpinfo2")) {
	    read IN,$win_id,128;
	    close IN;
	}
	@wid=split(/\s/,$win_id);
	$win_id=hex($wid[3]);
	chomp($win_id);

	$syscom="grep \"Width:\" $curtmpdir/tmpinfo > $curtmpdir/tmpinfo2";
	system ($syscom);
	if (defined(open IN,"< $curtmpdir/tmpinfo2")) {
	    read IN,$width,128;
	    close IN;
	}
	@widths=split(/Width: /,$width);
	$width=$widths[1];
	chomp($width);

	$syscom="grep \"Height:\" $curtmpdir/tmpinfo > $curtmpdir/tmpinfo2";
	system ($syscom);
	if (defined(open IN,"< $curtmpdir/tmpinfo2")) {
	    read IN,$height,128;
	    close IN;
	}
	@heights=split(/Height: /,$height);
	$height=$heights[1];
	chomp($height);

	$syscom="grep \"Depth:\" $curtmpdir/tmpinfo > $curtmpdir/tmpinfo2";
	system ($syscom);
	if (defined(open IN,"< $curtmpdir/tmpinfo2")) {
	    read IN,$bpp,128;
	    close IN;
	}
	@bpps=split(/Depth: /,$bpp);
	$bpp=$bpps[1];
	chomp($bpp);

	unlink "$curtmpdir/tmpinfo";
	unlink "$curtmpdir/tmpinfo2";

	&sig_complete($win_id,$width,$height,$bpp);
	exit 0;
    }


    if ($command eq "fill_and_redo_frames") {
	# remove any gaps in the play images
	$start=$ARGV[2];
	$end=$ARGV[3];
	
	$cut_section_last=0;
	$last_good_frame="";

	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if (! -f "$curtmpdir/$name$img_ext") {
		if ($last_good_frame eq "") {
		    $cut_section_last=$i;
		}
		else {
		    $syscom="cp $curtmpdir/$last_good_frame$img_ext $curtmpdir/$name$img_ext";
		    system($syscom);
		}
	    }
	    else {
		$last_good_frame=$name;
	    }
	}

	if ($cut_section_last) {
	    &cut($start,$cut_section_last);
	}

	&sig_complete($cut_section_last);

	exit 0;
    }


    if ($command eq "cdopen") {
	unlink "$curtmpdir/pause";

	$cdda2wav_command=&location("cdda2wav");
	if ($cdda2wav_command eq "") {
	    &sig_complete(0,0,0,0);
	    exit 0;
	}

	$cdplay_device=&rc_get("cdplay_device");
	if ($cdplay_device eq "") {
	    &sig_complete(0,0,0,0);
	    exit 0;
	}

	$track=$ARGV[2];
	$audiofile=$curtmpdir."/audio.wav";
	if (-f $audiofile) {
	    unlink $audiofile;
	}

	$syscom="$cdda2wav_command -q -x -D $cdplay_device -o cdr -t $track $audiofile >/dev/null 2>&1";
	system($syscom);
	$f_size=-s $audiofile;

	&sig_complete(44100,2,16,$f_size);
	exit 0;
    }


    if ($command eq "mp3open") {
	unlink "$curtmpdir/pause";

	$mp3file=$ARGV[2];
	$audiofile=$curtmpdir."/audio.wav";
	if (-f $audiofile) {
	    unlink $audiofile;
	}
	$syscom="mpg123 -w ".$audiofile." --rate 44100 --stereo \"".$mp3file."\" >/dev/null 2>&1";
	system($syscom);
	$f_size=-s $audiofile;

	&sig_complete(44100,2,16,$f_size);
	exit 0;
    }

    if ($command eq "oggopen") {
	unlink "$curtmpdir/pause";

	$oggfile=$ARGV[2];
	$audiofile=$curtmpdir."/audio.wav";
	if (-f $audiofile) {
	    unlink $audiofile;
	}
	$syscom="ogg123 -d wav -f ".$audiofile." \"".$oggfile."\" >/dev/null 2>&1";
	system($syscom);
	$f_size= -s $audiofile;
	&sig_complete(44100,2,16,$f_size);
	exit 0;
    }

    if ($command eq "wavopen") {
	unlink "$curtmpdir/pause";

	$wavfile=$ARGV[2];
	$mplay_command=&rc_get("video_play_command");
	$curtmpfile=$curtmpdir . "/.temp";
	$audiofile=$curtmpdir."/audio.wav";
	if (-f $audiofile) {
	    unlink $audiofile;
	}
	$syscom="cp \"".$wavfile."\" ".$audiofile;
	system($syscom);
	$syscom=$mplay_command . " -slave -frames 0 " . $audiofile . " >" . $curtmpfile . " 2>/dev/null";
        system($syscom);

	@info2=split / /, `grep AUDIO: $curtmpfile`;
	unlink $curtmpfile;
	$arate=$info2[1];
	$achans=$info2[3];
	$asamps=$info2[5];

	if ($arate==0&&$achans==0&&$asamps==0) {
	    # have to assume these
	    $arate=44100;
	    $achans=2;
	    $asamps=16;
	}

	$f_size= -s $audiofile;
	&sig_complete($arate,$achans,$asamps,$f_size);
	exit 0;
    }


    # two utility functions - the GUI can announce its play window ID 
    # no longer used since the playwindow changes so often now

    if ($command eq "tell_win") {
	$winidfile="/tmp/liveswin.".$handle;
	if (!defined(open OUT,"> $winidfile")) {
	    exit 0;
	}
	print OUT $ARGV[2];
	close OUT;
	exit 0;
    }


    if ($command eq "remove_win") {
	$winidfile="/tmp/liveswin.".$handle;
	unlink $winidfile;
	exit 0;
    }
	




	
    # we need an effects engine here for resizing frames
	$mogrify_command=&rc_get("effects_command");
	$mog_auto_bak=&rc_get("mog_auto_bak");

    if ($command eq "insert") {
	#TODO - insert w. audio
	unlink "$curtmpdir/pause";

	$where=$ARGV[2];
	$after=$ARGV[3];
	$start=$ARGV[4];
	$end=$ARGV[5];
	$from_handle=$ARGV[6];
	$with_audio=$ARGV[7];
	$num_frames=$ARGV[8];
	$width=$ARGV[9];
	$height=$ARGV[10];
	&clean_old;

	&insert;
	&sig_complete;
	exit 0;
    }

    if ($command eq "resize") {
	$start=$ARGV[2];
	$end=$ARGV[3];
	$height=$ARGV[4];
	$width=$ARGV[5];
	&clean_old;
	&resize_all;
	&mv_mgk;
	&sig_complete;
	exit 0;
    }





    # effects

    $start=$ARGV[2];
    $end=$ARGV[3];




    if ($command eq "edge") {
	if (!defined($ARGV[4])) {
	    $radius=1;
	}
	else {
	    $radius=$ARGV[4];
	}
	
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);

	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }

	    $syscom=$mogrify_command . " -edge " . $radius ." " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }

    if ($command eq "normalize") {
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);

	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }

	    $syscom=$mogrify_command . " -normalize " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }


    if ($command eq "monochrome") {
	$video_player=&rc_get("video_player");

	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -monochrome " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);

	    # need to recolorize for mplayer
	    if ($video_player eq "mplayer") {
		$cstring="'#FD02FF'";
		$syscom=$mogrify_command . " -pen " . $cstring . " -colorize 1/1/1 " . $curtmpdir . "/" . $name . ".mgk";
		system($syscom);
		if (!($mog_auto_bak eq "false")) {
		    rename "$curtmpdir/$name.mgk~","$curtmpdir/$name.mgk";
		}
	    }
	    
	}
    
	&mv_mgk;
	&sig_complete;
	
	exit 0;
    }

    if ($command eq "charcoal") {
	$radius=$ARGV[4];
	$video_player=&rc_get("video_player");

	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -charcoal " . $radius . " " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);

	    # need to recolorize for mplayer
	    if ($video_player eq "mplayer") {
		$cstring="'#FD02FF'";
		$syscom=$mogrify_command . " -pen " . $cstring . " -colorize 1/1/1 " . $curtmpdir . "/" . $name . ".mgk";
		system($syscom);
		if (!($mog_auto_bak eq "false")) {
		    rename "$curtmpdir/$name.mgk~","$curtmpdir/$name.mgk";
		}
	    }

	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    if ($command eq "negate") {
	$skip=$ARGV[4];

	if ($skip eq "" || $skip==0) {
	    $skip=1;
	}
	
	&clean_old;
	
	$count=1;

	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);

	    if ($count!=$skip) {
		&make_bak;
	    }
	 
	    else {

		if ($mog_auto_bak eq "false") {
		    &make_bak;
		}
		
		$syscom=$mogrify_command . " -negate " . $curtmpdir . "/" . $name . $img_ext;
		system($syscom);
		$count=0;
	    }
	    $count++;
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    if ($command eq "solarize") {

	$startval=$ARGV[4];
	$step=$ARGV[5];

	if ($step eq "") {
	    $step=-100;
	}

	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -solarize " . $startval ." " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	    $startval+=$step;
	    if ($startval>65535||$startval<0) {
		$step=-$step;
		$startval+=$step;
	    }
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    if ($command eq "enhance") {
	
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -enhance " ." " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    if ($command eq "cycle") {
	
	if (!defined($ARGV[4])) {
	    $col=1;
	}
	else {
	    $col=$ARGV[4];
	}
	if (!defined($ARGV[5])) {
	    $cstep=1;
	}
	else {$cstep=$ARGV[5];}
	
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -cycle " . $col ." " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	    $col+=$cstep;
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    if ($command eq "rotate") {
	$height=$ARGV[4];
	$width=$ARGV[5];
	if (!defined($ARGV[6])) {
		$rotstep=1;
	    }
	else {
	    $rotstep=$ARGV[6];
	}

	$bgcolour=&rc_get("bgcolour");
	&clean_old;

	$rotangle=0;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -rotate " . int($rotangle) ." -background \"" . $bgcolour . "\" " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	    $rotangle+=$rotstep;
	    &resize_frame($name,$height,$width);
	}
	
	&mv_mgk;
	&sig_complete;
	exit 0;
    }
    


    if ($command eq "randomzoom") {
	$height=$ARGV[4];
	$width=$ARGV[5];
	$max_zoom=50;


	&clean_old;
	
	$zoom=-1;
	$depth=1;
	$crop_left=0;
	$crop_up=0;

	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);

	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    
	    #zoom out at end
	    if ($end-$i+1<=$depth) {
		$zoom=-1;
	    }
	    
	    #zoom out
	    if ($zoom<0) {
		if ($depth>=0) {
		    $depth--;
		    if ($depth<=0) {
			#pick a new centre
			$c_x=int(rand $width);
			$c_y=int(rand $height);
			$zoom=int(rand $max_zoom)+1;
		    }
		}
	    }
	    
	    #zoom in
	    if ($zoom>0) {
		$depth++;
		$zoom--;
		if ($zoom==0) {
		    $zoom=-1;
		}
	    }
	    
	    $scale=1+$depth/10;

	    &resize_frame($name,$height*$scale,$width*$scale);
	    $crop_left=$c_x*$scale-$width/2;
	    $crop_up=$c_y*$scale-$height/2;

	    if ($crop_left<0) {
		$crop_left=0;
	    }
	    if ($crop_left+$width>=$width*$scale) {
		$crop_left-=$crop_left+$width-$width*$scale+1;
	    }
	    if ($crop_up<0) {
		$crop_up=0;
	    }
	    if ($crop_up+$height>=$height*$scale) {
		$crop_up-=$crop_up+$height-$height*$scale+1;
	    }

	    # crop about centre

	    $syscom=$mogrify_command . " -crop " . $width . "!x" . $height . "!+" . $crop_left . "+" . $crop_up . " " . $curtmpdir . "/" . $name . ".mgk";
	    system($syscom);

	    if (!($mog_auto_bak eq "false")) {
		rename $curtmpdir."/".$name.".mgk~",$curtmpdir."/".$name.".mgk";
	    }
	}

	&mv_mgk;
	&sig_complete;
	exit 0;
    }




    if ($command eq "swirl") {
	if (!defined($ARGV[4])) {
		$swirl=1;
	    }
	else {
	    $swirl=$ARGV[4];
	}
	if (!defined($ARGV[5])) {$swstep=1;}
	else {$swstep=$ARGV[5];}
	
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $syscom=$mogrify_command . " -swirl " . $swirl ." " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);
	    $swirl+=$swstep;
	}
	
	&mv_mgk;
	&sig_complete;
	exit 0;
    }
    
    if ($command eq "colorize") {
	if (!defined($ARGV[4])) {
	    $cstart=16759807;
	}
	else {
	    $cstart=$ARGV[4];
	}

	if (!defined($ARGV[5])) {
	    $pcstart=50;
	}
	else {
	    $pcstart=$ARGV[5];
	}

	if (!defined($ARGV[6])) {
	    $cstep=0;
	}
	else {
	    $cstep=$ARGV[6];
	}

	if (!defined($ARGV[7])) {
	    $pcend=$pcstart;
	}
	else {
	    $pcend=$ARGV[7];
	}
	
	if ($end==$start) {
	    $pcstep=0;
	}
	else {
	    $pcstep=($pcend-$pcstart)/($end-$start);
	}

	&clean_old;

	$cstart_red=int($cstart/65536);
	$cstart-=$cstart_red*65536;
	$cstart_green=int($cstart/256);
	$cstart-=$cstart_green*256;
	$cstart_blue=$cstart;

	$cstep_red=int($cstep/65536);
	$cstep-=$cstep_red*65536;
	$cstep_green=int($cstep/256);
	$cstep-=$cstep_green*256;
	$cstep_blue=$cstep;

	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($mog_auto_bak eq "false") {
		&make_bak;
	    }
	    $cstring=sprintf("'#%02X%02X%02X'",$cstart_red,$cstart_green,$cstart_blue);
	    $pcint=int($pcstart);
	    $pcstring="$pcint/$pcint/$pcint";
	    $syscom=$mogrify_command . " -pen " . $cstring . " -colorize " . $pcstring . " " . $curtmpdir . "/" . $name . $img_ext;
	    system($syscom);

	    $cstart_red+=$cstep_red;
	    if ($cstart_red>255) {
		$cstart_red=255;
	    }
	    elsif ($cstart_red<0) {
		$cstart_red=0;
	    }

	    $cstart_green+=$cstep_green;
	    if ($cstart_green>255) {
		$cstart_green=255;
	    }
	    elsif ($cstart_green<0) {
		$cstart_green=0;
	    }

	    $cstart_blue+=$cstep_blue;
	    if ($cstart_blue>255) {
		$cstart_blue=255;
	    }
	    elsif ($cstart_blue<0) {
		$cstart_blue=0;
	    }
	    
	    $cstart=$cstart_red;

	    $pcstart+=$pcstep;
	    if ($pcstart>100) {
		$pcstart=100;
	    }
	    elsif ($pcstart<0) {
		$pcstart=0;
	    }
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    else {
	print "smogrify: unrecognised command $command\n";
	exit 1;
    }
}


else {
    &usage;
    exit 1;
}








######################################################

#subroutines



sub usage {
    print "$command must have a parameter\n";
    print "show usage...\n";
}

sub mkname {
    my ($num)=@_;
    $len=length ($num);
    $ret=substr("00000000",$len) . $num;
    $ret;
}

sub check_for_pause {
    if (-f "$curtmpdir/pause") {
	`sync;sync;sync`;
    }
    while (-f "$curtmpdir/pause") {
	sleep 1;
    }
}


sub mv_mgk {
    # pass in first param as 1 to show progress of frames
    # new files are .mgk files, back up old files to .bak

    my ($option)=@_;
    my ($i,$name);

    if (defined ($oimg_ext)) {
	$img_ext=$oimg_ext;
    }

    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	if ($option==1) {
	    &sig_progress($i);
	}
	if (-f "$curtmpdir/$name.mgk") {
	    if (-f "$curtmpdir/$name$img_ext") {
		rename "$curtmpdir/$name$img_ext","$curtmpdir/$name.bak";
	    }
	    rename "$curtmpdir/$name.mgk","$curtmpdir/$name$img_ext";
	}
    }
}


sub undo {
    # recover .bak files, move current files to .mgk files
    my ($i,$name);
    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	&sig_progress($i);
	if (-f $curtmpdir."/".$name.".bak") {
	    rename $curtmpdir."/".$name.$img_ext,$curtmpdir."/".$name.".mgk";
	    rename $curtmpdir."/".$name.".bak",$curtmpdir."/".$name.$img_ext;
	}
    }
}


sub cut {
    my ($start,$end)=@_;
    my ($i,$name);
    chdir $curtmpdir;
    $frames_cut=$end-$start+1;
    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	unlink $name.$img_ext;
	&sig_progress($i);
    }

    for ($i=$end+1;$i<=$frames;$i++) {
	$from=&mkname($i);
	$to=&mkname($i-$frames_cut);

	rename $curtmpdir."/".$from.$img_ext,$curtmpdir."/".$to.$img_ext;
	&sig_progress($i);
    }
}




sub reverse {
    for ($i=$start;$i<int(($start+$end)/2+.5);$i++) {
	$from=&mkname($i);
	$to=&mkname($end-$i+1);
	&sig_progress($start+($i-$start)*2);
	rename $curtmpdir."/".$from.$img_ext,$curtmpdir."/.revtemp";
	rename $curtmpdir."/".$to.$img_ext,$curtmpdir."/".$from.$img_ext;
	&sig_progress($start+($i-$start)*2+1);
	rename $curtmpdir."/.revtemp",$curtmpdir."/".$to.$img_ext;
    }
}




sub insert {
    my ($from,$to,$frames_to_move);

    $fromdir=$tmpdir."/".$from_handle;
    $frames_to_move=$end-$start+1;


    # make space for new frames
    for ($i=$num_frames;$i>$where-!$after;$i--) {
	$from=&mkname($i);
	$to=&mkname($i+$frames_to_move);
	&sig_progress($num_frames-$i);
	if (! rename $curtmpdir."/".$from.$img_ext,$curtmpdir."/".$to.$img_ext) {
	    `sync;sync;sync`;
	    rename $curtmpdir."/".$from.$img_ext,$curtmpdir."/".$to.$img_ext;
	}
    }


    #move from $from_handle
    for ($i=$start;$i<=$end;$i++) {
	$from=&mkname($i);
	$to=&mkname($where+$i-$start+1-!$after);
	&sig_progress(int($num_frames-$where-!$after+$i-$start+1));
	$syscom="cp $fromdir/$from$img_ext $curtmpdir/$to$img_ext";
	system($syscom);

	if ($height && $width) {
	    &resize_frame($to,$height,$width);
	}
    }
}



sub resize_frame {

    # resize either .mgk file or $img_ext file

    my ($name,$height,$width)=@_;
    my ($present)=0;
    if (-f "$curtmpdir/$name.mgk") {
	$present=1;
    }

    if (!$present) {
	$syscom=$mogrify_command . " -resize ".$width."!x".$height."! ".$curtmpdir."/".$name.$img_ext;
	system($syscom);
	if (!($mog_auto_bak eq "false")) {
	    rename $curtmpdir."/".$name.".mgk",$curtmpdir."/".$name.$img_ext;
	}
    }
    else {
	$syscom=$mogrify_command . " -resize ".$width."!x".$height."! ".$curtmpdir."/".$name.".mgk";
	system($syscom);
	if (!($mog_auto_bak eq "false")) {
	    rename $curtmpdir."/".$name.".mgk~",$curtmpdir."/".$name.".mgk";
	}
    }
}


sub resize_all {
    #resize a $img_ext file to a .mgk file

    my ($i);
    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	if ($mog_auto_bak eq "false") {
	    # make_back will set $img_ext to ".mgk"
	    &make_bak;
	}
	$syscom=$mogrify_command . " -resize ".$width."!x".$height."! ".$curtmpdir."/".$name.$img_ext;
	system($syscom);
	&sig_progress($i);
    }
}


#not used yet
sub clear_aud_status {
    my ($audstat)=$statusfile.".aud";
    unlink $audstat;
}



sub convert_audio_to_raw {
    my ($mplay_command)=&rc_get("video_open_command");

    $syscom=$mplay_command . " -quiet -slave " . $audio_in . " -ao pcm -nowaveheader -aofile " .$audio_out . " >/dev/null 2>&1";
    system ($syscom);
    `sync;sync;sync`;
}


sub convert_audio_to_wav {
    chdir $curtmpdir;
    my ($mplay_command)=&rc_get("video_open_command");
    my ($aasamps)=$asamps/8;

    if (&rc_get("audio_player") eq "mplayer") {
	$syscom=$mplay_command . " -quiet -slave -ao pcm -rawaudio on:rate=$arate:channels=$achans:samplesize=$aasamps -waveheader -vo null $audio_in >/dev/null 2>&1";
    }
    else {

	if ($asamps==8) {
	    $sasamps="b";
	}
	else {
	    $sasamps="w";
	}
	
	$syscom="sox -t raw -r $arate -s -$sasamps -c $achans $audio_in -t wav -r 44100 -c 2 -s -w $curtmpdir/audiodump.wav > /dev/null 2>&1";
    }

    system($syscom);
    `sync;sync;sync`;
}



# switch endianness - not working yet
sub convert_audio_to_wav_reversed {
    $syscom="sox -x -t raw -r 44100 -s -w -c 2 $audio_in -u $curtmpdir/audiodump.wav";
    system($syscom);
    `sync;sync;sync`;
}


#clip the audio from $start_frame to $end_frame
sub clip_audio {
    my ($start,$end)=@_;
    $audio_in=$curtmpdir."/audio";
    $audio_out=$curtmpdir."/audioclip";

    $spos=$arate*$achans*$asamps/8*$start;
    $epos=$arate*$achans*$asamps/8*$end;

    my ($afile_size)= -s $audio_in;

    if ($epos>=$afile_size) {
	$epos=$afile_size-1;
    }

    my ($size)=($epos-$spos);

    $fblocks=int($size/32);
    $rblocks=$size-($fblocks*32);
    my ($syscom)="dd if=$audio_in of=$audio_out bs=32 count=$fblocks skip=".int($spos/32)." >/dev/null 2>&1";
    my ($syscom2)="dd if=$audio_in of=$audio_out bs=1 count=$rblocks skip=".(int($spos/32)+($fblocks*32))." seek=".(int($spos/32)+($fblocks*32))." >/dev/null 2>&1";
    unlink $audio_out;

    system($syscom);
    system($syscom2);
    `sync;sync;sync`;
    $audio_out;
}


sub clean_old {
    chdir $tmpdir . "/" . $handle;
    unlink <*.mgk *.bak>;
    unlink "$tmpdir/$handle/pause";
}



sub sig_complete {
    my($status)="completed";
    foreach (@_) {
	$status.="|".$_;
    }
    
    `sync;sync;sync`;

    &check_for_pause;

    open OUT,"> $statusfile";
    print OUT $status;
    close OUT;
    unlink $pidfile;
    
}


sub sig_complete_audio {
    my($status)="audio_ended";
    foreach (@_) {
	$status.="|".$_;
    }
    
    open OUT,">> $statusfile";
    print OUT $status;
    close OUT;
    if (!($img_ext eq ".mgk")) {
	unlink $pidfile;
    }
    else {
	if (! -f $pidfile) {
	    # the effect completed while we were playing a preview...
	    open OUT,"> $statusfile";
	    print OUT "completed";
	    close OUT;
	}
    }
}

sub sig_complete_video {
    my($status)="video_ended|";

    #foreach (@_) {
	#$status.=$_."|";
    #}

    open OUT,"> $statusfile";
    print OUT $status;
    close OUT;
    if (!($img_ext eq ".mgk")) {
	unlink $pidfile;
    }
    else {
	if (! -f $pidfile) {
	    # the effect completed while we were playing a preview...
	    open OUT,"> $statusfile";
	    print OUT "completed";
	    close OUT;
	}
    }
}


sub sig_error {
    # error strings must not contain newlines, instead you can pass up to 4 lines of text as params

    my($status)="error";
    foreach (@_) {
	$status.="|".$_;
    }
    $status.="|||ERROR|";
    open OUT,"> $statusfile";
    print OUT $status;
    close OUT;
    unlink $pidfile;
    exit 1;
}


sub sig_pid {
# write our pid in case we need to be cancelled
    open OUT,"> $pidfile";
    print OUT $$;   # process ID
    close OUT;
}



sub sig_progress {
# report progress of frame processing
    my ($frames)=@_;
    &check_for_pause;
    open OUT,"> $statusfile";
    print OUT $frames;
    close OUT;
}


sub stop {
    
# should find better way to stop processes
    $syscom="killall -HUP -g smogrify >/dev/null 2>&1";
    system($syscom);

    if (&rc_get("audio_player") eq "sox") {
	$syscom="killall -9 sox >/dev/null 2>&1";
	system($syscom);
    }
}


sub location {
    # return the location of an executable
    my ($command)=@_;
    $location=`which $command`;
    if ($type=~ m/[^where:,^which:]/) {
	$location="";
    }
    else {
	chomp($location);
    }
    $location;
}



sub rc_set {
    # set a value in the .rc file
    my ($key)=$_[0];
    shift (@_);
    my ($value)=join(" ",@_);

    my ($rcfile)=$home."/".$rc_filename;

    if (!defined(open OUT,"> $rcfile.new") && -e $rcfile) {
	exit 3;
    }

    # autoflush output
    select((select(OUT), $| = 1)[0]);

    if ($key eq "") {
	print OUT "# This file is autogenerated by LiVES\n# You are strongly advised to change the values only through the GUI.\n";
    }
	
    #replace <$key> to </$key> with our new value
    else {
	if (!defined(open IN,"$rcfile") && -e $rcfile) {
	    exit 2;
	}
	$string="";
	$part=0;
	$found=0;
	while (<IN>) {
	    $string=$_;
	    if ($part==1||$_=~ /(.*)(<$key>)(.*)/) {
		if ($part==0) {
		    $part=1;
		    print OUT "$1$2\n$value\n</$key>\n";
		    $string=$3;
		    $found=1;
		}
		if ($string=~ /(.*)(<\/$key>)(.*)/) {
		    print OUT $3;
		    $part=0;
		}
	    }
	    elsif ($part==0) {
		print OUT $string;
	    }
	}
	if ($found==0) {
	    print OUT "\n<$key>\n$value\n</$key>\n";
	}
    }
    close OUT;

    rename $rcfile.".new",$rcfile;
}


sub rc_get {
    # return a value from our .rc file
    my ($key)=@_;
    my ($rcfile)=$home."/".$rc_filename;
    my ($string)="";

    if (!defined(open IN,"$rcfile")) {
	exit 2;
	}
    $part=0;
	while (<IN>) {
	    if ($part==1||$_=~ /(<$key>)(.*)/) {
		if ($part==1) {
		    $string.=$_;
		}
		else {
		    $part=1;
		    $string.=$2;
		}
		$string=~ s/\n//g;
		if ($string=~ /(.*)(<\/$key>)/) {
		    return $1;
		}
	    }
	}
}


sub save_home_dir {
    # save the home directory in a temp file - for use later to find .rc file
    if (!defined(open OUT,"> $gui_bootstrap_file")) {
	exit 4;
    }
    print OUT $home."\n$version|$tmpdir|";
    close OUT;

    if (! -d $tmpdir) {
	# create working directory
	umask 0;
	mkdir $tmpdir,0777 or exit 5;
	umask $umask;
    }
}


sub get_home_dir {
    # get the home directory which we wrote in save_home_dir
    my ($string);
    open IN,"< $gui_bootstrap_file";
    read IN,$string,256;
    close IN;

    $string=~ /(.*)\n(.*)/;
    $1;
}


sub make_bak {
    # manually move a $img_ext file to a .mgk file
    if (! defined($oimg_ext)) {
	$oimg_ext=$img_ext;
	$img_ext=".mgk";
    }

    $syscom="cp $curtmpdir/$name$oimg_ext $curtmpdir/$name.mgk";
    system($syscom);
}




# get default values
sub rc_get_default {
    # return a value from our .rc file
    my ($key)=@_;
    my ($ret)="";

    if ($key eq "mplayer_play_video_command") {
	$ret=&location("mplayer");
	if (!($ret eq "")) {
	    $ret.=" -double";
	}
    }

    elsif ($key eq "sox_command") {
	$ret=&location("play");
    }

    elsif ($key eq "mplayer_audio_command") {
	$ret=&location("mplayer");
    }

    $ret;
}


sub version_check_and_upgrade {
    my ($ver_major,$ver_minor,$ver_micro)=split (/[.]/, $version,3);
    my $version_hash=($ver_major*1000+$ver_minor)*1000+$ver_micro;

    $msg="";
    
    if ($version_hash < 4000) {
	# pre - 0.4.0 - recommend users switch to internal player
	$msg="LiVES now has an internal player !";
	$msg2="You are strongly encouraged to try this out using the Tools/Preferences/Play menu";
	&rc_set("video_open_command",$video_open_command);
    }

    # bless this version
    &rc_set("version",$version);
}
