#!/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; # ->        -rw-------

$GUI_NAME="LiVES";

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

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

$composite_command="composite";
$convert_command="convert";



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

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

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


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


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


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

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

    if (!caller||$command eq "save"||$command eq "plugin_clear") {
	if (defined $DEBUG_SM_CMDS) { 
	    print "command is $command\n";
	}

## blocking calls

    if (!($command eq "report"||($command eq "version"))) {
	$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 2>/dev/null`;
		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 "stop_audio") {
	&stop_audio;
	exit 0;
    }

    if ($command eq "new") {
	# warning - this has changed recently, it now takes a $key

	$key=$ARGV[1];

	# write mini-info file
	$infofile=$tmpdir."/.info.".$key;
	
	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 1;
	}
	
	print OUT $handle;
	close OUT;
	exit 0;
    }
    

    if ($command eq "get_tempdir") {
	$id=$ARGV[1];
	open OUT,"> /tmp/.smogrify.$id" or exit 1;
	print OUT "$tmpdir\n";
	close OUT;
	exit 0;
    }


    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 -";
	    $encoder_command=$encoder;
	    $mogrify_command=&location("mogrify");

	    $audio_player="sox";
	    $audio_play_command=&rc_get_default("sox_command");
	    
	    $mogrify_version=`$mogrify_command | grep -i version`;
	    @mogrify_version=split(" ",$mogrify_version);
	    $mogrify_version=@mogrify_version[2];

	    $mogrify_version_hash=&version_hash($mogrify_version);

	    if ($audio_play_command eq "") {
		$audio_player="mplayer";
		$audio_play_command=&rc_get_default("mplayer_audio_command");
	    }

	    # get $tmpdir from .rc file, if no .rc file, create it
	    if (! -s $rcfile) {
		# set comment at start of file
		&rc_set("","");
	    }

	    &rc_set_if_not_set("version",$version);
	    &rc_set_if_not_set("tempdir",$default_tmpdir);
	    &rc_set_if_not_set("video_open_command",$video_open_command);
	    &rc_set_if_not_set("video_player",$video_player);
	    &rc_set_if_not_set("video_play_command",$video_play_command);
	    &rc_set_if_not_set("encoder",$encoder);
	    &rc_set_if_not_set("encoder_command",$encoder_command);
	    &rc_set_if_not_set("audio_encode_quality","high");
	    &rc_set_if_not_set("output_type","mjpeg");
	    &rc_set_if_not_set("effects_command",$mogrify_command);
	    &rc_set_if_not_set("audio_play_command",$audio_play_command);
	    &rc_set_if_not_set("audio_player",$audio_player);
	    &rc_set_if_not_set("audio_effect","none");
	    &rc_set_if_not_set("mplayer_extra_frame",1);
	    &rc_set_if_not_set("default_fps",25);
	    &rc_set_if_not_set("bgcolour","black");
	    &rc_set_if_not_set("vid_load_dir","/usr/local/");
	    &rc_set_if_not_set("vid_save_dir","/usr/local/");
	    &rc_set_if_not_set("image_dir","/usr/local/");
	    &rc_set_if_not_set("proj_dir","/usr/local/");
	    &rc_set_if_not_set("audio_dir","/usr/local/");
	    &rc_set_if_not_set("stop_screensaver","true");
	    &rc_set_if_not_set("open_maximised","true");
	    &rc_set_if_not_set("show_recent_files","true");
	    &rc_set_if_not_set("lives_warning_mask",0);
	    &rc_set_if_not_set("dl_bandwidth_K",64);
	    &rc_set_if_not_set("midisynch","false");
	    &rc_set_if_not_set("conserve_space","false");
	    &rc_set_if_not_set("insert_resample","false");
	    &rc_set_if_not_set("show_player_stats","false");
	    &rc_set_if_not_set("default_height",240);
	    &rc_set_if_not_set("default_width",320);
	    &rc_set_if_not_set("no_fast_keys","false");
	    &rc_set_if_not_set("show_toolbar","true");

	    # resize action when opening images - can be one of: "bound", "default" or "none"
	    &rc_set_if_not_set("image_resize_action","bound");

	    # test for mplayer mf syntax
	    $syscom="mplayer -mf on >/tmp/.smog.mptest 2>/dev/null";
	    system ($syscom);
	    $test=`grep deprecated /tmp/.smog.mptest`;
	    if (!($test eq "")) {
		&rc_set("mplayer_mf_version",2);
	    }
	    else {
		&rc_set("mplayer_mf_version",1);
	    }

	    unlink "/tmp/.smog.mptest";
	    
	    &store_kb_rpt;

	    $tmpdir=&rc_get("tempdir");
	    &save_home_dir;
	    
	    $mogrify_old_version=&rc_get("mogrify_version");
	    unless ($mogrify_old_version eq $mogrify_version) {
		&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|smogrify error|Home directory was not found";
	    close OUT;
	}

	open OUT,">> $gui_bootstrap_file" or exit 4;
	print OUT "$version|$tmpdir|$msg|$msg2";
	close OUT;

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

	exit 0;
    }

    
    if ($command eq "set_pref") {
	# WARNING - pref keys should not contain spaces, but values can
	$key=$ARGV[1];
	shift(@ARGV);
	shift(@ARGV);

	$value=join(" ",@ARGV);
	&rc_set($key,$value);
	exit 0;
    }
	
    if ($command eq "set_pref_if_not_set") {
	# WARNING - pref keys should not contain spaces, but values can
	$key=$ARGV[1];
	shift(@ARGV);
	shift(@ARGV);

	$value=join(" ",@ARGV);
	&rc_set_if_not_set($key,$value);
	exit 0;
    }
	
    if ($command eq "get_pref") {
	$key=$ARGV[1];
	if (defined($ARGV[2])) {
	    $first=$ARGV[2];
	}
	else {
	    $first=$uid;
	}
	if (defined($ARGV[3])) {
	    $second=$ARGV[3];
	}
	else {
	    $second=$gid;
	}
	$value=&rc_get($key,$home);

	open OUT,"> $tmpdir/.smogval.$first.$second" or exit 1;
	print OUT $value;
	close OUT;
	exit 0;
    }

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

    if ($command eq "get_location") {
	$exe=$ARGV[1];
	if (defined($ARGV[2])) {
	    $first=$ARGV[2];
	}
	else {
	    $first=$uid;
	}
	if (defined($ARGV[3])) {
	    $second=$ARGV[3];
	}
	else {
	    $second=$gid;
	}
	$value=&location($exe);
	open OUT,"> $tmpdir/.smogval.$first.$second" or exit 1;
	print OUT $value;
	close OUT;
	exit 0;
    }


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

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

    if ($command eq "pause") {
	$syscom="touch $tmpdir/$ARGV[1]/pause; chmod 600 $tmpdir/$ARGV[1]/pause";
	system($syscom);
	exit 0;
    }


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


    if ($command eq "get_details") {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	$statusfile="$curtmpdir/.status";
	unlink "$curtmpdir/pause";

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

	# only open the first image if it's a directory
	# otherwise it is too slow
	$only_first=1;
	&get_file_info;

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



    if ($command eq "undo_audio") {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	if (-f "$curtmpdir/audio.orig") {
	    unlink "$curtmpdir/audio";
	    rename "$curtmpdir/audio.orig","$curtmpdir/audio";
	    unlink "$curtmpdir/audio.bak";
	}
	else {
	    if (-f "$curtmpdir/audio.bak") {
		unlink "$curtmpdir/audio";
		rename "$curtmpdir/audio.bak","$curtmpdir/audio";
	    }
	}
	exit 0;
    }


    if ($command eq "save_frame") {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	$frame=$ARGV[2];
	$name=&mkname($frame);
	$nfile=$ARGV[3];
	$statusfile="$curtmpdir/.status";

	# check the file is writable
	unless (&is_writeable($nfile)) {
	    &sig_error("Unable to open output file !","$GUI_NAME could not write to $nfile.");
	}

	$syscom="$convert_command $curtmpdir/$name$img_ext \"$nfile\"";
	system ($syscom);
	exit 0;
    }


    if ($command eq "restore_details") {
	$handle=$ARGV[1];
	$nfile=$ARGV[2];

	$curtmpdir="$tmpdir/$handle";
	unlink "$curtmpdir/pause";
	$statusfile="$curtmpdir/.status";

	chdir $curtmpdir;
	$fsize= -s "$nfile";
	$afsize = -s "audio";
	unlink <*.tar>;
	unlink <event.*>;
	unlink "extended";

	$frames=-2;

	if (defined(open IN,"< header2")) {
	    read IN,$val,4;
	    $frames=&getint($val);
	    read IN,$title,256;
	    read IN,$author,256;
	    read IN,$comment,256;
	}

	$title=~ tr/\x00//d;
	$author=~ tr/\x00//d;
	$comment=~ tr/\x00//d;

	unlink <header*>;

	if ($frames<1) {
	    if (!($nfile eq "")) {
		unlink <event.*>;
		
		if (-f "audio") {
		    $frames--;
		}
		
		opendir DIR,$curtmpdir;
		@length=readdir(DIR);
		$frames+=scalar(@length);
		closedir DIR;
	    }
	}

	&sig_complete($fsize,$afsize,$frames,"$title","$author","$comment");
	exit 0;
    }

    if ($command eq "list_plugins") {
	# plugin_dir pref should be set first
	$plugin_type=$ARGV[1];
	$string="";
	$plugin_dir=&rc_get("plugin_dir");
	if ($plugin_dir eq "") {
	    exit 1;
	}

	$plugindir="$plugin_dir/$plugin_type";

	if (-d $plugindir) {
	    chdir $plugindir;
	    opendir DIR,$plugindir;
	    while ($plugname=readdir(DIR)) {
		if (-f $plugname && -x $plugname) {
		    $plugname=(split /\./,$plugname)[0];
		    $string.="$plugname|";
		}
	    }
	    closedir DIR;
	}
	else {
	    exit 1;
	}
	print "$string\n";
	exit 0;
    }


    if ($command eq "version"&&!caller) {
	print "smogrify $version\n";
	exit 0;
    }


	if ($command eq "plugin_clear") {
	    # this is a special function which does general cleanup
	    # and then does the "clear" command in the plugin
	    # non-perl plugins have to implement this command themselves
	    #
	    $handle=$ARGV[1];
	    $curtmpdir="$tmpdir/$handle";
	    unlink "$curtmpdir/pause";
	    $statusfile=$curtmpdir."/.status";
	    chdir $curtmpdir;
	    $start=$ARGV[2];
	    $end=$ARGV[3];
	    $type=$ARGV[4];

	    if ($type eq "encoders") {
		$areq=&get_format_request;
		if ($areq&1||$areq&2) {
		    unlink <audiodump.wav audioclip>;
		}
		if ($areq&4) {
		    for ($i=$end;$i>0;$i--) {
			$name=&mkname($i);
			$from=&mkname($start--);
			if (! -f "$name$img_ext") {
			    rename "$from$img_ext","$name$img_ext";
			    if (-f "$from.ign") {
				rename "$from.ign","$name$img_ext";
			    }
			}
		    }
		}
	    }
	    # let the plugin clean up any other temp files
	    $command="clear";
	    return 1;
	    exit 0; # just in case
	}


	if ($command eq "cleanup") {
	`rm /tmp/liveswin.*`;
	`rm /tmp/.smogrify*`;
	chdir $tmpdir or die "Couldn't cd to ".$tmpdir."\n";
	`rm -r *`;
	`rm .*`;
	$command="key_rpt_slow";
    }
	
    
####################################################################    
    # run the rest in background
    if ($background==1) {
	if (fork()) {
	    exit 0;
	}
    }
####################################################################

    if ($command eq "key_rpt_fast") {
	system("xset r rate 1 25");
	$syscom="";
	for ($i=9;$i<70;$i++) {
	    $syscom .="xset -r $i;";
	}
	system($syscom);
	exit 0;
    }

    if ($command eq "key_rpt_slow") {
	# restore original key repeat rate
	$delay=&rc_get("kbd_rpt_delay");
	$rate=&rc_get("kbd_rpt_rate");
	$state=&rc_get("kbd_rpt_state");

	if ($delay eq "") {
	    $delay=660;
	}
	if ($rate eq "") {
	    $rate=25;
	}
	$syscom="xset r rate $delay $rate";
	system($syscom);
	if ($state eq "off") {
	    $syscom="xset r off";
	}
	$syscom="";
	for ($i=9;$i<70;$i++) {
	    if ($i==29||$i==56||$i==42||$i==58) {
		$syscom .="xset -r $i;";
	    }
	    else {
		$syscom .= "xset r $i;";
	    }
	}
	system($syscom);
	exit 0;
    }

    umask $umask;

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

    $handle=$ARGV[1];

    $curtmpdir="$tmpdir/$handle";
    $statusfile=$curtmpdir."/.status";
    $pidfile=$curtmpdir."/.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 "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";
    }

    elsif ($command eq "play_opening_preview") {
	# play_preview is exactly like play, but we play .mgk files
	# unless we are opening
	# opening - we will play wav, not raw
	$opening_preview=1;
	$command="play";
    }

    else {
	unless ($command eq "keep") {
	    # tell the world our pid
	    &sig_pid;
	}
    }



    if ($command eq "play") {
	$play_vid=1;
	$endian=&get_endian;

	$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]!=0) {
	    $loop=$ARGV[6];
	    $loops=" -loop 0 ";
	    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";
	    }
	}

	if (!defined $ARGV[13]) {
	    $signed=1;
	}
	else {
	    $signed=$ARGV[13];
	}

	if (!defined $ARGV[14]) {
	    $aendian=$endian;
	}
	else {
	    $aendian=$ARGV[14];
	}

	if (defined($opening_preview)) {
	    $aformat="wav";
	    $audiofile=$curtmpdir."/audiodump.wav";
	}
	else {
	    $aformat="raw";
	    $audiofile=$curtmpdir."/audio";
	}

	$frames_played=0;
       	$thread=1;

	$vid_start=($start-1)/$fps;
	$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=" ";
	}

	if ($play_vid) {
	    $video_player=&rc_get("video_player");
	    $mplay_command=&rc_get("video_play_command");
	    $mf_ver=&rc_get("mplayer_mf_version");

	    # the mplayer developers changed the -mf format 
	    if ($mf_ver==2) {
		$syscom=$mplay_command . $fs . $loops . " -quiet -osdlevel 0 -slave -mf type=jpg:fps=" . $fps . $wid . " -ao null " . $height . $width . "-ss " . $vid_start . $frames . "mf://" . $curtmpdir . "/\\*" . $img_ext . " >/dev/null 2>&1";
	    }
	    else {
		$syscom=$mplay_command . $fs . $loops . " -quiet -osdlevel 0 -slave -mf on -mf type=jpg: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") {
		$audio_effect=&rc_get("audio_effect");
		$trimcom=" trim $audio_start";
		if ($audio_end>$audio_start) {
		    $trimcom .= " ".($audio_end-$audio_start);
		}
		else {
		    $trimcom .=" 10000000";
		}
		
		if ($signed==1) {
		    $signed="s";
		}
		else {
		    $signed="u";
		}

		if ($aendian==$endian) {
		    $aendian="";
		}
		else {
		    $aendian="-x";
		}
		
		if ($audio_effect eq "flanger1") {
		    $audio_effect="flanger 0.6 0.87 3.0 0.9 0.5 -s";
		}
		elsif ($audio_effect eq "echo1") {
		    $audio_effect="echo 0.8 0.9 1000.0 0.3";
		}
		elsif ($audio_effect eq "chorus1") {
		    $audio_effect="chorus 0.6 0.9 50.0 0.4 0.25 2.0 -t 60.0  0.32  0.4 1.3 -s";
		}
		elsif ($audio_effect eq "reverb1") {
		    $audio_effect="reverb 1.0 600.0 180.0 200.0";
		}
		elsif ($audio_effect eq "phaser1") {
		    $audio_effect="phaser 0.89 0.85 1.0 0.24 2.0 -t";
		}
		else {
		    $audio_effect="";
		}

		$syscom2=$audio_play_command." -c " . $achans . " -r " . $arate . " -f $signed $aendian -t $aformat -s " . $stype . " " . $audiofile . $mute . $trimcom . " $audio_effect  >/dev/null 2>&1";
	    } else {
		# mplayer seemingly has no easy way of playing unsigned or wrong endian
		$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 {
	    if ($play_vid) {
		system ($syscom);
		&sig_complete_video;
	    }
	    if (! -d "$curtmpdir" || -f "$curtmpdir/.stoploop") {
		$loop=0;
	    }
	    if ($loop<0) {
		# continuous loop audio
		do {
		    # wait for this process to be killed
		    sleep 60;
		    if (! -d "$curtmpdir" || -f "$curtmpdir/.stoploop") {
			$loop=0;
		    }
		} while ($loop);
		&sig_complete_audio;
		if ($play_vid) {
		    &stop;
		}
	    }
	    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 {
	    do {
		system($syscom2);
		if (! -d "$curtmpdir" || -f "$curtmpdir/.stoploop" || ! -f $audiofile) {
		    $loop=0;
		}
	    } while ($loop<0);
	    &sig_complete_audio;

	    if ($loop && $play_vid) {
		&stop;
	    }
	    exit 0;
	}
    }

    if ($command eq "open") {

	$file=$ARGV[2];
	$ss="";

	$withsound=$ARGV[3];

	if (defined($ARGV[4]) && $ARGV[4] > 0) {
	    $ss=" -ss $ARGV[4] ";
	}
	$frames="";
	if (defined($ARGV[5]) && $ARGV[5] > 0 ) {
	    $frames=" -frames $ARGV[5] ";
	}
	$bandwidth=&rc_get("dl_bandwidth_K");
	if ($bandwidth eq "") {
	    $bandwidth=64;
	}
	$band="-bandwidth $bandwidth"."000";
	unlink "$curtmpdir/pause";

	chdir $curtmpdir;

	$mplay_command=&rc_get("video_open_command");

	# process video

	# 2 other files we will use
	$curtmpfile=".temp";
	$audio_out="audio";
	$audio_in="audiodump.wav";
	$got_audio=0;

	if ($withsound eq "0") {
	    $syscom=$mplay_command . " -quiet -slave $band -osdlevel 0 -vo jpeg -fps 100000 $ss $frames -ao null \"$file\" > $curtmpfile 2>/dev/null";
	}
	else {
	    $syscom=$mplay_command . " -quiet -slave $band -osdlevel 0 -vo jpeg $ss $frames -ao pcm -mc 0  \"$file\" > $curtmpfile 2>/dev/null";
	}
	
	
	if (defined($DEBUG_OPEN)) {
	    print "open command is: $syscom\n";
	}
	
	system($syscom);

	@info=split /  /, `grep VIDEO: $curtmpfile`;
	@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];
	chomp($bpp);
	$fps=(split / /,$info[4])[0];

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

	&get_file_info;
	
	# get number of frames
	if ($count>0) {
	    # double check number of frames
	    $found=0;
	    for ($i=$count;$i--;$i>0&&$found==0) {
		$name=&mkname($i);
		if (-f $name) {
		    $found=$i;
		}
	    }
	    $count=$found;
	}

	unless ($type eq "jpeg"||$type eq "Frames"||$count>0) {
	    opendir DIR,$curtmpdir;
	    while ($file=readdir(DIR)) {
		if ($file =~ /$img_ext$/) {
		    $count++;
		}
	    }
	    closedir DIR;
	}
	
	if (-f $audio_in) {
	    &convert_audio_to_raw;
	    unlink $audio_in;
	    $af_size=-s $audio_out;
	}

	if ($count==0||$type eq "Frames" ||$type eq "jpeg"||$type eq "Audio") {
	    # we could have audio or images
	    if (-f $audio_in) {
		# just in case...
		$type="Audio";
	    }

	    if ($f_size>0) {
		&sig_complete($handle,$count,$type,$hsize,$vsize,$bpp,$fps,$f_size,$arate,$achans,$asamps,$signed,$endian,$af_size,$title,$author,$comment);
		exit 0;
	    }
	    # should have audio then
	    if (!($type eq "Audio")) {
		&sig_error("This does not appear to be a valid video or image file","$GUI_NAME was unable to open it.");
	    }
	}

	# mplayer seems to sometimes output one extra frame for jpg
	if (&rc_get("mplayer_extra_frame")&&(($frames eq "")||(!($frames eq "") && ($count<$ARGV[4])||($ARGV[3]==0)))) {
	    $name=&mkname($count);
	    unlink "$name$img_ext";
	    $count--;
	}

	unless ($frames eq "") {
	    $tfps=$fps;
	    if ($fps<1) {
		$tfps=1;
	    }
	    if ($ARGV[3]>=(1/$tfps)) {
		# if we opened a selection, mplayer wrongly outputs frame 1, 
		# so we usually need to delete it
		for ($i=2;$i<=$count;$i++) {
		    $from=&mkname($i);
		    $to=&mkname($i-1);
		    rename "$from$img_ext", "$to$img_ext";
		}
		$count--;
	    }
	}

	# if we were interrupted, the last frame can have 0 size, if so delete it
	$name=&mkname($count);
	unless (-s "$name$img_ext") {
	    unlink "$name$img_ext";
	    $count--;
	}

	unless ($ss eq ""||$achans==0) {
	    # try as best we can to sync sound and video for a selection
	    $audio_out=&clip_audio(0.5,1000000.);
	    rename $audio_out,"audio";
	    $audio_out="audio";
	}

	$af_size= -s $audio_out;
	if (-d $curtmpdir) {
	    &sig_complete($handle,$count,$type,$hsize,$vsize,$bpp,$fps,$f_size,$arate,$achans,$asamps,$signed,$endian,$af_size,$title,$author,$comment);
	}
	exit 0;
    }

	
	if ($command eq "save") {
	    # since 0.7.5, this command is called via the plugin
	    # and the command is then set to "encode"

	    unlink "$curtmpdir/pause";
	    chdir $curtmpdir;
	    
	    $fps=$ARGV[2];
	    $nfile=$ARGV[3];

	    # check the file is writable
	    unless (&is_writeable($nfile)) {
		&sig_error("Unable to open output file !","$GUI_NAME could not write to $nfile.");
	    }

	    $start=$ARGV[4];
	    $end=$ARGV[5];
	    $arate=$ARGV[6];
	    $achans=$ARGV[7];
	    $asamps=$ARGV[8];
	    if (defined($ARGV[9])) {
		$ssigned=$ARGV[9];
	    }
	    
	    else {
		$ssigned=1;
		if ($asamps==8) {
		    $ssigned=0;
		}
	    }
	    if ($ssigned==1) {
		$asigned="-s";
	    }
	    else {
		$asigned="-u";
	    }
	    
	    $otype=&rc_get("output_type");
	    $encoder=&rc_get("encoder");
	    $encoder_command=&rc_get("encoder_command");
	    if ($encoder_command eq "") {
		$encoder_command=$encoder;
	    }

	    $audiofile="";
	    $areq=&get_format_request;

	    #prepare audio stream if requested
	    if (-f "$curtmpdir/audio" && $arate>0) {
		$audiofile=$audio_in="$curtmpdir/audio";
		if ($areq&1) {
		    $aud_start=($start-1)/$fps;
		    $aud_end=$end/$fps;
			
		    # encode sound up to the next nearest second
		    # seems to be the norm...
		    $aud_length=($aud_end-$aud_start);
		    $aud_length=int($aud_length+1.0);
		    $aud_end=$aud_start+$aud_length;
				    
		    # clip the (raw) audio
		    $audiofile=$audio_in=&clip_audio($aud_start,$aud_end);
		}
		if ($areq&2) { 
		    # convert raw audio to wav
		    $audio_out=$curtmpdir . "/audiodump.wav";
		    &convert_audio_to_wav;
		    unlink $audio_in;
		    $audiofile=$audio_out;
		}
	    }

	    if ($areq&4) {
		#move the selection down so that frames start at 1
		if ($start>1) {
		    for ($i=1;$i<=$end;$i++) {
			$name=&mkname($i);
			$from=&mkname($i+$start-1);
			if ($i<$start) {
			    rename "$name$img_ext","$name.ign";
			}
			if (-f "$from$img_ext") {
			    rename "$from$img_ext","$name$img_ext";
			}
		    }
		}
	    }

	    if (-f ".comment") {
		open IN,"< .comment";
		read IN,$string,1040;
		unlink ".comment";
	    }
	    @tmp=split(/\|\|\%/,$string);
	    $title=$tmp[0];
	    $author=$tmp[1];
	    $comment=$tmp[2];
	    chomp($comment);

	    if (!defined($comment)||$comment eq "") {
		$comment=" ";
	    }
	    if (!defined($author)||$author eq "") {
		$author=" ";
	    }
	    if (!defined($title)||$title eq "") {
		$title=" ";
	    }

	    $command="encode";
	    return 1;
	    exit 0; # just in case
	}


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


    if ($command eq "backup") {
	$withaudio=$ARGV[2];
	$start=$ARGV[3];
	$end=$ARGV[4];

	if ($withaudio==0) {
	    $audio="";
	}
	else {
	    $audio="audio";
	}

	$nfile=$ARGV[5];

	unlink "$curtmpdir/pause";
	chdir $curtmpdir;

	# check the file is writable
	unless (&is_writeable($nfile)) {
	    &sig_error("Unable to open output file !","$GUI_NAME could not write to $nfile.");
	}

	unlink <*.tar>;
	$syscom="tar -cf header.tar $audio header* extended* event.* 2>/dev/null";
	system ($syscom);

	if ($start>0) {
	    # we have to add frames 100 at a time, otherwise tar complains about 'too many parameters'
	    $oend=$end;
	    $nexthundred=int($start/100)+1;
	    if ($end>$nexthundred*100-1) {
		$end=$nexthundred*100-1;
	    }
	    
	    $tarcount=1;
	    $tarname=&mkname($tarcount).".tar";
	    
	    for ($i=$start;$i<=$end;$i++) {
		&sig_progress($i);
		$name=&mkname($i);
		$syscom="tar -rf $tarname $name$img_ext";
		system $syscom;
	    }
	    
	    while (($nexthundred+1)*100<=$oend) {
		$end=$nexthundred*100;
		&sig_progress($end);
		$len=length ($nexthundred);
		$name=substr("000000",$len) . $nexthundred . "*";
		$tarname=&mkname(++$tarcount).".tar";
		$syscom="tar -cf $tarname $name$img_ext";
		system $syscom;
		$nexthundred++;
	    }
	    
	    $tarname=&mkname(++$tarcount).".tar";
	    
	    for ($i=$end;$i<=$oend;$i++) {
		&sig_progress($i);
		$name=&mkname($i);
		$syscom="tar -rf $tarname $name$img_ext";
		system $syscom;
	    }
	}
	$syscom="tar -cf back.tar *.tar";
	system ($syscom);
	unlink <header.tar 0*.tar>;
	$syscom="gzip -fq back.tar";
	system ($syscom);
	$syscom="mv back.tar.gz \"$nfile\"";
	system ($syscom);
	$size=-s "$nfile";
	&sig_complete($size);
	exit 0;
    }


    if ($command eq "restore") {
	$nfile=$ARGV[2];
	unlink "$curtmpdir/pause";
	chdir $curtmpdir;

	$syscom="cp \"$nfile\" back.tar.gz 2>/dev/null";
	system ($syscom);

	$syscom="gzip -dq back.tar.gz 2>/dev/null";
	system ($syscom);

	$syscom="tar -xf back.tar 2>/dev/null";
	system ($syscom);

	unlink "back.tar";

	$syscom="for i in `ls *.tar 2>/dev/null`; do tar -xf \$i; done";
	system ($syscom);

	unlink <*.tar>;

	unless (-f "header") {
	    &sig_error("This does not appear to be a valid backup file","$GUI_NAME was unable to open it.");
	}
	&sig_complete;
	exit 0;
    }



    if ($command eq "reorder") {
	if (!defined($ARGV[2])) {
	    $endian=&get_endian;
	}
	else {
	    $endian=$ARGV[2];
	}
	$newframe=-1;
	$event_file="$curtmpdir/event.frames";
	chdir $curtmpdir;

	&clean_old;

	if (defined(open IN,"< $event_file")) {
	    read IN,$val,4;
	    $pstart=&getint($val);
	    $count=$pstart;
	    
	    while ($newframe!=0) {
		read IN,$val,4;
		$newframe=&getint($val);
		if ($newframe>0) {
		    $from=&mkname($newframe);
		    $to=&mkname($count);
		    $syscom="cp $from$img_ext $to.mgk";
		    system ($syscom);
		    $count++;
		}
	    }
	    close IN;
	}
	
	else {
	    &sig_error;
	}

	$start=$pstart;
	$end=$count;

	&mv_mgk;
	&sig_complete(--$count);
	exit 0;
    }



    if ($command eq "deorder") {
	$start=$ARGV[2];
	$end=$ARGV[3];
	$frames=$ARGV[4];
	chdir $curtmpdir;

	$oend=$end;
	if ($end>$frames) {
	    $end=$frames;
	}

	&undo(1);

	for ($i=$frames+1;$i<=$oend;$i++) {
	    $name=&mkname($i);
	    unlink "$name$img_ext";
	}

	&clean_old;
	&sig_complete;
	exit 0;
    }


    if ($command eq "cut") {
	&clean_old;

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

	if ($cut_audio) {
	    $fps=$ARGV[6];
	    $arate=$ARGV[7];
	    $achans=$ARGV[8];
	    $asamps=$ARGV[9];
	    if ($arate*$asamps*$achans) {
		$start=($start-1)/$fps;
		$end=$end/$fps;
		&cut_audio;
	    }
	}
	&sig_complete;
	exit 0;
    }


	if ($command eq "delete_all") {
	    $frames=$ARGV[2];
	    if (! -d $curtmpdir) {
		&sig_complete;
		exit 1;
	    }
	    chdir $curtmpdir;
	    unlink <* *.>;
	    if ($GUI_NAME eq "LiVES") {
		# LiVES needs this to stop the progress dialog from flickering 
		sleep(1);
	    }
	    &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 "fs_preview") {
	unlink "$curtmpdir/pause";
	$win=$ARGV[2];
	$hsize=$ARGV[3];
	$vsize=$ARGV[4];
	$start_time=$ARGV[5];
	$preview_frames=$ARGV[6];
	$file=$ARGV[7];
	$mplayer_command=&location("mplayer");

	$syscom="$mplayer_command -quiet -slave -x $hsize -y $vsize -wid $win -ss $start_time -frames $preview_frames \"$file\" >/dev/null 2>&1";
	system($syscom);
	&sig_complete;
	exit 0;
    }


    if ($command eq "export_audio") {
	unlink "$curtmpdir/pause";
	$audio_start=$ARGV[2];
	$audio_end=$ARGV[3];
	$arate=$ARGV[4];
	$achans=$ARGV[5];
	$asamps=$ARGV[6];
	$asigned=$ARGV[7];
	$nrate=$ARGV[8];
	$nfile=$ARGV[9];

	# check the file is writable
	unless (&is_writeable($nfile)) {
	    &sig_error("Unable to open output file !","$GUI_NAME could not write to $nfile.");
	}
	if ($asigned==1) {
	    $asigned="-s";
	}
	else {
	    $asigned="-u";
	}
	
	if ($audio_end>0.) {
	    $audio_in=&clip_audio($audio_start,$audio_end);
	}
	else {
	    $audio_in="$curtmpdir/audio";
	}
	# convert raw audio to wav
	$audio_out=$curtmpdir . "/audiodump.wav";
	&convert_audio_to_wav;
	if ($audio_end>0.) {
	    unlink $audio_in;
	}
  
	$syscom="mv $audio_out \"$nfile\" >/dev/null 2>&1";
	system ($syscom);

	&sig_complete;
	exit 0;
    }


    if ($command eq "append_audio") {
	# here we end up with (raw) 'audiodump', which will be renamed to 'audio' in commit_audio
	$endian=&get_endian;
	unlink "$curtmpdir/pause";

	$type=$ARGV[2];
	$nrate=$ARGV[3];
	$nchans=$ARGV[4];
	$nsamps=$ARGV[5];
	$nsigned=$ARGV[6];
	$nendian=$ARGV[7];
	$file=$ARGV[8];
	$audio_in="audiodump.wav";

	chdir $curtmpdir;
	$audiofile="audio";

	if ($type eq "mp3") {
	    &mp3_open;
	}
	elsif ($type eq "ogg") {
	    &ogg_open;
	}
	else {
	    &wav_open;
	}

	`sync;sync;sync`;

	unless (-f $audio_in) {
	    &sig_error("$GUI_NAME was not able to open the file","$file");
	}
	
	$audio_out="audio.new";
	&convert_audio_to_raw;
	unlink $audio_in;

	$audio_bak="audio.bak";

	# resample $audio_out (if required)
	if (!($arate==$nrate&&$achans==$nchans&&$asamps==$nsamps&&$nsigned==$signed&&$nendian==$endian)) {
	    $audio_in=$audio_out;
	    $audio_out=$audio_bak;
	    &resample_audio;
	    unlink $audio_in;
	    rename $audio_out,$audio_in;
	    $audio_out=$audio_in;
	}

	$syscom="cat audio $audio_out > audiodump";
	system ($syscom);

	unlink $audio_out;
	$fsize=-s "audiodump";
	&sig_complete($fsize);
	exit 0;
    }


    if ($command eq "trim_audio") {
	unlink "$curtmpdir/pause";
	$audio_start=$ARGV[2];
	$audio_end=$ARGV[3];
	$arate=$ARGV[4];
	$achans=$ARGV[5];
	$asamps=$ARGV[6];

	$syscom="cp $curtmpdir/audio $curtmpdir/audio.bak";
	unless (&rc_get("conserve_space") eq "true") {
	    system($syscom);
	}

	# $audio_from is the new clip
	$audio_from=&clip_audio($audio_start,$audio_end);
	unlink $audio_in;
	$where=$audio_start;
	$end=$audio_end-$audio_start;
	$start=0;
	# insert_audio will insert silence at start
	&insert_audio;
	unlink $audio_out;
	&append_silence(&align($audio_end*$arate*$align));

	&sig_complete;
	exit 0;
    }

    if ($command eq "resample_audio") {
	unlink "$curtmpdir/pause";
	$arate=$ARGV[2];
	$achans=$ARGV[3];
	$asamps=$ARGV[4];
	$asigned=$ARGV[5];
	$aendian=$ARGV[6];


	$nrate=$ARGV[7];
	$nchans=$ARGV[8];
	$nsamps=$ARGV[9];
	$nsigned=$ARGV[10];
	$nendian=$ARGV[11];
	if (defined($ARGV[12])) {
	    $stretch=$ARGV[12];
	    $audio_in="$curtmpdir/audio.orig";
	}
	else {
	    $audio_in="$curtmpdir/audio.bak";
	}
	$audio_out="$curtmpdir/audio";

	unlink $audio_in;
	rename $audio_out,$audio_in;

	&resample_audio;

	if (&rc_get("conserve_space") eq "true" && (-s $audio_out)) {
	    unlink $audio_in;
	}

	&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") {
	unlink "$curtmpdir/pause";
	# remove any gaps in the play images
	$start=$ARGV[2];
	if (defined($ARGV[3])) {
	    $pre_cut_frames=$ARGV[4];
	}

	$new_frames=&fill_and_redo_frames;
	&sig_complete($new_frames,$hsize,$vsize,$bpp);
	exit 0;
    }


    if ($command eq "commit_audio") {
	# commit the audio file in either audiodump or audiodump.wav as new audio file
	unlink "$curtmpdir/pause";
	$audio_out="$curtmpdir/audio";
	$audio_bak="$curtmpdir/audio.bak";

	$file=$audio_in="$curtmpdir/audiodump.wav";

	$f_size=-s $audio_in;
	if ($f_size>0) {
	    # wav format
	    if (&rc_get("conserve_space") eq "true") {
		unlink $audio_out;
	    }
	    else {
		rename $audio_out,$audio_bak;
	    }
	    &get_file_info;
	    &convert_audio_to_raw;
	}
	else {
	    $audio_in="$curtmpdir/audiodump";
	    if (-f $audio_in) {
		# raw format
		if (&rc_get("conserve_space") eq "true") {
		    unlink $audio_out;
		}
		else {
		    rename $audio_out,$audio_bak;
		}
		rename $audio_in,$audio_out;
		# dummy values, the GUI should know these
		$arate=$achans=$asamps=0;
		$signed=$endian=1;
	    }
	    else {
		&sig_error("$GUI_NAME audio error.");
	    }
	}

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

    if ($command eq "cancel_audio") {
	# remove the audio file in audiodump.wav
	# plus any audio.* files (e.g. from append audio)
	unlink "$curtmpdir/pause";

	chdir $curtmpdir;
	unlink <audiodump*>;

	&sig_complete;
	exit 0;
    }


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

	$cdda2wav_command=&location("cdda2wav");
	if ($cdda2wav_command eq "") {
	    &sig_error("cdda2wav is required for this function.","Please install it first.");
	}

	$cdplay_device=&rc_get("cdplay_device");
	if ($cdplay_device eq "") {
	    &sig_error("You must set the CD device first in Preferences.");
	}

	$track=$ARGV[2];

	$audiofile=$curtmpdir."/audiodump.wav";
	if (-f $audiofile) {
	    unlink $audiofile;
	}

	$syscom="$cdda2wav_command -q -x -D $cdplay_device -t $track $audiofile >/dev/null 2>&1";
	system($syscom);
	$f_size=-s $audiofile;
	# the '-x' option will force these
	$arate=44100;
	$achans=2;
	$asamps=16;
	$aendian=&get_endian;
	$asigned=1;
	if ($asamps==8) {
	    $asigned=0;
	}
	&sig_complete($arate,$achans,$asamps,$asigned,$aendian,$f_size);
	exit 0;
    }



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

	$file=$ARGV[2];
	$audio_in=$curtmpdir."/audiodump.wav";

	$ext=&get_ext($file);
	if ($ext eq ".mp3") {
	    &mp3_open;
	}
	if ($ext eq ".ogg") {
	    &ogg_open;
	}
	if ($ext eq ".wav") {
	    &wav_open;
	}

	&sig_complete($arate,$achans,$asamps,$asigned,$aendian,$f_size);
	exit 0;
    }


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

	$file=$ARGV[2];
	$audio_in=$curtmpdir."/audiodump.wav";

	&mp3_open;

	&sig_complete($arate,$achans,$asamps,$asigned,$aendian,$f_size);
	exit 0;
    }

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

	$file=$ARGV[2];
	$audio_in=$curtmpdir."/audiodump.wav";

	&ogg_open;
	&sig_complete($arate,$achans,$asamps,$asigned,$aendian,$f_size);
	exit 0;
    }

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

	$file=$ARGV[2];
	$audio_in=$curtmpdir."/audiodump.wav";

	&wav_open;
	&sig_complete($arate,$achans,$asamps,$asigned,$aendian,$f_size);
	exit 0;
    }


    if ($command eq "xmmsrandom") {
	#messy, but it should work for now
	# TODO - compile list of all audio files, then pick one at random
	unlink "$curtmpdir/pause";
	$trackstoplay=$ARGV[2];
	$descend=$ARGV[3];
	$minmeg=$ARGV[4];
	$maxmeg=$ARGV[5];
	$origdir=$dir=$ARGV[6];
	$meg=1024*1024;

	$xmms_command=&location("xmms");
	unless ($xmms_command eq "") {
	    $first=1;
	    $tracks=0;
	    # give up if we tried 200 files and didn't get an audio file
	    $giveup=200;

	    do {
		opendir DIR,$dir;
		@length=readdir(DIR);
		$count=scalar(@length)-2;
		closedir DIR;
		
		if ($count>0) {
		    $filetoplay=int(rand $count)+2;
		    $file=@length[$filetoplay];

		    if ((-d "$dir$file")&&$descend==1) {
			$dir="$dir$file/";
		    }

		    elsif (-f "$dir$file") {
			$size=-s "$dir$file";
			if (($size>$minmeg*$meg)&&($size<$maxmeg*$meg)) {
			    $ext=&get_ext($file);
			    $l=length($file);
			    # TODO - add more extensions (and allow editing thru prefs...)
			    if (($ext eq ".mp3")||($ext eq ".ogg")||($ext=".mod")) {
				$tracks++;
				$giveup=200;
				$found=1;
				if ($first==1) {
				    $syscom=$xmms_command." -p \"$dir$file\"";
				    $first=0;
				}
				else {
				    $syscom=$xmms_command." -p -e \"$dir$file\"";
				}
				system($syscom);
				$dir=$origdir;
			    }
			}
		    }
		}
		else {
		    $dir=$origdir;
		}
	    } while ($tracks<$trackstoplay&&($giveup--)>0);
	    if ($giveup<=0) {
		&sig_error("$GUI_NAME could not find enough tracks fitting your request.","Try again with different values.");
	    }
	}
	&sig_complete;
	exit 0;
    }




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

    if ($command eq "undo_cut") {
	$undo_cut=1;
	$command="insert";
    }


    if ($command eq "insert") {

	unlink "$curtmpdir/pause";
	unless ($undo_cut==1) {
	    &clean_old;
	}

	$where=$ARGV[2];
	$start=$ARGV[3];
	$end=$ARGV[4];
	$from_handle=$ARGV[5];
	$with_audio=$ARGV[6]; # boolean
	$num_frames=$ARGV[7];
	$width=$ARGV[8];
	$height=$ARGV[9];
	$times=1;

	if (defined ($ARGV[14])) {
	    $times=$ARGV[14];
	}

	&insert;

	if ($with_audio) {
	    $fps=$ARGV[10];
	    $arate=$ARGV[11];
	    $achans=$ARGV[12];
	    $asamps=$ARGV[13];
	    $start=($start-1)/$fps;
	    $end=$end/$fps;
	    $where/=$fps;
	    &insert_audio;
	}

	if ($undo_cut==1) {
	    &clean_old;
	}

	&sig_complete;
	exit 0;
    }


    if ($command eq "resize") {
	$start=$ARGV[2];
	$end=$ARGV[3];
	$height=$ARGV[4];
	$width=$ARGV[5];
	&clean_old;
	$resize_ext=".mgk";
	for ($i=$start;$i<=$end;$i++) {
	    $name=&mkname($i);
	    &resize_frame($name,$height,$width);
	    &sig_progress($i);
	}
	&mv_mgk;
	&sig_complete;
	exit 0;
    }


    if ($command eq "merge_frames") {
	$where=$ARGV[2];
	$start=$ARGV[3];
	$end=$ARGV[4];
	$from_handle=$ARGV[5];
	$width=$ARGV[6];
	$height=$ARGV[7];
	$owidth=$ARGV[8];
	$oheight=$ARGV[9];
	$x=$ARGV[10];
	$y=$ARGV[11];
	$threshold=$ARGV[12];
	$transwhite=$ARGV[13];
	$pc_start=100;

	if (defined $ARGV[14] && !($ARGV[14] eq "")) {
	    $pc_start=$ARGV[14];
	}
	$pc_step=0;
	if (defined $ARGV[15] && !($ARGV[15] eq "")) {
	    $pc_step=$ARGV[15];
	}
	$pc_end=-1;
	if (defined $ARGV[16] && !($ARGV[16] eq "")) {
	    $pc_end=$ARGV[16];
	    if ($pc_step>0&&$pc_end<$pc_start) {
		$pc_end=-1;
	    }
	    if ($pc_step<0&&$pc_end>$pc_start) {
		$pc_end=-1;
	    }
	}

	&clean_old;

	$threshold_plus=-(++$threshold);

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

	    $from=&mkname($j);
	    $fromimg="$tmpdir/$from_handle/$from$img_ext";

	    unless (-f $fromimg) {
		# end of clipboard reached, loop back to start
		$j=$start;
		$from=&mkname($j);
		$fromimg="$tmpdir/$from_handle/$from$img_ext";
	    }

	    $name=&mkname($i-$start+$where);
	    $toimg="$curtmpdir/$name.mgk";

	    &merge;

	    $pc_start+=$pc_step;
	    if ($pc_start>100) {
		$pc_start=100;
	    }
	    if ($pc_start<0) {
		$pc_start=0;
	    }

	    if ($pc_end!=-1) {
		if ($pc_step>0&&$pc_start>$pc_end) {
		    $pc_start=$pc_end;
		}
		if ($pc_step<0&&$pc_start<$pc_end) {
		    $pc_start=$pc_end;
		}
	    }

	    $j++;
	}
	
	$end=$where+$end-$start;
	$start=$where;

	&mv_mgk;
	&sig_complete;
	exit 0;
    }




    if ($command eq "open_image") {
	unlink "$curtmpdir/pause";
	$file=$ARGV[2];
	$height=$ARGV[3];
	$width=$ARGV[4];
	$start=1;
	if (defined $ARGV[5] && !($ARGV[5] eq "")) {
	    $start=$ARGV[5];
	}
	&open_single_image($start);
	&sig_complete;
	exit 0;
    }








    # effects

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




    if ($command eq "edge") {
	unless (defined($ARGV[4])) {
	    $radius=1;
	}
	else {
	    $radius=$ARGV[4];
	}
	
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    $syscom="$convert_command -edge $radius $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    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);
	    $syscom="$convert_command -normalize $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }

    if ($command eq "jumble") {
	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $from=&mkname(int(rand $end)+$start);
	    $to=&mkname($i);
	    $syscom="cp $from.jpg $to.mgk";
	    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);
	    $syscom="$convert_command -monochrome $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);

	    # need to recolorize for mplayer
	    if ($video_player eq "mplayer") {
		$cstring="'#FD02FF'";
		if (&version_hash(&rc_get("mogrify_version"))>=5005004) {
		    $syscom="$convert_command -fill $cstring -colorize 1/1/1 $curtmpdir/$name.mgk $curtmpdir/$name.mgk";
		}
		else {
		    $syscom=&location("mogrify") . " -pen " . $cstring . " -colorize 1/1/1 " . $curtmpdir . "/" . $name . ".mgk";
		}
		system($syscom);
		if (-f "$curtmpdir/$name.mgk~") {
		    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);
	    $syscom="$convert_command -charcoal $radius $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);

	    # need to recolorize for mplayer
	    if ($video_player eq "mplayer") {
		$cstring="'#FD02FF'";
		if (&version_hash(&rc_get("mogrify_version"))>=5005004) {
		    $syscom="$convert_command -fill $cstring -colorize 1/1/1 $curtmpdir/$name.mgk $curtmpdir/$name.mgk";
		}
		else {
		    $syscom=&location("mogrify") . " -pen " . $cstring . " -colorize 1/1/1 " . $curtmpdir . "/" . $name . ".mgk";
		}
		system($syscom);
		if (-f "$curtmpdir/$name.mgk~") {
		    rename "$curtmpdir/$name.mgk~","$curtmpdir/$name.mgk";
		}
	    }
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
    if ($command eq "wave") {
	$amp=1;
	$wavemin=50;
	$wavemax=100;

	if (defined $ARGV[4] && !($ARGV[4] eq "")) {
	    $wavemax=$ARGV[4];
	}

	$wavestep=-1;
	$ampstep=1;

	$wave=$wavemax;

	# background seems to be ignored
	$bgcolour=&rc_get("bgcolour");

	&clean_old;

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

	    $syscom="$convert_command -wave $amp" . "x$wave -background \"$bgcolour\" $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);

	    $amp+=$ampstep;

	    $wave+=$wavestep;
	    if ($wave>=$wavemax) {
		$wave=$wavemax;
		$wavestep=-$wavestep;
	    }
	    elsif ($wave<$wavemin) {
		$wave=$wavemin;
		$wavestep=-$wavestep;
	    }
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }

    if ($command eq "flip") {
	&clean_old;
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    $syscom="$convert_command -flip $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);

	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    if ($command eq "flop") {
	&clean_old;
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    $syscom="$convert_command -flop $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);

	}
	
	&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) {
		#need continuous frames for preview
		$syscom="cp $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    }
	 
	    else {
		$syscom="$convert_command -negate $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
		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);
	    $syscom="$convert_command -solarize $startval $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    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);
	    $syscom="$convert_command -enhance $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    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);
	    $syscom="$convert_command -cycle $col $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);
	    $col+=$cstep;
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }

    if ($command eq "tunnel") {
	$d_type="tunnel";
	$command="dream";
    }

    
    if ($command eq "dream") {
	$start=$ARGV[2];
	$end=$ARGV[3];
	$owidth=$width=$ARGV[4];
	$oheight=$height=$ARGV[5];
	$x=0;
	$y=0;
	$pc_start=45;

	$threshold=-1;
	$threshold_plus=16000;
	$threshold=100000;

	if ($d_type eq "tunnel") {
	    $x=int($width/8)+1;
	    $y=int($height/8)+1;
	    $width-=$x*2;
	    $height-=$y*2;
	    $pc_start=90;
	}

	$transwhite=0;

	$diffmax=10;
	$diff=0;

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

	    # merge frame from diff frames ago at 50%
	    $from=&mkname(int($i-$diff));
	    if (-f "$curtmpdir/$from.mgk") {
		$fromimg="$curtmpdir/$from.mgk";
	    }
	    else {
		$fromimg="$curtmpdir/$from$img_ext";
	    }
	    $toimg="$curtmpdir/$name.mgk";

	    &merge;

	    # make the diff grow at the beginning and shrink at the end
	    $diff+=(rand 5)-2;

	    if ($diff<1) {
		$diff+=2;
	    }
	    if ($i-$diff<$start||$diff+$i>$end||$diff>$diffmax) {
		$diff-=3;
	    }
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }

    if ($command eq "rotate") {
	$command="spin";
    }

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

	$bgcolour=&rc_get("bgcolour");
	&clean_old;
	
	# this is for resize_frame
	$input_ext=".mgk";
	$resize_ext=".mgk";

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


    if ($command eq "randomzoom") {
	$height=$ARGV[4];
	$width=$ARGV[5];
	$max_zoom=50;
	$zoom=-1;
	$depth=1;
	$crop_left=0;
	$crop_up=0;

	&clean_old;
	
	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    
	    #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_ext=$img_ext;
	    &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="$convert_command -crop $width" . "!x" . $height . "!+" . $crop_left . "+" . $crop_up . " $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    system($syscom);
	}

	&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);
	    $syscom="$convert_command -swirl $swirl $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    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])) {
	    $cend=0;
	}
	else {
	    $cend=$ARGV[6];
	}

	if (!defined($ARGV[7])) {
	    $pcend=$pcstart;
	}
	else {
	    $pcend=$ARGV[7];
	}
	
	$random=0;
	if (defined($ARGV[8])) {
	    $random=$ARGV[8];
	}

	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;

	$cend_red=int($cend/65536);
	$cend-=$cend_red*65536;
	$cend_green=int($cend/256);
	$cend-=$cend_green*256;
	$cend_blue=$cend;

	$length=$end-$start+1;
	$cstep_red=($cend_red-$cstart_red)/$length;
	$cstep_green=($cend_green-$cstart_green)/$length;
	$cstep_blue=($cend_blue-$cstart_blue)/$length;

	for ($i=$start;$i<=$end;$i++) {
	    &sig_progress($i);
	    $name=&mkname($i);
	    if ($random) {
		$cstart_red=int(rand 256);
		$cstart_green=int(rand 256);
		$cstart_blue=int(rand 256);
	    }
	    $cstring=sprintf("'#%02X%02X%02X'",int($cstart_red),int($cstart_green),int($cstart_blue));
	    $pcint=int($pcstart);
	    $pcstring="$pcint/$pcint/$pcint";

	    if (&version_hash(&rc_get("mogrify_version"))>=5005004) {
		$syscom="$convert_command -fill $cstring -colorize $pcstring $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    }
	    else {
		$syscom="$mogrify_command -pen $cstring -colorize $pcstring $curtmpdir/$name$img_ext $curtmpdir/$name.mgk";
	    }

	    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;
	    }
	    
	    $pcstart+=$pcstep;
	    if ($pcstart>100) {
		$pcstart=100;
	    }
	    elsif ($pcstart<0) {
		$pcstart=0;
	    }
	}
	
	&mv_mgk;
	&sig_complete;

	exit 0;
    }
    
	if (caller) {
	    print "smogrify: unrecognised command $command\n";
	    exit 1;
	}
    }
    return 1;
    exit 99; # just in case
}







######################################################
    
#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 check_for_stop {
    if (defined(open IN,"< $curtmpdir/.status.fileop")) {
	close IN;
	return 1;
    }
    return 0;
}


sub is_writeable {
    my $file=@_;
    if (! -s $nfile) {
	unlink $nfile;
    }
    $exists=(-f "$nfile");
    if (!$exists) {
	$syscom="touch \"$nfile\"; chmod o+w \"$nfile\"";
	system ($syscom);
    }
    `sync;sync;sync`;
    $ret=(-w "$nfile");
    if (!$exists) {
	unlink "$nfile";
    `sync`;
    }
    return $ret;
}


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);

    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";
	}
    }
    `sync;sync;sync`;
}


sub undo {
    # recover .bak files; if param==0 or undefined, move current files to .mgk files
    my ($param)=@_;
    if ($param eq "") {
	$param=0;
    }

    my ($i,$name);
    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	&sig_progress($i);
	if (-f "$curtmpdir/$name.bak") {
	    if ($param==0) {
		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);
	rename "$name$img_ext","$name.bak";
	&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);
    $times_inserted=0;
    $factor=1;
    my ($nend)=$end;
    my ($nstart)=$start;
    my ($nwhere)=$where;
    my ($nfromdir)=$fromdir;

    if (!defined($times)) {
	$times=1;
    }

    $fromdir="$tmpdir/$from_handle";
    $frames_to_move=($nend-$nstart+1)*$times;

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

    $resize_ext=$img_ext;

    #move from $from_handle
    $j=$start;
    while ($times_inserted<$times) {
	for ($i=$nstart;$i<=$nend;$i++) {
	    $from=&mkname($i);
	    $to=&mkname($nwhere+$i-$nstart+1);
	    &sig_progress(int($num_frames-$where+$j++-$start+1));
	    if ($undo_cut==1) {
		$syscom="cp $fromdir/$from.bak $curtmpdir/$to$img_ext";
	    }
	    else {
		$syscom="cp $fromdir/$from$img_ext $curtmpdir/$to$img_ext";
	    }
	    system($syscom);
	    
	    if ($height && $width && !($fromdir eq $curtmpdir)) {
		&resize_frame($to,$height,$width);
	    }
	}

	$times_inserted+=$factor;
	$inserted=$nend-$nstart+1;

	if ($times_inserted==2) {
	    $nend=$nwhere;
	    $nstart=$nwhere-$inserted+1;
	    $fromdir=$curtmpdir;
	}
	$nwhere+=$inserted;

	if ($times_inserted>1) {
	    $nend+=$inserted;
	    $factor*=2;
	}
	while ($times>$times_inserted&&$factor>$times-$times_inserted) {
	    $nstart+=($nend-$nstart+1)/2;
	    $factor/=2;
	}
    }
}



sub resize_frame {
    my ($name,$height,$width)=@_;

    if (!defined($input_ext)) {
	$input_ext=$img_ext;
    }

    $syscom="$convert_command -size $widthx$height -resize ".$width."!x".$height."! $curtmpdir/$name$input_ext $curtmpdir/$name$resize_ext";
    system($syscom);
}


sub merge { # heh :-)	    'the underwater function'
    # a $threshold of 100000 means no transparency, >0 otherwise is transparent over, <0 is transparent under

    if (!defined($transwhite)) {
	$transwhite=0;
    }

    if ($threshold<100000&&$threshold>=0) {
	$syscom="$convert_command -size $width"."x$height $fromimg -resize $width!x$height! resize.png";
	system($syscom);
    
	$syscom="$convert_command -size $width!x$height! xc:transparent -matte blank.png";
	system($syscom);
	if ($transwhite==0) {
	    $syscom="$convert_command -threshold $threshold resize.png mask.png";
	}
	else {
	    $syscom="$convert_command -threshold ".(65535-$threshold)." -negate resize.png mask.png";
	}
	system($syscom);
	$syscom="$composite_command resize.png blank.png mask.png output.png";
	system($syscom);
	$syscom="$composite_command -compose plus -dissolve $pc_start -geometry +$x!+$y! output.png $curtmpdir/$name$img_ext $toimg.jpg";
	system($syscom);
	$syscom="$convert_command +matte $toimg.jpg $toimg";
	system($syscom);

	unlink <mask.png blank.png output.png resize.png resize2.png>;
	unlink "$toimg.jpg";
    }
    elsif ($threshold<0) {

	$syscom="$convert_command -size $width"."x$height $fromimg -resize $width!x$height! resize.png";
	system($syscom);
    
	$syscom="$convert_command $curtmpdir/$name$img_ext resize2.png";
	system($syscom);
	$syscom="$convert_command -size $owidth!x$oheight! xc:transparent -matte blank.png";
	system($syscom);
	if ($transwhite==0) {
	    $syscom="$convert_command -threshold $threshold_plus resize2.png mask.png";
	}
	else {
	    $syscom="$convert_command -threshold ".(65535-$threshold_plus)." -negate resize2.png mask.png";
	}
	system($syscom);
	$syscom="$composite_command resize2.png blank.png mask.png output.png";
	system($syscom);

	if ($height!=$oheight||$width!=$owidth) {
	    $syscom="$composite_command -compose plus -dissolve $pc_start -geometry -$x!-$y! output.png resize.png $toimg.png";
	    system($syscom);	
	    $syscom="$composite_command -geometry +$x!+$y! $toimg.png resize2.png $toimg.jpg";
	    system($syscom);
	}
	else {
	    $syscom="$composite_command -compose plus -dissolve $pc_start -geometry -$x!-$y! output.png resize.png $toimg.jpg";
	    system($syscom);	
	}

	$syscom="$convert_command +matte $toimg.jpg $toimg";
	system($syscom);

	unlink <mask.png blank.png output.png resize.png resize2.png>;
	unlink "$toimg.jpg";
	unlink "$toimg.png";
    }
    else {
	$syscom="$composite_command -compose plus -dissolve $pc_start -geometry $width!x$height!+$x!+$y! $fromimg $curtmpdir/$name$img_ext $toimg >/dev/null 2>&1";
	system($syscom);
    }
}


sub get_ext {
    # return the extension of a file (including the '.')
    my ($file)=@_;
    return substr($file,length($file)-4,4);
}



sub get_formats {
    if ($asamps==8){
	$format=16;
	$signed=0;
	$xsigned="-u";
    }
    else {
	$signed=1;
	$xsigned="-s";
	if ($endian==1) {
	    # little
	    $format=128;
	}
	else {
	    # big
	    $format=256;
	}
    }
}




sub get_file_info {
    $af_size=0;
    $count=0;
    $bpp=24;  # default if none is found
    $fps=0; # let the front-end handle this if we can't get it ;-)
    $type="Unknown";
    $hsize=0;
    $vsize=0;
    $signed=1;
    $f_size=0;
    $arate=$asamps=$achans=0;
    $frames=0;

    $mplay_command=&rc_get("video_open_command");
    $file_ident="$curtmpdir/file_info";

    $id_vid_form="";

    # if mplayer supports the -identify command, use that
    # the format changed for 1.0pre1 so now we use -vo null -ao null -frames 0
    $syscom=$mplay_command . " -identify -v -slave -vo null -ao null -frames 0 \"" . $file . "\">" . $file_ident." 2>/dev/null";
    system($syscom);

    if (-f $file_ident) {
	$id_vid_form=`grep -m 1 ID_VIDEO_FORMAT $file_ident 2>/dev/null`;
	@tmp=split("=",$id_vid_form);
	$id_vid_form=@tmp[1];
	chomp($id_vid_form);
	
	$id_aud_form=`grep -m 1 ID_AUDIO_FORMAT $file_ident 2>/dev/null`;
	@tmp=split("=",$id_aud_form);
	$id_aud_form=@tmp[1];
	chomp($id_aud_form);

	if ($id_vid_form eq "") {
	    if (!($id_aud_form eq "")) {
		$id_vid_form="Audio";
	    }
	}

	unless ($id_vid_form eq "") {
	    # this could probably be done better using regexp...
	    $type=$id_vid_form;
	    
	    $bpp=`grep -m 1 VIDEO: $file_ident 2>/dev/null`;
	    @tmp=split("  ",$bpp);
	    $bpp=@tmp[3];
	    @tmp=split("bpp",$bpp);
	    $bpp=@tmp[0];

	    $hsize=`grep -m 1 ID_VIDEO_WIDTH $file_ident 2>/dev/null`;
	    @tmp=split("=",$hsize);
	    $hsize=@tmp[1];
	    chomp($hsize);
	    
	    $vsize=`grep -m 1 ID_VIDEO_HEIGHT $file_ident 2>/dev/null`;
	    @tmp=split("=",$vsize);
	    $vsize=@tmp[1];
	    chomp($vsize);
	    
	    $fps=`grep -m 1 ID_VIDEO_FPS $file_ident 2>/dev/null`;
	    @tmp=split("=",$fps);
	    $fps=@tmp[1];
	    chomp($fps);
	    
	    $arate=`grep -m 1 ID_AUDIO_RATE $file_ident 2>/dev/null`;
	    @tmp=split("=",$arate);
	    $arate=@tmp[1];
	    chomp($arate);
	    
	    $achans=`grep -m 1 ID_AUDIO_NCH $file_ident 2>/dev/null`;
	    @tmp=split("=",$achans);
	    $achans=@tmp[1];
	    chomp($achans);
	  
	    # mplayer seems to halve the bitrate sometimes
	    $abitrate=`grep -m 1 ID_AUDIO_BITRATE $file_ident 2>/dev/null`;
	    @tmp=split("=",$abitrate);
	    $abitrate=@tmp[1];
	    chomp($abitrate);

	    $comment=`grep -m 1 Comments: $file_ident 2>/dev/null`;
	    @tmp=split(" ",$comment);
	    shift(@tmp);
	    $comment=join(" ",@tmp);
	    chomp($comment);

	    $title=`grep -m 1 Name: $file_ident 2>/dev/null`;
	    @tmp=split(" ",$title);
	    shift(@tmp);
	    $title=join(" ",@tmp);
	    chomp($title);

	    $author=`grep -m 1 Artist: $file_ident 2>/dev/null`;
	    @tmp=split(" ",$author);
	    shift(@tmp);
	    $author=join(" ",@tmp);
	    chomp($author);

	    $count=`grep -m 1 \"frames  total\" $file_ident 2>/dev/null`;
	    @tmp=split(" ",$count);
	    $count=@tmp[2];
	    chomp($count);

	}
	if ($arate*$achans>0) {
	    $asamps=$abitrate/$arate/$achans;
	}
	else {
	    $asamps=0;
	}
    }
    if ($hsize*$vsize==0&&!($type eq "Audio")) {
	# see if it is image(s)

	# in the command version of get_details, we set $open_first to 1
	&open_images(1);
	opendir DIR,$curtmpdir;
	while ($file=readdir(DIR)) {
	    if ($file =~ /$img_ext$/) {
		$f_size+=-s $file;
		$count++;
		if ($open_first) {
		    unlink $file;
		}
	    }
	    else {
		unlink $file;
	    }
	}
	
	closedir DIR;
	if ($count) {
	    # got image(s)
	  
	    $frames=$count;
	    # got image(s)
	    $type="jpeg";
	    
	    if ($count>1) {
		$type="Frames";
	    }
	    $name=&mkname(1);
	}
    }

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

    if ($asamps==8) {
	$signed=0;
    }
    else {
	$signed=1;
    }

    $endian=&get_endian; # assume audio endian matches machine endian

    # get file size
    if ($f_size==0) {
	$f_size= -s $file;
    }

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





