#!/usr/bin/perl

# smogrify - Copyright G. Finch (salsaman@gmail.com) 2003 - 2013
# Released under the GPL 3 or later - see file COPYING or www.gnu.org for details


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

# these MUST match with the definition in the GUI

$rfx_builder_multi="build-lives-rfx-plugin-multi";

$umask=0177; # ->        -rw-------

$GUI_NAME="LiVES";

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

# default values

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

$background=1;
$version="2.0.5";
#$dyneversion="LIVES-20091209";

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



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

if ($^O eq "MSWin32") {
#TODO - get this from the registry when needed
#only if we installed perl ourselves
    $installdir="C:\\Program Files\\LiVES";

    push @INC,"$installdir\\lib";

    use File::Path qw(make_path);

#TODO - get this from the registry when needed
    $login = getlogin;
    $home="C:\\users\\$login\\Application Data\\LiVES";

    $rc_filename="LiVES.ini";
    $lives_home_dir="$home\\Config";


# default temporary directory (can be overridden from the GUI)
    $default_tmpdir="$home\\livescache\\";
    $is_mingw=1;
    $nulfile="NUL";

    $encoder="ffmpeg_encoder";
    $smog_composite_command="composite.exe -quiet";
    $smog_convert_command="mgkvert.exe -quiet";

}
else {
    $home=$ENV{"HOME"};
    $rc_filename=".lives";
    $lives_home_dir="$home/.lives-dir";

# default temporary directory (can be overridden from the GUI)
    $default_tmpdir="$home/livestmp/";
    $nulfile="/dev/null";

    $encoder="mencoder_encoder";
    $smog_composite_command="composite -quiet";
    $smog_convert_command="convert -quiet";

}


#$DEBUG_SMOGRIFY=1;


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

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

    unless ($command eq "version") {
	my $rcfile="$home/$rc_filename";
	if (-f $rcfile) {
	    $tmpdir=&rc_get("session_tempdir");

	    if ($tmpdir eq ""||$command eq "report") {
		$tmpdir=&rc_get("tempdir");
		$priority=1;
		&rc_set("session_tempdir","$tmpdir");
		$priority=0;
	    }

            if (! -d $tmpdir) {
		unless ($command eq "report" || $command eq "set_pref") {
		    &mktmpdir;
		}

	    }

	}
    }

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

	if ($command eq "save" && $ARGV[1] eq "get_rfx") {
	    $background=0;
	}


## blocking calls
	if ($command eq "get_pid_for_handle") {
	    $handle=$ARGV[1];
	    $curtmpdir="$tmpdir/$handle";

	    if ($^O eq "MSWin32") {
		$pidfile="$curtmpdir/pid";
	    }
	    else {
		$pidfile="$curtmpdir/.pid";
	    }

	    if (defined(open IN,"< $pidfile")) {
		read IN,$target_pid,8;
		close IN;
		
		if ($target_pid eq NULL) {
		    print " ";
		    exit 1;
		}
	    }
	    print $target_pid;
	    exit 0;
	}


    if ($command eq "stopsubsub"||$command eq "stopsubsubs"||$command eq "stop_audio") {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	my $sig="KILL";
	if ($command eq "stop_audio") {
	    $pidfile="$curtmpdir/.pidpb";
	}
	else {
	    $pidfile="$curtmpdir/.pid";
	    if (defined($ARGV[2])) {
		$sig=$ARGV[2];
	    }
	}
	$statusfile="$curtmpdir/.status";

	unless (-d "$curtmpdir") {
	    exit 2;
	}

	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 "Smogrify: Unable to stop process $target_pid.\n";
	    }
	    elsif ($command eq "stopsubsub"||$command eq "stop_audio") {
		# kill all subprocesses of target_pid, and target_pid
		&kill_child_pids($target_pid,$sig);
	    }
	    elsif ($command eq "stopsubsubs") {
		# kill all subprocesses of target_pid, but not target_pid
		smog_system("pgrep -P $target_pid > \"$tmpdir/.pids.$target_pid\" 2>/dev/null");
		if (-s "$tmpdir/.pids.$target_pid"&&defined(open IN,"$tmpdir/.pids.$target_pid")) {
		    while (<IN>) {
			$pid=$_;
			chomp($pid);
			&kill_child_pids($pid,$sig);
		    }
		    close IN;
		}
		unlink "$tmpdir/.pids.$target_pid";
	    }
	    
	    
	    
	    else {
		print "Attempting to stop process ".$target_pid." with $command\n";
		if ($sig eq "KILL") {
		    smog_system("kill -$sig $target_pid >/dev/null 2>&1");
		}
		else {
		    smog_system("pkill -$sig -P $target_pid");# must not ! >/dev/null 2>&1");
		}
	    }

	    if ($sig eq "KILL" && $command ne "stop_audio") {
		&sig_killed;
	    }
	    exit 0;
	}
	exit 1;
    }



    if ($command eq "new") {
	# $key can normally be set to getpid()

	$key=$ARGV[1];

	# write mini-info file

	if ($^O eq "MSWin32") {
	    $infofile="$tmpdir/info.$key";
	}
	else {
	    $infofile="$tmpdir/.info.$key";
	}
	do {
	    $handle=int(rand 1000000000)+65536;
	    $curtmpdir="$tmpdir/$handle";
	} while (-d $curtmpdir);
	
	umask 0;
	unless (mkdir $curtmpdir,0777) {
	    sig_system_error("Creating directory \"$curtmpdir\"",0);
	    exit 1;
	}
	umask $umask;
	
	if (!defined(open OUT,"> $infofile")) {
	    sig_write_error("$infofile",0);
	    exit 1;
	}
	
	print OUT $handle;
	close OUT;
	exit 0;
    }
	

    if ($command eq "get_tempdir") {
	# TODO - take dirname as second param
	if (defined($ARGV[1])) {
	    $id=$ARGV[1];

	    if ($^O ne "MSWin32") {
		open OUT,"> /tmp/.smogrify.$id" or exit 1;
	    }
	    else {
		open OUT,"> C:\\smogrify.$id" or exit 1;
	    }
	    print OUT "$tmpdir";
	    close OUT;
	}
	else {print $tmpdir};
	exit 0;
    }

	if ($command eq "get_version_hash") {
	    $com=$ARGV[1];
	    $sep=$ARGV[2];
	    $piece=$ARGV[3];
	    $res=smog_system_direct("$com");
	    chomp($res);

	    $ver=(split (/$sep/,$res))[$piece];

	    print &version_hash($ver);
	    exit 0;
	}

    if ($command eq "report") {

	$gui_bootstrap_file="";
	if (defined($ARGV[1])) {
	    $gui_bootstrap_file=$ARGV[1];
	}

	if (! -d "$lives_home_dir") {
	    # this can fail here, but succeed later
	    if ($^O ne "MSWin32") {
		smog_system("/bin/mkdir -p -mode=777 \"$lives_home_dir\"");
	    }
	    else {
		make_path("$lives_home_dir");
	    }
	}

	$startup_phase=0;

	$video_open_command="\"".&location("mplayer")."\"";

	$audio_player="sox";
	$audio_play_command=&rc_get_default("sox_command");
	    
	$convert_version_hash=&get_convert_version_hash;
	
	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 "$home/$rc_filename") {
	    # set comment at start of file
	    &rc_set("","");
	    $startup_phase=-1;
	    &rc_set("startup_phase",-1);
	}
	else {
	    $startup_phase=&rc_get("startup_phase");
	    if ($startup_phase eq "" || $startup_phase eq "100") {
		$startup_phase=0;
		&rc_delete("startup_phase");
	    }
	}

	$default_keymap="default.keymap";

	&rc_set_if_not_set("version","$version");
	&rc_set_if_not_set("tempdir","$default_tmpdir");

	if (defined($dyneversion)) {
	    &rc_set_if_not_set("lib_dir","/opt/$dyneversion/lib/");
	    &rc_set_if_not_set("prefix_dir","/opt/$dyneversion/");
	}

	&rc_set_if_not_set("video_open_command","$video_open_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("midisynch","false");
	&rc_set_if_not_set("pb_quality",2);
	&rc_set_if_not_set("insert_resample","true");
	&rc_set_if_not_set("open_compression_percent",15);
	&rc_set_if_not_set("conserve_space","false");
	&rc_set_if_not_set("antialias","true");
	# resize action when opening images - can be either: "bound" or "none"
	&rc_set_if_not_set("image_resize_action","none");
	&rc_set_if_not_set("encoder",$encoder);
	&rc_set_if_not_set("output_type","mjpeg");
	&rc_set_if_not_set("audio_effect","none");
	&rc_set_if_not_set("default_image_format","png");
	&rc_set_if_not_set("default_fps",25);
	&rc_set_if_not_set("save_directories","false");
	&rc_set_if_not_set("stop_screensaver","true");
	&rc_set_if_not_set("open_maximised","true");
	&rc_set_if_not_set("filesel_maximised","true");
	&rc_set_if_not_set("show_recent_files","true");
	&rc_set_if_not_set("ce_maxspect","true");
	&rc_set_if_not_set("lives_warning_mask",797696); # bits 19,18,13,11 and 10
	&rc_set_if_not_set("dl_bandwidth_K",512);
	&rc_set_if_not_set("show_player_stats","false");
	&rc_set_if_not_set("show_toolbar","true");
	&rc_set_if_not_set("encoder_acodec",1); # default of PCM
	&rc_set_if_not_set("record_opts",-1);

	&rc_set_if_not_set("gui_theme","crayons");
	&rc_set_if_not_set("jack_opts",0); # start aserver on startup
	&rc_set_if_not_set("audio_opts",3);
	&rc_set_if_not_set("rte_keys_virtual",9);

	&rc_set_if_not_set("instant_open","true");
	&rc_set_if_not_set("auto_deinterlace","true");
	&rc_set_if_not_set("auto_cut_borders","false");

	&rc_set_if_not_set("mt_undo_buf",32);
	&rc_set_if_not_set("mt_enter_prompt","true");
	&rc_set_if_not_set("render_prompt","true");
	&rc_set_if_not_set("mt_exit_render","true");
	&rc_set_if_not_set("mt_def_width",640);
	&rc_set_if_not_set("mt_def_height",480);
	&rc_set_if_not_set("mt_def_achans",2);
	&rc_set_if_not_set("mt_def_signed_endian",2*!&get_endian);
	&rc_set_if_not_set("mt_backaudio","1");
	&rc_set_if_not_set("mt_pertrack_audio","true");
	&rc_set_if_not_set("mt_auto_back",120);

	&rc_set_if_not_set("ar_clipset","");
	&rc_set_if_not_set("ar_layout","");
	
	&rc_set_if_not_set("rec_desktop_audio","false");
	&rc_set_if_not_set("osc_start","false");
	&rc_set_if_not_set("osc_port",49999);

	&rc_set_if_not_set("concat_images","true");
	&rc_set_if_not_set("mouse_scroll_clips","true");

	&rc_set_if_not_set("omc_dev_opts",3);

	&rc_set_if_not_set("rec_stop_gb",10);

	&rc_set_if_not_set("def_autotrans","chroma blend");
	&rc_set_if_not_set("audio_src",0); #internal

	$tmpdir=&rc_get("tempdir");
	&write_bootstrap_file;
	&mktmpdir;
	
	$convert_old_version=&rc_get("convert_version");
	unless ($convert_old_version eq $convert_version) {
	    &rc_set("convert_version",$convert_version);
	}
	&version_check_and_upgrade;

	unless ($gui_bootstrap_file eq "") {
	    open OUT,"> $gui_bootstrap_file" or exit 4;
	    print OUT "$version|$tmpdir|$startup_phase|$msg|$msg2";
	    close OUT;
	}

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

	exit 0;
    }

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


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

	&rc_set("$key","$value");
	exit 0;
    }
	
    if ($command eq "set_pref_if_not_set") {
	$key=$ARGV[1];
	$value=$value=$ARGV[2];
	&rc_set_if_not_set("$key","$value");
	exit 0;
    }
	


	if ($command eq "get_clip_value") {
	    $command="get_pref";
	    $prefs_file=$ARGV[4];
	}


	if ($command eq "print_pref") {
	    $command="get_pref";
	    $ARGV[2]="-";
	}

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

	if ($first eq "-"&&!defined($ARGV[3])) {
	    print $value;
	    exit 0;
	}
	if ($^O eq "MSWin32") {
	    open OUT,"> $tmpdir/smogval.$first.$second" or exit 1;
	}
	else {
	    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);

	if ($value eq "") {
	    $value="NULL";
	}

	if ($^O eq "MSWin32") {
	    open OUT,"> $tmpdir/smogval.$first.$second" or exit 1;
	}
	else {
	    open OUT,"> $tmpdir/.smogval.$first.$second" or exit 1;
	}
	print OUT $value;
	close OUT;
	exit 0;
    }

    if ($command eq "delete_pref") {
	$key=$ARGV[1];
	&rc_delete($key);
    }

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

	if ($^O eq "MSWin32") {
	    open OUT,"> $tmpdir/smogval.$first.$second" or exit 1;
	}
	else {
	    open OUT,"> $tmpdir/.smogval.$first.$second" or exit 1;
	}

	print OUT $value;
	close OUT;
	exit 0;
    }


    if ($command eq "pause") {
	if ($^O eq "MSWin32") {
	    $smres=smog_system("touch.exe \"$tmpdir\\$ARGV[1]\\pause\"");
	    if ($smres) {
		sig_write_error("$tmpdir\\$ARGV[1]\\pause"); 
		exit 1;
	    }
	}
	else {
	    $smres=smog_system("touch \"$tmpdir/$ARGV[1]/pause\"");
	    if ($smres) {
		sig_write_error("$tmpdir/$ARGV[1]/pause"); 
		exit 1;
	    }
	}
	if ($^O eq "MSWin32") {
	    #smog_system("chmod.exe 600 \"$tmpdir\\$ARGV[1]\\pause\"");
	}
	else {
	    smog_system("/bin/chmod 600 \"$tmpdir/$ARGV[1]/pause\"");
	}
	exit 0;
    }


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


	if ($command eq "clear_tmp_files") {
	    $handle=$ARGV[1];
	    #clear old backups (e.g. when saving a set)
	    $curtmpdir="$tmpdir/$handle";
	    &clean_old;
	    &sig_complete;
	    exit 0;
	}

	if ($command eq "clear_pre_files") {
	    $handle=$ARGV[1];
	    #clear old backups (e.g. when saving a set)
	    $curtmpdir="$tmpdir/$handle";
	    smog_chdir("$curtmpdir");
	    unlink glob "*.pre";
	    &sig_complete;
	    exit 0;
	}


    if ($command eq "undo_audio") {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	if ($^O eq "MSWin32") {
	    $statusfile="$curtmpdir/status";
	}
	else {
	    $statusfile="$curtmpdir/.status";
	}

	&undo_audio; # will abort on failure
	&sig_complete;
	exit 0;
    }

    if ($command eq "backup_audio") {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	if ($^O eq "MSWin32") {
	    $statusfile="$curtmpdir/status";
	}
	else {
	    $statusfile="$curtmpdir/.status";
	}
	smog_chdir("$curtmpdir");
	&backup_audio;
	if ($panic) {
	    exit 1;
	}
	&sig_complete;
	exit 0;
    }


    if ($command eq "save_frame") {
	my $hsize=-1;
	my $vsize=-1;

	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
	$frame=$ARGV[2];
	$name=&mkname($frame);
	$nfile=$ARGV[3];
	if (defined $ARGV[4]) {
	    $hsize=$ARGV[4];
	}
	if (defined $ARGV[5]) {
	    $vsize=$ARGV[5];
	}

	if ($^O eq "MSWin32") {
	    $statusfile="$curtmpdir/status";
	}
	else {
	    $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.");
	}

	$img_ext=&get_img_ext($curtmpdir,$frame);
	$img_prefix=&get_img_prefix($img_ext);

	umask 0111;

	if ($hsize==-1||$vsize==-1) {
	    $com="$smog_convert_command $img_prefix\"$curtmpdir/$name$img_ext\" \"$nfile\" >$nulfile 2>&1";
	}
	else {
	    if ($antialias eq "false") {
		$com="$smog_convert_command +antialias $img_prefix\"$curtmpdir/$name$img_ext\" -scale $hsize"."!x"."$vsize! \"$nfile\" > $nulfile 2>&1";
	    }
	    else {
		$com="$smog_convert_command -antialias $img_prefix\"$curtmpdir/$name$img_ext\" -resize $hsize"."!x"."$vsize! \"$nfile\" > $nulfile 2>&1";
	    }
	}

	$smerr=smog_system($com);
	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    exit 1;
	};

	exit 0;
    }


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

	$curtmpdir="$tmpdir/$handle";

	if ($^O eq "MSWin32") {
	    $statusfile="$curtmpdir/status";
	}
	else {
	    $statusfile="$curtmpdir/.status";
	}

	smog_chdir("$curtmpdir");
	$fsize= -s "$nfile";
	$afsize = -s "audio";
	unlink glob "*.tar";
	unlink glob "event.*";
	unlink "extended";

	$img_type="jpg";

	unless (-f "header.lives") {
	    
	    if (defined(open IN,"< header2")) {
		read IN,$val,4;
		
		# unfortunately...old clips used whatever endian the machine used
		$endian=&get_endian;
		$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;
	    
	    # very old backups didn't have a header2
	    if (! -f "header2") {
		unless ($nfile eq "") {
		    unlink glob "event.*";
		    $img_ext=&get_img_ext($curtmpdir);
		    $img_prefix=&get_img_prefix($img_ext);
		    $frames=&count_frames;
		}
	    }

	    # leave headers for restoring VJ sets
	    if ($leave_headers==1) {
		$fsize=0;
	    }
	    else {
		unlink <header header2>;
	    }
	}
	else {
	    unlink <header header2>;
	}

	$name=&mkname(1);

	if (-f "$name.png") {
	    $img_type="png";
	}

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

    if ($command eq "list_plugins") {

	# prefix_dir pref should be set first
	# plugins are returned in sort() order
	my $allow_nonex=$ARGV[1];
	my $allow_subdirs=$ARGV[2];
	my $plugindir=$ARGV[3];
	my $ext=$ARGV[4];

	my $string="";

	if ($ext =~ /^-/) {
	    $strip_ext=1;
	    $ext=substr($ext,1,length($ext));
	}

	if (-d $plugindir) {
	    smog_chdir("$plugindir");
	    opendir DIR,$plugindir;
	    my @files=readdir(DIR);
	    closedir DIR;
	    @files = sort (@files);

	    foreach my $plugname (@files) {
		unless ($plugname =~ /^\./) {
		    if ((-f $plugname && (-x $plugname||$allow_nonex))||($allow_subdirs==1&& -d $plugname)) {
			unless($allow_nonex) {
			    next if ! -x "$plugindir/$plugname";
			}
			unless ($plugname eq "") {
			    if ($ext eq ""|| $plugname =~ /$ext$/) {
				if ($strip_ext==1) {
				    $plugname=(split(/\./,$plugname))[0];
				}
				$string.="$plugname|";
			    }
			}
		    }
		}
	    }
	}
	else {
	    print "\n";
	    exit 1;
	}
	print "$string\n";
	exit 0;
    }


	if ($command eq "build_rfx_plugins") {
	    if ($^O ne "MSWin32") {
		if (&location($rfx_builder_multi) eq "") {
		    &sig_error("Unable to locate the program $rfx_builder_multi");
		    exit 1;
		}
	    }

	    $type=$ARGV[1];
            $script_dir=$ARGV[2];
            $exec_dir=$ARGV[3];
	    if ($^O ne "MSWin32") {
		smog_system("$rfx_builder_multi \"$type\" \"$script_dir\" \"$exec_dir\"");
	    }
	    else {
		smog_system("perl \"$installdir\\$rfx_builder_multi\" \"$type\" \"$script_dir\" \"$exec_dir\"");
	    }
	    exit 0;
	}



	if ($command eq "make_thumb") {
	    $handle=$ARGV[1];
	    $curtmpdir="$tmpdir/$handle";
	    if ($^O eq "MSWin32") {
		$statusfile="$curtmpdir/status";
	    }
	    else {
		$statusfile="$curtmpdir/.status";
	    }
	    $dwidth=$ARGV[2];
	    $dheight=$ARGV[3];
	    $img_ext=".".$ARGV[4];
	    $img_prefix=&get_img_prefix($img_ext);
	    $file=$ARGV[5];

	    if (! -d "$curtmpdir") {
		mkdir ("$curtmpdir",0700);
	    }

	    $antialias=&rc_get("antialias");

	    $imresact="bound";
	    &get_image_size("$file");
	    if ($panic) {
		exit 1;
	    }
	    if (!($vsize*$hsize)||$hsize==-1) {
		&sig_complete(0,0);
		exit 0;
	    }

	    smog_chdir("$curtmpdir");

	    $name=&mkname(1);
	    
	    if ($antialias eq "false") {
		smog_system("$smog_convert_command +antialias \"$file\" -scale $hsize"."!x"."$vsize! $img_prefix\"$curtmpdir/$name$img_ext\" > $nulfile 2>&1");
	    }
	    else {
		smog_system("$smog_convert_command -antialias \"$file\" -resize $hsize"."!x"."$vsize! $img_prefix\"$curtmpdir/$name$img_ext\" > $nulfile 2>&1");
	    }
	    &sig_complete($hsize,$vsize);
	    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";
	    if ($^O eq "MSWin32") {
		$statusfile="$curtmpdir/status";
	    }
	    else {
		$statusfile="$curtmpdir/.status";
	    }
	    smog_chdir("$curtmpdir");
	    $start=$ARGV[2];
	    $end=$ARGV[3];
	    $plugdir=$ARGV[4];
	    $type=$ARGV[5];
	    $rest=$ARGV[6];
	    $plugin="";


	    unless ($rest eq "") {
		$plugin="$plugdir$type/$rest";
	    }

	    if ($type eq "encoders" || $type eq "effects/rendered/") {
		$img_ext=&get_img_ext($curtmpdir,$start);
		$img_prefix=&get_img_prefix($img_ext);
		if ($type eq "encoders") {
		    if (-f "audio.origbak") {
			unlink "audio";
			smog_rename( "audio.origbak","audio");
		    }
		    $areq=&get_form_request($plugin);
		    if ($areq&1||$areq&2) {
			unlink <audiodump.wav audioclip>;
		    }
		    if ($areq&4) {
			if ($^O ne "MSWin32") {
			    # TODO **** !!!!!
			    &clear_symlinks;
			}
		    }
		}
	    }

	    #need this again - something resets us
	    smog_chdir("$curtmpdir");

	    if ($^O eq "MSWin32") {
		# filter by file ext
		my $ext=&get_ext("$plugin");
		if ($ext eq ".py") {
		    $cmd="python";
		}
		else {
		    $cmd="perl";
		}
		$com="$cmd \"$plugin\" clear $start $end $img_ext";
	    }
	    else {
		$com="\"$plugin\" clear $start $end $img_ext";
	    }

	    $smerr=smog_system($com);
	    if ($smerr) {
		sig_system_error("Command failed: $com",$smerr); 
		exit 1;
	    }
	    &sig_complete;
	    exit 0;
	}



	if ($command eq "check_for_lock") {
#TODO - mingw

	    # check if there is a lock file for a set, and if so is it in use
	    my $setname=$ARGV[1];
	    my $exename=$ARGV[2];
	    my $pid=$ARGV[3];

            opendir DIR,"$tmpdir/$setname";

	    while (my $file=readdir(DIR)) {
		if ($file=~/^lock\.(.*)/) {
		    #found a lockfile
		    my $proc=$1;
		    unless ($proc eq $pid) {
			my $exe=smog_system_direct("/bin/readlink -n /proc/$proc/exe");  #allowed to fail
			my $short_exe=&my_basename($exe);

			if ($short_exe eq $exename) {
			    # and it's in use
			    closedir DIR;
			    print $proc."\n";
			    exit 0;
			}
			unlink "$tmpdir/$setname/$file";
		    }
		}
	    }
            closedir DIR;
	    exit 0;
	}




#TODO - mingw
	if ($command eq "get_recovery_file") {
	    #find first non-in-use recovery file for given uid, gid and filepart
	    my $uid=$ARGV[1];
	    my $gid=$ARGV[2];
	    my $exename=$ARGV[3];
	    my $filepart=$ARGV[4];
            opendir DIR,"$tmpdir";

	    while (my $file=readdir(DIR)) {
		if ($file=~/^$filepart\.$uid\.$gid\.(.*)/) {
		    if (-z $file) {
			unlink "$file";
		    }
		    else {
			my $exe=smog_system_direct("/bin/readlink -n /proc/$1/exe");  #allowed to fail
			my $short_exe=&my_basename($exe);
			
			unless ($short_exe eq $exename) {
			    $output=(split /\./,$file)[-1];
			    print $output;
			    closedir DIR;
			    exit 0;
			}
		    }
		}
	    }
	    print 0;
            closedir DIR;
	    exit 0;
	}

#TODO - mingw
	if ($command eq "clean_recovery_files") {
	    #remove non-in-use recovery/layout files for given uid, gid
	    my $uid=$ARGV[1];
	    my $gid=$ARGV[2];
	    my $exename=$ARGV[3];
            opendir DIR,"$tmpdir";

	    smog_chdir("$tmpdir");

	    while (my $file=readdir(DIR)) {
		if ($file=~/^recovery\.$uid\.$gid\.(.*)/ || $file=~/^layout\.$uid\.$gid\.(.*)/ || $file=~/^layout_numbering\.$uid\.$gid\.(.*)/) {
		    my $exe=smog_system_direct("/bin/readlink -n /proc/$1/exe"); #allowed to fail
		    my $short_exe=&my_basename($exe);
		    unless ($short_exe eq $exename) {
			unlink "$file";
		    }
		}
	    }
            closedir DIR;
	    exit 0;
	}


        if ($command eq "get_next_in_set") {
            # this is a special function for lives-exe. It will look for
            # clips on the disk that have the marker file "set.$name"
	    # only used now for pre 0.9.6 versions

            $last=$ARGV[1];
            $setname=$ARGV[2];
            $pid=$ARGV[3];

            opendir DIR,"$tmpdir";
	    @allfiles=readdir(DIR);
            closedir DIR;

            foreach my $subdir (@allfiles) {
		if (-f "$tmpdir/$subdir/set.$setname") {
                    if ($last eq "none") {
                        $found=$subdir;
                        last;
                    }
                    if ($last eq $subdir) {
                        $last="none";
                    }
                }
	    }

	    if ($found eq "") {
                $found="none";
            }

	    if ($^O eq "MSWin32") {
		$infofile="$tmpdir/info.$pid";
	    }
	    else {
		$infofile="$tmpdir/.info.$pid";
	    }
            smog_system_sync();

            if (-d $tmpdir) {
                open OUT,"> $infofile";
                print OUT $found;
                close OUT;
            }
            exit 0;
        }


	if ($command eq "cleanup"||$command eq "weed") {
	    smog_chdir("$tmpdir");
	    print "Cleaning up temporary space for $GUI_NAME.\n";
	    if ($command eq "cleanup") {
		if ($^O ne "MSWin32") {
		    unlink "/tmp/.smogval*";  # TODO *** !!!!!!!!!!
		}
		unlink glob "* .* *.*";  # will not remove directories
	    }
	    else {
		$new_temp=$ARGV[1];
		unless ($new_temp eq "") {
		    # here is where we create a new temp directory
		    unless (-d $new_temp||(mkdir $new_temp,0777)) {
			print "Smogrify: Unable to create new directory $new_temp!\n";
			exit 1;
		    }
		    print "Smogrify: moving sets from $tmpdir to $new_temp\n";
		}
		else {
		    print "Smogrify: Leaving sets intact.\n";
		}
		&weed(0);
	    }
	    exit 0;
	}
	
	if (substr($command,0,7) eq "fxinit_") {
	    # call onchange_init
	    $command=substr($command,7);
	    $handle=$ARGV[1];

	    $dir=$ARGV[2];

            $width=$ARGV[3];
            $height=$ARGV[4];

	    $plugin_name="$dir/$command";
	    shift; shift; shift; shift;

	    $curtmpdir="$tmpdir/$handle";

	    $ARGV[0]="onchange_init";
	    require("$plugin_name");
	    exit 0;
	}



	if ($command eq "count_frames") {
	    $img_ext=".".$ARGV[2];
	    print &count_frames;
	    exit 0;
	}


	if ($command eq "get_proj_set") {
	    my $proj_file=$ARGV[1];
	    my $out=smog_system_direct("tar --exclude=*/* -tzf $proj_file");
	    unless ($out=~/\/$/) {
		exit 1;
	    }
	    chomp $out;
	    chop $out;
	    print $out;

	    exit 0;
	}

        if ($command eq "mv_pre") {
	    $handle=$ARGV[1];
	    $curtmpdir="$tmpdir/$handle";
	    if ($^O eq "MSWin32") {
		$statusfile="$curtmpdir/status";
	    }
	    else {
		$statusfile="$curtmpdir/.status";
	    }
	    unlink "$curtmpdir/pause";
	    $start=$ARGV[2];
	    $end=$ARGV[3];
	    $img_ext=".".$ARGV[4];
	    &mv_pre;
	    if ($panic) {
		exit 1;
	    }
	    &sig_complete;
	    exit 0;
	}



	if ($command eq "clear_symlinks") {
	    $handle=$ARGV[1];
	    &clear_symlinks;
	    exit 0;
	}


    
####################################################################    
    if ($^O ne "MSWin32") {
	# run the rest in background
	if ($background==1) {
	    if (fork()) {
		exit 0;
	    }
	}
    }
####################################################################
    umask $umask;

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

    $handle=$ARGV[1];

    $curtmpdir="$tmpdir/$handle";

    if ($^O eq "MSWin32") {
	$statusfile=$curtmpdir."/status";
    }
    else {
	$statusfile=$curtmpdir."/.status";
    }

    if ($command eq "play"||$command eq "play_opening_preview") {
	if ($^O eq "MSWin32") {
	    $pidfile=$curtmpdir."/pidpb";
	}
	else {
	    $pidfile=$curtmpdir."/.pidpb";
	}
    }
    else {
	if ($^O eq "MSWin32") {
	    $pidfile=$curtmpdir."/pid";
	}
	else {
	    $pidfile=$curtmpdir."/.pid";
	}
    }

    if ($command eq "play_opening_preview") {
	# play_opening_preview is exactly like play
	# except our audio filename is different
	$opening_preview=1;
	$command="play";
    }


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


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

	$fps=$ARGV[2];
	if ($fps==0) {
	    $fps=&rc_get("default_fps");
	}

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


	$loop=0;
	if ($ARGV[5]!=0) {
	    $loop=$ARGV[5];
	}


	if (!defined $ARGV[6]||$ARGV[6]==0) {
	    $arate=44100;
	}
	else {
	    $arate=$ARGV[6];
	}
	if (!defined $ARGV[7]||$ARGV[7]==0) {
	    $achans=2;
	}
	else {
	    $achans=$ARGV[7];
	}

	if (!defined $ARGV[8]||$ARGV[8]==0) {
	    $asamps=2;
	    $stype="w";
	}
	else {
	    $asamps=$ARGV[8];
	    if ($asamps>7) {
		$asamps/=8;
	    }
	}
	if (!defined $ARGV[9]) {
	    $signed=1;
	}
	else {
	    $signed=$ARGV[9];
	}

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

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


	$audio_player=&rc_get("audio_player");

	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)&&$end>0) {
		    $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="";
		}
		$sox_version=&get_sox_version;

		if ($sox_version<13) {
		    if ($asamps==1) {
			$stype="b";
		    }
		    elsif ($asamps==2) {
			$stype="w";
		    }
		    $aformat=".".$signed.$stype;
		    $syscom2="$audio_play_command $mute -c $achans -r $arate -$signed $aendian -t $aformat -$stype $audiofile $trimcom $audio_effect";
		}
		else {
		    $syscom2="$audio_play_command $mute -c $achans -r $arate -$signed $aendian -t $aformat -$asamps $audiofile $trimcom $audio_effect";
		}
	    } else {
		# mplayer seemingly has no easy way of playing unsigned or wrong endian
		$syscom2="$audio_play_command -demuxer rawaudio -rawaudio rate=$arate :channels=$achans:samplesize=$asamps $mute $audiofile -ss ".($audio_start-1);
	    }
	}
	
	$showed_err=0;

	do {
	    if (! -d "$curtmpdir" || -f "$curtmpdir/.stoploop" || -f "$curtmpdir/stoploop" || ! -f $audiofile) {
		$loop=0;
	    }
	    else {
		$syscom3=$syscom2.">$nulfile 2>&1";
		$retval=smog_system("$syscom3");
		if ($retval>255) {
		    unless ($showed_err) {
			print STDERR "Error playing audio !\n";
			print STDERR "Failed command was: $syscom2\n";
			smog_system("$syscom2");
			$showed_err=1;
		    }
		}
	    }
	} while ($loop);
	&sig_complete_audio;

	unlink "$curtmpdir/.stoploop";
	unlink "$curtmpdir/stoploop";

	exit 0;
    }


	if ($command eq "open_test") {
	    $command="open";
	    $opentest=1;
	}


	if ($command eq "open") {

	unlink "$curtmpdir/pause";

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

	$withsound=$ARGV[3];

	if (defined($ARGV[4])) {
	    $img_ext=".".$ARGV[4];
	}
		
	if (defined($ARGV[5]) && $ARGV[5] > 0) {
	    $ss=" -ss $ARGV[5] ";
	}
	$frames="";
	if (defined($ARGV[6]) && $ARGV[6] > 0 ) {
	    $ARGV[6]++;  # cf below, we remove 1st frame
	    $frames=" -frames $ARGV[6] ";
	}
	$extra_params=$ARGV[7];
	$band="";
	if ($extra_params eq "nobandwidth") {
	    $extra_params="";
	}
	else {
	    if ($extra_params eq "sendbandwidth") {
		$bandwidth=&rc_get("dl_bandwidth_K");
		if ($bandwidth eq "") {
		    $bandwidth=64;
		}
		$band="-bandwidth $bandwidth"."000";
		$extra_params="";
	    }
	}
	$compression=&rc_get("open_compression_percent");
	if ($compression eq "") {
	    $compression=15;
	}

	smog_chdir("$curtmpdir");

	# let get_file_info set this
	#$mplay_command=&rc_get("video_open_command");

	# process video

	# 3 other files we will use
	$curtmpfile="tempresult";
	$audio_out="audio";
	$audio_in="audiodump.pcm";

	if ($img_ext eq ".png") {
	    $compression=int($compression/10.001)
	}
	else {
	    $quality=int(100-$compression);
	}

	$xframes=$frames;

	my $wavhead=":nowaveheader";

	if ($file=~/:\/\//) {
	    $is_remote=2;
	    #$wavhead=""; # need wav header, as user will probably finish by
	                 # quitting, thus we won't send full info
	}
	&get_file_info;

	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}

	$aformnew="";
	if ($asamps==32) {
	    # cannot handle this yet, need to resample to s16
	    if ($endian==1) {
		$aformnew="-format s16le";
	    }
	    else {
		$aformnew="-format s16be";
	    }
	    $asamps=16;
	}
#TODO - mingw
	$threads=smog_system_direct("/bin/grep processor /proc/cpuinfo | wc -l");
	$threads=int($threads);
	if ($threads==0) {
	    $threads=1;
	}

	if ($type eq "jpeg" || $type eq "png") {
	    $count=$frames;
	}
	else {
	    $frames=$xframes;

	    if ($achans eq "" || $achans eq "0") {
		$channs="";
	    }
	    else {
		$channs="-channels $achans";
	    }
	    
	    if ($withsound eq "0") {
             #video only
		if ($img_ext eq ".jpg") {
		    $syscom=$mplay_command . " -quiet $band -osdlevel 0 -vo jpeg:quality=$quality -lavdopts o=threads=$threads -fps 100000 $ss $frames -noframedrop -ao null \"$file\" $extra_params <$nulfile";
		}
		else {
		    $syscom=$mplay_command . " -quiet $band -osdlevel 0 -vo png:z=$compression:alpha -lavdopts o=threads=$threads -fps 100000 $ss $frames -noframedrop -ao null \"$file\" $extra_params <$nulfile";
		}
	    }
	    elsif ($withsound eq "1") {
             #video and audio 
		if ($img_ext eq ".jpg") {
		    $syscom=$mplay_command . " -quiet $band -osdlevel 0 -vo jpeg:quality=$quality $ss -lavdopts o=threads=$threads -noframedrop $frames -ao pcm:fast$wavhead $channs $aformnew -mc 0  \"$file\" $extra_params <$nulfile";
		}
		else {
		    $syscom=$mplay_command . " -quiet $band -osdlevel 0 -vo png:z=$compression:alpha $ss -lavdopts o=threads=$threads -noframedrop $frames -ao pcm:fast$wavhead $channs $aformnew -mc 0  \"$file\" $extra_params <$nulfile";
		}
            }
            else {
             #audio only 
		if ($ss eq "" && $frames eq "") {
		    $syscom=$mplay_command . " -quiet $band -vo null -vc null -ao pcm:fast$wavhead $aformnew $channs -mc 0 \"$file\" $extra_params <$nulfile";
		}
		else {
		    $syscom=$mplay_command . " -quiet $band -vo null $ss $frames -ao pcm:fast$wavhead $aformnew $channs -mc 0 \"$file\" $extra_params <$nulfile";
		}
	    }
	    if (defined($DEBUG_OPEN)) {
		print STDERR "open command for $handle is: $syscom\n";
	    }
	    
	    $syscom2=$syscom." >\"$curtmpfile\" 2>$nulfile";

	    unless ($opentest==1) {
		&sig_progress(0);
	    }

	    smog_system("$syscom2"); # this may well fail...we just have to use what we get back
	    
            if (!-f $curtmpfile) {
		unlink $pidfile;
		exit 1;
            }

	    @info=split /  /, smog_system_direct("grep VIDEO: \"$curtmpfile\" 2>$nulfile");
	    @info2=split / /, smog_system_direct("grep AUDIO: \"$curtmpfile\" 2>$nulfile");
	    
	    unlink "$curtmpfile";
	    #$type=$info[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];

	    if (! -d $curtmpdir) {
		#curtmpdir can be removed by cancel
		exit 1;
	    }
	    
	    if ($withsound eq "-1") {
		$count=0;
	    }
	    else {
		# double check number of frames
		$count=&count_frames;
	    }
	}

	# if the last frame has zero size, delete it
	if ($count>0) {

	    $name=&mkname($count);
	    while ($count > 0 && -f "$name$img_ext" && -z "$name$img_ext") {
		unlink "$name$img_ext";
		$name=&mkname(--$count);
	    }
	    if ($count==0) {
		if ($img_ext==".png") {
		    &sig_error("Your version of mplayer/ffmpeg may be broken","See http://bugzilla.mplayerhq.hu/show_bug.cgi?id=2071"," ","You can work around this by switching to jpeg output in Preferences/Decoding.");
		}
	    }

	    # double check image size
	    $name=&mkname(1);
	    $imresact="none";
	    &get_image_size("$name$img_ext");
	    if ($panic||$hsize==-1) {
		unlink $pidfile;
		&sig_error;
	    }
	}

	if (-f $audio_in) {
	    smog_rename( "$audio_in","$audio_out");
            $af_size = -s $audio_out; 
	}

	if ($img_ext eq ".png" || $type eq "png") {
	    $bpp=32;
	}

	unless (0||$withsound ne "-1" || $achans==0) {
	    # try as best we can to sync sound and video for a selection
	    $audio_out=&clip_audio(0.3,1000000.);
	    if (!$panic) {
		smog_rename( "$audio_out","audio");
	    }
	    $audio_out="audio";
	}


	if ($count==0||$type eq "jpeg"||$type eq "png"||$type eq "Audio") {
	    # we could have audio or images
	    if (-f $audio_out) {
		# just in case...
		$type="Audio";
	    }
	    if (! -d $curtmpdir) {
		#curtmpdir can be removed by cancel
		exit 1;
	    }

	    if ($af_size>0||$count>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")) {
		if (!defined($DEBUG_OPEN)&&$withsound>=0) {
		    print STDERR "\nFailed to open file - I tried:\n\n $syscom\n";
		    print STDERR "\nMaybe you are missing a library in mplayer (or it is not a valid media file) ?\n";
		}
		if ($^O eq "MSWin32") {
		    &sig_error("This does not appear to be a valid video or image file","$GUI_NAME was unable to open it."," ");
		}
		else {
		    &sig_error("This does not appear to be a valid video or image file","$GUI_NAME was unable to open it.","","Check the terminal window for more details.");
		}
	    }
	}

	# mplayer seems to sometimes output one extra frame for jpg
	if (defined($MPLAYER_EXTRA_OPEN_FRAME_BUG)&&($count==$xframes+1)&&(($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

		#TODO ** - check if this is still the case

		for ($i=2;$i<=$count;$i++) {
		    $from=&mkname($i);
		    $to=&mkname($i-1);
		    smog_rename( "$from$img_ext", "$to$img_ext");
		}
		$count--;
	    }
	}

	$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 "get_details") {

	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";


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

	$img_ext=".".$ARGV[3];

	if (defined($ARGV[4])) {
	    $is_remote=$ARGV[4];
	}
	if (defined($ARGV[5])) {
	    $is_audio=$ARGV[5];
	}
	&get_file_info;
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}

	if ($asamps==32) {
	    # cant handle this yet, so we will resample on open
	    $asamps=16;
	}

	smog_chdir("$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 "open_tv_card") {
	    $chanstr=$ARGV[2];
	    $devstr=$ARGV[3];
	    $fifofile=$ARGV[4];

	    $inputstr=$sizestr=$fpsstr=$driverstr=$outfmt="";

	    if (defined($ARGV[5])) {
		$inputstr=":input=$ARGV[5]";
	    }
	    if (defined($ARGV[6])&&defined($ARGV[7])) {
		if ($ARGV[6]>0&&$ARGV[7]>0) {
		    $sizestr=":width=$ARGV[6]:height=$ARGV[7]";
		}
	    }

	    if (defined($ARGV[8])) {
		if ($ARGV[8]>0.) {
		    $fpsstr=":fps=$ARGV[8]";
		}
	    }

	    if (defined($ARGV[9])) {
		if ($ARGV[9] ne "autodetect") {
		    $driverstr=":driver=$ARGV[9]";
		}
	    }

	    if (defined($ARGV[10])) {
		if ($ARGV[10] ne "autodetect") {
		    $outfmt=":outfmt=$ARGV[10]";
		}
	    }

	    $com="mplayer -really-quiet tv://$chanstr -tv device=$devstr$inputstr$driverstr$sizestr$outfmt$fpsstr -vo yuv4mpeg:file=$fifofile >$nulfile 2>&1 <$nulfile";
	    $smerr=smog_system($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    exit 0;
	}



        if ($command eq "open_fw_card") {
	    $cardno=$ARGV[2];
	    $cache=$ARGV[3];
	    $fifofile=$ARGV[4];
	    $com="dvgrab -s 0 -noavc -card $cardno -o - 2>$nulfile | mplayer - -really-quiet -demuxer lavf -vo yuv4mpeg:file=$fifofile >$nulfile 2>&1";
	    $smerr=smog_system("$com");
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    exit 0;
	}






	if ($command eq "close") {
	    if (defined(open IN,"< $pidfile")) {
		close IN;
		# cancel any processing
		if ($^O ne "MSWin32") {
		    smog_system("smogrify stopsubsub \"$handle\"");
		}
	    }
	    if (chdir("$curtmpdir")) {
		unlink glob "* .* *.*";
		smog_chdir("$tmpdir");
		rmdir $curtmpdir;
	    }
	    exit 0;
	}
    

	if ($command eq "ext_save") {
	    shift(@ARGV);
	    $curtmpdir="$tmpdir/$handle";
	    $execname=$ARGV[1];
	    shift(@ARGV);

	    smog_chdir("$curtmpdir");

	    $com="\"$execname\" encode @ARGV";
	    $smerr=smog_system($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    &sig_complete;
	    exit 0;

	}


	
	if ($command eq "save") {
	    my $cmd="";

	    $get_rfx=0;

	    if ($handle eq "get_rfx") {
		$get_rfx=1;
		shift(@ARGV);
		$handle=$ARGV[1];

		$curtmpdir="$tmpdir/$handle";
		if ($^O eq "MSWin32") {
		    $statusfile="$curtmpdir/status";
		    $pidfile=$curtmpdir."/pid";
		}
		else {
		    $statusfile="$curtmpdir/.status";
		    $pidfile=$curtmpdir."/.pid";
		}

	    }

	    unlink "$curtmpdir/pause";
	    smog_chdir("$curtmpdir");

	    $plugin=$ARGV[2];

	    $fps=$ARGV[3];
	    $nfile=$ARGV[4];

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

	    $start=$ARGV[5];
	    $is_linked=0;

	    #create symlinks in /tmp (for dynebolic)
	    $linksdir="/tmp/lives-symlinks/$handle/";

	    if ($start==-1) {
		# special value which tells us we are dealing with symlinks
		$start=1;
		$is_linked=1;
	    }

	    $end=$ARGV[6];
	    $arate=$ARGV[7];
	    $achans=$ARGV[8];
	    $asamps=$ARGV[9];
	    if (defined($ARGV[10])) {
		$ssigned=$ARGV[10]&1; #CAREFUL - this is reversed in LiVES (there unsigned==1, signed==0)
		$aendian=$ARGV[10]&2; # value of 2 means bigendian
		$aendian=!$aendian; #endian is revesed here (0 means bigend)
	    }
	    
	    else {
		$ssigned=1;
		if ($asamps==8) {
		    $ssigned=0;
		}
		$aendian=&get_endian;
	    }
	    if ($ssigned==1) {
		$asigned="-s";
	    }
	    else {
		$asigned="-u";
	    }
	    
	    if (defined($ARGV[11])) {
		$aud_start=$ARGV[11];
	    }
	    else {
		$aud_start=($start-1.)/($fps*1.);
	    }
	    if (defined($ARGV[12])) {
		$aud_end=$ARGV[12];
	    }
	    else {
		$aud_end=($end*1.)/($fps*1.);
	    }

	    $img_ext=&get_img_ext($curtmpdir,$start);

	    # get image size ($hsize x $vsize)
	    $imresact="none";
	    my $firstframe=&mkname($start);

	    &get_image_size("$firstframe$img_ext");

	    if ($panic||$hsize==-1) {
		unlink $pidfile;
		exit 1;
	    }
	    $otype=&rc_get("output_type");

	    $encoder=&rc_get("encoder");

	    $DEBUG_ENCODERS=1;

	    $audiofile="";


	    if ($get_rfx==0) {

		unlink "audiodump.wav";

		$areq=&get_form_request($plugin);

		#prepare audio stream if requested
		if (-f "$curtmpdir/audio" && $arate>0) {

		    $origaudio=$audiofile=$audio_in="$curtmpdir/audio";
		    if ($areq&1||$aud_start!=0.) {

			# 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 ($panic) {
			    unlink "$pidfile"; 
			    exit 1;
			}

			#pad to end with silence
			$desired_length=$aud_length*$arate*$achans*$asamps/8;
			&append_silence(0,$desired_length,$asamps,$achans,$ssigned,$aendian,$audiofile);
			if ($panic) {
			    unlink "$pidfile"; 
			    exit 1;
			}

			if ($aud_start!=0.&&!($areq&1)) {
			    if (&rc_get("conserve_space") eq "true") {
				unlink "$origaudio";
			    }
			    else {
				smog_rename( "$origaudio","$origaudio.origbak");
			    }
			    smog_rename( "$curtmpdir/$audiofile","$origaudio");
			    $audio_file=$audio_in=$origaudio;
			}
		    }
		    if ($areq&2) { 
			# convert raw audio to wav
			$audio_out=$curtmpdir . "/audiodump.wav";
			&convert_audio_to_wav;
			if ($panic) {
			    unlink "$pidfile"; 
			    exit 1;
			}
			unlink "$audio_in";
			$audiofile=$audio_out;
		    }
		}

		if ($areq&4) {
	
		    if ($start>1) {
			# move the selection down so that frames start at 1
			# note, LiVES never uses this any more, 
			# instead it first calls link_frames, and possibly sets $start to -1
			# (see above)

			if (-d $linksdir) {
			    smog_system("/bin/rm -rf \"$linksdir\"");
			}
		    
			$com="/bin/mkdir -p \"$linksdir\"";
			$smerr=smog_system($com);
			if ($smerr) {
			    sig_system_error("Creating directory \"$linksdir\"",$smerr);
			    unlink "$pidfile"; 
			    exit 1;
			};


			smog_system ("/bin/chmod -R 777 \"$linksdir\"");

			# need to make hard links, as some encoders complain about symbolic links


			for ($i=1;$i<=($end-$start+1);$i++) {
			    $name=&mkname($i);
			    $from=&mkname($i+$start-1);
			    if (-f "$curtmpdir$from$img_ext") {
				$smerr=smog_system ("/bin/ln \"$curtmpdir/$from$img_ext\" \"$linksdir$name$img_ext\"");
				if ($smerr) {
				    sig_system_error("Symlinking \"$curtmpdir/$from$img_ext\" from \"$linksdir$name$img_ext\"",$smerr);
				    unlink "$pidfile"; 
				    exit 1;
				}
			    }
			}
			if (-f $audiofile) {
			    my $xaudiofile=(split(/\//,$audiofile))[-1];
			    if (! -f "$linksdir$xaudiofile") {
				$smerr=smog_system("/bin/ln \"$curtmpdir/$audiofile\" \"$linksdir$xaudiofile\"");
				if ($smerr) {
				    sig_system_error("Symlinking \"$curtmpdir/$audiofile\" from \"$linksdir$xaudiofile\"",$smerr);
				    unlink "$pidfile"; 
				    exit 1;
				}
			    }
			}
			$is_linked=1;
		    }
		}
		
		if ($is_linked==1) {
		    smog_chdir("$linksdir");
		}

	    }


	    unless ($plugin eq "") {
		$atype=&rc_get("encoder_acodec");
		$fields="$fps \"$nfile\" $start $end $img_ext $otype $atype $hsize $vsize";
		$fields.=" $DEBUG_ENCODERS";
		$fields.=" $arate $achans $asamps $ssigned";

		if ($plugin =~ /multi_encoder$/) {
		    $extra_opts="-v";
		}

		if ($^O eq "MSWin32") {
		    # adjust depending on file ext
		    my $ext=&get_ext("$plugin");
		    if ($ext eq ".py") {
			$cmd="python";
		    }
		    else {
			$cmd="perl";
		    }
		    if ($get_rfx==0) {
			$fields.=@ARGV[13..$#ARGV];
			$com="$cmd \"$plugin\" $extra_opts encode $fields";

		    }
		    else {
			$com="$cmd \"$plugin\" get_rfx $fields";
		    }
		}
		else {
		    if ($get_rfx==0) {
			$fields.=@ARGV[13..$#ARGV];
			$com="\"$plugin\" $extra_opts encode $fields";
		    }
		    else {
			$com="\"$plugin\" get_rfx $fields";
		    }
		}

		$smerr=smog_system("$com");

		&sig_complete;
		exit 0;
	    }

	    if (-f ".comment") {
		open IN,"< .comment";
		read IN,$string,1040;
		close IN;
		unlink ".comment";
	    }
	    @tmp=split(/\|\|\%/,$string);

	    $title=$tmp[0];
	    $author=$tmp[1];
	    $comment=$tmp[2];
	    chomp($comment);

	    if ($get_rfx==0) {
		$command="encode";
	    }
	    else {
		$command="get_rfx";
	    }

	    return 1;
	    exit 0; # just in case
	}


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

	    $start=$ARGV[2];
	    $end=$ARGV[3];
	
	    $astart=$ARGV[4];
	    $aend=$ARGV[5];

	    $arate=$ARGV[6];
	    $achans=$ARGV[7];
	    $asamps=$ARGV[8];
	    $asigned=$ARGV[9];
	    $aendian=$ARGV[10];

	    $from_handle=$ARGV[11];

	    my $i;
	    my $audiofile="audio";

	    if ($from_handle eq "") {
		#create symlinks in /tmp for dynebolic
		$linksdir="/tmp/lives-symlinks/$handle/";

		if (-d $linksdir) {
		    if ($^O eq "MSWin32") {
			smog_system("DEL /q \"$linksdir\"");
			smog_system("RMDIR \"$linksdir\"");
		    }
		    else {
			smog_system("/bin/rm -rf \"$linksdir\"");
		    }
		}

		if ($^O eq "MSWin32") {
		    $com="mkdir.exe -p \"$linksdir\"";
		}
		else {
		    $com="/bin/mkdir /p \"$linksdir\"";
		}
		$smerr=smog_system($com);
		if ($smerr) {
		    sig_system_error("Creating directory \"$linksdir\"",$smerr); 
		    unlink "$pidfile"; 
		    exit 1;
		}
		
		if ($^O eq "MSWin32") {
		    smog_system ("chmod.exe -R 777 \"$linksdir\"");
		}
		else {
		    smog_system ("/bin/chmod -R 777 \"$linksdir\"");
		}
	    }
	    else {
		$linksdir=$curtmpdir;
		$handle=$from_handle;
		$curtmpdir="$tmpdir/$handle/";
	    }

	
	# copy a slice of the audio file into our links dir
	# this allows us to resample it
	# and also aligns the start of audio with new frame 1

	unless ($aend==0.) {

	    my $ocurtmpdir=$curtmpdir;
	    my $ofrom_handle=$from_handle;
	    my $ostart=$start;
	    my $oend=$end;

	    $start=$astart;
	    $end=$aend;
	    $where=0.;
	    $from_handle=$handle;
	    $curtmpdir=$linksdir;

	    &insert_audio($asamps,$achans,$asigned,$aendian,0);
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }

	    $curtmpdir=$ocurtmpdir;
	    $from_handle=$ofrom_handle;
	    $start=$ostart;
	    $end=$oend;

	  }

	    $img_ext=&get_img_ext($curtmpdir,$start);

	    for ($i=1;$i<=($end-$start+1);$i++) {
		$name=&mkname($i);
		$from=&mkname($i+$start-1);
		if (-f "$curtmpdir/$from$img_ext") {
		    if ($^O eq "MSWin32") {
			$smerr=smog_system ("cp.exe \"$curtmpdir/$from$img_ext\" \"$linksdir/$name$img_ext\"");
			if ($smerr) {
			    sig_system_error("Copying \"$curtmpdir/$from$img_ext\" to \"$linksdir/$name$img_ext\"",$smerr);
			    unlink "$pidfile"; 
			    exit 1;
			}
		    }
		    else {
			$smerr=smog_system ("/bin/ln \"$curtmpdir/$from$img_ext\" \"$linksdir/$name$img_ext\"");
			if ($smerr) {
			    sig_system_error("Linking \"$curtmpdir/$from$img_ext\" to \"$linksdir/$name$img_ext\"",$smerr);
			    unlink "$pidfile"; 
			    exit 1;
			}
		    }
	
		}
		&sig_progress($i);
	    }

 	    unless ($from_handle eq "") {
		$curtmpdir=$linksdir;
	    }

	    &sig_complete;
	    exit 0;
    }


	if ($command eq "resize_all") {
	    unlink "$curtmpdir/pause";
	    smog_chdir("$curtmpdir");
	    $end=$ARGV[2];

	    my $iwidth=$ARGV[3];
	    my $iheight=$ARGV[4];

	    $img_ext=".".$ARGV[5];
	    $img_prefix=&get_img_prefix($img_ext);

	    $resize_ext=".mgk";

	    my $letterbox=0;

	    if (defined($ARGV[6]) && $ARGV[6]>0) {
		# letterboxen - next values are image-in-frame size
		$letterbox=1;
		$owidth=$iwidth;
		$oheight=$iheight;
		$iwidth=$ARGV[6];
		$iheight=$ARGV[7];
		$in_ext=$resize_ext;
		$out_ext=$img_ext;
	    }

	    &clean_old;

	    if ($letterbox) {
		# make a background frame
		if (!defined($bgcolour)) {
		    $bgcolour="#000000";
		}
		
		$com="$smog_convert_command -size $owidth!x$oheight! xc:$bgcolour blank.jpg >$nulfile 2>&1";
		$smerr=smog_system($com);
		if ($smerr) {
		    sig_system_error("$com",$smerr);
		    unlink "blank.jpg";
		    unlink "$pidfile"; 
		    exit 1;
		}
	    }



	    for ($i=1;$i<=$end;$i++) {
		$name=&mkname($i);
		
		smog_copy("$name$img_ext","$name.bak");
		if ($panic) {
		    if ($letterbox) {
			try_to_recover($start,$i-1);
			unlink "blank.jpg";
		    }
		    if ($DEBUG_SMOGRIFY) {
			print STDERR "smogrify debug - rename failed ($!): \"$curtmpdir/$name$img_ext\",\"$curtmpdir/$name.bak\"\n";
		    }
		    sig_system_error("Renaming \"$curtmpdir/$name$img_ext\" to \"$curtmpdir/$name.bak\"");

		    unlink "$pidfile"; 
		    exit 1;
		}


		if ($iwidth>0 && $iheight>0) {
		    &resize_frame($name,$iwidth,$iheight);
		}

		if ($panic) {
		    if ($letterbox) {
			try_to_recover($start,$i);
			unlink "blank.jpg";
		    }
		    unlink "$pidfile"; 
		    exit 1;
		}

		if ($letterbox) {
		    &letterbox_frame($name,abs($iwidth),abs($iheight),$owidth,$oheight);
		}

		if ($panic) {
		    if ($letterbox) {
			try_to_recover($start,$i);
			unlink "blank.jpg";
		    }
		    unlink "$pidfile"; 
		    exit 1;
		}

		&sig_progress($i);
	    }


	    $start=1;

	    unless ($letterbox) {
		&mv_mgk;
		if ($panic) {
		    unlink "$pidfile"; 
		    exit 1;
		}
	    }
	    else {
		unlink "blank.jpg";
	    }

	    &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";
	smog_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 glob "*.tar";
	$com="tar --ignore-failed-read -cf header.tar \"$audio\" header* extended* event.* subs.* file* 2>$nulfile";
	smog_system ($com);
	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    unlink "$pidfile"; 
	    exit 1;
	}

	&sig_progress(0);
	
	$com="tar -cf temp.tar header.tar 2>.tar_err";
	$smerr=smog_system ($com);
	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    unlink "$pidfile"; 
	    exit 1;
	}
	
	$com="tar --exclude=*.bak --exclude=*.tar --exclude=./.* --exclude=*.mgk --exclude=*.tmp --exclude=audioclip* -rf temp.tar * 2>>.tar_err";
	$smerr=smog_system ($com);
	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    unlink "$pidfile"; 
	    exit 1;
	}
	
	if ($^O eq "MSWin32") {
	    $com="gzip.exe -S .lv1 temp.tar";
	}
	else {
	    $com="gzip -S .lv1 temp.tar";
	}

	$smerr=smog_system ($com);
	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    unlink "$pidfile"; 
	    exit 1;
	}
	
	unlink "temp.tar";
	
	smog_rename("temp.tar.lv1","$nfile");
	
	unlink <header.tar 0*.tar>;
	if (-f ".tar_err") {
	    open IN,"< .tar_err";
	    read IN,$tarerr,255;
	    close IN;
	    unless ($tarerr eq "") {
		&sig_error("Error creating new backup.",$tarerr);
		unlink glob "*.tar .tar_err";
		unlink "$pidfile";
		exit 1;
	    }
	}
	$size=-s "$nfile";
	&sig_complete($size);
	exit 0;
    }


    if ($command eq "restore") {
	$nfile=$ARGV[2];

	smog_chdir("$curtmpdir");
	
	if ($^O ne "MSWin32") {
	    $com="tar -zxf \"$nfile\" 2>$nulfile";
	    smog_system ($com);

	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	}
	else {
	    smog_copy("$nfile","$curtmpdir\\temp.gz");
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	    $com="gzip.exe -d temp.gz";
	    smog_system ($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    $com="tar.exe -xf temp";
	    smog_system ($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    unlink "temp";
	}

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

	opendir DIR,$curtmpdir;
	while ($file=readdir(DIR)) {
	    if ($file =~ /.tar$/) {
		smog_system("tar -xf $file 2>$nulfile");
		unlink "$file";
	    }
	}
	close DIR;

	&sig_complete;
	exit 0;
    }



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

	$img_ext=".".$ARGV[2];
	$img_prefix=&get_img_prefix($img_ext);

	if (!defined($ARGV[3])) {
	    $endian=&get_endian;
	}
	else {
	    $endian=$ARGV[3];
	}
	if (defined $ARGV[4]) {
	    $newwidth=$ARGV[4];
	}
	if (defined $ARGV[5]) {
	    $newheight=$ARGV[5];
	}
	if (defined $ARGV[6]) {
	    if ($ARGV[6]==1) {
		$leave_bak=1;
	    }
	}
	if (defined $ARGV[7]) {
	    $old_end=$ARGV[7];
	}

	$letterbox=0;

	if (defined $ARGV[8] && $ARGV[8]!=0) {
	    #letterbox size
	    $letterbox=1;
	    $owidth=$newwidth;
	    $oheight=$newheight;

	    $newwidth=$ARGV[8];
	    $newheight=$ARGV[9];
	    $in_ext=".mgk";
	    $out_ext=$img_ext;
	}

	$newframe=-1;
	$event_file="$curtmpdir/event.frames";
	$resize_ext=".tmp";
	smog_chdir("$curtmpdir");

	unless ($leave_bak==1) {
	    &clean_old;
	}

	if (defined(open IN,"< $event_file")) {
	    $fcount=1;
	    read IN,$val,4;
	    $pstart=&getint($val);
	    $count=$pstart;
	    

	    if ($letterbox&&!$leave_bak) {
		# make a background frame
		if (!defined($bgcolour)) {
		    $bgcolour="#000000";
		}
		
		$com="$smog_convert_command -size $owidth!x$oheight! xc:$bgcolour blank.jpg >$nulfile 2>&1";
		$smerr=smog_system($com);
		if ($smerr) {
		    sig_system_error("$com",$smerr);
		    unlink "$pidfile"; 
		    exit 1;
		}
	    }



	    while ($newframe!=0) {
		read IN,$val,4;
		$newframe=&getint($val);
		if ($newframe>0) {
		    $from=&mkname($newframe);
		    if (-f "$curtmpdir/$from$img_ext") {
			$to=&mkname($count);

			if ($newwidth > 0 && $newheight > 0) {
			    &resize_frame($from,$newwidth,$newheight);
			    if ($panic) {
				if ($letterbox) {
				    try_to_recover($start,$i-1);
				    unlink "blank.jpg";
				}
				unlink "$pidfile"; 
				exit 1;
			    }

			    $smerr=rename "$curtmpdir/$from$resize_ext","$curtmpdir/$to.mgk";
			    if (!$smerr) {
				if ($letterbox) {
				    try_to_recover($start,$i);
				    unlink "blank.jpg";
				}
				sig_system_error("Renaming \"$curtmpdir/$from$resize_ext\" to \"$curtmpdir/$to.mgk\"",$!);
				unlink "$pidfile"; 
				exit 1;
			    }
			}
			else {
			    unless (($from eq $to) || $letterbox) {
				smog_copy("$curtmpdir/$from$img_ext", "$curtmpdir/$to.mgk");
				if ($panic) {
				    unlink "$pidfile"; 
				    exit 1;
				}
			    }
			}

                        #now resized are in .mgk
			if ($letterbox) {
			    if ($leave_bak) {
				#letterboxing was done with resize
				unless ($from eq $to) {
				    smog_copy("$curtmpdir/$from$img_ext", "$curtmpdir/$to$img_ext.2");
				    if ($panic) {
					try_to_recover($start,$i);
					unlink "$pidfile"; 
					exit 1;
				    }
				    smog_copy("$curtmpdir/$from.mgk", "$curtmpdir/$to.mgk.2");
				    if ($panic) {
					try_to_recover($start,$i);
					unlink "$pidfile"; 
					exit 1;
				    }
				}
			    }
			    else {
				smog_copy("$curtmpdir/$to$img_ext", "$curtmpdir/$to.bak");
				if ($panic) {
				    try_to_recover($start,$i-1);
				    unlink "$pidfile";
				    unlink "blank.jpg";
				    exit 1;
				}
				# make $to.mgk $to.img_ext
				&letterbox_frame($to,abs($newwidth),abs($newheight),$owidth,$oheight);
				
				if ($panic) {
				    try_to_recover($start,$i-1);
				    unlink "$pidfile"; 
				    unlink "blank.jpg";
				    exit 1;
				}

                                #lb are in .img
			    }

			}

		    }
		    $count++;
		}
		&sig_progress($fcount++);
	    }
	    close IN;

	    if ($letterbox&&!$leave_bak) {
		unlink "blank.jpg";
	    }

	}
	
	else {
	    &sig_error;
	}

	$start=$pstart;
	$new_count=--$count;
	$end=$count;

	unless ($letterbox) {
	    # mv mgk -> $img_ext
	    &mv_mgk;
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	}

	if ($letterbox&&$leave_bak) {

	    # because of potential ovewrite we made .mgk2 (resized/no letterbox) and $img_ext.2 (resized)
	    # now copy these
	    unless (!defined($pidfile)||$pidfile eq "") {
		# don't want to get killed in this stage...
		unlink "$pidfile";
	    }
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    for ($i=$start;$i<=$end;$i++) {
		$name=&mkname($i);
		if (-f "$curtmpdir/$name.mgk.2") {
		    if (-f "$curtmpdir/$name$img_ext.2") {
			unlink "$curtmpdir/$name$img_ext";
			$smerr=rename "$curtmpdir/$name$img_ext.2","$curtmpdir/$name$img_ext";
			if (!$smerr) {
			    if ($letterbox) {
				try_to_recover($start,$end);
			    }
			    sig_system_error("Renaming \"$curtmpdir/$name$img_ext.2\" to \"$curtmpdir/$name$img_ext\"",$!);
			    unlink "$pidfile"; 
			    exit 1;
			}

		    }
		    unlink "$curtmpdir/$name.mgk";
		    $smerr=rename "$curtmpdir/$name.mgk.2","$curtmpdir/$name.mgk";
		    
		    if (!$smerr) {
			if ($letterbox) {
			    try_to_recover($start,$end);
			}
			sig_system_error("Renaming \"$curtmpdir/$name.mgk.2\" to \"$curtmpdir/$name.mgk\"",$!);
			unlink "$pidfile"; 
			exit 1;
		    }
		}
	    }
	}

	if ($end<$old_end) {
	    # remove excess frames
	    for ($i=$end+1;$i<=$old_end;$i++) {
		$name=&mkname($i);
		if (-f "$curtmpdir/$name$img_ext") {
		    unlink "$curtmpdir/$name.bak";
		    smog_rename( "$curtmpdir/$name$img_ext","$curtmpdir/$name.bak");
		}
	    }
	    &sig_progress($i);
	}

	&sig_complete($new_count);
	exit 0;
    }



    if ($command eq "deorder") {
	$start=$ARGV[2];
	$end=$ARGV[3];
	$frames=$ARGV[4];
	$img_ext=".".$ARGV[5];
	if (defined $ARGV[6]) {
	    $leave_bak=$ARGV[6];
	}
	else {
	    $leave_bak=0;
	}

	smog_chdir("$curtmpdir");

	$oend=$end;

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

	&undo(!$leave_bak);  # will abort on failure

	if ($frames<$oend) {
	    # frames were upsampled
	    for ($i=$frames+1;$i<=$oend;$i++) {
		$name=&mkname($i);
		if (!$leave_bak) {
		    unlink "$curtmpdir/$name$img_ext";
		}
		else {
		    smog_rename( "$curtmpdir/$name$img_ext","$curtmpdir/$name.mgk");
		}
	    }
	}
	else {
	    # frames were downsampled
	    for ($i=$end+1;$i<=$frames;$i++) {
		$name=&mkname($i);
		if (!$leave_bak) {
		    if (-f  "$curtmpdir/$name.bak" ) {
			smog_rename( "$curtmpdir/$name.bak","$curtmpdir/$name$img_ext");
		    }
		}
		else {
		    smog_rename( "$curtmpdir/$name$img_ext","$curtmpdir/$name.mgk");
		}
	    }
	}

	if (!$leave_bak) {
	    #remove .mgk files from undo
	    &clean_old;
	}

	&sig_complete;
	exit 0;
    }


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

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

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


	if ($command eq "delete_all") {
	    $frames=$ARGV[2];
	    if (! -d $curtmpdir) {
		&sig_complete;
		exit 1;
	    }
	    smog_chdir("$curtmpdir");
	    unlink glob "* *.* .*";
	    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];
	$img_ext=".".$ARGV[4];
	&reverse;
	&sig_complete;
	exit 0;
    }


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

	$start=$ARGV[2];
	$end=$ARGV[3];
	$img_ext=".".$ARGV[4];
	&undo;  # will abort on failure
	&sig_complete;
	exit 0;
    }


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

	$start=$ARGV[2];
	$end=$ARGV[3];
	$img_ext=".".$ARGV[4];

	&mv_mgk(1);
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}
	&sig_complete;
	exit 0;
    }


    if ($command eq "fs_preview") {
	$win=$ARGV[2];
	$hsize=$ARGV[3];
	$vsize=$ARGV[4];
	$start_time=$ARGV[5];
	$preview_frames=$ARGV[6];
	$file=$ARGV[7];
	$extra_params=$ARGV[8];
	$mplayer_command=&location("mplayer");

	if ($^O ne "MSWin32") {
	    smog_system("\"$mplayer_command\" -quiet -x $hsize -y $vsize -wid $win -vo x11 -zoom -ss $start_time -frames $preview_frames \"$file\" $extra_params >$nulfile 2>&1 <$nulfile");
	}
	else {
	    smog_system("\"$mplayer_command\" -quiet -x $hsize -y $vsize -wid $win -vo direct3d -zoom -ss $start_time -frames $preview_frames \"$file\" $extra_params >$nulfile 2>&1 <$nulfile");
	}
	&sig_complete;
	exit 0;
    }

        if ($command eq "mv_mgk") {
	    unlink "$curtmpdir/pause";
	    $start=$ARGV[2];
	    $end=$ARGV[3];
	    $img_ext=".".$ARGV[4];
	    if (defined($ARGV[5])) {
		$leave_bak=$ARGV[5];
	    }

	    &mv_mgk;
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	    &sig_complete;
	    exit 0;
	}



    if ($command eq "export_audio") {
	$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);
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	}
	else {
	    $audio_in="$curtmpdir/audio";
	}

	# convert raw audio to wav
	$audio_out=$curtmpdir . "/audiodump.wav";

	&convert_audio_to_wav;
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}
	if ($audio_end>0.) {
	    unlink "$audio_in";
	}
  
	smog_rename("$audio_out","$nfile");
	if ($^O eq "MSWin32") {
	    smog_system("chmod.exe 644 \"$nfile\" >$nulfile 2>&1");
	}
	else {
	    smog_system("/bin/chmod 644 \"$nfile\" >$nulfile 2>&1");
	}

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

	$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";

	smog_chdir("$curtmpdir");

	$audiofile="audio";

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

	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}

	smog_system_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;
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}
	unlink "$audio_in";

	$audio_bak="audio.bak";


	# resample $audio_out (if required)
	unless ($arate==$nrate&&$achans==$nchans&&$asamps==$nsamps&&$nsigned==$signed&&$nendian==$endian) {
	    $audio_in=$audio_out; #audio.new
	    $audio_out=$audio_bak; #audio.bak
	    &resample_audio; # audio.new -> audio.bak
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }

	    unlink "$audio_in"; #audio.new
	    smog_rename( "$audio_out","$audio_in");  #audio.bak -> audio.new
	    $audio_out=$audio_in; #audio.new
	}

	unless (&rc_get("conserve_space") eq "true") {
	    smog_rename("$audio_out","keep_$audio_out");
	    &backup_audio;
	    if ($panic) {
		unlink "keep_$audio_out";
		unlink "$pidfile"; 
		exit 1;
	    }
	    smog_rename("keep_$audio_out","$audio_out");
	}

	# cat audio audio.new -> audiodump
	if ($^O eq "MSWin32") {
	    $smerr=smog_system("cat.exe \"$curtmpdir/audio\" \"$audio_out\" > \"$curtmpdir/audiodump\"");
	}
	else {
	    $smerr=smog_system("/bin/cat \"$curtmpdir/audio\" \"$audio_out\" > \"$curtmpdir/audiodump\"");
	}

	if ($smerr) {
	    sig_system_error("Creating \"$curtmpdir\audiodump\"",$smerr);
	    unlink "$pidfile"; 
	    exit 1;
	}

	unlink "$audio_out"; #audio.new
	$fsize=-s "$curtmpdir/audiodump";
	&sig_complete($fsize);
	exit 0;
    }



    if ($command eq "trim_audio") {
	$audio_start=$ARGV[2];
	$audio_end=$ARGV[3];

	$arate=$ARGV[4];
	$achans=$ARGV[5];
	$asamps=$ARGV[6];
	$asigned=$ARGV[7];
	$aendian=$ARGV[8];

	smog_chdir("$curtmpdir");

	unless (&rc_get("conserve_space") eq "true") {
	    &backup_audio; #audio -> audio.bak
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	}

	#trim audio to selection
	
	# $audio_from is the new clip ("audioclip")
	$audio_from=&clip_audio($audio_start,$audio_end);
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}
	
	$align=$achans*$asamps/8;
	$acsize=-s $audio_from;
	$acsize/=$align*$arate;

	unlink "$audio_in"; # audio_in is "audio"
	
	$where=$audio_start;
	$end=$audio_end-$audio_start;
	$start=0;
	
	if ($end>$acsize) {
	    #the part we cliṕped may have been *shorter* than the selection
	    # need to check for this now, as we are more careful with reads and writes
	    $end=$acsize;
	}

	# insert_audio will insert silence at start, and should recreate "audio" from "audioclip" ($audio_from)

	&insert_audio($asamps,$achans,$asigned,$aendian,1);
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	    $panic=0;
	}

	#append silence to pad to end
	
	&append_silence(0,&align($audio_end*$arate*$align),$asamps,$achans,$asigned,$aendian);
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	    $panic=0;
	}

	&sig_complete;
	exit 0;
    }


    if ($command eq "delete_audio") {
	$start=$ARGV[2];
	$end=$ARGV[3];
	$arate=$ARGV[4];
	$achans=$ARGV[5];
	$asamps=$ARGV[6];

	&cut_audio;
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}

	&sig_complete;
	exit 0;
    }

    if ($command eq "resample_audio") {
	$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];
	$audio_in="$curtmpdir/audio.bak";
	$audio_out="$curtmpdir/audio";

	if (defined($ARGV[12])) {
	    $stretch=$ARGV[12];
	    $audio_in="$curtmpdir/audio.orig";
	}

	unlink "$audio_in";
	smog_rename( "$audio_out","$audio_in"); # aborts on failuer

	smog_system_sync();

	&resample_audio;
	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}

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

	&sig_complete;
	exit 0;
    }



    if ($command eq "get_window_id") {
	smog_system("xwininfo > \"$curtmpdir/tmpinfo\"");

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

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

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

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


	smog_system("grep \"Visual:\" \"$curtmpdir/tmpinfo\" > \"$curtmpdir/tmpinfo2\"");
	if (defined(open IN,"< $curtmpdir/tmpinfo2")) {
	    read IN,$visual,128;
	    close IN;
	}
	@visuals=split(/Visual: /,$visual);
	$visual=$visuals[1];
	chomp($visual);

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

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


    if ($command eq "fill_and_redo_frames") {
	# remove any gaps in the play images
	unlink "$curtmpdir/pause";
	$end=$ARGV[2];
	$width=$ARGV[3];
	$height=$ARGV[4];
	$img_ext=".".$ARGV[5];
	$img_prefix=&get_img_prefix($img_ext);
	$has_audio=0;
	$fps=$ARGV[6];
	$arate=$ARGV[7];
	$achans=$ARGV[8];
	$asamps=$ARGV[9];
	$asigned=$ARGV[10];
	$aendian=$ARGV[11];

	if ($achans>0) {
	    $has_audio=1;
	}

	&fill_and_redo_frames;
	
	if ($panic) {
	    unlink $pidfile;
	    exit 1;
	}

	if ($has_audio) {
	    $aud_end=($end/$fps)*$achans*$arate*($asamps/8);
	    $audio_in="$curtmpdir/audio";
	    $audsize=-s $audio_in;
	    if ($audsize > $aud_end) {
		$audio_out=&clip_audio(0.,$end/$fps);
		if ($panic) {
		    unlink "$pidfile"; 
		    exit 1;
		}
		unlink "$audio_in";
		smog_rename( "$audio_out","$audio_in");
	    }
	    elsif ($audsize<$aud_end) {
		$audio_out="$curtmpdir/audio.bak";
		smog_rename( "$audio_in","$audio_out");
		&append_silence(0,$aud_end-$audsize,$asamps,$achans,$asigned,$aendian,$audio_in);
		if ($panic) {
		    unlink "$pidfile"; 
		    exit 1;
		}
		if ($^O eq "MSWin32") {
		    smog_system("cat.exe \"$audio_out\">>\"$audio_in\"");
		}
		else {
		    smog_system("/bin/cat \"$audio_out\">>\"$audio_in\"");
		}
		unlink "$audio_out";
	    }
	}

	&sig_complete;
	exit 0;
    }


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

	my $gotit=0;

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

	my $f_size=-s $audio_in;
	if ($f_size>0) {
	    # wav format
	    unless (&rc_get("conserve_space") eq "true") {
		if (-f $audio_out) {
		    $smres=smog_rename( "$audio_out","$audio_bak");
		}
		else {
		    if ($^O eq "MSWin32") {
			$smres=smog_system("touch.exe \"$audio_bak\"");
		    }
		    else {
			$smres=smog_system("touch \"$audio_bak\"");
		    }
		    if ($smres) {
			sig_write_error("$audio_bak"); 
			unlink "$pidfile"; 
			exit 1;
		    }
		}
	    }
	    $is_audio=TRUE;
	    &get_file_info;
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	    &convert_audio_to_raw;
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	}
	else {
	    $audio_in="audiodump";
	    if (! -f $audio_in) {
		$audio_in="audiodump.pcm";
	    }
	    else {
		$gotit=1;
	    }
	    if ($gotit || -f $audio_in) {
		# raw format
		unless (&rc_get("conserve_space") eq "true") {
		    if (-f $audio_out) {
			$smres=smog_rename( "$audio_out","$audio_bak"); #aborts on failure
		    }
		    else {
			if ($^O eq "MSWin32") {
			    $smres=smog_system("touch.exe \"$audio_bak\"");
			}
			else {
			    $smres=smog_system("touch \"$audio_bak\"");
			}
			if ($smres) {
			    sig_write_error("$audio_bak"); 
			    unlink "$pidfile"; 
			    exit 1;
			}
		    }
		}
		smog_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.");
	    }
	}
	unlink glob "audiodump.*";

	$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/audiodump.wav
	# plus any audio.new files (e.g. from append audio)

	smog_chdir("$curtmpdir");
	unlink glob "audiodump* audio.new";

	&sig_complete;
	exit 0;
    }


    if ($command eq "download_clip") {
	my $url=$ARGV[2];
	my $dfile=$ARGV[3];

	if ($url =~ /youtube/ || $url =~ /youtu\.be/) {
	    # youtube-dl

	    if (-f $dfile) {
		unlink "$dfile";
	    }

	    if (-f $dfile) {
		&sig_error;
		exit 2;
	    }

	    $com="youtube-dl -o \"$dfile\" -f 43 -q --no-part \"$url\"";

	    &sig_progress(1);

	    $res=smog_system("$com");

	    if ($res || !(-f $dfile) || -z $dfile) {

		if (-f $dfile) {
		    unlink "$dfile";
		}

		sig_system_error("$com",$res);
		unlink "$pidfile";
		exit 1;
	    }
	}

	&sig_complete;
	exit 0;
    }



    if ($command eq "cdopen") {

	$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";
	}

	$com="\"$cdda2wav_command\" -q -x -D \"$cdplay_device\" -t $track \"$audiofile\"| >$nulfile 2>&1";
	$smerr=smog_system($com);
	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    unlink "$pidfile"; 
	    exit 1;
	}
	$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") {
	# TODO - allow front end to specify rate/channels, etc.

	$file=$ARGV[2];

	smog_chdir("$curtmpdir");

	$audio_in="audiodump.wav";

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


	if ($panic) {
	    unlink "$pidfile"; 
	    exit 1;
	}

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

    if ($command eq "insert") {
	# with_audio: 0, no audio, 1 video and audio, 2 ONLY audio
	# for 0 and 1, start, end, where are in frames; for 2, start,end,where are in seconds
	# with -ve arate means insert silence [with_audio should be 1 or 2]
	# with -ve times means undo cut/delete [source is same clip dir]

	unlink "$curtmpdir/pause";
	$img_ext=".".$ARGV[2];
	$img_prefix=&get_img_prefix($img_ext);
	$where=$ARGV[3];
	$start=$ARGV[4];
	$end=$ARGV[5];
	$from_handle=$ARGV[6];
	$with_audio=$ARGV[7];
	$num_frames=$ARGV[8];

	$width=$ARGV[9];
	$height=$ARGV[10];
	$times=1;
	$undo_cut=0;

        $new_frames=$num_frames;

	smog_chdir("$curtmpdir");

	if (defined ($ARGV[17])) {
	    $times=$ARGV[17];
	    if ($times<0) {
		# this indicates undoing a previous cut
		$times=-$times;
		$undo_cut=1;
		$new_frames=$num_frames+$end-$start+1;
	    }
	    else {
		if ($start>=0) {
		    &clean_old;
		}
		else {
		    # $start < 0 tells us to leave backups alone
		    $start=-$start;
		}
		$antialias=&rc_get("antialias");
	    }
	}

	if ($with_audio<2) {
	    if (!$undo_cut) {
		$img_ext2=&get_img_ext("$tmpdir/$from_handle",$start);
		$img_prefix2=&get_img_prefix($img_ext2);
	    }

	    &insert;
	    if ($panic) {
		unlink "$pidfile"; 
		exit 1;
	    }
	}

	if ($with_audio) {
	    $fps=$ARGV[11];
	    $arate=$ARGV[12];
	    $achans=$ARGV[13];
	    $asamps=$ARGV[14];
	    $asigned=$ARGV[15];
	    $aendian=$ARGV[16];

	    if ($arate*$achans*$asamps!=0) {
		if (!$undo_cut) {
		    if ($with_audio<2) {
			$end/=$fps;
			$start=($start-1.)/$fps;
			$where/=$fps;
		    }
		    if (! -f "audio") {
			unlink "audio.bak";
		    }
		    else {
			smog_copy("audio","audio.bak");
		    }
		    if ($panic) {
			unlink "$pidfile"; 
			exit 1;
		    }
		    smog_system_sync();
		}
		else {
		    $end=-s "$curtmpdir/audio.bak";
		    $end/=$arate*$achans*$asamps/8;
		    $start=0.;
		    if ($with_audio<2) {
			$where/=$fps;
		    }
		}
		&insert_audio($asamps,$achans,$asigned,$aendian,$undo_cut);
		if (&rc_get("conserve_space") eq "true") {
		    unlink "audio.bak";
		}
		if ($panic) {
		    unlink "$pidfile"; 
		    exit 1;
		}
	    }
	}

	if ($undo_cut) {
	    &clean_old;
	}

	&sig_complete;
	exit 0;
    }

	if ($command eq "undo_insert") {
	    #clean up after an interrupted insert
	    $start=$ARGV[2];
	    $end=$ARGV[3];
	    $frames=$ARGV[4];
	    $img_ext=".".$ARGV[5];

	    # move extra frames back to end
	    smog_system_sync();
	    &undo_insert;

	    $end=$frames;
	    &undo(1);  # will abort on failure
	    &undo_audio;  # will abort on failure
	    &sig_complete;
	    exit 0;
	}


    # effects

	if (substr($command,0,10) eq "pfxrender_") {
	    #fx preview
	    $fx_prev=1;
	    $command=substr($command,1);
	    $out_ext=".pre";
	}

	if (substr($command,0,9) eq "fxrender_") {

	    smog_chdir("$curtmpdir");
	    unlink "$curtmpdir/pause";
	    $command=substr($command,9);
	    $status=$ARGV[2];
	    $start=$ARGV[3];
	    $end=$ARGV[4];
	    $nwidth=$width=$ARGV[5];
	    $nheight=$height=$ARGV[6];

	    $img_ext=".".$ARGV[7];
	    $img_prefix=&get_img_prefix($img_ext);
	    splice(@ARGV,7,1);
	    

	    $convert_command=$smog_convert_command;
	    $composite_command=$smog_composite_command;

	    # call render plugins in plugins/effects/rendered/$command

	    unless (defined($fx_prev)) {
		$out_ext=".mgk";
		$out_prefix=$img_prefix;
		&clean_old;
	    }
	    if ($status==0) {
		$plugin_name="$command";
	    }
	    elsif ($status==1) {
		$plugin_dir="$lives_home_dir/plugins/effects/rendered/custom";
		$plugin_name="$plugin_dir/$command";
	    }
	    else {
		$plugin_dir="$lives_home_dir/plugins/effects/rendered/test";
		$plugin_name="$plugin_dir/$command";
	    }
	    if ($^O eq "MSWin32") {
		# adjust depending on file ext
		my $ext=&get_ext("$plugin_name");
		if ($ext eq ".py") {
		    $cmd="python";
		}
		else {
		    $cmd="perl";
		}
		$smcom="$cmd \"$plugin_name\" get_description";
	    }
	    else {
		$smcom="\"$plugin_name\" get_description";
	    }
	    $fx_desc=smog_system_direct($smcom);
	    if ($?) {
		sig_system_error($smcom,$?);
		unlink $pidfile;
		exit 1;
	    }
	    $num_in_channels=(split(/\|/,$fx_desc))[3];
	    
	    if ($num_in_channels==0) {
		# generators get the out_extension
		$out_ext=$img_ext;
		$out_prefix=&get_img_prefix($img_ext);
	    }

	    if ($num_in_channels==2) {
		# transitions get some extra params
		$img_ext2=".".$ARGV[7];
		$img_prefix2=&get_img_prefix($img_ext2);
		splice(@ARGV,7,1);
		$start2=$ARGV[7];
		$clipboard=$ARGV[8];
	    }

	    $fps=0;

	    if ($^O eq "MSWin32") {
		# adjust depending on file ext
		my $ext=&get_ext("$plugin_name");
		if ($ext eq ".py") {
		    $cmd="python";
		}
		else {
		    $cmd="perl";
		}
		$fx_caps=smog_system_direct("$cmd \"$plugin_name\" get_capabilities");
	    }
	    else {
		$fx_caps=smog_system_direct("\"$plugin_name\" get_capabilities");
	    }
	    if ($?) {
		sig_system_error($smcom,$?);
		unlink $pidfile;
		exit 1;
	    }
	    if ($fx_caps&0x8000) {
		# is autogenerated
		# we are so nice :-), we set: 
		# $handle,$curtmpdir,$img_ext,$start,$end,$width,$height
		# and for transitions $where, $chandle
		# remove fxrender $handle $status start end width height (start2 clipboard)
		shift; shift; shift; shift; shift; shift;
		if ($num_in_channels==2) {
		    shift; shift;
		}

		if ($^O eq "MSWin32") {
		    push @INC,"$plugin_dir";
		}

		$ARGV[0]="process";
		if ($status>1) {
		    $error="";
		    if ($^O eq "MSWin32") {
			unless (eval "require (\"$command\")") {
			    $error=$@;
			}
		    }
		    else {
			unless (eval "require (\"$plugin_name\")") {
			    $error=$@;
			}
		    }
		    unless ($error eq "") {
			&sig_error("$plugin_name failed:","$error");
		    }
		}
		else {
		    if ($^O eq "MSWin32") {
			require("$command");
		    }
		    else {
			require("$plugin_name");
		    }
		}
		if ($num_in_channels>0) {
		    unless (defined($fx_prev)) {
			&mv_mgk;
			if ($panic) {
			    unlink "$pidfile"; 
			    exit 1;
			}
		    }
		}
		else {
		    $frames=&count_frames;
		}
		&sig_complete($nwidth,$nheight,$fps,$frames);
		exit 0;
	    }
	    else {
		# other (non-perl) language
		shift; shift; shift; #remove "fxrender_.." and $handle $status
		my ($res_file)="$curtmpdir/.rfx_result";

		my $err;

		#img_ext is also in ARGV, but never mind

		if ($num_in_channels==2) {
		    if ($^O eq "MSWin32") {
			# adjust depending on file ext
			my $ext=&get_ext("$plugin_name");
			if ($ext eq ".py") {
			    $cmd="python";
			}
			else {
			    $cmd="perl";
			}
			$err=smog_system("$cmd \"$plugin_name\" process \"$curtmpdir\" $img_ext $img_ext2 $out_ext @ARGV > \"$res_file\"");
		    }
		    else {
			$err=smog_system("\"$plugin_name\" process \"$curtmpdir\" $img_ext $img_ext2 $out_ext @ARGV > \"$res_file\"");
		    }
		}
		elsif ($num_in_channels==1) {
		    if ($^O eq "MSWin32") {
			# adjust depending on file ext
			my $ext=&get_ext("$plugin_name");
			if ($ext eq ".py") {
			    $cmd="python";
			}
			else {
			    $cmd="perl";
			}
			$err=smog_system("$cmd \"$plugin_name\" process \"$curtmpdir\" $img_ext $out_ext @ARGV > \"$res_file\"");
		    }
		    else {
			$err=smog_system("\"$plugin_name\" process \"$curtmpdir\" $img_ext $out_ext @ARGV > \"$res_file\"");
		    }
		}
		else {
		    if ($^O eq "MSWin32") {
			# adjust depending on file ext
			my $ext=&get_ext("$plugin_name");
			if ($ext eq ".py") {
			    $cmd="python";
			}
			else {
			    $cmd="perl";
			}
			$err=smog_system("$cmd \"$plugin_name\" process \"$curtmpdir\" $out_ext @ARGV > \"$res_file\"");
		    }
		    else {
			$err=smog_system("\"$plugin_name\" process \"$curtmpdir\" $out_ext @ARGV > \"$res_file\"");
		    }
		}
		if (!$err) {
		    if (defined(open IN,"< $res_file")) {
			read IN,$size,128;
			($nwidth,$nheight)=split(" ",$size);
			close IN;
			unlink "$res_file";
		    }
		}
		else {
		    my (@err)="";
		    if (defined(open IN,"< $res_file")) {
			read IN,$errt,512;
			@err=split("\n",$errt);
			close IN;
			unlink "$res_file";
		    }
		    &sig_error(@err);
		    exit 1;
		}
		unless (defined($fx_prev)) {
		    &mv_mgk;
		    if ($panic) {
			unlink "$pidfile"; 
			exit 1;
		    }
		}
		&sig_complete($nwidth,$nheight);
		exit 0;
	    }
	}


	if ($command eq "import_project") {
	    $proj_file=$ARGV[2];
	    smog_chdir("$tmpdir");
	    umask 0;
	    $com="tar --no-same-owner -xzf \"$proj_file\"";
	    $smerr=smog_system($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    }
	    &sig_complete;
	    exit 0;
	}

	if ($command eq "export_project") {
	    my $set=$ARGV[2];
	    my $proj_file=$ARGV[3];
	    smog_chdir("$tmpdir");
	    $com="tar --exclude=*.bak --exclude=.pid* --exclude .status* --exclude=lock.* --exclude=*.mgk --exclude=*.tmp --exclude=audioclip* -czf \"$proj_file\" $set";
	    $smerr=smog_system($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		unlink "$pidfile"; 
		exit 1;
	    };


	    &sig_complete;
	    exit 0;
	}

	if ($command eq "bg_weed") {
	    my $opts=$ARGV[2];
	    my $bytes_cleaned=&weed($opts);
	    &sig_complete($bytes_cleaned);
	    exit 0;
	}

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