sub convert_audio_to_raw {
    &get_formats;
    if (&rc_get("audio_player") eq "sox") {
	$syscom="sox -t wav $audio_in -t raw $xsigned $audio_out > /dev/null 2>&1";
    }
    else {
	$format=&get_mplayer_format;
	$syscom=&rc_get("video_open_command") . " -quiet -slave $audio_in -ao pcm -nowaveheader $format -aofile $audio_out >/dev/null 2>&1";
    }
    system ($syscom);

    `sync;sync;sync`;
}


sub convert_audio_to_wav {
    chdir $curtmpdir;
    if (&rc_get("audio_player") eq "mplayer") {
	my ($aasamps)=$asamps/8;
	$format=&get_mplayer_format;
	$syscom=&rc_get("video_open_command") . " -quiet -slave -ao pcm -rawaudio on:rate=$arate:channels=$achans:samplesize=$aasamps -waveheader $format -vo null $audio_in >/dev/null 2>&1";
    }
    else {
	if ($asamps==8) {
	    $sasamps="b";
	}
	else {
	    $sasamps="w";
	}
	
	if (!defined($nrate)) {
	    $nrate=$arate;
	}

	if (!defined($nchans)) {
	    $nchans=$achans;
	}

	if (!defined($nasamps)) {
	    $nasamps=$sasamps;
	}

	if (!defined($asigned)) {
	    if ($sasamps==8) {
		$asigned="-u";
	    }
	    else {
		$asigned="-s";
	    }
	}

	if (!defined($nsigned)) {
	    if ($nasamps==8) {
		$nsigned="-u";
	    }
	    else {
		$nsigned=$asigned;
	    }
	}

	if (!defined($nendian)) {
	    $nendian=$aendian;
	}

	$syscom="sox -t raw -r $arate $asigned -$sasamps -c $achans $aendian $audio_in -t wav -r $nrate -c $nchans $nsigned -$nasamps $nendian $curtmpdir/audiodump.wav > /dev/null 2>&1";
    }

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





#clip (actually, trim) the audio from $start seconds to $end seconds
sub clip_audio {
    my ($start,$end)=@_;

    if ($achans==0) {
	return;
    }

    $audio_in=$curtmpdir."/audio";
    $audio_out=$curtmpdir."/audioclip";

    unlink $audio_out;
    `touch $audio_out; chmod 600 $audio_out`;

    $align=$achans*$asamps/8;

    $spos=&align($arate*$align*$start);
    $epos=&align($arate*$align*$end);

    my ($fsize)= (-s $audio_in);

    if ($epos>$fsize) {
	$epos=$fsize;
    }
    if ($spos>$epos) {
	$spos=$epos;
    }

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

    if ($size>0) {
	$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=".($spos+$fblocks*32)." seek=".($fblocks*32)." >/dev/null 2>&1";
	unlink $audio_out;

	system($syscom);
	system($syscom2);
    }

    `sync;sync;sync`;
    $audio_out;
}


sub resample_audio {
    $endian=&get_endian;

    if (&rc_get("audio_player") eq "mplayer") {
	my ($aasamps)=$asamps/8;
	# doesn't work...
	$format=&get_mplayer_format;
	$syscom=&rc_get("video_open_command") . " -quiet -slave -ao pcm -rawaudio on:rate=$arate:channels=$achans:samplesize=$aasamps -nowaveheader $format -vo null $audio_in >/dev/null 2>&1";
    }
    else {
	if ($asamps==8) {
	    $osamps="b";
	}
	else {
	    $osamps="w";
	}
	
	
	if ($nsamps==8) {
	    $nsamps="b";
	}
	else {
	    $nsamps="w";
	}

	if ($asigned==1) {
	    $osigned="-s";
	}
	else {
	    $osigned="-u";
	}
	
	if ($nsigned==1) {
	    $nsigned="-s";
	}
	else {
	    $nsigned="-u";
	}
	
	if ($aendian==$endian) {
	    $oendian="";
	}
	else {
	    $oendian="-x";
	}
	
	if ($nendian==$endian) {
	    $nendian="";
	}
	else {
	    $nendian="-x";
	}
	
	if (defined($stretch)) {
	    $syscom="sox -t raw -r $arate -c $achans $osigned -$osamps $oendian $audio_in -t raw -r $nrate -c $nchans $nsigned $nendian -$nsamps $audio_out stretch $stretch";#>/dev/null 2>&1";
	    system($syscom);
	}
	else {
	    $syscom="sox -t raw -r $arate -c $achans $osigned -$osamps $oendian $audio_in -t raw -r $nrate -c $nchans $nsigned $nendian -$nsamps $audio_out $stretch>/dev/null 2>&1";
	}
    }
    system $syscom;
}



sub insert_audio {
#what we are going to do:
# 1) copy the end of the audio (after insertion) to a new file
# 2) then insert the new section
# 3) then put the end back

#input params:
# $where - insertion pt -in seconds
# $start, $end - new section - in seconds
# $from_handle - handle of from file

    #if $arate<0, we will insert silence (not tested)


    if ($achans==0) {
	return;
    }

    if (!defined($times)) {
	$times=1;
    }

    my ($audio_from)=$audio_from;

    my ($audio_to)="$curtmpdir/audio";
    if (defined($from_handle)) {
	if ($undo_cut==1) {
	    $audio_from="$tmpdir/$from_handle/audio.bak";
	}
	else {
	    $audio_from="$tmpdir/$from_handle/audio";
	}
    }
    my ($audio_temp)="$curtmpdir/audio.temp";

    unlink $audio_temp;
    `touch $audio_temp; chmod 600 $audio_temp`;

    $silence=0;
    if ($arate<0) {
	$silence=1;
	$arate=-$arate;
	$audio_from="/dev/zero";
    }

    $align=$achans*$asamps/8;

    $spos=&align($arate*$align*$start);
    $oepos=$epos=&align($arate*$align*$end);
    $wpos=&align($arate*$align*$where);

    if ($silence==0) {
	$fsize=&align(-s $audio_from);
	if ($epos>$fsize) {
	    $epos=$fsize;
	}
	if ($spos>$epos) {
	    $spos=$epos;
	}
    }

    # step 1 - move end to temp
    $fsize=-s $audio_to;

    my ($size)=$fsize-$wpos;
    if ($size>0) {
	$fblocks=int($size/32);
	$rblocks=$size-($fblocks*32);
	$syscom="dd if=$audio_to of=$audio_temp bs=32 count=$fblocks skip=".int($wpos/32)." >/dev/null 2>&1";
	$syscom2="dd if=$audio_to of=$audio_temp bs=1 count=$rblocks skip=".($wpos+$fblocks*32)." seek=".($fblocks*32)." >/dev/null 2>&1";
	
	system($syscom);
	system($syscom2);
	
    }

    # step 2 - insert new section (possibly multiple times)


    # pad with silence to insertion point
    &append_silence($wpos);

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

    my ($nepos)=$epos;
    my ($nspos)=$spos;
    my ($nwpos)=$wpos;

    $times_inserted=0;
    $factor=1;
    $silence_remembered=0;

    my ($audio_from)=$audio_from;

    while ($times_inserted<$times) {
	$size=$nepos-$nspos;
	if ($size>0) {
	    $fblocks=int($size/32);
	    $rblocks=$size-($fblocks*32);
	    $syscom="dd if=$audio_from of=$audio_to bs=32 count=$fblocks skip=".int($nspos/32)." seek=".int($nwpos/32)." >/dev/null 2>&1";
	    $syscom2="dd if=$audio_from of=$audio_to bs=1 count=$rblocks skip=".($nspos+$fblocks*32)." seek=".($nwpos+$fblocks*32)." >/dev/null 2>&1";
	    
	    system($syscom);
	    system($syscom2);
	    
	}
	
	$times_inserted+=$factor;
	# pad with silence if necessary
	if (-s $audio_temp||$times_inserted<$times) {
	    $inserted=$oepos-$nspos;
	    &append_silence($nwpos+$inserted);
	    unless ($silence_remembered>0) {
		$silence_remembered=$oepos-$nepos;
	    }
	}
	else {
	    $inserted=$nepos-$nspos;
	}

	if ($times_inserted==2) {
	    $oepos=$nepos=$nwpos;
	    $nspos=$nwpos-$inserted;
	    $audio_from=$audio_to;
	}

	$nwpos+=$inserted;

	if ($times_inserted>1) {
	    $nepos+=$inserted;
	    $oepos+=$inserted;
	    $factor*=2;
	}
	while ($times>$times_inserted&&$factor>$times-$times_inserted) {
	    $nspos+=($oepos-$nspos)/2;
	    $factor/=2;
	}
	if ($factor+$times_inserted==$times) {
	    # this will be our last insertion...
	    if (-s $audio_temp==0) {
		# ...so don't copy silence uneccesarily
		$nepos-=$silence_remembered;
	    }
	}
    }
    


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

    #step 3 - copy end back after insertion
    $size=&align(-s $audio_temp);
    if ($size>0) {
	$fblocks=int($size/32);
	$rblocks=$size-($fblocks*32);
	$syscom="dd if=$audio_temp of=$audio_to bs=32 count=$fblocks seek=".int(($nwpos)/32)." >/dev/null 2>&1";
	$syscom2="dd if=$audio_temp of=$audio_to bs=1 count=$rblocks skip=".($fblocks*32)." seek=".($nwpos+$fblocks*32)." >/dev/null 2>&1";

	system($syscom);
	system($syscom2);
    }

    unlink $audio_temp;
    `sync;sync;sync`;

}


sub cut_audio {
#what we are going to do:
    #1) copy up to $start to a new file
    #2) back up the section to be deleted
    #3) append the section after $end to the new file
    #4) copy the new file to the original
    #$start, $end are in seconds

    if ($achans==0) {
	return;
    }

    my ($audio_in)="$curtmpdir/audio";
    my ($audio_temp)="$curtmpdir/audio.temp";
    my ($audio_bak)="$curtmpdir/audio.bak";

    unlink $audio_temp;
    `touch $audio_temp; chmod 600 $audio_temp`;

    $align=$achans*$asamps/8;

    # step 1
    $spos=&align($arate*$align*$start);
    $epos=&align($arate*$align*$end);

    my ($fsize)=&align(-s $audio_in);

    if ($epos>$fsize) {
	$epos=$fsize;
    }
    if ($spos>$epos) {
	$spos=$epos;
    }

    $seekstart=$spos;
    my ($size)=$spos;
    
    if ($size>0) {
	$fblocks=int($size/32);
	$rblocks=$size-($fblocks*32);
	$syscom="dd if=$audio_in of=$audio_temp bs=32 count=$fblocks >/dev/null 2>&1";
	$syscom2="dd if=$audio_in of=$audio_temp bs=1 count=$rblocks skip=".($fblocks*32)." seek=".($fblocks*32)." >/dev/null 2>&1";
	
	system($syscom);
	system($syscom2);
    }

    #step 2
    $size=($epos-$spos);

    if ($size>0) {
	$fblocks=int($size/32);
	$rblocks=$size-($fblocks*32);
	$syscom="dd if=$audio_in of=$audio_bak bs=32 count=$fblocks >/dev/null 2>&1";
	$syscom2="dd if=$audio_in of=$audio_bak bs=1 count=$rblocks skip=".($fblocks*32)." seek=".($fblocks*32)." >/dev/null 2>&1";
	
	system($syscom);
	system($syscom2);
    }


    #step 3
    $spos=$epos;
    $epos=$fsize;
    $size=($epos-$spos);
    
    if ($size>0) {
	$fblocks=int($size/32);
	$rblocks=$size-($fblocks*32);
	$syscom="dd if=$audio_in of=$audio_temp bs=32 count=$fblocks skip=".int($spos/32)." seek=".int($seekstart/32)." >/dev/null 2>&1";
	$syscom2="dd if=$audio_in of=$audio_temp bs=1 count=$rblocks skip=".($spos+$fblocks*32)." seek=".($seekstart+$fblocks*32)." >/dev/null 2>&1";
	
	system($syscom);
	system($syscom2);
    }

    # step 4
    unlink $audio_in;
    rename "$audio_temp", "$audio_in";
    `sync;sync;sync`;

}


sub align {
    my ($guess)=@_;
    # align our audio cuts so we don't end halfway through a sample/channel
    $guess=int($guess/$align)*$align;
    return $guess;
}


sub append_silence {
    #pad from end of file to byte $end-1 with zeros
    my ($end,$audio_file)=@_;
    my ($audio_file)="$curtmpdir/audio";

    unless (-f $audio_file) {
	`touch $audio_file; chmod 600 $audio_file`;
    }

    my ($fsize)= -s $audio_file;
    if (defined($align)) {
	$fsize=&align($fsize);
    }
    $fsize--;
    $end--;

    $size=($end-$fsize);
    
    if ($size>0) {
	$fblocks=int($size/32);
	$rblocks=$size-($fblocks*32);
	$syscom="dd if=/dev/zero of=$audio_file bs=32 count=$fblocks seek=".int($fsize/32)." >/dev/null 2>&1";
	$syscom2="dd if=/dev/zero of=$audio_file bs=1 count=$rblocks seek=".($fsize+$fblocks*32)." >/dev/null 2>&1";
	
	system($syscom);
	system($syscom2);
    }
}





sub open_images {
    # set $file before calling this function
    # set $only_first to 1 to just open the first image in a directory
    # set $height $width to force a particular size, otherwise $height=$width=0 to open all to the first image size
    # if first image size cannot be obtained $dwidth and $dheight are used

    my ($i)=@_;

    if (-d $file) {
	# is a directory
	$dir=$file;
	$skip=0;
	opendir DIR,$dir;
	readdir(DIR);
	readdir(DIR);
	while (($file=readdir(DIR))&&$skip==0) {
	    $file=$dir.$file;
	    &open_single_image($i);
	    if (-s "$curtmpdir/$name$img_ext") {
		$i++;
		if ($only_first==1) {
		    $skip=1;
		}
	    }
	    else {
		unlink "$curtmpdir/$name$img_ext";
	    }
	}
	closedir DIR;
    }
    else {
	&open_single_image($i);
	unless (-s "$curtmpdir/$name$img_ext") {
	    unlink "$curtmpdir/$name$img_ext";
	}
    }
}


sub open_single_image {
    # set $hsize and $vsize to force the image size, set to 0 to autoresize
    #identify hangs if the file extension is ".avi"
    if (&get_ext($file) eq ".avi") {
	return;
    }
    my ($i)=@_;
    $name=&mkname($i);
    if ($hsize*$vsize==0) {
	&get_image_size;
    }
    $syscom="convert -size $hsize"."x"."$vsize \"$file\" -resize $hsize"."!x"."$vsize! +profile \"*\" $curtmpdir/$name$img_ext > /dev/null 2>&1";
    system ($syscom);
}



sub get_image_size {
    $bpp=8; # default for images
    $dwidth=&rc_get("default_width");
    $dheight=&rc_get("default_height");
    if (!defined($imresact)) {
	$imresact=&rc_get("image_resize_action");
    }
    # imresact can be: default - resize all images to default; bound - use as max size but keep aspect; or none: use the size of the first image

    my ($id_cmd)=&location("identify");
    if (!($id_cmd eq "")) {
	@info=split / /, `$id_cmd \"$file\" 2>/dev/null`;
	$bppstr=@info[4];
	@bpp=split /-/,$bppstr;
	$bpp=@bpp[0];
	@sizestr=split /\+/,@info[2];
	@size=split /x/,@sizestr[0];

	$hsize=@size[0];
	$vsize=@size[1];
	
	if ($imresact eq ""||$imresact eq "default") {
	    $hsize=$vsize="";
	}
	elsif ($imresact eq "bound") {
	    if ($hsize>$dwidth) {
		$shrink=$hsize/$dwidth;
		$hsize/=$shrink;
		$vsize/=$shrink;
	    }
	    if ($vsize>$dheight) {
		$shrink=$vsize/$dheight;
		$hsize/=$shrink;
		$vsize/=$shrink;
	    }
	    $hsize=int($hsize);
	    $vsize=int($vsize);
	}
    }
    if (!defined($hsize)||$hsize eq "") {
	$hsize=$dwidth;
    }
    if (!defined($vsize)||$vsize eq "") {
	$vsize=$dheight;
    }
}




sub fill_and_redo_frames {
    # starting at start and ending at end, we will remove any gaps in image files
    # we return the last repacked frame number

    # if pre_cut_frames is defined, we will cut that many frames from start first
    if (defined($pre_cut_frames)) {
	for ($i=$start;$i<$start+$pre_cut_frames;$i++) {
	    $name=&mkname($i);
	    unlink "$curtmpdir/$name$img_ext";
	}
    }
    $hsize=$vsize=$bpp=0;

    $missing=0;
    $last_frame=0;
    
    opendir DIR,$curtmpdir;
    while ($file=readdir(DIR)) {
	$lastfile=$file;
    }
    closedir DIR;
    @ends=split($img_ext,$lastfile);
    $end=$ends[0];

    if ($end==0) {
	return 0;
    }

    for ($i=$start;$i+$pre_cut_frames+$missing<=$end;$i++) {
	$name=&mkname($i);
	$found=0;

	while ($found==0&&$i+$pre_cut_frames+$missing<=$end) {
	    $next=$i+$pre_cut_frames+$missing;
	    $nextname=&mkname($next);

	    if (-f "$curtmpdir/$nextname$img_ext") {
		$lastframe=$i;
		$found=1;
		if (!($next==$i)) {
		    $syscom= "cp $curtmpdir/$nextname$img_ext $curtmpdir/$name$img_ext";
		    system ($syscom);
		}
		if ($missing>0) {
		    $missing--;
		}
	    }
	    else {
		# frame not found - try the next one
		$missing++;
	    }
	}
    }
    
    for ($i=$end-$pre_cut_frames+1;$i<=$end;$i++) {
	$name=&mkname($i);
	unlink "$curtmpdir/$name$img_ext";
    }

    $imresact="none";
    $file=&mkname(1);
    $file="$curtmpdir/$file$img_ext";
    &get_image_size;
    return $lastframe;
}



sub mp3_open {
    if (-f $audio_in) {
	unlink $audio_in;
    }
    $f_size=0;

    # prefer mpg321 if it's available
    unless (&location("mpg321") eq "") {
	$syscom="mpg321 -w ".$audio_in." --rate 44100 --stereo \"".$file."\" >/dev/null 2>&1";
	system($syscom);
	$f_size=-s $audio_in;
    }
    if ($f_size==0) {
	$syscom="mpg123 -w ".$audio_in." --rate 44100 --stereo \"".$file."\" >/dev/null 2>&1";
	system($syscom);
	$f_size=-s $audio_in;
    }

    $arate=44100;
    $achans=2;
    $asamps=16;

    $asigned=1;
    if ($asamps==8) {
	$asigned=0;
    }
    $aendian=&get_endian;
}


sub ogg_open {
    if (-f $audio_in) {
	unlink $audio_in;
    }
    
    $syscom="ogg123 -d wav -f ".$audio_in." \"".$file."\" >/dev/null 2>&1";
    system($syscom);
    $f_size= -s $audiofile;

    # values are assumed here
    $arate=44100;
    $achans=2;
    $asamps=16;

    $asigned=1;
    if ($asamps==8) {
	$asigned=0;
    }
    $aendian=&get_endian;
}


sub wav_open {
    $mplay_command=&rc_get("video_open_command");
    $curtmpfile=$curtmpdir . "/.temp";

    if (-f $audio_in) {
	unlink $audio_in;
    }
    
    $syscom="cp \"$file\" $audio_in";
    system($syscom);
    $syscom="$mplay_command -slave -frames 0 $audio_in > $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 for now
	$arate=44100;
	$achans=2;
	$asamps=16;
    }
    
    # and it seems we *must* assume these...
    $asigned=1;
    if ($asamps==8) {
	$asigned=0;
    }
    $aendian=&get_endian;

    $f_size= -s $audio_in;
}