######################################################
    
#subroutines


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

sub mkname {
    my ($num)=shift;
    $ret=sprintf("%08d",$num);
    $ret;
}

sub check_for_pause {
    if (-f "$curtmpdir/pause") {
	smog_system_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 kill_child_pids {
    my ($target_pid,$sig)=@_;
    my $pid;
    smog_system("pgrep -P $target_pid > \"$tmpdir/.pids.$target_pid\" 2>/dev/null");

    if ($sig eq "KILL") {
	smog_system("kill -$sig $target_pid >/dev/null 2>&1");
    }
    else {
	smog_system("pkill -$sig -P $target_pid");# must not ! >/dev/null 2>&1");
    }

    if (-s "$tmpdir/.pids.$target_pid"&&defined(open IN,"$tmpdir/.pids.$target_pid")) {
	while (<IN>) {
	    $pid=$_;
	    chomp($pid);
	    &kill_child_pids($pid,$sig);
	}
	close IN;
    }

    unlink "$tmpdir/.pids.$target_pid";
}

sub is_writeable {
    # see if is writable or creatable
    # but do not actually create the file
    my $file=shift;
    if (! -s $nfile) {
	unlink "$nfile";
    }
    $exists=(-f "$nfile");
    if (!$exists) {
	umask 0;
	if ($^O eq "MSWin32") {
	    $ret=smog_system("touch.exe \"$nfile\"");
	}
	else {
	    $ret=smog_system("touch \"$nfile\"");
	}
	if ($ret) {return !$ret;}
	if ($^O eq "MSWin32") {
	    smog_system("chmod.exe o+w \"$nfile\"");
	}
	else {
	    smog_system("/bin/chmod o+w \"$nfile\"");
	}
	umask $umask;
    }
    smog_system_sync();
    $ret=(-w "$nfile");
    if (!$exists) {
	unlink "$nfile";
    }
    return $ret;
}



sub get_img_ext {
    my $ckdir=shift;
    my $imgno=shift;

    if ($imgno eq "") {
	$imgno=1;
    }

    my $tfile=&mkname($imgno);

    if (-f "$ckdir/$tfile.png") {
	return ".png";
    }

    unless(-f "$ckdir/$tfile.jpg") {
	print STDERR "Warning ! command $command needs to set image type !! Please report this as a bug.\n";
	print STDERR "details: $ckdir/$tfile.jpg not found\n";
    }

    return ".jpg";
}
    

sub get_img_prefix {
    my $imgext=shift;

    if ($imgext eq ".png") {
	return "PNG32:";
    }

    return "";

}



sub try_to_recover {
    #something failed badly during processing
    # attempt to recover clip from backup images

    my ($smstart,$smend)=@_;
    if ($DEBUG_SMOGRIFY) {
	print STDERR "Beginning clip recovery process for $curtmpdir\n";
    }
    for ($i=$smstart;$i<=$smend;$i++) {
	$name&mkname($i);
	smog_rename("$name.bak","$name$img_ext");
    }
    if ($DEBUG_SMOGRIFY) {
	print STDERR "Clip recovery process succeeded for $curtmpdir\n";
    }
}







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)=shift;
    my ($i,$name);

    unless (!defined($pidfile)||$pidfile eq "") {
	# don't want to get killed in this stage...
	unlink "$pidfile";
    }


    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	if (-f "$curtmpdir/$name.mgk") {
	    if (-f "$curtmpdir/$name$img_ext") {
		if ($leave_bak==1&&-f "$curtmpdir/$name.bak") {
		    unlink "$curtmpdir/$name$img_ext";
		}
		else {
		    $smres=rename "$curtmpdir/$name$img_ext","$curtmpdir/$name.bak";
		    if (!$smres) {
			try_to_recover($start,$i-1);
			if ($DEBUG_SMOGRIFY) {
			    print STDERR "smogrify debug - rename failed ($!): \"$curtmpdir/$name$img_ext\",\"$curtmpdir/$name.bak\"\n";
			}
			sig_system_error("Renaming \"$curtmpdir/$name$img_ext\" to \"$curtmpdir/$name.bak\"");
			unlink $pidfile;
			exit 1;
		    }
		}
	    }
	    $smerr=rename( "$curtmpdir/$name.mgk","$curtmpdir/$name$img_ext");
	    if (!$smerr) {
		try_to_recover($start,$i);
		if ($DEBUG_SMOGRIFY) {
		    print STDERR "smogrify debug - rename failed ($!): \"$curtmpdir/$name.mgk\",\"$curtmpdir/$name$img_ext\"\n";
		}
		sig_system_error("Renaming \"$curtmpdir/$name.mgk\" to \"$curtmpdir/$name$img_ext\"");
		unlink $pidfile;
		exit 1;
	    }
	}
	if ($option==1) {
	    &sig_progress($i);
	}
    }
    
    &sig_pid();
    smog_system_sync();
}



sub mv_pre {
    # new files are .pre files, back up old files to .bak
    my ($i,$name);

    unless (!defined($pidfile)||$pidfile eq "") {
	# don't want to get killed in this stage...
	unlink "$pidfile";
    }

    smog_chdir("$curtmpdir");
    unlink glob "*.mgk";

    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	if (-f "$curtmpdir/$name.pre") {
	    if (-f "$curtmpdir/$name$img_ext") {
		unlink "$curtmpdir/$name.bak";
		$smerr=rename( "$curtmpdir/$name$img_ext","$curtmpdir/$name.bak");
		if (!$smerr) {
		    try_to_recover($start,$i-1);
		    if ($DEBUG_SMOGRIFY) {
			print STDERR "smogrify debug - rename failed ($!): \"$curtmpdir/$name$img_ext\",\"$curtmpdir/$name.bak\"\n";
		    }
		    sig_system_error("Renaming \"$curtmpdir/$name$img_ext\" to \"$curtmpdir/$name.bak\"");
		    unlink "$pidfile";
		    exit 1;
		}
	    }
	    $smerr=rename "$curtmpdir/$name.pre","$curtmpdir/$name$img_ext";
	    if (!$smerr) {
		try_to_recover($start,$i);
		if ($DEBUG_SMOGRIFY) {
		    print STDERR "smogrify debug - rename failed ($!): \"$curtmpdir/$name.pre\",\"$curtmpdir/$name$img_ext\"\n";
		}
		sig_system_error("Renaming \"$curtmpdir/$name.pre\" to \"$curtmpdir/$name$img_ext\"");
		unlink "$pidfile";
		exit 1;
	    }
	}
    }
    &sig_pid();
    smog_system_sync();
}


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

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