################################################
# utility subroutines

sub get_current_date {
    ($Second, $Minute, $Hour, $Day, $Month, $Year, $WeekDay, $DayOfYear, $IsDST) = localtime(time);
    my $RealMonth = $Month + 1; # Months of the year are not zero-based

    if($RealMonth < 10)
    {
	$RealMonth = "0" . $RealMonth; # add a leading zero to one-digit months
    }
    if($Day < 10)
    {
	$Day = "0" . $Day; # add a leading zero to one-digit days
    }
    
    $Fixed_Year = $Year + 1900;

    return $Fixed_Year."-".$RealMonth."-".$Day;
}




sub getint {
    my ($string)=@_;
    my (@val)=unpack("CCCC",$string);
    if (!defined($endian)||$endian==1) {
	return (((@val[3]*256+@val[2])*256+@val[1])*256)+@val[0];
    }
    else {
	return (((@val[0]*256+@val[1])*256+@val[2])*256)+@val[3];
    }
}

sub get_endian {
    return unpack("h*", pack("s", 1)) =~ /^1/;
}



sub get_mplayer_format {
    if (!defined($nrate)) {
	$nrate=$arate;
    }
    if (!defined($nchans)) {
	$nchans=$achans;
    }
    if (!defined($nasamps)) {
	$nasamps=$asamps;
    }
    if (!defined($asigned)) {
	$asigned=$signed;
    }
    if (!defined($aendian)) {
	$aendian=$endian;
    }
    if (!defined($nsigned)) {
	$nsigned=$asigned;
    }
    if (!defined($nendian)) {
	$nendian=$aendian;
    }

    if ($nasamps==8) {
	if ($nsigned==0) {
	    return "-format 16";
	}
	return "-format 8";
    }
    if ($nsigned==0) {
	if ($nendian==0) {
	    return "-format 64";
	}
	return "-format 32";
    }
    if ($nendian==0) {
	return "-format 256";
    }
    return "-format 128";
}




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



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

    if (-d $curtmpdir) {
	&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.play";
    # autoflush output
    select((select(OUT), $| = 1)[0]);
    print OUT $status;
    close OUT;

    unless ($img_ext eq ".mgk"||$opening_preview==1) {
	unlink $pidfile;
    }
}