sub undo_audio {
    if (-f "$curtmpdir/audio.orig") {
	unlink "$curtmpdir/audio";
	smog_rename( "$curtmpdir/audio.orig","$curtmpdir/audio");
	unlink "$curtmpdir/audio.bak";
    }
    else {
	unlink "$curtmpdir/audio.new";
	if (-f "$curtmpdir/audio") {
	    smog_rename( "$curtmpdir/audio","$curtmpdir/audio.new");
	}
	if (-f "$curtmpdir/audio.bak") {
	    smog_rename( "$curtmpdir/audio.bak","$curtmpdir/audio");
	}
	if (-f "$curtmpdir/audio.new") {
	    smog_rename( "$curtmpdir/audio.new","$curtmpdir/audio.bak");
	}
    }
}


sub backup_audio {
    &clean_old;
    smog_copy("$curtmpdir/audio", "$curtmpdir/audio.bak"); # may set $panic
}

sub cut {
    my ($start,$end)=@_;
    my ($i,$name);
    smog_chdir("$curtmpdir");
    $frames_cut=$end-$start+1;

    for ($i=$start;$i<=$end;$i++) {
	$name=&mkname($i);
	smog_rename( "$name$img_ext","$name.bak");
	&sig_progress($i);
    }

    for ($i=$end+1;$i<=$frames;$i++) {
	$from=&mkname($i);
	$to=&mkname($i-$frames_cut);
	smog_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);
	smog_rename( "$curtmpdir/$from$img_ext","$curtmpdir/$from.revtemp");
	smog_rename( "$curtmpdir/$to$img_ext","$curtmpdir/$from$img_ext");
	&sig_progress($start+($i-$start)*2);
	smog_rename( "$curtmpdir/$from.revtemp","$curtmpdir/$to$img_ext");
	&sig_progress($start+($i-$start)*2+1);
    }
}



sub undo_insert {
    $frames_inserted=$end-$start+1;

    for ($i=$frames+$frames_inserted;$i>$frames;$i--) {
	# move any shifted frames back
	$name=&mkname($i);
	$to=&mkname($i-$frames_inserted);
	if (-f "$curtmpdir/$name$img_ext") {
	    unlink "$curtmpdir/$to$img_ext";
	    smog_rename( "$curtmpdir/$name$img_ext","$curtmpdir/$to$img_ext");
	}
    }
}



sub smog_system_sync {
    smog_system("sync");
    smog_system("sync");
    smog_system("sync");
}




sub smog_system {
    my ($smcommand)=@_;
    my $smcomret;

    $smcomret=system($smcommand);

    if ($smcomret && $DEBUG_SMOGRIFY) {
	print STDERR "smogrify debug: $smcommand\n";
	print STDERR "smogrify debug - command failed: result was $smcomret\n";
    }

    $smcomret;

}


sub smog_system_direct {
    my ($smcommand)=@_;
    my $smcomret;

    $smcomret=`$smcommand`;

    if ($DEBUG_SMOGRIFY && $? != 0) {
	print STDERR "smogrify debug: command was $smcommand\n";
	print STDERR "smogrify debug: result was $smcomret\n";
	print STDERR "smogrify debug: error was $?\n";
    }

    $smcomret;
}



sub smog_rename {
    # CAUTION: return value of 0 is error here

   my ($smfrom,$smto)=@_;
   my $smcomret=rename "$smfrom","$smto";

   if ($smcomret==0) {
       if ($DEBUG_SMOGRIFY) {
	   print STDERR "smogrify debug - rename failed ($!): $smfrom $smto\n";
       }
       sig_system_error("Renaming \"$smfrom\" to \"$smto\"",$!);
       unlink "$pidfile";
       exit 1;
   }

   $smcomret;

}



sub smog_copy {
   my ($smfrom,$smto)=@_;
   my $smcomret;

   if ($^O eq "MSWin32") {
       $smcomret=system("cp.exe -f \"$smfrom\" \"$smto\"");
       
   }
   else {
       $smcomret=system("/bin/cp -f \"$smfrom\" \"$smto\"");
   }

   if ($smcomret!=0 && -f $smfrom) {
       if ($DEBUG_SMOGRIFY) {
	   print STDERR "smogrify debug - copy failed ($!): $smfrom $smto\n";
       }
       $panic=sig_system_error("Copying $smcomret \"$smfrom\" to \"$smto\"",$!);
   }

   $smcomret;

}


sub smog_chdir {
    # CAUTION: return value of 0 is error here

    my ($smdir)=shift;

    $smcomret=chdir "$smdir";

    if (!$smcomret) {
	if ($DEBUG_SMOGRIFY) {
	    print STDERR "smogrify debug - chdir failed ($!): $smdir\n";
	}

	sig_system_error("Changing to directory \"$smdir\"",$!);
	unlink "$pidfile";
	exit 1;
    }
}

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;

    my $quick_copy=-1;

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

    if (!defined($img_prefix2)) {
	$img_prefix2=&get_img_prefix($img_ext2);
    }
    if (!defined($img_prefix)) {
	$img_prefix=&get_img_prefix($img_ext);
    }

    $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);
	if ($i<=($new_frames-$frames_to_move)&&!$undo_cut&&-f "$curtmpdir/$from$img_ext") {
	    # this frame will get overwritten, so back it up
	    smog_copy("$curtmpdir/$from$img_ext", "$curtmpdir/$from.bak");
	    return if $panic;
	}
	smog_rename( "$curtmpdir/$from$img_ext","$curtmpdir/$to$img_ext");
	&sig_progress($new_frames-$i);
    }

    $resize_ext=$img_ext;

    if (!defined($antialias)) {
	$antialias="false";
    }


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

	    if ($undo_cut==1) {
		smog_rename( "$fromdir/$from.bak","$curtmpdir/$to$img_ext");
		return if $panic;
	    }
	    else {
		while (! -f "$fromdir/$from$img_ext2") {
		    print STDERR "waiting on image $fromdir/$from$img_ext2\n";
		    sleep 1;
		    smog_system_sync();
		}

		if ($quick_copy==-1) {
		    $quick_copy=0;
		    if ($img_ext eq $img_ext2) {
			if ($height*$width==0) {
			    $quick_copy=1;
			}
			else {
			    $imresact="none";
			    &get_image_size("$fromdir/$from$img_ext2");
			    return if $panic;

			    if ($hsize==$width&&$vsize==$height) {
				$quick_copy=1;
			    }
			}
		    }
		}

		if ($quick_copy==1) {
		    smog_copy("$fromdir/$from$img_ext2","$curtmpdir/$to$img_ext");
		    return if $panic;
		}
		else {
		    if ($antialias eq "false") {
			$com="$smog_convert_command +antialias -size $width"."x$height $img_prefix2\"$fromdir/$from$img_ext2\" -scale $width!x$height! $img_prefix\"$curtmpdir/$to$img_ext\" >$nulfile 2>&1";
			$smerr=smog_system($com);
			if ($smerr) {
			    sig_system_error("$com",$smerr);
			    $panic=1;
			    return;
			}
		    }
		    else {
			$com="$smog_convert_command -antialias -size $width"."x$height $img_prefix2\"$fromdir/$from$img_ext2\" -scale $width!x$height! $img_prefix\"$curtmpdir/$to$img_ext\" >$nulfile 2>&1";
			$smerr=smog_system($com);
			if ($smerr) {
			    sig_system_error("$com",$smerr);
			    $panic=1;
			    return;
			}
		    }
		}
		
	    }
	    
	    &sig_progress(int($new_frames-$where+$j++-$start+1));
	}

	$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,$width,$height)=@_;

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

    if (!defined($input_prefix)) {
	$input_prefix=&get_img_prefix($input_ext);
    }
    if (!defined($resize_prefix)) {
	$resize_prefix=&get_img_prefix($resize_ext);
    }

    # TODO ****  !!! provide alternate if imagemagick is not available

    if ($antialias eq "false") {
	$com="$smog_convert_command +antialias -size $width"."x$height $input_prefix\"$name$input_ext\" -scale $width!x$height! $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    }
    else {
	$com="$smog_convert_command -size $width"."x$height $input_prefix\"$name$input_ext\" -resize $width!x$height! $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    }
    $smerr=smog_system($com);
    if ($smerr) {
	sig_system_error("$com",$smerr);
	$panic=1;
    }
}



sub letterbox_frame {
    my ($name,$iwidth,$iheight,$owidth,$oheight)=@_;

# frame is at size $iwidth x $iheight (if known, or 0)
# from name is $name$input_ext, to name is $name$output_ext
# center overlay frame in black rect size $owidth x $oheight

    # TODO ****  !!! provide alternate if imagemagick is not available

    my $from="$name$in_ext";
    my $to="$name$out_ext";

    # overlay frame
    $xstart=int(($owidth-$iwidth)/2);
    $ystart=int(($oheight-$iheight)/2);
    
    #must unlink first in case $to is a symlink (when saving selection)
    unlink "$to";

    $com="$smog_composite_command -compose plus -dissolve 100 -geometry $iwidth!x$iheight!+$xstart!+$ystart! \"$from\" blank.jpg \"$to\" >$nulfile 2>&1";
    $smerr=smog_system($com);
    if ($smerr) {
	sig_system_error("$com",$smerr);
	$panic=1; 
	return;
    }

}



sub zoom_frame {
    my ($name,$centre_x,$centre_y,$width,$height,$rscale)=@_;
    # zoom into a frame

    # first we will crop the frame to just slightly larger than
    # our target, then we will resize, then do an exact crop
    
    # TODO - if we expand an edge a lot, our final crop needs
    # adjusting

    if (!defined($antialias)) {
	$antialias="false";
    }
    if (!defined($input_ext)) {
	$input_ext=$img_ext;
    }
    
    if (!defined($input_prefix)) {
	$input_prefix=&get_img_prefix($input_ext);
    }
    if (!defined($resize_prefix)) {
	$resize_prefix=&get_img_prefix($resize_ext);
    }

    my ($left)=int($centre_x-($width/(2.*$rscale)))-1;
    my ($top)=int($centre_y-($height/(2.*$rscale)))-1;
    my ($right)=int($centre_x+($width/(2.*$rscale)))+1;
    my ($bottom)=int($centre_y+($height/(2.*$rscale)))+1;
    
    if ($left<0) {
	$right-=$left;
	$left=0;
    }
    if ($top<0) {
	$bottom-=$top;
	$top=0;
    }
    if ($right>=$width) {
	$left-=$right-$width+1;
	if($left<0) {
	    $left=0;
	}
	$right=$width-1;
    }
    if ($bottom>=$height) {
	$top-=$bottom-$height+1;
	if($top<0) {
	    $top=0;
	}
	$bottom=$height-1;
    }
    my ($nwidth)=$right-$left+1;
    my ($nheight)=$bottom-$top+1;
    
    if ($antialias eq "false") {
	$com="$smog_convert_command -antialias -size $width"."x$height $input_prefix\"$name$input_ext\" -crop $nwidth!x$nheight!+$left!+$top! -resize $width!x$height! $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    }
    else {
	$com="$smog_convert_command +antialias -size $width"."x$height $input_prefix\"$name$input_ext\" -crop $nwidth!x$nheight!+$left!+$top! -resize $width!x$height! $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    }

    $smerror=smog_system($com);
    
    if ($smerror) {
	sig_system_error("$com",$smerror);
	$panic=1;
    }

}



sub trim_frame {
    my ($name,$width,$height,$x,$y,$nwidth,$nheight)=@_;

    if (!defined($input_ext)) {
	$input_ext=$img_ext;
    }
    if (!defined($input_prefix)) {
	$input_prefix=&get_img_prefix($input_ext);
    }
    if (!defined($resize_prefix)) {
	$resize_prefix=&get_img_prefix($resize_ext);
    }
    if (!defined($antialias)) {
	$antialias="false";
    }
    if ($antialias eq "false") {
	$com="$smog_convert_command +antialias -size $width"."x$height $input_prefix\"$name$input_ext\" -crop $nwidth!x$nheight!+$x+$y $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    }
    else {
	$com="$smog_convert_command -antialias -size $width"."x$height $input_prefix\"$name$input_ext\" -crop $nwidth!x$nheight!+$x+$y $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    }

    $smerror=smog_system($com);
    
    if ($smerror) {
	sig_system_error("$com",$smerror);
	$panic=1;
    }


}


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

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

    $imresact="none";
    &get_image_size("$name$input_ext");
    return if ($panic||$hsize==-1);

    if (!defined($input_prefix)) {
	$input_prefix=&get_img_prefix($input_ext);
    }

    if (!defined($resize_prefix)) {
	$resize_prefix=&get_img_prefix($resize_ext);
    }

    unless ($hsize>=$width&&$vsize>=$height) {
	#composite over a large enough frame
	$size=$hsize;
	if ($vsize>$size) {
	    $size=$vsize;
	}

	$xstart=int(($size-$hsize)/2);
	$ystart=int(($size-$vsize)/2);

	# make a background frame
	if (!defined($bgcolour)) {
	    $bgcolour="#000000";
	}

	$com="$smog_convert_command -size $size!x$size! xc:$bgcolour $input_prefix"."blank$input_ext >$nulfile 2>&1";

	$smerror=smog_system($com);
	
	if ($smerror) {
	    sig_system_error("$com",$smerror);
	    $panic=1;
	    return;
	}

	$com="$smog_composite_command -compose plus -dissolve 100 -geometry $hsize!x$vsize!+$xstart!+$ystart! $input_prefix\"$name$input_ext\" $input_prefix"."blank$input_ext $input_prefix\"$name$input_ext\" >$nulfile 2>&1";

	$smerror=smog_system($com);
	
	if ($smerror) {
	    sig_system_error("$com",$smerror);
	    $panic=1;
	    return;
	}

	$hsize=$vsize=$size;
    }

    $x=int(($hsize-$width)/2);
    $y=int(($vsize-$height)/2);

    $com="$smog_convert_command $input_prefix\"$name$input_ext\" -crop $width" . "!x" . $height . "!+" . $x . "+" . $y . " $resize_prefix\"$name$resize_ext\" >$nulfile 2>&1";
    
    $smerror=smog_system($com);
    
    if ($smerror) {
	sig_system_error("$com",$smerror);
	$panic=1;
	return;
    }

}



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 {
    $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;
    $asamps=0;

    $endian="";

    if ($^O ne "MSWin32") {
	# try to force language to English
	$mplay_command="LANGUAGE=en LANG=en ";
    }

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

    if ($mplay_command eq "") {
	$mplay_command="\"".&location("mplayer")."\"";
    }

    if ($mplay_command eq "") {
	$panic=1;
	return;
    }

    $id_vid_form="";

    if ($is_audio) {
	$type="Audio";
    }

    smog_chdir("$curtmpdir");

    # if mplayer supports the -identify command, use that
    # the format changed for 1.0pre1 so now we use -vo null -ao null -frames 0

    unless ($mplay_command eq "") {
	if (defined($is_remote)&&($is_remote>0)) {
	    # remote files might be streams, so we need to cache a bit before we can identify them
	    $com="$mplay_command -identify -vo null -ao null -frames 0 -cache 32 \"$file\" > $file_ident 2>$nulfile <$nulfile";
	    
	}
	else {
	    $com="$mplay_command -identify -vo null -ao null -frames 0 \"$file\" > $file_ident 2>$nulfile <$nulfile";
	}

	$smerr=smog_system($com);

	if ($smerr) {
	    sig_system_error("$com",$smerr);
	    $panic=1;
	    return;
	}



	
	if (-f $file_ident) {

	    $length=smog_system_direct("grep ID_LENGTH \"$file_ident\" 2>$nulfile");
	    $length=(split("=",(split("\n",$length))[0]))[1];
	    chomp($length);

	    if ($length eq "0" &&!(defined($is_remote)&&($is_remote>0))) {
		$com="$mplay_command -demuxer lavf -identify -vo null -ao null -frames 0 \"$file\" > $file_ident 2>$nulfile <$nulfile";

		$smerr=smog_system($com);
		
		if ($smerr) {
		    sig_system_error("$com",$smerr);
		    $panic=1;
		    return;
		}



	    }

	}


	if (-f $file_ident) {
	$id_vid_form=smog_system_direct("grep ID_VIDEO_CODEC \"$file_ident\" 2>$nulfile");
        $id_vid_form=(split("=",(split("\n",$id_vid_form))[0]))[1];
        chomp($id_vid_form);

	if ($id_vid_form eq "") {
	    $id_vid_form=smog_system_direct("grep ID_VIDEO_FORMAT \"$file_ident\" 2>$nulfile");
	    $id_vid_form=(split("=",(split("\n",$id_vid_form))[0]))[1];
	    chomp($id_vid_form);
	}

	if ($id_vid_form eq "") {
	    $id_vid_form=smog_system_direct("grep VIDEO: \"$file_ident\" 2>$nulfile");
	    $id_vid_form=(split("  ",(split("\n",$id_vid_form))[0]))[1];
	    chomp($id_vid_form);
	}
	if (! -f $file_ident) {return;};

	$id_aud_form=smog_system_direct("grep ID_AUDIO_FORMAT \"$file_ident\" 2>$nulfile");
	$id_aud_form=(split("=",(split("\n",$id_aud_form))[0]))[1];
	chomp($id_aud_form);

	if ($id_aud_form eq "") {
	    $id_aud_form=smog_system_direct("grep AUDIO: \"$file_ident\" 2>$nulfile");
	    $id_aud_form=(split("  ",(split("\n",$id_aud_form))[0]))[1];
	    chomp($id_aud_form);
	}

	if (! -f $file_ident) {return;};

	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;
	    if (! -f $file_ident) {return;};
	    $asamps=smog_system_direct("grep AUDIO: \"$file_ident\" 2>$nulfile");
	    $asamps=(split(" ",$asamps))[5];
	    chomp($asamps);

	    if ($asamps =~ /^f/) {
		$asamps=32;
		$signed=1;
	    }
	    elsif ($asamps =~ /^s/) {
		$asamps=substr($asamps,1,-1);
		$signed=1;
	    }
	    elsif ($asamps =~ /^u/) {
		$asamps=substr($asamps,1,-1);
		$signed=0;
	    }

	    $audend=substr($asamps,-2,2);

	    if ($audend eq "le") {
		# le
		$endian=1; 
	    }
	    elsif ($audend eq "be") {
		$endian=0;
	    }

	    if (! -f $file_ident) {return;};
	    $bpp=smog_system_direct("grep VIDEO: \"$file_ident\" 2>$nulfile");
	    $bpp=(split("bpp",(split("  ",$bpp))[3]))[0];
	    chomp($bpp);

	    if (! -f $file_ident) {return;};
	    if ($bpp eq "") {
		$bpp=smog_system_direct("grep \"Image size:\" \"$file_ident\" 2>$nulfile");
		@tmp=split(" ",$bpp);
		$bpp=substr($tmp[5],1,2);
	    }

	    if (! -f $file_ident) {return;};
	    $hsize=smog_system_direct("grep ID_VIDEO_WIDTH \"$file_ident\" 2>$nulfile");
	    $hsize=(split("=",(split("\n",$hsize))[0]))[1];
	    chomp($hsize);
	    
	    if (! -f $file_ident) {return;};
	    $vsize=smog_system_direct("grep ID_VIDEO_HEIGHT \"$file_ident\" 2>$nulfile");
	    $vsize=(split("=",(split("\n",$vsize))[0]))[1];
	    chomp($vsize);
	    
	    if (! -f $file_ident) {return;};
	    $fps=smog_system_direct("grep ID_VIDEO_FPS \"$file_ident\" 2>$nulfile");
	    $fps=(split("=",(split("\n",$fps))[0]))[1];
	    chomp($fps);

	    if (! -f $file_ident) {return;};
	    $arate=smog_system_direct("grep ID_AUDIO_RATE \"$file_ident\" 2>$nulfile");
	    my @results=split("\n",$arate);
	    foreach my $val (@results) {
		$arate=(split("=",$val))[1];
		chomp($arate);
		last if $arate>0;
	    }
	    if (! -f $file_ident) {return;};
	    if ($arate==0) {
		$arate=smog_system_direct("grep Samplerate \"$file_ident\" 2>$nulfile");
		$arate=(split(": ",(split("\n",$arate))[0]))[1];
		chomp($arate);
	    }

	    
	    if (! -f $file_ident) {return;};
	    $achans=smog_system_direct("grep ID_AUDIO_NCH \"$file_ident\" 2>$nulfile");
	    chomp($achans);
	    $achans=(split("\n",$achans))[-1];
	    $achans=(split("=",(split("\n",$achans))[0]))[1];

	    if (! -f $file_ident) {return;};
	    $abitrate=smog_system_direct("grep ID_AUDIO_BITRATE \"$file_ident\" 2>$nulfile");
	    chomp($abitrate);
	    $abitrate=(split("\n",$abitrate))[-1];
	    $abitrate=(split("=",(split("\n",$abitrate))[0]))[1];


	    unless ($id_aud_form eq "" || $achans>0) {
		# need to look deeper for $achans
		$adets=smog_system_direct("grep AO: \"$file_ident\" 2>$nulfile");

		$adets=(split("AO: ",$adets))[1];
		chomp($adets);

		$arate=(split(" ",$adets))[1];
		if ($arate eq "[oss]") {
		    $arate=(split(" ",$adets))[2];
		    $achans=(split(" ",$adets))[3];

		}
		else {
		    $achans=(split(" ",$adets))[2];
		}
		chomp($arate);
		chomp($achans);

		$arate=$arate*1;
		$achans=$achans*1;
	    }

	    # grep may fail with error 256

	    if (! -f $file_ident) {return;};
	    $comment=smog_system_direct("grep Comments: \"$file_ident\" 2>$nulfile");
	    @tmp=split(" ",$comment);
	    shift(@tmp);
	    $comment=join(" ",@tmp);
	    chomp($comment);

	    if (! -f $file_ident) {return;};
	    $title=smog_system_direct("grep Title: \"$file_ident\" 2>$nulfile");
	    @tmp=split(" ",$title);
	    shift(@tmp);
	    $title=join(" ",@tmp);
	    chomp($title);

	    if (! -f $file_ident) {return;};
	    $title=smog_system_direct("grep Author: \"$file_ident\" 2>$nulfile");
	    @tmp=split(" ",$author);
	    shift(@tmp);
	    $author=join(" ",@tmp);
	    chomp($author);

	    if (! -f $file_ident) {return;};
	    $count=smog_system_direct("grep \"frames  total\" \"$file_ident\" 2>$nulfile");
	    @tmp=split(" ",$count);
	    $count=$tmp[2];
	    chomp($count);

            if ($count eq "") {
                $count=int($fps*$length+.5);
            }
            if ($count eq "") {
                $count=1000000; #take a guess...
            }
	}
	if ($asamps==0&&$arate*$achans>0) {
	    $asamps=$abitrate/$arate/$achans;
	}
	unlink "$file_ident";
    }

}


    if (($hsize*$vsize==0||$count eq "")&&!$is_audio) {
	# test even if mplayer thinks it is audio (at least some png files are misread)
	$name=&mkname(1);
	unless (-f "$curtmpdir/$name") {
	    # see if it is image(s)
	    &open_images(1);
	    if ($hsize==-1) {
		$hsize=$vsize=0;
	    }
	    opendir DIR,$curtmpdir;
	    while ($file2=readdir(DIR)) {
		if ($file2 =~ /$img_ext$/) {
		    $count++;
		    $imresact="none";
		    if ($only_first) {
			unlink glob "$curtmpdir/*$img_ext";
			last;
		    }
		    else {
			$f_size+=-s $file2;
		    }
		}
		else {
		    unless ($file2 eq $audio_in) {
			unlink "$file2";
		    }
		}
	    }
	    closedir DIR;

	    if ($count) {
		# got image(s)
		$frames=$count;
		# got image(s)
		if ($img_ext eq ".jpg") {
		    $type="jpeg";
		}
		else {
		    $type="png";
		}
		
		$count=0;
		$name=&mkname(1);
	    }
	}
    }

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

    if ($endian eq "") {
	$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;
    }
    
    if ($type=~ m/^\[/) {
	$type=substr($type,1,-1);
    }

}





sub convert_audio_to_raw {
    #convert .wav to raw pcm using sox or mplayer

    my $smcom;
    &get_formats;
    if (!(&rc_get("audio_player") eq "mplayer")) {
	$smcom="sox -t .wav \"$audio_in\" -t .raw $xsigned \"$audio_out\" > $nulfile 2>&1";
    }
    else {
	$format=&get_mplayer_format;
	$smcom=&rc_get("video_open_command") . " -quiet \"$audio_in\" -ao pcm:nowaveheader:file=\"$audio_out\" $format >$nulfile 2>&1";
    }

    my $smerr=smog_system($smcom);

    if ($smerr) {
	sig_system_error("$smcom",$smerr);
	$panic=1;
	return;
    }

    smog_system_sync();
}



sub convert_audio_to_wav {
    #convert raw to .wav

    #WARNING - $asamps and $nasamps are in bits
    my ($aasamps)=$asamps/8;
    my $smcom;

    smog_chdir("$curtmpdir");
    if (&location("sox") eq "") {
	$format=&get_mplayer_format;
	$smcom=&rc_get("video_open_command") . " -quiet -ao pcm -demuxer rawaudio -rawaudio rate=$arate:channels=$achans:samplesize=$aasamps -ao pcm:waveheader $format -vo null \"$audio_in\" >$nulfile 2>&1 <$nulfile";
    }
    else {
	if (!defined($sox_version)) {
	    $sox_version=&get_sox_version;
	}

	if ($sox_version<13) {
	    $nodither="";
	    if ($asamps==8) {
		$sasamps="b";
	    }
	    else {
		$sasamps="w";
	    }
	}
	else {
	    $nodither="-D";
	    $sasamps=$aasamps;
	}


	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;
	    }
	}
	$smcom="sox $nodither -t .raw -r $arate $asigned -$sasamps -c $achans \"$audio_in\" -t .wav -r $nrate -c $nchans $nsigned -$nasamps \"$curtmpdir/audiodump.wav\" >$nulfile 2>&1";
    }

    my $smerr=smog_system("$smcom");
    if ($smerr) {
	sig_system_error("$smcom",$smerr);
	$panic=1;
	return;
    }

    smog_system_sync();
}




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

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

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

    unlink "$audio_out";
    if ($^O eq "MSWin32") {
	$smres=smog_system("touch.exe \"$audio_out\"");
    }
    else {
	$smres=smog_system("touch \"$audio_out\"");
    }
    if ($smres) {
	$panic=sig_write_error("$audio_out"); 
	return;
    }

    if ($^O eq "MSWin32") {
	#smog_system("chmod.exe 600 \"$audio_out\"");
    }
    else {
	smog_system("/bin/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) {
	unlink "$audio_out";

	$fdd_in=$audio_in;
	$fdd_out=$audio_out;

	&fast_dd($size,$spos,0);
	return if ($panic);
    }

    smog_system_sync();
    $audio_out;
}


sub resample_audio {
    #WARNING - $asamps and $nasamps are in bits

    $endian=&get_endian;

    if (!defined($sox_version)) {
	$sox_version=&get_sox_version;
    }
    
    if ($sox_version<13) {
	if ($asamps==8) {
	    $osamps="b";
	}
	else {
	    $osamps="w";
	}
	
	
	if ($nsamps==8) {
	    $nsamps="b";
	}
	else {
	    $nsamps="w";
	}
	$nodither="";
    }
    else {
	$osamps=$asamps/8;
	$nsamps/=8;
	$nodither="-D";
    }
    
    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)) {
	$com="sox $nodither -t .raw -r $arate -c $achans -$osigned -$osamps $oendian \"$audio_in\" -t .raw -r $nrate -c $nchans -$nsigned $nendian -$nsamps \"$audio_out\" stretch $stretch>$nulfile 2>&1";
    }
    else {
	$com="sox $nodither -t .raw -r $arate -c $achans -$osigned -$osamps $oendian \"$audio_in\" -t .raw -r $nrate -c $nchans -$nsigned $nendian -$nsamps \"$audio_out\">$nulfile 2>&1";
    }
    $smerr=smog_system($com);
    if ($smerr) {
	sig_system_error("$com",$smerr);
	$panic=1;
    }
}