sub sig_complete_video {
    my($status)="video_ended|";
    #foreach (@_) {
	#$status.=$_."|";
    #}

    open OUT,"> $statusfile.play";
    print OUT $status;
    close OUT;
    unless ($img_ext eq ".mgk") {
	unlink $pidfile;
    }
}


sub sig_error {
    # WARNING: error strings MUST NOT contain newlines, instead you can pass up to 4 lines of text as params

    my($status)="error";
    my ($count)=0;
    foreach (@_) {
	$status.="|".$_;
	$count++;
    }
    if ($count<4) {
	$status.="|||ERROR|";
    }

    if (-d $curtmpdir) {
	open OUT,"> $statusfile";
	print OUT $status;
	close OUT;
	unlink $pidfile;
    }
    `sync;sync;sync`;
    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 sig_clear {
    &check_for_pause;
    unlink $statusfile;
}



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

    &stop_audio;
}


sub stop_audio {
    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 2>/dev/null`;
    chomp($location);
    $location;
}


sub rc_set_if_not_set {
    my ($key)=$_[0];
    shift (@_);
    my ($value)=join(" ",@_);
    if (&rc_get($key) eq "") {
	&rc_set($key,$value);
    }
}

sub rc_delete {
    my ($key)=$_[0];

    $delpref=1;
    &rc_set($key,"");
}



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 (! defined ($delpref)) {
	$delpref=0;
    }

    if ($key eq "") {
	$date=&get_current_date;
	print OUT "# This file is autogenerated by $GUI_NAME on $date\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 ($found==0||$part==1) {
		if ($part==1||$_=~ /(.*)(<$key>)(.*)/) {
		    if ($part==0) {
			$part=1;
			if ($delpref==0) {
			    print OUT "$1$2\n$value\n</$key>\n";
			}
			else {
			    $date=&get_current_date;
			    print OUT "# $key deleted by $GUI_NAME $date\n";
			}
			$string=$3;
			$found=1;
		    }
		    if ($string=~ /(.*)(<\/$key>)(.*)/) {
			if ($delpref==0) {
			    print OUT $3;
			}
			$part=0;
		    }
		}
		elsif ($part==0) {
		    print OUT $string;
		}
	    }
	    else {
		print OUT $string;
	    }
	}
	if ($found==0&&$delpref==0) {
	    print OUT "\n<$key>\n$value\n</$key>\n";
	}
    }
    close OUT;
    rename $rcfile.".new",$rcfile or exit 3;
    $delpref=0;
}


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

    unless (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
    unless (defined(open OUT,"> $gui_bootstrap_file")) {
	exit 4;
    }
    print OUT "$home\n";
    close OUT;

    unless (-d $tmpdir) {
	# create working directory
	umask 0;
	unless (mkdir $tmpdir,0777) {
	    open OUT,">> $gui_bootstrap_file" or exit 4;
	    print OUT "$version|$tmpdir|$msg|$msg2";
	    close OUT;
	    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;
}



# 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 store_kb_rpt {
    $kbdfile="/tmp/.smog.kbd";
    $syscom="xset -q | grep \"auto repeat delay\" > $kbdfile";
    system ($syscom);

    open IN,"< $kbdfile";
    read IN,$text,128;
    close IN;
    unlink "$kbdfile";
    
    # convert multiple spaces to single spaces
    $text=~ s/\s+/ /g; 
    @words=split(" ",$text);
    &rc_set("kbd_rpt_delay",$words[3]);
    &rc_set("kbd_rpt_rate",$words[6]);

    $syscom="xset -q | grep \"auto repeat:\" > $kbdfile";
    system ($syscom);

    open IN,"< $kbdfile";
    read IN,$text,128;
    close IN;
    unlink "$kbdfile";
    
    # convert multiple spaces to single spaces
    $text=~ s/\s+/ /g; 
    @words=split(" ",$text);
    &rc_set("kbd_rpt_state",$words[2]);

}


sub version_check_and_upgrade {
    my $version_hash=&version_hash(&rc_get("version"));

    $msg="";

    if ($version_hash>0) {
	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";
	}
    }

    # not used now
    &rc_delete("frontend");
    &rc_delete("mog_auto_bak");

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


sub version_hash {
    my ($string)=@_;

    if ($string eq "") {
	return 0;
    }

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

    $version_hash;
}