sub get_ext {
    my $fname=shift;

    my $ext=(split(/\./,$fname))[-1];

    if ($ext=~/(.*)\"$/) {
	$ext=$1;
    }

    return ".".$ext;
}



sub insert_audio {
#what we are going to do:
# 1) copy the end of the audio (after insertion) to a new file [$where to $to_end]
# 2) insert silence up to $where (if necessary)
# 3) insert the new section [$start to $end at $where]
# 4) insert silence up to $where+$end (if necessary)
# 5) then put the end back

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

    #if $arate<0, we will insert silence from $where to $where+$start-$end

    my ($xxsamps,$xxchans,$xxsigned,$xxendian,$needstemp)=@_;

    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) {
	    $audio_from="$tmpdir/$from_handle/audio.bak";
	}
	else {
	    $audio_from="$tmpdir/$from_handle/audio";
	}
    }
    my ($audio_temp)="$curtmpdir/audio.temp";

    unlink "$audio_temp";
    if ($^O eq "MSWin32") {
	$smres=smog_system("touch.exe \"$audio_temp\"");
    }
    else {
	$smres=smog_system("touch \"$audio_temp\"");
    }
    
    if ($smres) {
	$panic=sig_write_error("$audio_temp");
	return;
    }

    if ($^O eq "MSWin32") {
	#smog_system("chmod.exe 600 \"$audio_temp\"");
    }
    else {
	smog_system("/bin/chmod 600 \"$audio_temp\"");
    }

    $silence=0;
    if ($arate<0) {
	$silence=1;
	$arate=-$arate;
    }

    $align=$achans*$asamps/8;

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


    # step 1 - copy end to temp
    if ($needstemp) {
	my $to_end=-s $audio_to;
	my $size=$to_end-$wpos;
	if ($size>0) {
	    $fdd_in=$audio_to;
	    $fdd_out=$audio_temp;
	    
	    &fast_dd($size,$wpos,0);
	    return if ($panic);
	}
    }

    $audio_file=$audio_to;


    # step 2 - pad with silence to insertion point (if necessary)
    &append_silence(0,$wpos,$xxsamps,$xxchans,$xxsigned,$xxendian);
    return if $panic;


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

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

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

    my ($xaudio_from)=$audio_from;

    while ($times_inserted<$times) {
	$size=$nepos-$nspos;
	if ($size>0) {
	    if ($silence) {
		$fsize=-s $audio_to;
		$fsize=&align($fsize-$nwpos); #becomes offset
		&append_silence($fsize,$nwpos+$size,$xxsamps,$xxchans,$xxsigned,$xxendian);
		return if $panic;
	    }
	    else {
		$fdd_in=$xaudio_from;
		$fdd_out=$audio_to;

		&fast_dd($size,$nspos,$nwpos);
		return if ($panic);
	    }
	}

	$times_inserted+=$factor;
	# step 4, pad with silence if necessary
	if (! $silence && (-s $audio_temp||$times_inserted<$times)) {
	    $inserted=$oepos-$nspos;
	    &append_silence(0,$nwpos+$inserted,$xxsamps,$xxchans,$xxsigned,$xxendian);
	    return if ($panic);
	    unless ($silence_remembered>0) {
		$silence_remembered=$oepos-$nepos;
	    }
	}
	else {
	    $inserted=$nepos-$nspos;
	}

	if ($times_inserted==2) {
	    $oepos=$nepos=$nwpos;
	    $nspos=$nwpos-$inserted;
	    $xaudio_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) {
		$nepos-=$silence_remembered;
	    }
	}

    }


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

    #step 5 - copy end back after insertion
    if ($needstemp) {
	$size=&align(-s $audio_temp);
	if ($size>0) {
	    $fdd_in=$audio_temp;
	    $fdd_out=$audio_to;
	    
	    &fast_dd($size,0,$nwpos);
	    return if ($panic);
	}
	
	unlink "$audio_temp";
    }
    else {
	# copy from audio.bak to output
	my ($audio_bak)="$curtmpdir/audio.bak";
	$size=-s $audio_bak;
	$size=&align($size-$owpos);
	if ($size>0) {
	    $fdd_in=$audio_bak;
	    $fdd_out=$audio_to;

	    &fast_dd($size,$owpos,$nwpos);
	    return if ($panic);
	}
    }

    smog_system_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";
    unlink "$audio_bak";

    if ($end==0.) {
	#delete all audio
	smog_rename("$audio_in","$audio_bak");
    }
    else {
	if ($^O eq "MSWin32") {
	    $smres=smog_system("touch.exe \"$audio_temp\"");
	}
	else {
	    $smres=smog_system("touch \"$audio_temp\"");
	}
	if ($smres) {
	    $panic=sig_write_error("$audio_temp"); 
	    return;
	}
	if ($^O eq "MSWin32") {
	    #smog_system("chmod.exe 600 \"$audio_temp\"");
	}
	else {
	    smog_system("/bin/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) {
	    $fdd_in=$audio_in;
	    $fdd_out=$audio_temp;

	    &fast_dd($size,0,0);
	    return if $panic;
	}
	
	#step 2
	$size=($epos-$spos);
	
	if ($size>0) {
	    $fdd_in=$audio_in;
	    $fdd_out=$audio_bak;

	    &fast_dd($size,$spos,0);
	    return if $panic;
	}
	
	
	#step 3
	$spos=$epos;
	$epos=$fsize;
	$size=($epos-$spos);
	
	if ($size>0) {
	    $fdd_in=$audio_in;
	    $fdd_out=$audio_temp;

	    &fast_dd($size,$spos,$seekstart);
	    return if $panic;
	}
	
	# step 4
	unlink "$audio_in";
	smog_rename( "$audio_temp", "$audio_in");
    }

    if (-z $audio_in) {
	unlink "$audio_in";
    }

    smog_system_sync();
}


sub align {
    my ($guess)=shift;
    # align our audio cuts so we don't end halfway through a sample/channel
    if (!defined($align)) {
	$align=$achans*$asamps/8;
    }
    $guess=int($guess/$align+.5)*$align;
    return $guess;
}


sub append_silence {
    #pad from end - $offset of $audio_file to byte $end-1 with zeros
    my ($offset,$end,$asamps,$achans,$asigned,$aendian,$audio_file)=@_;
    if ($audio_file eq "") {
	$audio_file="$curtmpdir/audio";
    }

    unless (-f $audio_file) {
	if ($^O eq "MSWin32") {
	    $smres=smog_system("touch.exe \"$audio_file\"");
	}
	else {
	    $smres=smog_system("touch \"$audio_file\"");
	}
	if ($smres) {
	    $panic=sig_write_error("$audio_file"); 
	    return;
	}
	if ($^O eq "MSWin32") {
	    #smog_system("chmod.exe 600 \"$audio_file\"");
	}
	else {
	    smog_system("/bin/chmod 600 \"$audio_file\"");
	}
    }

    my $fsize= -s $audio_file;
    $fsize=&align($fsize-$offset); #insert at $fsize-$offset
    my $size=&align($end-$fsize); #insert to $end

    if ($size>0) {
	if ($asigned==1) {
	    $fdd_out=$audio_file;
	    if ($^O eq "MSWin32") {
		$smres=smog_system("touch.exe \"$audio_file\"");
		if ($smres) {
		    $panic=sig_write_error("$audio_file"); 
		    return;
		}
		open AUD,"+<","$audio_file" or $panic=sig_write_error("$audio_file");
		return if $panic;
		seek AUD,-$offset,SEEK_END or $panic=sig_write_error("$audio_file");
		return if $panic;
		for ($i=0;$i<$size;$i++) {
		    print AUD chr(0) or $panic=sig_write_error("$audio_file");
		    return if $panic;
		}
	    }
	    else {
		$fdd_in="/dev/zero";
		&fast_dd($size,0,$fsize);
	    }
	    return if $panic;
	}
	else {
	    if ($^O eq "MSWin32") {
		$smres=smog_system("touch.exe \"$audio_file\"");
	    }
	    else {
		$smres=smog_system("touch \"$audio_file\"");
	    }
	    if ($smres) {
		$panic=sig_write_error("$audio_file"); 
		return;
	    }
	    open AUD,"+<","$audio_file" or $panic=sig_write_error("$audio_file");
	    return if $panic;
	    seek AUD,-$offset,SEEK_END or $panic=sig_write_error("$audio_file");
	    return if $panic;
	    if ($asamps==8) {
		for ($i=0;$i<$size;$i++) {
		    print AUD chr(128) or $panic=sig_write_error("$audio_file");
		    return if $panic;
		}
	    }
	    else {
		if ($aendian) {
		    for ($i=0;$i<$size;$i+=2*$achans) {
			for ($j=0;$j<$achans;$j++) {
			    print AUD chr(0) or $panic=sig_write_error("$audio_file");
			    return if $panic;
			    print AUD chr(128) or $panic=sig_write_error("$audio_file");
			    return if $panic;
			}
		    }
		}
		else {
		    for ($i=0;$i<$size;$i+=2*$achans) {
			for ($j=0;$j<$achans;$j++) {
			    print AUD chr(128) or $panic=sig_write_error("$audio_file");
			    return if $panic;
			    print AUD chr(0) or $panic=sig_write_error("$audio_file");
			    return if $panic;
			}
		    }
		}
	    }
	    close AUD;
	}
    }
}




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)=shift;
    my $orig=$i;

    if (-d $file) {
	# is a directory

	$dir=$file;
	opendir DIR,$dir;
	my @files=readdir(DIR);
	closedir DIR;

	unless ($only_first) {
	    # open in numeric order
	    @files = sort {byfile($a,$b)} (@files);
	    if (!defined($antialias)) {
		$antialias=&rc_get("antialias");
	    }
	}

	foreach (@files) {
	    $file="$dir/$_";
	    unless ($_ =~ /^\./||(! -f $file)||(-z $file)) {
		$nframes=&open_single_image($i);
		return if $panic;
		if (-s "$curtmpdir/$name$img_ext") {
		    if ($only_first) {
			$i++;
			last;
		    }
		    else {
			$i+=$nframes;
		    }
		}
		else {
		    unlink "$curtmpdir/$name$img_ext";
		}
	    }
	}
    }
    else {
	&open_single_image($i);
	return if $panic;
	unless (-s "$curtmpdir/$name$img_ext") {
	    unlink "$curtmpdir/$name$img_ext";
	}
    }
    return $i-$orig;
}



sub byfile {
    # sort files in numerical-alpha order

    #thanks to dominus !
    my @a = split /(\d+)/, $a;
    my @b = split /(\d+)/, $b;
    my $M = @a > @b ? @a : @b;
    my $res = 0;
    for (my $i = 0; $i < $M; $i++) {
	return -1 if ! defined $a[$i];
	return 1 if  ! defined $b[$i];
	if ($a[$i] =~ /\d/) {
	    $res = $a[$i] <=> $b[$i];
	} else {
	    $res = $a[$i] cmp $b[$i];
	}
	last if $res;
    }
    $res;
}





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" or ".mp4" or ".mpg"


    my $i=shift;
    my $j;

    $name=&mkname($i);
    my $nframes=0;

    if ($file =~ /:\/\//) {
	return 0;
    }

    if (!defined($file_ext)) {
	$file_ext=&get_ext("$file");
    }

    if ($file_ext eq ".avi"||$file_ext eq ".mp4"||$file_ext eq ".mpg"||$file_ext eq ".ogg"||$file_ext eq ".mov"||$file_ext eq ".ogv"||$file_ext eq ".webm"||$file_ext eq ".mkv"||$file_ext eq ".asf"||$file_ext eq ".wmv"||$file_ext eq ".flv"||$file_ext eq ".mng"||$file_ext eq ".dv"||$file_ext eq ".mp3" || $file_ext eq ".mpeg") {
	# need to set $name before we return
	return 0;
    }

    if ($hsize*$vsize==0) {
	&get_image_size($file);
	return 0 if ($panic||$hsize==-1);
    }

    if ($hsize*$vsize==0) {
	return 0;
    }

    smog_system_sync();

    if ($antialias eq "false") {
	$com="$smog_convert_command +antialias \"$file\" -scale $hsize"."!x"."$vsize! $img_prefix\"$curtmpdir/$name$img_ext\" > $nulfile 2>&1";
    }
    else {
	$com="$smog_convert_command \"$file\" -resize $hsize"."!x"."$vsize! $img_prefix\"$curtmpdir/$name$img_ext\" >$nulfile 2>&1";
    }


    $smerr=smog_system($com);

    if ($smerr) {
	sig_system_error("$com",$smerr);
	$panic=1;
	return 0;
    }


    #convert gives multiple frames for e.g. animated gif
    $found=1;
    for ($j=0;$found==1;$j++) {
	if (-f "$curtmpdir/$name-$j$img_ext") {
	    $newname=&mkname($i+$j);
	    smog_rename( "$curtmpdir/$name-$j$img_ext","$curtmpdir/$newname$img_ext");
	    $nframes++;

	}
	else {
	    $found=0;
	}
    }

    if ($hsize*$vsize==0) {
	&get_image_size("$curtmpdir/$name$img_ext");
    }

    if ($hsize*$vsize>0&&$nframes==0) {
	$nframes=1;
    }

    return $nframes;

}



sub get_image_size {
    # returns $hsize,$vsize and $bpp for $1
    my ($file)=shift;
    my ($i);

    #identify hangs if the file extension is ".avi" or ".mp4"...maybe more...
    my ($file_ext)=&get_ext("$file");

    if ($file_ext eq ".avi"||$file_ext eq ".mp4"||$file_ext eq ".mpg"||$file_ext eq ".mpeg") {
	$height=$width=$bpp=0;
	return;
    }

    $bpp=8; # default for images
    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 (e.g. for thumbnails); or none: return actual size

    my ($id_cmd)=&location("identify");

    unless ($id_cmd eq "") {
	$com="\"$id_cmd\" \"$file\" 2>$nulfile";
	@info=split / /, smog_system_direct($com);

	if ($?) {
	    # probably not an image then
	    $hsize=$vsize=$bpp=-1;
	    return;
	}

	my ($file2)=$file;
	$space_count=($file2 =~ tr/ //);
	for ($i=0;$i<$space_count;$i++) {
	    shift(@info);
	}
	$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 "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);
	}
	elsif ($imresact eq "default") {
	    $hsize=$vsize="";
	}
    }
    if (!defined($hsize)||$hsize eq "") {
	$hsize=$dwidth;
    }
    if (!defined($vsize)||$vsize eq "") {
	$vsize=$dheight;
    }
}



sub fill_and_redo_frames {
    # resample and fill gaps in frames

    my $next=0;

    smog_chdir("$curtmpdir");


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

	if ($next<$i&&$next>-1) {
	    $last=$next;
	    $next=&get_next_frame;
	    return if $panic;
	}
	if ($last>0&&($i-$last)<($next-$i)) {
	    $fromname=&mkname($last);
	}
	else {
	    $fromname=&mkname($next);
	}

	unless ($fromname eq $name) {
	    smog_copy("$curtmpdir/$fromname$img_ext","$curtmpdir/$name$img_ext");
	    return if $panic;
	}
	&sig_progress($i);
    }

}


sub get_next_frame {
    my $j;
    my $xname;
    $imresact="none";

    for ($j=$i;$j<=$end;$j++) {
	$xname=&mkname($j);
	if (-f "$xname$img_ext") {
	    # found next frame, resize it
	    &get_image_size("$xname$img_ext");
	    return if ($panic||$hsize==-1);

	    if ($hsize<$width||$vsize<$height) {
		$com="$smog_convert_command -resize $width" . "!x" . $height . "!+0+0 $img_prefix_prefix\"$xname$img_ext\" $img_prefix\"$xname.mgk\" >$nulfile 2>&1";
	    }
	    else {
		$com="$smog_convert_command -crop $width" . "!x" . $height . "! $img_prefix\"$xname$img_ext\" $img_prefix\"$xname.mgk\" >$nulfile 2>&1";
	    }
	    $smerr=smog_system($com);
	    if ($smerr) {
		sig_system_error("$com",$smerr);
		$panic=1;
		return;
	    }
	    smog_rename( "$xname.mgk","$xname$img_ext");
	    return $j;
	}
    }
    # no next frame found
    return -1;
}




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

    # prefer mpg321 if it's available
    unless (&location("mpg321") eq "") {
	smog_system("mpg321 -w \"$audio_in\" --rate 44100 --stereo \"$file\" >$nulfile 2>&1");
	$f_size=-s $audio_in;
    }
    if ($f_size==0) {
	unless (&location("mpg123") eq "") {
	    smog_system("mpg123 -w \"$audio_in\" --rate 44100 --stereo \"$file\" >$nulfile 2>&1");
	    $f_size=-s $audio_in;
	}
    }
    if ($f_size==0) {
	&othera_open;
	return;
    }

    $file=$audio_in;
    $is_audio=1;

    &get_file_info;
    return if $panic;

    $asigned=$signed;
    $aendian=$endian;

    if ($arate==0&&$achans==0&&$asamps==0) {
	# TODO - find way to read these
	# have to assume these for now
	$arate=44100;
	$achans=2;
	$asamps=16;

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

    if ($f_size==0) {
	$f_size= -s $audio_in;
    }

}


sub ogg_open {
    $f_size=0;

    if (-f $audio_in) {
	unlink "$audio_in";
    }
    
    unless (&location("ogg123") eq "") {
	smog_system("ogg123 -d wav -f \"$audio_in\" \"$file\" >$nulfile 2>&1");
	$f_size= -s $audio_in;
    }

    if ($f_size==0) {
	&othera_open;
	return;
    }


    $file=$audio_in;
    $is_audio=1;

    &get_file_info;
    return if $panic;

    $asigned=$signed;
    $aendian=$endian;

    if ($arate==0&&$achans==0&&$asamps==0) {
	# TODO - find way to read these
	# have to assume these for now
	$arate=44100;
	$achans=2;
	$asamps=16;

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

    if ($f_size==0) {
	$f_size= -s $audio_in;
    }
    
}


sub wav_open {
    $curtmpfile=$curtmpdir . "/.temp";

    if (-f $audio_in) {
	unlink "$audio_in";
    }
    
    smog_copy("$file","$audio_in");
    return if $panic;

    $file=$audio_in;
    $is_audio=1;

    &get_file_info;
    return if $panic;

    $asigned=$signed;
    $aendian=$endian;

    if ($arate==0&&$achans==0&&$asamps==0) {
	# TODO - find way to read these
	# have to assume these for now
	$arate=44100;
	$achans=2;
	$asamps=16;

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

    if ($f_size==0) {
	$f_size= -s $audio_in;
    }
}



sub othera_open {
    $arate=44100;
    $achans=2;
    $asamps=16;
    $aendian=&get_endian;
    $asigned=1;
    $format=&get_mplayer_format;

    $com=&rc_get("video_open_command") . " -quiet \"$file\" -ao pcm:waveheader:file=\"$audio_in\" $format >$nulfile 2>&1 <$nulfile";

    $smerr=smog_system($com);

    if ($smerr) {
	sig_system_error("$com",$smerr);
	$panic=1;
	return;
    }


    $file=$audio_in;
    $is_audio=1;

    &get_file_info;
    return if $panic;

    $asigned=$signed;
    $aendian=$endian;

    if ($arate==0&&$achans==0&&$asamps==0) {
	# TODO - find way to read these
	# have to assume these for now
	$arate=44100;
	$achans=2;
	$asamps=16;

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

    if ($f_size==0) {
	$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 {
    # get an int from a file in $endian format
    my ($string)=shift;
    my (@val)=unpack("CCCC",$string); #unpack $string to 4 chars in @val array

    # TODO - this needs checking !

    if (!defined($endian)||$endian==1) {
	#little endian
	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 {
    # 0 == big-endian
    # 1 == little-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 u8";
	}
	return "-format s8";
    }
    if ($nsigned==0) {
	if ($nendian==0) {
	    return "-format u16be";
	}
	return "-format u16le";
    }
    if ($nendian==0) {
	return "-format s16be";
    }
    return "-format s16le";
}




sub clean_old {
    # clear away any old backup files, etc.
    return if (! -d "$tmpdir/$handle");
    smog_chdir("$tmpdir/$handle");
    unlink glob "*.mgk *.bak *.pre *.tmp pause audio.* audiodump* audioclip";
    smog_system_sync();
}



sub sig_complete {

    return if (! -d "$curtmpdir");
    my($status)="completed";
    foreach (@_) {
	$status.="|".$_;
    }

    smog_system_sync();

    if (-d $curtmpdir) {
	open OUT,">","$statusfile";
	print OUT "$status";
	close OUT;
	unlink "$pidfile";
    }
}


sub sig_killed {
    return if (! -d "$curtmpdir");
    return if (-f "$statusfile");
    my($status)="killed";

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



sub sig_complete_audio {
    return if (! -d "$curtmpdir");
    my($status)="audio_ended|";
    foreach (@_) {
	$status.="|".$_;
    }
    
    open OUT,"> $statusfile.play";
    # autoflush output
    select((select(OUT), $| = 1)[0]);
    print OUT $status;
    close OUT;

    unless ($opening_preview==1) {
	unlink "$pidfile";
    }
}


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

    # write to backend -> frontend $statusfile

    return if (! -d $curtmpdir);

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

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

    smog_system_sync();
    exit 1;
}



sub sig_write_error {
    # got an error writing to a file. pass in filename as 1st param

    # write to backend -> frontend $statusfile

    # in params: 1 - filename, 2 - optional additional info (1 line)

    return 1 if (! -d $curtmpdir);

    my ($smfile,$smaddinfo)=@_;
    my ($smstatus)="error|write|$smfile";

    if ($smaddinfo ne "") {
	$smstatus.="|$smaddinfo";
    }

    open OUT,"> $statusfile";
    print OUT $smstatus;
    close OUT;
    return 1;
}


sub sig_read_error {
    # got an error reading from a file. pass in filename as 1st param

    # write to backend -> frontend $statusfile

    # in params: 1 - filename, 2 - optional additional info (1 line)

    return 1 if (! -d $curtmpdir);
    my ($smfile,$smaddinfo)=@_;
    my ($smstatus)="error|read|$smfile";

    if ($smaddinfo ne "") {
	$smstatus.="|$smaddinfo";
    }
    
    open OUT,"> $statusfile";
    print OUT $smstatus;
    close OUT;
    return 1;
}


sub sig_system_error {
    # got an error running a system command. pass in command as 1st param
    # error code is second param
    # use only for commands that really really must never fail

    # preference is to use read/write errors as this is clearer to the user what failed

    # write to backend -> frontend $statusfile

    #
    # in params: 1 - command which failed or description of command, 2 - error value returned, 
    # 3 - optional additional info (1 line)

    return 1 if (! -d $curtmpdir);

    my ($smcom,$smerrno,$smaddinfo)=@_;
    my ($smstatus)="error|system|$smcom|$smerrno";

    if ($smaddinfo ne "") {
	$smstatus.="|$smaddinfo";
    }

    open OUT,"> $statusfile";
    print OUT $smstatus;
    close OUT;
    return 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
###
###
# write to backend -> frontend $statusfile
    my $status="";
    foreach (@_) {
	if ($status eq "") {
	    $status=$_;
	}
	else {
	    $status.="|".$_;
	}
    }
    &check_for_pause;
    open OUT,"> $statusfile";
    print OUT $status;
    close OUT;
}


sub sig_clear {
    &check_for_pause;
    unlink "$statusfile";
}



sub location {
    # return the location of an executable
    my ($command)=shift;

    if ($^O eq "MSWin32") {
	return "$command.exe";
    }

    my ($location)=smog_system_direct("which \"$command\" 2>$nulfile");
    chomp($location);

    $location;
}


sub rc_set_if_not_set {
    my ($key,$value)=@_;
    $skip_if_exist=1;
    &rc_set("$key","$value");
}

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

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



sub rc_set {
    # set/modify/delete a value in the .rc file
    my ($key,$value)=@_;

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

    if (defined($prefs_file)) {
	$rcfile=$prefs_file;
    }

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

    if (substr($value,-1,1) eq "\"" && substr($value,0,1) ne "\"") {
	$value=substr($value,0,length($value)-1);
    }

    # 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";
	print OUT "# 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;
	}
	my $string="";
	my $current="";
	my $part=0;
	my $found=0;
	while (<IN>) {
	    my $laststring=$current;
	    $current=$_;
	    if ($delpref!=1) {
		$string=$current;

		if ($string=~/^#.*/) {
		    #ignore comments
		    print OUT $string;
		    next;
		}
	    }
	    else {
		$string=$laststring;
		next if ($laststring eq "");
	    }

	    if ($priority==2) {
		print OUT "\n<$key>\n$value\n</$key>\n";
		$found=1;
		$priority=1;
	    }
	    if ($found==0||$part==1) {
		if ($part==1||$current=~ /^(<$key>)/) {
		    if ($skip_if_exist==1) {
			close IN;
			close OUT;
			unlink "$rcfile.new";
			$skip_if_exist=0;
			return;
		    }
		    if ($part==0) {
			$part=1;
			if ($delpref==0) {
			    #update existing value
			    print OUT "<$key>\n$value\n</$key>\n";
			}
			else {
			    #delete
			    #$date=&get_current_date;
			    #print OUT "# $key deleted by $GUI_NAME $date\n";
			    if ($string ne "\n") {
				print OUT $string;
			    }
			    $delpref=2;
			}
			$found=1;
		    }
		    if ($current=~ /(<\/$key>)$/) {
			$part=0;
		    }
		}
		elsif ($part==0) {
		    print OUT $string;
		}
	    }
	    else {
		print OUT $string;
	    }
	}
	if ($found==0&&$delpref==0) {
	    if ($priority==1) {
		$priority=2;
		&rc_set("$key","$value");
	    }
	    else {
		print OUT "\n<$key>\n$value\n</$key>\n";
	    }
	}
	if ($delpref==1) {
	    print OUT $current;
	}
    }
    close IN;
    close OUT;
    smog_rename( "$rcfile.new","$rcfile") or exit 3;
    $delpref=0;
}


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

    if (defined($prefs_file)) {
	$rcfile=$prefs_file;
    }

    unless (defined(open IN,"$rcfile")) {
	print STDERR "Smogrify: Unable to read values from rc file, $rcfile\n";
	exit 2;
    }
    $part=0;
    while (<IN>) {
	if ($part==1&&$_=~ /(<\/$key>)$/) {
	    close IN;
	    return $string;
	}
	if ($part==1||$_=~ /^(<$key>)/) {
	    if ($part==1) {
		$_=~ s/\n//g;
		if ($string eq "") {
		    $string=$_;
		}
		else {
		    $string.="\n".$_;
		}
	    }
	    else {
		$part=1;
		$string=$2;
	    }
	}
    }
    close IN;
    return $string;
}



sub fast_dd {
    # copy size bytes from $fdd_in to $fdd_out - skip $skip bytes in input and $seek bytes in output
    # no truncation is done
    my ($size,$skip,$seek)=@_;
    my ($smres);

    return if ($size<0);

    my $bs=4096; # block size

    my $fblocks=int($size/$bs);
    my $rbytes=$size-$fblocks*$bs;

    my $data,$i;

    my $sizecheck = -s $fdd_in;

    if ($fdd_in ne "/dev/zero" && $size > $sizecheck) {
	if ($DEBUG_SMOGRIFY) {
	    print STDERR "smogrify debug - fast_dd was asked to read $size bytes from a $sizecheck byte file\n$fdd_in\n";
	}
	$size=$sizecheck;
    }

    if (! open FFIN,"<","$fdd_in") {
	if (-f "$fdd_in") {
	    $panic=sig_read_error("$fdd_in");
	}
	return;
    }

    seek FFIN,$skip,SEEK_START or $panic=sig_read_error("$fdd_in");
    return if $panic;

    if ($^O eq "MSWin32") {
	$smres=smog_system("touch.exe $fdd_out");
    }
    else {
	$smres=smog_system("touch $fdd_out");
    }
    if ($smres) {
	$panic=sig_write_error("$fdd_out"); 
	return;
    }
    
    open FFOUT,"+<","$fdd_out" or $panic=sig_write_error("$fdd_out");
    return if $panic;
    seek FFOUT,$seek,SEEK_START or $panic=sig_write_error("$fdd_out");
    return if $panic;

    for ($i=0;$i<$fblocks;$i++) {
	read FFIN,$data,$bs or $panic=sig_read_error("$fdd_in");
	return if $panic;
	print FFOUT $data or $panic=sig_write_error("$fdd_out");
	return if $panic;
    }

    while ($rbytes>0) {
	$bs/=2;
	if ($rbytes>=$bs) {
	    read FFIN,$data,$bs or $panic=sig_read_error("$fdd_in");
	    return if $panic;
	    print FFOUT $data or $panic=sig_write_error("$fdd_out");
	    return if $panic;
	    $rbytes-=$bs;
	}
    }

    close FFOUT;
    close FFIN;
}


sub count_frames {
    #count number of frames using a binary search

    my $count=0,$gap=1;
    if (!defined $handle) {
	$handle=$ARGV[1];
	$curtmpdir="$tmpdir/$handle";
    }

    $name=&mkname(1);

    unless (-f "$curtmpdir/$name$img_ext") {
	return 0;
    }

    while (1) {
	$name=&mkname($count+$gap);
	if (-f "$curtmpdir/$name$img_ext") {
	    $count+=$gap;
	    $gap*=2;
	}
	else {
	    if ($gap==1) {
		last;
	    }
	    $gap/=2;
	}
    }
    return $count;
}
    


sub my_basename {
    my ($exe)=shift;
    my @parts=split(/\//,$exe);
    return $parts[-1];
}



sub write_bootstrap_file {
    #write our bootstrap file and make our tmpdir
    unlink "$gui_bootstrap_file";
    unless (defined(open OUT,"> $gui_bootstrap_file")) {
	exit 4;
    }
    close OUT;
}


sub mktmpdir {
    unless (-d $tmpdir) {
	# create working directory
	umask 0;
	unless (mkdir $tmpdir,0777) {
	    unless ($gui_bootstrap_file eq "") {
		open OUT,"> $gui_bootstrap_file" or exit 4;
		print OUT "$version#$tmpdir#$msg#$msg2";
		close OUT;
	    }
	    exit 5;
	}
	umask $umask;
    }
}


sub fndset {
    # check if a directory is in a set (old style)
    # but useful for frontend marking the currently opened clips, so we do not prune them by accident

    if ($^O eq "MSWin32") {
	/^set/ or return;
    }
    else {
	/^set\./ or return;
    }

    $in_set=1;
}



sub get_home_dir {
    return $ENV{"HOME"};
}



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

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

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

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

    $ret;
}



sub version_check_and_upgrade {
    # various things that should happen on update

    # mainly for very very old versions


    my $old_version=&rc_get("version");
    my $version_hash=&version_hash($old_version);
    my $new_version_hash=&version_hash($version);

    $msg="";

    # throw a warning if using png and convert_version < 6.0.0
    if (&rc_get("default_image_format") eq "png" && $convert_version_hash<6000 && $convert_version_hash>0) {
	$msg="You are advised to upgrade to at least version 6.0 of Image Magick if using png files";
    }

    if ($version_hash>0) {

	if ($new_version_hash>$version_hash) {
	    $msg="!updmsg";
	}

	if ($version_hash<8005) {
	    # not used now
	    &rc_delete("frontend");
	    &rc_delete("mog_auto_bak");
	    &rc_delete("plugin_dir");
	}
	if ($version_hash<9001) {
	    # convert_version is used instead now
	    &rc_delete("mogrify_version");

	    &rc_delete("effects_command");

	    # hey, now we use our own keyboard poller
	    # things are getting groovy !
	    &rc_delete("no_fast_keys");

	    # changed to "open_compression_percent"
	    &rc_delete("open_quality");

	    &rc_delete("kbd_rpt_state");
	    &rc_delete("kbd_rpt_delay");
	    &rc_delete("kbd_rpt_rate");

	    #encoder_command not used any more
	    &rc_delete("encoder_command");

	    #encoder_acodec used instead
	    &rc_delete("audio_encode_quality");

	    #encoder plugin names changed
	    $msg="Make sure you delete all the old encoder plugins and install the new ones.";
	    $msg2="Then change your encoder from Preferences / Encoders";
	}
	if ($version_hash<9006) {
	    &rc_delete("video_player");
	    &rc_delete("video_play_command");
	    &rc_delete("PAL/NTSC");
	}
	if ($version_hash<9006) {
	    &rc_delete("osc_opts");
	}
	if ($version_hash<1001005) {
	    &rc_delete("debug_encoders");
	}
	if ($version_hash<1002000) {
	    my $wmask=&rc_get("lives_warning_mask");
	    $wmask|=8192;
	    &rc_set("lives_warning_mask",$wmask);
	}
	if ($version_hash<1003011) {
	    &rc_set("osc_port",49999);
	    $msg="Default OSC port has been changed to 49999.";
	}
	unless ($old_version eq $version) {
	    $current_keymap=smog_system_direct("grep \"keymap file version 4\" \"$lives_home_dir/$default_keymap\" 2>$nulfile");
	    if ($current_keymap eq "") {
		unlink "$lives_home_dir/$default_keymap";
	    }
	    unless (&location("build-lives-plugin-multi") eq "") {
		smog_system("build-lives-plugin-multi custom");
		smog_system("build-lives-plugin-multi test");
	    }
	}
    }


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


sub version_hash {
    # turn a version like
    # a.b.c into an integer
    # a * 1,000,000 plus b * 1,000 plus c
    # eg. 1.4.6 becomes 10004006

    my ($string)=shift;
    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;
}


sub RGB24_to_string {
    # convert input like 255,255,255 to #FFFFFF

    my ($red,$green,$blue)=@_;
    sprintf("\"#%02X%02X%02XFF\"",int($red),int($green),int($blue));
}


sub get_form_request {
    # get format request from a (encoder) plugin
    # part of the encoding process
    my ($plugin)=shift;
    if ($plugin eq "") {
	return &get_format_request;
    }
    else {
	
	if ($^O eq "MSWin32") {
	    # filter by file ext
	    my $ext=&get_ext("$plugin");
	    if ($ext eq ".py") {
		$cmd="python";
	    }
	    else {
		$cmd="perl";
	    }
	    return smog_system_direct("$cmd \"$plugin\" get_format_request");
	}
	return smog_system_direct("\"$plugin\" get_format_request");
    }
}


sub get_convert_version_hash {
    # turn a version like
    # a.b.c into an integer
    # a * 1,000,000 plus b * 1,000 plus c
    # eg. 1.4.6 becomes 10004006

    my $real_convert_command=(split(" ",$smog_convert_command))[0];
    my $convert_version=smog_system_direct("$real_convert_command | grep -i version");
    $convert_version=(split(" ",$convert_version))[2];
    return &version_hash($convert_version);
}


sub weed {
    # here is where we clear up diskspace

    my $opts=shift;

    # opts: - if unset bit 0 (1)    delete clips not in any set and not marked as "in-use" (protected with set.* file)
    #         if unset bit 1 (2)    prune (clean up) any non "in-use" clips (remove backup files etc.) (prot. w. noprune)
    #	      if set   bit 2 (4)    remove any sets (directories) with layouts but no clips (subdirectories)
    #         if unset bit 3 (8)    remove marker files
    #         if unset bit 4 (16)   remove misc. files
    #        

    #

    my $file;
    smog_chdir("$tmpdir");

    # get starting disk space
    my $start_total=(split / /, (smog_system_direct("du -sb 2>$nulfile")))[0];

    use File::Find;
    opendir DIR,$tmpdir;
    while ($file=readdir(DIR)) {
	unless ($file =~ /^\./) {

	    if (-d "$tmpdir/$file") {
                # remove a set unlesss it has clips OR (layouts AND NOT (opts & 0x04))
		if (&is_non_empty_dir("$tmpdir/$file/clips") || (&is_non_empty_dir("$tmpdir/$file/layouts") && !($opts & 0x04))) {
		    # new style sets, check clips and optionally layouts
		    unless (!defined($new_temp)||$new_temp eq "") {
                    # we are to move this set
                        if (-d "$new_temp/$file") {
                            smog_rename( "$new_temp/$file","$new_temp/$file.bak");
                        }
                        unless ($opts & 0x02) {
			  unless (-f "$tmpdir/$file/noprune") {
			    smog_chdir("$tmpdir/$file");
			    &prune;
			    smog_chdir("$tmpdir");
			  }
                         }
			smog_rename( "$tmpdir/$file","$new_temp/$file");
		    }
		}
		else {


                    # dir is of no interest
		    $in_set=0;
		    find (\&fndset,"$tmpdir/$file/");
		    if (!$in_set && !($opts & 0x01)) {
    	                if ($^O eq "MSWin32") {
	                  if (chdir("$tmpdir/$file")) {
		            unlink glob "* *.* .*";
		            smog_chdir("$tmpdir");
		            rmdir "$tmpdir/$file";
	                  }

	                }
	                else {
                           smog_system("/bin/rm -rf \"$tmpdir/$file\"");
	                }
		    }
		    else {
                       unless ($opts & 0x02) {
			unless (-f "$tmpdir/$file/noprune") {
			    smog_chdir("$tmpdir/$file");
			    &prune;
			    smog_chdir("$tmpdir");
			}
                      }
		     unless (!defined($new_temp)||$new_temp eq "") {
                # tempdir was changed, so we move all sets (subdirs) from old tempdir to new 
                # [TODO - check for set name collision !]
                       if (-d "$tmpdir/$file") {
                          if (-d "$new_temp/$file") {
                               smog_rename( "$new_temp/$file","$new_temp/$file.bak");
                          }
			 smog_rename( "$tmpdir/$file","$new_temp/$file");
		      }
                    }
                  }
		}
              unless ($opts & 0x08) {
                unlink "$tmpdir/$file/set.";
                unlink "$tmpdir/$file/noprune";
              }
	    }
	    else {
                # move crash recovery files
		unless (!defined($new_temp)||$new_temp eq "") {
		    if ($file =~ /^recovery/ || $file =~ /^layout/) {
			smog_rename( "$tmpdir/$file","$new_temp/$file");
		    }
		}
	    }
	}
    }

    closedir DIR;

    unless ($opts & 0x10) {
      # clean up a few extra remnants
      smog_chdir("$tmpdir");
      unlink glob ".* rfx.* smogplugin.*";
      if ($^O ne "MSWin32") {
	  unlink glob "/tmp/.smogval* /tmp/lives-symlinks";
      }
      unless (!defined($new_temp)||$new_temp eq "") {
	  unlink glob "firew.* encoder_log_* stream.*";
      }
    }

    # total disk space at end
    my $end_total=(split / /, (smog_system_direct("du -sb")))[0];

    # return disk space gained (in bytes)
    return $start_total-$end_total;
}


sub get_sox_version {
    #try to get version of sox

    # parse a line like "sox: SoX v14.3.2"
    # which would return 14.3.2
    # (take third word and strip leading v)

    my $soxv=smog_system_direct("sox -h 2>&1 | grep -i sox:");

    my $soxvv=(split(/\./,(split(" ",$soxv))[2]))[0];

    if (substr($soxvv,0,1) eq "v") {
	$soxvv=substr($soxvv,1,length($soxvv));
    }
    if ($soxvv eq "") {
	# version 12.x.x or lower
	$soxvv=0;
    }

    return $soxvv;
}


sub prune {
    # clean up old backup images etc in $tmpdir/$file
    # part of the diskspace recovery process

    $old_handle=$handle;
    $handle=$file;
    &clean_old;
    $handle=$old_handle;
}




sub clear_symlinks {
    # clear symlinks directory - called after encoding a selection (part of a clip which gets symlinked)

    # this is for "safe" symlinks
    # we say that symlinks may be created only within /tmp/lives-symlinks on whatever system

    # this allows e.g dynebolic to use non-Linux disk as working directory


    my $linksdir="lives-symlinks/$handle/";
    
    smog_chdir("/tmp");
    
    if (-d $linksdir) {
	# remove directory + contents
	if ($^O eq "MSWin32") {
	    smog_system("DEL /q \"$linksdir\"");
	    smog_system("RMDIR \"$linksdir\"");
	}
	else {
	    smog_system("/bin/rm -rf \"$linksdir\"");
	}
    }
    
    #create dir so next step works
    if ($^O eq "MSWin32") {
	smog_system("mkdir.exe /p \"$linksdir\"");
    }
    else {
	make_path("$linksdir");
    }

    # remove dir + any empty parents
    if ($^O eq "MSWin32") {
	smog_system("rmdir.exe /p \"$linksdir\" 2>$nulfile");
    }
    else {
	smog_system("/bin/rmdir -p \"$linksdir\" 2>$nulfile");
    }
}


sub is_non_empty_dir {
    #return 1 if "the parameter" is the name of a directory containing at least one non-dotted file

    #uses opendir and readdir


    my $dirname=shift;
    my $xfile;

    opendir TDIR,$dirname;
    while ($xfile=readdir(TDIR)) {
	unless ($xfile =~ /^\./) {
	    closedir TDIR;
	    return 1;
	}
    }
    
    closedir TDIR;
    return 0;
}
