#!/usr/bin/perl

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

# stop astyle indenting
=begin comment
    // *INDENT-OFF*
    =end comment
=cut
    
    ##################################################################
    
    # 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.10.0";
#$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_workdir = "$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_workdir = "$home";
    $nulfile = "/dev/null";

    $encoder = "ffmpeg_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) {
            $workdir = &rc_get("session_tempdir");

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

        }
    }

    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];
            $curworkdir = "$workdir/$handle";
            $target_pid = " ";

            if ($^O eq "MSWin32") {
                $pidfile = "$curworkdir/pid";
            } else {
                $pidfile = "$curworkdir/.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];
            $curworkdir = "$workdir/$handle";
            my $sig = "KILL";
            if ($command eq "stop_audio") {
                $pidfile = "$curworkdir/.pidpb";
            } else {
                $pidfile = "$curworkdir/.pid";
                if (defined($ARGV[2])) {
                    $sig = $ARGV[2];
                }
            }
            $statusfile = "$curworkdir/.status";

            unless(-d "$curworkdir") {
                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 > \"$workdir/.pids.$target_pid\" 2>/dev/null");
                    if (-s "$workdir/.pids.$target_pid" && defined(open IN, "$workdir/.pids.$target_pid")) {
                        while (<IN>) {
                            $pid = $_;
                            chomp($pid);
                            &kill_child_pids($pid, $sig);
                        }
                        close IN;
                    }
                    unlink "$workdir/.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="$workdir/info.$key";
            }
            else {
                $infofile="$workdir/.info.$key";
            }
            do {
                $handle=int(rand 1000000000)+65536;
                $curworkdir="$workdir/$handle";
            } while (-d $curworkdir);

            umask 0;
            unless (mkdir $curworkdir) {
                sig_system_error("Creating directory \"$curworkdir\"", 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") {
	    ## deprecated - DO NOT USE, use get_workdir instead
	    # 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 "$workdir";
                close OUT;
            } else {
                print $workdir
            };
            exit 0;
        }

        if ($command eq "get_workdir") {
	    # safer version - writes to stdout
            print $workdir;
            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 \"$lives_home_dir\"");
                } else {
                    make_path("$lives_home_dir");
                }
            }

            $startup_phase = 0;

            $video_open_command = "\"". & get_mplayer_location."\"";

            $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 $workdir 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_workdir");

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

	    # NOTE: the following definitions must match with equivalent keys in preferences.h
            &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_trim_pad_audio", "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", 1024);
            &rc_set_if_not_set("mt_def_height", 768);
            &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

            &rc_set_if_not_set("ce_thumb_mode", "false");

            &rc_set_if_not_set("show_button_icons", "false");

            &rc_set_if_not_set("max_disp_vtracks", 5);

	    &rc_set_if_not_set("show_audio_src", "true");

	    &rc_set_if_not_set("midi_rcv_chan", 0);

	    &rc_set_if_not_set("hide_framebar_when_not_playing", "false");

            $workdir = &rc_get("tempdir");
            &write_bootstrap_file;

            $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|$workdir|$startup_phase|$msg|$msg2";
                close OUT;
            }

	    #make sure we can write to tempir
	    #open OUT,"> $workdir/testfile" or exit 5;
	    #close OUT;
	    #unlink "$workdir/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, "> $workdir/smogval.$first.$second" or exit 1;
            } else {
                open OUT, "> $workdir/.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, "> $workdir/smogval.$first.$second" or exit 1;
            } else {
                open OUT, "> $workdir/.smogval.$first.$second" or exit 1;
            }
            print OUT $value;
            close OUT;
            exit 0;
        }

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

        if ($command eq "get_location") {
            $exe = $ARGV[1];
            if (defined($ARGV[2])) {
                $first = $ARGV[2];
            } else {
                $first = $uid;
            }
            if (defined($ARGV[3])) {
                $second = $ARGV[3];
            } else {
                $second = $gid;
            }
            $value = &location($exe);

            if ($^O eq "MSWin32") {
                open OUT, "> $workdir/smogval.$first.$second" or exit 1;
            } else {
                open OUT, "> $workdir/.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 \"$workdir\\$ARGV[1]\\pause\"");
                if ($smres) {
                    sig_write_error("$workdir\\$ARGV[1]\\pause");
                    exit 1;
                }
            } else {
                $smres = smog_system("touch \"$workdir/$ARGV[1]/pause\"");
                if ($smres) {
                    sig_write_error("$workdir/$ARGV[1]/pause");
                    exit 1;
                }
            }
            if ($^O eq "MSWin32") {
		#smog_system("chmod.exe 600 \"$workdir\\$ARGV[1]\\pause\"");
            } else {
                smog_system("/bin/chmod 600 \"$workdir/$ARGV[1]/pause\"");
            }
            exit 0;
        }

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

        if ($command eq "clear_tmp_files") {
            $handle = $ARGV[1];
	    #clear old backups (e.g. when saving a set)
            $curworkdir = "$workdir/$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)
            $curworkdir = "$workdir/$handle";
            smog_chdir("$curworkdir");
            unlink glob "*.pre";
            &sig_complete;
            exit 0;
        }

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

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

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

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

            $handle = $ARGV[1];
            $curworkdir = "$workdir/$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 = "$curworkdir/status";
            } else {
                $statusfile = "$curworkdir/.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($curworkdir, $frame);
            $img_prefix = &get_img_prefix($img_ext);

            umask 0111;

            if ($hsize == -1 || $vsize == -1) {
                $com = "$smog_convert_command $img_prefix\"$curworkdir/$name$img_ext\" \"$nfile\" >$nulfile 2>&1";
            } else {
                if ($antialias eq "false") {
                    $com = "$smog_convert_command +antialias $img_prefix\"$curworkdir/$name$img_ext\" -scale $hsize"."x"."$vsize\\! \"$nfile\" > $nulfile 2>&1";
                } else {
                    $com = "$smog_convert_command -antialias $img_prefix\"$curworkdir/$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];

            $curworkdir = "$workdir/$handle";

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

            smog_chdir("$curworkdir");
            $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($curworkdir);
                        $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];
            $curworkdir="$workdir/$handle";
            if ($^O eq "MSWin32") {
                $statusfile="$curworkdir/status";
            }
            else {
                $statusfile="$curworkdir/.status";
            }
            $dwidth=$ARGV[2];
            $dheight=$ARGV[3];
            $img_ext=".".$ARGV[4];
            $img_prefix=&get_img_prefix($img_ext);
            $file=$ARGV[5];

            if (! -d "$curworkdir") {
                mkdir ("$curworkdir");
            }

            $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("$curworkdir");

            $name=&mkname(1);

            if ($antialias eq "false") {
                smog_system("$smog_convert_command +antialias \"$file\" -scale $hsize"."x"."$vsize\\! $img_prefix\"$curworkdir/$name$img_ext\" > $nulfile 2>&1");
            }
            else {
                smog_system("$smog_convert_command -antialias \"$file\" -resize $hsize"."x"."$vsize\\! $img_prefix\"$curworkdir/$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];
            $curworkdir="$workdir/$handle";
            if ($^O eq "MSWin32") {
                $statusfile="$curworkdir/status";
            }
            else {
                $statusfile="$curworkdir/.status";
            }
            smog_chdir("$curworkdir");
            $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($curworkdir,$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") {
                            &clear_symlinks;
                        }
                    }
                }
            }

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

            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,"$workdir/$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 "$workdir/$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, "$workdir";
            my @files = readdir(DIR);

	    # sort most recent first (in case of multiple files)
            @files = sort {(stat "$workdir/$b")[10] <=> (stat "$workdir/$a")[10]}(@files);

            foreach my $file(@files) {
                if ($file =~ /^$filepart\.$uid\.$gid\.(.*)/) {
                    if (-z $file) {
			# clean up zero length files
                        unlink "$file";
                    } else {
			# check to make sure pid is not a running LiVES
                        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, "$workdir";

            smog_chdir("$workdir");

            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, "$workdir";
            @allfiles = readdir(DIR);
            closedir DIR;

            foreach my $subdir(@allfiles) {
                if (-f "$workdir/$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 = "$workdir/info.$pid";
            } else {
                $infofile = "$workdir/.info.$pid";
            }
            smog_system_sync();

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

        if ($command eq "cleanup" || $command eq "weed") {
            smog_chdir("$workdir");
            print "Cleaning up temporary space for $GUI_NAME.\n";
            if ($command eq "cleanup") {
                unlink glob "* .* *.*";
		# will not remove directories
            } else {
                $new_work = $ARGV[1];
                unless($new_work eq "") {
		    # here is where we create a new work directory
                    unless(-d $new_work || (mkdir $new_work)) {
                        print "Smogrify: Unable to create new directory $new_work!\n";
                        exit 1;
                    }
                    print "Smogrify: moving sets from $workdir to $new_work\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;

            $curworkdir = "$workdir/$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];
            $curworkdir = "$workdir/$handle";
            if ($^O eq "MSWin32") {
                $statusfile = "$curworkdir/status";
            } else {
                $statusfile = "$curworkdir/.status";
            }
            unlink "$curworkdir/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 ($command eq "create_package") {
	    # args are 1: pkg name, 2 dir to pack contents of

            if ($^O ne "MSWin32") {
                my $com = "tar czf \"$ARGV[1]\" -C \"$ARGV[2]\" .";

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

            } else {
                chdir("$workdir");
                unlink "temp.pakage";
                my $com = "tar.exe -cf temp.pakage -C \"$ARGV[2]\"";
                $smerr = smog_system($com);
                if ($smerr) {
                    sig_system_error("$com", $smerr);
                    unlink "$pidfile";
                    exit 1;
                }
                my $com = "gzip.exe -c temp.pakage > \"$ARGV[1]\" ";
                $smerr = smog_system($com);
                if ($smerr) {
                    sig_system_error("$com", $smerr);
                    unlink "$pidfile";
                    exit 1;
                }
                unlink "temp.pakage";
            }
            exit 0;
        }

        if ($command eq "import_package") {
	    ## args are 1: file to unpack, 2 dir to unpack in

            $com = "/bin/mkdir -p \"$ARGV[2]\"";
            smog_system($com);
            chdir $ARGV[2];

            if ($^O ne "MSWin32") {
                $com = "tar xzf \"$ARGV[1]\" .";

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

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

        $curworkdir = "$workdir/$handle";

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

        if ($command eq "play" || $command eq "play_opening_preview") {
            if ($^O eq "MSWin32") {
                $pidfile = $curworkdir."/pidpb";
            } else {
                $pidfile = $curworkdir."/.pidpb";
            }
        } else {
            if ($^O eq "MSWin32") {
                $pidfile = $curworkdir."/pid";
            } else {
                $pidfile = $curworkdir."/.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 = "$curworkdir/audiodump.pcm";
            } else {
                $aformat = ".raw";
                $audiofile = "$curworkdir/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_play_command eq "" or $audio_play_command eq '""') {
                    $time = ($audio_end - $audio_start);
                    select(undef, undef, undef, $time);
                }
                elsif($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 < 13000000) {
                        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 {
		    ## TODO - mpv
		    # 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 "$curworkdir" || -f "$curworkdir/.stoploop" || -f "$curworkdir/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 "$curworkdir/.stoploop";
            unlink "$curworkdir/stoploop";

            exit 0;
        }

        if ($command eq "open_test") {
            $mplay_command = $ARGV;
            shift(@ARGV);
            $command = "open";
            $opentest = 1;
        }

        if ($command eq "open") {
            unlink "$curworkdir/pause";

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

            $withsound = $ARGV[3];

            if (defined($ARGV[4])) {
                $img_ext = ".".(split(":", $ARGV[4]))[0];
                $ximg_ext = ".".(split(":", $ARGV[4]))[1];
                if ($ximg_ext eq "") {
                    $ximg_ext = $img_ext;
                }
            }

            smog_chdir("$curworkdir");

            unless($file =~ /^http/) {
                if (! -f "$file" && ! -d "$file") {
                    &sig_error("$file", "could not be found", "$GUI_NAME was unable to open it.", "Please check the file name and try again.");
                }
            }

            &get_file_info;
	    ## here we set $mplay_command, $is_mpv

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

            if (defined($ARGV[5]) && $ARGV[5] > 0) {
                if (!$is_mpv) {
                    $ss = " -ss $ARGV[5] ";
                } else {
                    $ss = " -start=$ARGV[5] ";
                }
            }
            $stframes = "";
            if (defined($ARGV[6]) && $ARGV[6] > 0) {
                $nframes = $ARGV[6]++;
		# cf below, we remove 1st frame
                if (!$is_mpv) {
                    $stframes = " -frames $ARGV[6] ";
                } else {
                    $stframes = " -frames=$ARGV[6] ";
                }
            }

            $extra_params = $ARGV[7];
            $band = "";
            if ($is_mpv || $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;
            }

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

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

	    my $wavhead="nowaveheader";

	    if ($is_mpv) {
		$wavhead="no-waveheader";
	    }

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

	    if ($is_mpv) {
		$aformnew="-audio-format=s16";
	    }

	    #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;
		$img_ext=$ximg_ext;
	    }
	    else {
		if ($achans eq "" || $achans eq "0") {
		    $channs="";
		}
		else {
		    if ($is_mpv) {
			$channs="-audio-channels=$achans";
		    }
		    else {
			$channs="-channels $achans";
		    }
		}
		if ($is_mpv) {
		    if ($img_ext eq ".jpg") {
			$img_fmt="-vo image:format=jpg:jpeg-quality=$quality";
		    }
		    else {
			$img_fmt="-vo image:format=png:png-compression=$compression";
		    }
		}
		else {
		    if ($img_ext eq ".jpg") {
			$img_fmt="-vo jpeg:quality=$quality";
		    }
		    else {
			$img_fmt="-vo png:z=$compression";
		    }
		}

		if (!$is_mpv) {
		    $osd="-osdlevel 0";
		    $threadopts="-lavdopts o=threads=$threads";
		    $noframedrop="-noframedrop";
		    $vcnull="-vc null";
		    $audcom="-ao pcm:fast:$wavhead";
		}
		else {
		    $osd="-osd-level=0";
		    $threadopts="-vd-lavc-threads=$threads";
		    $noframedrop="-framedrop=no";
		    $vcnull="--no-video";
		    $audcom="-ao=pcm:$wavhead";
		}

		if ($withsound eq "0") {
		    #video only
		    $syscom="$mplay_command -quiet $band $osd $img_fmt $threadopts -fps 1000000. $ss $stframes $noframedrop -ao null \"$file\" $extra_params <$nulfile";
		}
		elsif ($withsound eq "1") {
		    #video and audio
		    $syscom=$mplay_command . " -quiet $band $osd -fps 1000000. $img_fmt $ss $threadopts $noframedrop $stframes $audcom $channs $aformnew -mc 0"
                        . "  \"$file\" $extra_params <$nulfile";
		}
		else {
		    #audio only 
		    $syscom=$mplay_command . " -quiet $band -vo null $vcnull $ss $stframes $audcom $aformnew $channs \"$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;
		}

		if (!$is_mpv) {
		    @info=split /  /, smog_system_direct("grep VIDEO: \"$curtmpfile\" 2>$nulfile");
		    @info2=split / /, smog_system_direct("grep AUDIO: \"$curtmpfile\" 2>$nulfile");
		}
		else {
		    @info=split /  /, smog_system_direct("grep VO: \"$curtmpfile\" 2>$nulfile");
		    @info2=split / /, smog_system_direct("grep AO: \"$curtmpfile\" 2>$nulfile");
		}

		unlink "$curtmpfile";
		#$type=$info[1];

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

		if (!$is_mpv) {
		    $xbpp=int($info[3]);

		    $xfps=(split / /,$info[4])[0];

		    $xarate=int($info2[1]);
		    $xachans=int($info2[3]);
		}
		else {
		    $xbpp = 0;
		    $xfps = 0.;
		    $xarate=int($info2[2]);
		    $xachans=int($info2[4]);
		}

		if ($xbpp>0) {
		    $bpp=$xbpp;
		}
		if ($xfps>0.) {
		    $fps=$xfps;
		}
		if ($xarate>0) {
		    $arate=$xarate;
		}
		if ($xachans>0) {
		    $achans=$xachans;
		}

		if (! -d $curworkdir) {
		    #curworkdir 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 $curworkdir) {
		    #curworkdir 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";
			if (!$is_mpv) {
			    print STDERR "\nMaybe you are missing a library in mplayer (or it is not a valid media file) ?\n";
			}
			else {
			    print STDERR "\nMaybe you are missing a library in mpv (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--;
		}
	    }

	    ## this is not accurate
	    #if ($fps==0.&&$length!=0) {
	    #	$fps=$count/$length;
	    #    }

	    $af_size= -s $audio_out;
	    if (-d $curworkdir) {
		&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") {
	    #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("$curworkdir");

	    &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]";
		}
	    }

	    my $mp=&get_mplayer_location;

	    ## note does not function with mpv since it cannot convert to yuv4mpeg (complains about missing pixel_fmt)
	    $com="$mp -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];
	    my $mp=&get_mplayer_location;
	    ## note does not function with mpv since it cannot convert to yuv4mpeg (complains about missing pixel_fmt)
	    $com="dvgrab -s 0 -noavc -card $cardno -o - 2>$nulfile | $mp - -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("$curworkdir")) {
		unlink glob "* .* *.*";
		smog_chdir("$workdir");
		rmdir $curworkdir;
	    }
	    exit 0;
	}

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

	    smog_chdir("$curworkdir");

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

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

	    }

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

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

	    $linksdir="$curworkdir/lives-symlinks/";

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

	    $sox_version=&get_sox_version;

	    if ($sox_version < 14004001) {
		if ($ssigned==1) {
		    $asigned="-s";
		}
		else {
		    $asigned="-u";
		}
	    }
	    else {
		if ($ssigned==1) {
		    $asigned="-e signed-integer";
		}
		else {
		    $asigned="-e unsigned-integer";
		}
	    }

	    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($curworkdir,$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 "$curworkdir/audio" && $arate>0) {
		    $origaudio=$audiofile=$audio_in="$curworkdir/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( "$curworkdir/$audiofile","$origaudio");
			    $audio_file=$audio_in=$origaudio;
			}
		    }
		    if ($areq&2) {
			# convert raw audio to wav
			$audio_out=$curworkdir . "/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;
			};

			if ($^O ne "MSWin32") {
			    $fstype=&get_fs_type($curworkdir);
			}

			# 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 "$curworkdir$from$img_ext") {
				if ($^O eq "MSWin32") {
				    $smerr=smog_system ("cp.exe \"$curworkdir/$from$img_ext\" \"$linksdir$name$img_ext\"");
				    if ($smerr) {
					sig_system_error("Copying \"$curworkdir/$from$img_ext\" to \"$linksdir$name$img_ext\"",$smerr);
					unlink "$pidfile";
					exit 1;
				    }
				}
				else {
				    if ($fstype eq "vfat" || $fstype eq "fat32" || $fstype eq "msdos") {
					$smerr=smog_system ("/bin/cp -f \"$curworkdir/$from$img_ext\" \"$linksdir$name$img_ext\"");
					if ($smerr) {
					    sig_system_error("Copying \"$curworkdir/$from$img_ext\" from \"$linksdir$name$img_ext\"",$smerr);
					    unlink "$pidfile";
					    exit 1;
					}
				    }
				    else {
					$smerr=smog_system ("/bin/ln \"$curworkdir/$from$img_ext\" \"$linksdir$name$img_ext\"");
					if ($smerr) {
					    sig_system_error("Symlinking \"$curworkdir/$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") {
				if ($^O eq "MSWin32") {
				    $smerr=smog_system ("cp.exe \"$curworkdir/$audiofile\" \"$linksdir$xaudiofile\"");
				    if ($smerr) {
					sig_system_error("Copying \"$curworkdir/$audiofile\" to \"$linksdir$xaudiofile\"",$smerr);
					unlink "$pidfile";
					exit 1;
				    }
				}
				else {
				    if ($fstype eq "vfat" || $fstype eq "fat32" || $fstype eq "msdos") {
					$smerr=smog_system("/bin/cp -f \"$curworkdir/$audiofile\" \"$linksdir$xaudiofile\"");
					if ($smerr) {
					    sig_system_error("Copying \"$curworkdir/$audiofile\" from \"$linksdir$xaudiofile\"",$smerr);
					    unlink "$pidfile";
					    exit 1;
					}
				    }
				    else {
					$smerr=smog_system("/bin/ln \"$curworkdir/$audiofile\" \"$linksdir$xaudiofile\"");
					if ($smerr) {
					    sig_system_error("Symlinking \"$curworkdir/$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 "$curworkdir/pause";
	    smog_chdir("$curworkdir");

	    $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 "") {
		$linksdir="$curworkdir/lives-symlinks/";

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

	    }
	    else {
		$linksdir=$curworkdir;
		$handle=$from_handle;
		$curworkdir="$workdir/$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 $ocurworkdir=$curworkdir;
		my $ofrom_handle=$from_handle;
		my $ostart=$start;
		my $oend=$end;

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

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

		$curworkdir=$ocurworkdir;
		$from_handle=$ofrom_handle;
		$start=$ostart;
		$end=$oend;

	    }

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

	    if ($^O ne "MSWin32") {
		$fstype=&get_fs_type($curworkdir);
	    }

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

		}
		&sig_progress($i);
	    }

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

	    &sig_complete;
	    exit 0;
	}

	if ($command eq "resize_all") {
	    unlink "$curworkdir/pause";
	    smog_chdir("$curworkdir");
	    $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";

	    $blankname="blank.jpg";

	    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 $blankname >$nulfile 2>&1";
		$smerr=smog_system($com);
		if ($smerr) {
		    sig_system_error("$com",$smerr);
		    unlink $blankname;
		    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 $blankname;
		    }
		    if ($DEBUG_SMOGRIFY) {
			print STDERR "smogrify debug - rename failed ($!): \"$curworkdir/$name$img_ext\",\"$curworkdir/$name.bak\"\n";
		    }
		    sig_system_error("Renaming \"$curworkdir/$name$img_ext\" to \"$curworkdir/$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 $blankname;
		    }
		    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 $blankname;
		    }
		    unlink "$pidfile";
		    exit 1;
		}

		&sig_progress($i);
	    }

	    $start=1;

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

	    &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 "$curworkdir/pause";
	    smog_chdir("$curworkdir");

	    # 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("$curworkdir");

	    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","$curworkdir\\temp.gz");
		if ($panic) {
		    unlink "$pidfile";
		    exit 1;
		}
		$com="gzip.exe -d temp.gz";
		$smerr=smog_system ($com);
		if ($smerr) {
		    sig_system_error("$com",$smerr);
		    unlink "$pidfile";
		    exit 1;
		}
		$com="tar.exe -xf temp";
		$smerr=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,$curworkdir;
	    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 "$curworkdir/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="$curworkdir/event.frames";
	    $resize_ext=".tmp";
	    smog_chdir("$curworkdir");

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

	    $blankname="blank.jpg";

	    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 $blankname>$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 "$curworkdir/$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 $blankname;
				    }
				    unlink "$pidfile";
				    exit 1;
				}

				$smerr=rename "$curworkdir/$from$resize_ext","$curworkdir/$to.mgk";
				if (!$smerr) {
				    if ($letterbox) {
					try_to_recover($start,$i);
					unlink $blankname;
				    }
				    sig_system_error("Renaming \"$curworkdir/$from$resize_ext\" to \"$curworkdir/$to.mgk\"",$!);
				    unlink "$pidfile";
				    exit 1;
				}
			    }
			    else {
				unless (($from eq $to) || $letterbox) {
				    smog_copy("$curworkdir/$from$img_ext", "$curworkdir/$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("$curworkdir/$from$img_ext", "$curworkdir/$to$img_ext.2");
					if ($panic) {
					    try_to_recover($start,$i);
					    unlink "$pidfile";
					    exit 1;
					}
					smog_copy("$curworkdir/$from.mgk", "$curworkdir/$to.mgk.2");
					if ($panic) {
					    try_to_recover($start,$i);
					    unlink "$pidfile";
					    exit 1;
					}
				    }
				}
				else {
				    smog_copy("$curworkdir/$to$img_ext", "$curworkdir/$to.bak");
				    if ($panic) {
					try_to_recover($start,$i-1);
					unlink "$pidfile";
					unlink $blankname;
					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 $blankname;
					exit 1;
				    }

				    #lb are in .img
				}

			    }

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

		if ($letterbox&&!$leave_bak) {
		    unlink $blankname;
		}

	    }

	    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 "$curworkdir/$name.mgk.2") {
			if (-f "$curworkdir/$name$img_ext.2") {
			    unlink "$curworkdir/$name$img_ext";
			    $smerr = rename "$curworkdir/$name$img_ext.2", "$curworkdir/$name$img_ext";
			    if (!$smerr) {
				if ($letterbox) {
				    try_to_recover($start, $end);
				}
				sig_system_error("Renaming \"$curworkdir/$name$img_ext.2\" to \"$curworkdir/$name$img_ext\"", $!);
				unlink "$pidfile";
				exit 1;
			    }

			}
			unlink "$curworkdir/$name.mgk";
			$smerr = rename "$curworkdir/$name.mgk.2", "$curworkdir/$name.mgk";

			if (!$smerr) {
			    if ($letterbox) {
				try_to_recover($start, $end);
			    }
			    sig_system_error("Renaming \"$curworkdir/$name.mgk.2\" to \"$curworkdir/$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 "$curworkdir/$name$img_ext") {
			unlink "$curworkdir/$name.bak";
			smog_rename("$curworkdir/$name$img_ext", "$curworkdir/$name.bak");
		    }
		}
		&sig_progress($i);
	    }

	    &sig_complete($new_count);
	    exit 0;
	}

	if ($command eq "deorder") {
	    $curworkdir = "$workdir/$handle";
	    $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("$curworkdir");

	    $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 "$curworkdir/$name$img_ext";
		    } else {
			smog_rename("$curworkdir/$name$img_ext", "$curworkdir/$name.mgk");
		    }
		}
	    } else {
		# frames were downsampled
		for ($i = $end + 1; $i <= $frames; $i++) {
		    $name = &mkname($i);
		    if (!$leave_bak) {
			if (-f  "$curworkdir/$name.bak") {
			    smog_rename("$curworkdir/$name.bak", "$curworkdir/$name$img_ext");
			}
		    } else {
			smog_rename("$curworkdir/$name$img_ext", "$curworkdir/$name.mgk");
		    }
		}
	    }

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

	    &sig_complete;
	    exit 0;
	}

	if ($command eq "cut") {
	    unlink "$curworkdir/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 $curworkdir) {
		&sig_complete;
		exit 1;
	    }
	    smog_chdir("$curworkdir");
	    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 "$curworkdir/pause";
	    $start = $ARGV[2];
	    $end = $ARGV[3];
	    $img_ext = ".".$ARGV[4];
	    &reverse;
	    &sig_complete;
	    exit 0;
	}

	if ($command eq "undo") {
	    unlink "$curworkdir/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 "$curworkdir/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 = &get_mplayer_location;

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

	if ($command eq "mv_mgk") {
	    unlink "$curworkdir/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.");
	    }

	    $sox_version = &get_sox_version;

	    if ($sox_version < 14004001) {
		if ($asigned == 1) {
		    $asigned = "-s";
		} else {
		    $asigned = "-u";
		}
	    } else {
		if ($asigned == 1) {
		    $asigned = "-e signed-integer";
		} else {
		    $asigned = "-e unsigned-integer";
		}
	    }

	    if ($audio_end > 0.) {
		$audio_in = &clip_audio($audio_start, $audio_end);
		if ($panic) {
		    unlink "$pidfile";
		    exit 1;
		}
	    } else {
		$audio_in = "$curworkdir/audio";
	    }

	    # convert raw audio to wav
	    $audio_out = $curworkdir . "/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 "recover_audio") {
	    $audio_in = "audiodump.pcm";

	    $audio_out = "audio";
	    &convert_audio_to_raw;
	    if ($panic) {
		unlink "$pidfile";
		exit 1;
	    }
	    unlink "$audio_in";

	}

	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("$curworkdir");

	    $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 \"$curworkdir/audio\" \"$audio_out\" > \"$curworkdir/audiodump\"");
	    } else {
		$smerr = smog_system("/bin/cat \"$curworkdir/audio\" \"$audio_out\" > \"$curworkdir/audiodump\"");
	    }

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

	    unlink "$audio_out";
	    #audio.new
	    $fsize = -s "$curworkdir/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("$curworkdir");

	    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 = "$curworkdir/audio.bak";
	    $audio_out = "$curworkdir/audio";

	    if (defined($ARGV[12])) {
		$stretch = $ARGV[12];
		$audio_in = "$curworkdir/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 > \"$curworkdir/tmpinfo\"");

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

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

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

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

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

	    unlink "$curworkdir/tmpinfo";
	    unlink "$curworkdir/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 "$curworkdir/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 = "$curworkdir/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 = "$curworkdir/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

	    $allow_nonex = 0;
	    if (defined $ARGV[2]) {
		$allow_nonex = $ARGV[2];
	    }

	    smog_chdir("$curworkdir");
	    $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 {
		    unless($allow_nonex) {
			&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("$curworkdir");
	    unlink glob "audiodump* audio.new";

	    &sig_complete;
	    exit 0;
	}

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

	    print("OPEN $url\n");
	    
	    if ($url =~ /youtube/ || $url =~ /youtu\.be/) {
		# youtube-dl

		print("OK\n");
		
		if (-f $dfile) {
		    unlink "$dfile";
		}

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

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

		print ("RUNNING $com\n");
		
		&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 "") {
		$cdda2wav_command = &location("icedax");
		if ($cdda2wav_command eq "") {
		    &sig_error("cdda2wav or icedax 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 = $curworkdir."/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("$curworkdir");

	    $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]

	    # with -ve end means "allow missing frames" (used for copying to clipboard)

	    unlink "$curworkdir/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("$curworkdir");

	    $allow_missing = 0;
	    if ($end < 0) {
		# insert (copy) from (virtual) clip into clipboard
		$allow_missing = 1;
		$end = -$end;
	    }

	    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;
			if ($with_audio) {
			    if (-f "audio") {
				## back this up here for undo
				smog_rename("audio", "audio.bak");
			    }
			}
		    } else {
			# $start < 0 tells us to leave backups alone
			$start = -$start;
		    }
		    $antialias = &rc_get("antialias");
		}
	    }

	    if ($with_audio < 2) {
		if (!$undo_cut) {
		    if (!$allow_missing) {
			$img_ext2 = &get_img_ext("$workdir/$from_handle", $start);
			$img_prefix2 = &get_img_prefix($img_ext2);
		    } else {
			$img_ext2 = $img_ext;
			$img_prefix2 = $img_prefix;
		    }
		}

		&insert;

		if ($panic) {
		    unlink "$pidfile";
		    exit 1;
		}
		if ($allow_missing) {
		    &sig_progress($new_frames);
		    sleep(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.bak") {
			    smog_copy("audio.bak", "audio");
			}
			if ($panic) {
			    unlink "$pidfile";
			    exit 1;
			}
			smog_system_sync();
		    } else {
			$end = -s "$curworkdir/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;

	    if (-f "$curworkdir/audio.bak") {
		&undo_audio;
	    }
	    # will abort on failure

	    &clean_old;

	    &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("$curworkdir");
	    unlink "$curworkdir/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,$curworkdir,$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) = "$curworkdir/.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 \"$curworkdir\" $img_ext $img_ext2 $out_ext @ARGV > \"$res_file\"");
		    } else {
			$err = smog_system("\"$plugin_name\" process \"$curworkdir\" $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 \"$curworkdir\" $img_ext $out_ext @ARGV > \"$res_file\"");
		    } else {
			$err = smog_system("\"$plugin_name\" process \"$curworkdir\" $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 \"$curworkdir\" $out_ext @ARGV > \"$res_file\"");
		    } else {
			$err = smog_system("\"$plugin_name\" process \"$curworkdir\" $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("$workdir");
	    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("$workdir");
	    $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 "$curworkdir/pause") {
        smog_system_sync();
    }
    while (-f "$curworkdir/pause") {
        sleep 1;
    }
}


sub check_for_stop {
    if (defined(open IN, "< $curworkdir/.status.fileop")) {
        close IN;
        return 1;
    }
    return 0;
}


sub kill_child_pids {
    my($target_pid, $sig) = @_;
    my $pid;
    smog_system("pgrep -P $target_pid > \"$workdir/.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 "$workdir/.pids.$target_pid"&&defined(open IN,"$workdir/.pids.$target_pid")) {
        while (<IN>) {
            $pid=$_;
            chomp($pid);
            &kill_child_pids($pid,$sig);
        }
        close IN;
    }

    unlink "$workdir/.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 $curworkdir\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 $curworkdir\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 "$curworkdir/$name.mgk") {
            if (-f "$curworkdir/$name$img_ext") {
                if ($leave_bak==1&&-f "$curworkdir/$name.bak") {
                    unlink "$curworkdir/$name$img_ext";
                }
                else {
                    $smres=rename "$curworkdir/$name$img_ext","$curworkdir/$name.bak";
                    if (!$smres) {
                        try_to_recover($start,$i-1);
                        if ($DEBUG_SMOGRIFY) {
                            print STDERR "smogrify debug - rename failed ($!): \"$curworkdir/$name$img_ext\",\"$curworkdir/$name.bak\"\n";
                        }
                        sig_system_error("Renaming \"$curworkdir/$name$img_ext\" to \"$curworkdir/$name.bak\"");
                        unlink $pidfile;
                        exit 1;
                    }
                }
            }
            $smerr=rename( "$curworkdir/$name.mgk","$curworkdir/$name$img_ext");
            if (!$smerr) {
                try_to_recover($start,$i);
                if ($DEBUG_SMOGRIFY) {
                    print STDERR "smogrify debug - rename failed ($!): \"$curworkdir/$name.mgk\",\"$curworkdir/$name$img_ext\"\n";
                }
                sig_system_error("Renaming \"$curworkdir/$name.mgk\" to \"$curworkdir/$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("$curworkdir");
    unlink glob "*.mgk";

    for ($i = $start; $i <= $end; $i++) {
        $name = &mkname($i);
        if (-f "$curworkdir/$name.pre") {
            if (-f "$curworkdir/$name$img_ext") {
                unlink "$curworkdir/$name.bak";
                $smerr = rename("$curworkdir/$name$img_ext", "$curworkdir/$name.bak");
                if (!$smerr) {
                    try_to_recover($start, $i - 1);
                    if ($DEBUG_SMOGRIFY) {
                        print STDERR "smogrify debug - rename failed ($!): \"$curworkdir/$name$img_ext\",\"$curworkdir/$name.bak\"\n";
                    }
                    sig_system_error("Renaming \"$curworkdir/$name$img_ext\" to \"$curworkdir/$name.bak\"");
                    unlink "$pidfile";
                    exit 1;
                }
            }
            $smerr = rename "$curworkdir/$name.pre", "$curworkdir/$name$img_ext";
            if (!$smerr) {
                try_to_recover($start, $i);
                if ($DEBUG_SMOGRIFY) {
                    print STDERR "smogrify debug - rename failed ($!): \"$curworkdir/$name.pre\",\"$curworkdir/$name$img_ext\"\n";
                }
                sig_system_error("Renaming \"$curworkdir/$name.pre\" to \"$curworkdir/$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 "$curworkdir/$name.bak") {
            if ($param == 0 && -f "$curworkdir/$name$img_ext") {
		# file may not exist if we are undoing resample for example
                smog_rename("$curworkdir/$name$img_ext", "$curworkdir/$name.mgk");
            }
            smog_rename("$curworkdir/$name.bak", "$curworkdir/$name$img_ext");
        }
        &sig_progress($i);
    }
}


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


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


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

    for ($i = $start; $i <= $end; $i++) {
        $name = &mkname($i);
        if (-f "$name$img_ext") {
            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);
        if (-f "$curworkdir/$from$img_ext") {
            smog_rename("$curworkdir/$from$img_ext", "$curworkdir/$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);
        $hasfrom = -f "$curworkdir/$from$img_ext";
        $hasto = -f "$curworkdir/$to$img_ext";
        if ($hasfrom) {
            if ($hasto) {
                smog_rename("$curworkdir/$from$img_ext", "$curworkdir/$from.revtemp");
                smog_rename("$curworkdir/$to$img_ext", "$curworkdir/$from$img_ext");
            }
            &sig_progress($start + ($i - $start) * 2);
            if ($hasto) {
                smog_rename("$curworkdir/$from.revtemp", "$curworkdir/$to$img_ext");
            } else {
                smog_rename("$curworkdir/$from$img_ext", "$curworkdir/$to$img_ext");
            }
        } else {
            &sig_progress($start + ($i - $start) * 2);
            if ($hasto) {
                smog_rename("$curworkdir/$to$img_ext", "$curworkdir/$from$img_ext");
            }
        }
        &sig_progress($start + ($i - $start) * 2 + 1);
    }
}


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

    if ($start <= $frames) {
	## insert was BEFORE

	## $frames is orig frames
	## we inserted $frames_inserted from start to end

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

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

    } else {
	## insert was AFTER, remove excess frames
        for ($i = $frames + 1; $i <= $frames + $frames_inserted; $i++) {
            $name = &mkname($i);
            unlink "$curworkdir/$name$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 = "$workdir/$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 (-f "$curworkdir/$from$img_ext") {
            smog_rename("$curworkdir/$from$img_ext", "$curworkdir/$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) {
                if (-f "$fromdir/$from.bak") {
                    smog_rename("$fromdir/$from.bak", "$curworkdir/$to$img_ext");
                    return if $panic;
		}
	    } else {
		if (! -f "$fromdir/$from$img_ext2") {
                    next if ($allow_missing);
                    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", "$curworkdir/$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\"$curworkdir/$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\"$curworkdir/$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 = $curworkdir;
        }
        $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\" $blankname \"$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 $sizex$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 $hsizex$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 (!defined($sox_version)) {
        $sox_version = &get_sox_version;
    }

    if ($asamps == 8) {
        $format = 16;
        $signed = 0;
        if ($sox_version < 14004001) {
            $xsigned = "-u";
        } else {
            $xsigned = "-e unsigned-integer";
        }
    } else {
        $signed = 1;
        if ($sox_version < 14004001) {
            $xsigned = "-s";
        } else {
            $xsigned = "-e signed-integer";
        }
        if ($endian == 1) {
	    # little
            $format = 128;
        } else {
	    # big
            $format = 256;
        }
    }
}


sub get_mpv_info {
    ## we will set the following if possible:
    # $count, $bpp, $type, $hsize, $vsize, $signed, $f_size, $arate, $achans, $asamps, $endian, $abitrate
    # $length, $id_vid_form, $id_aud_form
    # $comment, $title, $author

    my @allprops = split("\n",
			 "filename
    path
    stream-start
    stream-end
    stream-length

    demuxer

    length
    chapters
    editions
    titles

    audio
    audio-bitrate
    audio-codec
    audio-format
    audio-channels
    audio-samplerate

    video
    angle
    video-bitrate
    video-codec
    video-format
    video-aspect
    fps
    width
    height
    dwidth
    dheight

    sub");

    my $propstr = "";

    foreach my $line(@allprops) {
        $line =~ s/^\s+//;
        $propstr.= "XDATA:$line=\\\${=$line}\n";
    }

    if (defined($is_remote) && ($is_remote > 0)) {
        $com = $mplay_command.' --term-playing-msg="'.$propstr.'" --vo=null --ao=null --frames=1 --quiet --cache=32 --no-config "'
	    .$file.'">'.$file_ident.' 2>'.$nulfile;
    } else {
        $com = $mplay_command.' --term-playing-msg="'.$propstr.'" --vo=null --ao=null --frames=1 --quiet --no-cache --no-config "'
	    .$file.'">'.$file_ident.' 2>'.$nulfile;
    }

    $smerr = smog_system($com);

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

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

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

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

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

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

    $type = $id_vid_form;

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

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

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

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

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

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

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

    ## as of 0.9.2 cannot get asamps, endian, frames, author, title, comment
    $count = int($fps*$length);

    $asamps = 16;
    ## cannot get this from mpv (guess)
    $endian = &get_endian;
    ## (guess)

    $author = $title = $comment = "";
}


sub get_mplayer_info {
    ## we will set the following if possible:
    # $count, $bpp, $type, $hsize, $vsize, $signed, $f_size, $arate, $achans, $asamps, $endian, $abitrate
    # $length, $id_vid_form, $id_aud_form
    # $comment, $title, $author

    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];
            $fps = trim($fps);
            if ($fps eq "nan" or $fps eq "-nan") {
                $fps = 0.;
            }

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


sub trim {
    ## trim whitespace from beginning and end
    my $str = shift;
    $str =~ s/^\s+//;
    $str =~ s/\s+$//;
    $str;
}


sub get_file_info {
    ## we will set the following if possible:
    # $count, $bpp, $type, $hsize, $vsize, $signed, $f_size, $arate, $achans, $asamps, $endian, $abitrate
    # $length, $id_vid_form, $id_aud_form
    # $comment, $title, $author

    # $type may be a video format ($id_vid_form) or "Audio" or an image format ("jpeg", "png", etc).

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

    $endian="";

    $is_mpv=0;
    $file_ident="file_info";

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

    $mp_command=&rc_get("video_open_command");
    if ($mp_command ne "") {
        my $mpc1=(split(" ",$mp_command))[0];

        if ($mpc1 =~ /mpv$/ || $mpc1 =~ /mpv\"$/) {
	    $is_mpv=1;
	}
    }
    else {
	# sets $is_mpv
        $mp_command="\"".&get_mplayer_location."\"";
    }

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

    if (!defined $mplay_command || $mplay_command eq "") {
        if ($^O ne "MSWin32") {
	    # try to force language to English
            $mplay_command="LANGUAGE=en LANG=en $mp_command";
        }
        else {
            $mplay_command = $mp_command;
        }
    }

    $id_vid_form="";

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

    smog_chdir("$curworkdir");

    # 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 ($is_mpv) {
            &get_mpv_info;
        }
        else {
            &get_mplayer_info;

        }

    }

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

    unlink "$file_ident";

    $fwtype=(split(" ",$type))[0];

    #print ("IMG FILT .$count. .$type. .$length. .$fps.\n");

    if (($hsize*$vsize==0||$count eq "")
	## test for various image types...
	||(($type eq "ffpng" || $fwtype eq "PNG" || (($type eq "mjpeg" || $type=="png") && $length eq "" && int($fps)==1) || $type eq "gif")
	   && $count eq "0")
	||(($type eq "ffmjpeg" || $fwtype eq "MJPEG")
	   && $count eq "1")
	||($fps==1. && $count eq "1" && $id_aud_form eq "" && (int($length)==1 || $length eq ""))

	&&!$is_audio) {
        $origcount=$count;

        $name=&mkname(1);

        unless (-f "$curworkdir/$name") {
	    # see if it is image(s)
            &open_images(1);
            if ($hsize==-1) {
                $hsize=$vsize=0;
            }
            opendir DIR,$curworkdir;
            while ($file2=readdir(DIR)) {
                if ($file2 =~ /$ximg_ext$/) {
                    $count++;
                    $count-=$origcount;
                    $origcount=0;
                    $imresact="none";
                    if ($only_first) {
                        unlink glob "$curworkdir/*$ximg_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 ($ximg_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;

    my $ap=&rc_get("audio_player");

    if (&location("sox") ne "") {
        $smcom="sox -t .wav \"$audio_in\" -t .raw $xsigned \"$audio_out\" > $nulfile 2>&1";
    }
    else {
        $mp_com=&get_mplayer_location;

        $format=&get_mplayer_format;

        if ($is_mpv && $format eq "") {
            sig_system_error("mpv cannot fully resample");
            $panic=1;
            return;
        }

        $smcom="$mp_com -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("$curworkdir");
    if (&location("sox") eq "") {
        $mp_com=&get_mplayer_location;
        $format=&get_mplayer_format;

        if ($is_mpv and $format eq "") {
            sig_system_error("mpv cannot fully resample");
            $panic=1;
            return;
        }

        $smcom="$mp_com -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<13000000) {
            $nodither="";
            if ($asamps==8) {
                $sasamps="b";
            }
            else {
                $sasamps="w";
            }
        }
        else {
            $nodither="-D";
            $sasamps=$aasamps;

            if ($sox_version >= 14004001) {
                if ($aasamps == 1) {
                    $sasamps = "b 8";
                }
                elsif ($aasamps == 2) {
                    $sasamps = "b 16";
                }
                elsif ($aasamps == 3) {
                    $sasamps = "b 24";
                }
                elsif ($aasamps == 4) {
                    $sasamps = "b 32";
                }
                elsif ($aasamps == 8) {
                    $sasamps = "b 64";
                }

            }

        }

        if (!defined($nrate)) {
            $nrate=$arate;
        }

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

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

        if (!defined($asigned)) {
            if ($sox_version < 14004001) {
                if ($asamps==8) {
                    $asigned="-u";
                }
                else {
                    $asigned="-s";
                }
            }
            else {
                if ($aasamps==8) {
                    $asigned="-e unsigned-integer";
                }
                else {
                    $asigned="-e signed-integer";
                }
            }
        }

        if (!defined($nsigned)) {
            if ($nasamps==8) {
                if ($sox_version < 14004001) {
                    $nsigned="-u";
                }
                else {
                    $nsigned="-e unsigned-integer";
                }
            }
            else {
                $nsigned=$asigned;
            }
        }

        $nnasamps = $nasamps/8;

        if ($sox_version >= 14004001) {
            if ($nnasamps == 1) {
                $nnasamps = "b 8";
            }
            elsif ($nnasamps == 2) {
                $nnasamps = "b 16";
            }
            elsif ($nnasamps == 3) {
                $nnasamps = "b 24";
            }
            elsif ($nnasamps == 4) {
                $nnasamps = "b 32";
            }
            elsif ($nnasamps == 8) {
                $nnasamps = "b 64";
            }

        }

        $smcom="sox $nodither -t .raw -r $arate $asigned -$sasamps -c $achans \"$audio_in\" -t .wav -r $nrate -c $nchans $nsigned -$nnasamps "
	    . "\"$curworkdir/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=$curworkdir."/audio";
    $audio_out=$curworkdir."/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>$fsize) {
        $size=$fsize;
    }

    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<13000000) {
        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 ($sox_version >= 14004001) {
            if ($osamps == 1) {
                $osamps = "b 8";
            }
            elsif ($osamps == 2) {
                $osamps = "b 16";
            }
            elsif ($osamps == 3) {
                $osamps = "b 24";
            }
            elsif ($osamps == 4) {
                $osamps = "b 32";
            }
            elsif ($osamps == 8) {
                $osamps = "b 64";
            }

            if ($nsamps == 1) {
                $nsamps = "b 8";
            }
            elsif ($nsamps == 2) {
                $nsamps = "b 16";
            }
            elsif ($nsamps == 3) {
                $nsamps = "b 24";
            }
            elsif ($nsamps == 4) {
                $nsamps = "b 32";
            }
            elsif ($nsamps == 8) {
                $nsamps = "b 64";
            }

        }

    }

    if ($sox_version < 14000000) {
        if ($asigned==1) {
            $osigned="-s";
        }
        else {
            $osigned="-u";
        }

        if ($nsigned==1) {
            $nsigned="-s";
        }
        else {
            $nsigned="-u";
        }
    }
    else {
        if ($asigned==1) {
            $osigned="-e signed-integer";
        }
        else {
            $osigned="-e unsigned-integer";
        }

        if ($nsigned==1) {
            $nsigned="-e signed-integer";
        }
        else {
            $nsigned="-e unsigned-integer";
        }
    }

    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)="$curworkdir/audio";
    if (defined($from_handle)) {
	if ($undo_cut) {
	    $audio_from="$workdir/$from_handle/audio.bak";
	}
	else {
	    $audio_from="$workdir/$from_handle/audio";
	}
    }
    my ($audio_temp)="$curworkdir/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;
		$afsize=-s $fdd_in;
		if ($size+$nspos>$afsize) {
                    $size=$afsize-$nspos;
                }
                &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);
	}

    }
    else {
	# copy from audio.bak to output
	my ($audio_bak)="$curworkdir/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);
	}
    }

    unlink "$audio_temp";
    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)="$curworkdir/audio";
    my ($audio_temp)="$curworkdir/audio.temp";
    my ($audio_bak)="$curworkdir/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 = "$curworkdir/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 $gsize = -s $audio_file;
    my $fsize = &align($gsize - $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, $gsize - $offset, SEEK_SET 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, $gsize - $offset, SEEK_SET 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 "$curworkdir/$name$img_ext") {
                    if ($only_first) {
                        $i++;
                        last;
                    } else {
                        $i += $nframes;
                    }
                } else {
                    unlink "$curworkdir/$name$img_ext";
                }
            }
        }
    } else {
        &open_single_image($i);
        return if $panic;
	unless(-s "$curworkdir/$name$img_ext") {
            unlink "$curworkdir/$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 (!defined($ximg_ext)) {
	$ximg_ext = $img_ext;
	$ximg_prefix = $img_prefix;
    }
    elsif(!defined($ximg_prefix)) {
	$ximg_prefix = &get_img_prefix($img_ext);
    }

    if ($antialias eq "false") {
	$com = "$smog_convert_command +antialias \"$file\" -scale $hsize"."x"."$vsize\\! $ximg_prefix\"$curworkdir/$name$ximg_ext\" > $nulfile 2>&1";
    } else {
	$com = "$smog_convert_command \"$file\" -resize $hsize"."x"."$vsize\\! $ximg_prefix\"$curworkdir/$name$ximg_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 "$curworkdir/$name-$j$ximg_ext") {
	    $newname = &mkname($i + $j);
	    smog_rename("$curworkdir/$name-$j$ximg_ext", "$curworkdir/$newname$ximg_ext");
	    $nframes++;

	} else {
	    $found = 0;
	}
    }

    if ($hsize*$vsize == 0) {
	&get_image_size("$curworkdir/$name$ximg_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("$curworkdir");

    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("$curworkdir/$fromname$img_ext", "$curworkdir/$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=$curworkdir . "/.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;

    $mplayer_command=&get_mplayer_location;
    $format=&get_mplayer_format;

    if ($is_mpv && $format eq "") {
        sig_system_error("mpv cannot fully resample");
        $panic=1;
        return;
    }

    $com="\"$mplayer_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 ($is_mpv) {
        if ($nsamps==8) {
            if ($nsigned==0) {
                return "--af=lavrresample,format=u8";
            }
        }
        if ($nsamps==16) {
            if ($nsigned==1) {
                return "--af=lavrresample,format=s16";
            }
        }
        return "";
    }

    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 "$workdir/$handle");
    smog_chdir("$workdir/$handle");
    unlink glob "*.mgk *.bak *.pre *.tmp pause audio.* audiodump* audioclip";
    smog_system_sync();
}


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

    smog_system_sync();

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


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

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


sub sig_complete_audio {
    return if (! -d "$curworkdir");
    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 $curworkdir);

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

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

    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 get_mplayer_location {
    $is_mpv=0;
    $mpc=&location("mplayer");
    if ($mpc eq "") {
        $mpc=&location("mplayer2");
    }
    if ($mpc eq "") {
	## experimental
        $is_mpv=1;
        $mpc=&location("mpv");
    }

    return $mpc;
}


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 value $key 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 + $skip > $sizecheck) {
        if ($DEBUG_SMOGRIFY) {
            print STDERR "smogrify debug - fast_dd was asked to read $size bytes at offset $skip from a $sizecheck byte file\n$fdd_in\n";
        }
        $size=$sizecheck-$skip;
        if ($skip>=$sizecheck) {
            return;
        }
    }

    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];
        $curworkdir="$workdir/$handle";
    }

    $name=&mkname(1);

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

    while (1) {
        $name=&mkname($count+$gap);
        if (-f "$curworkdir/$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 workdir
    unlink "$gui_bootstrap_file";
    unless (defined(open OUT,"> $gui_bootstrap_file")) {
        exit 4;
    }
    close OUT;
}


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="\"".&get_mplayer_location."\"";
        if (!($ret eq "")) {
            $ret.=" -double";
        }
    }

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

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

    $ret;
}


sub get_fs_type {
    my $dir = shift;
    my @res = split /\n/, smog_system_direct("df -T");

    my $xfstype = "Unknown";

    my @pieces,$mp,$mplen;
    my $maxlen = 0;

    foreach (@res) {
        @pieces = split /\s+/, $_;
        $mp=$pieces[6];
        $mplen=length($mp);
        $fstype=$pieces[1];
        if ($dir =~ /$mp/ && $mplen > $maxlen) {
            $maxlen = $mplen;
            $xfstype = $fstype;
        }
    }
    $xfstype;
}


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 ($^O ne "MSWin32") {
            if ($new_version_hash>2008000&&$version_hash<=2008000) {
                $msg="!updmsg2";
            }
        }

        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.";
        }
        if ($version_hash==2002005 && &rc_get("video_open_command") eq "") {
            $video_open_command="\"".&get_mplayer_location."\"";
            &rc_set("video_open_command","$video_open_command");
        }
        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 2>$nulfile | 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("$workdir");

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

    use File::Find;
    opendir DIR,$workdir;
    while ($file=readdir(DIR)) {
        unless ($file =~ /^\./) {
            if (-d "$workdir/$file") {
		# remove a set unlesss it has clips OR (layouts AND NOT (opts & 0x04))
                if (&is_non_empty_dir("$workdir/$file/clips") || (&is_non_empty_dir("$workdir/$file/layouts") && !($opts & 0x04))) {
		    # new style sets, check clips and optionally layouts
                    unless (!defined($new_work)||$new_work eq "") {
			# we are to move this set
                        if (-d "$new_work/$file") {
                            smog_rename( "$new_work/$file","$new_work/$file.bak");
                        }
                        unless ($opts & 0x02) {
                            unless (-f "$workdir/$file/noprune") {
                                smog_chdir("$workdir/$file");
                                &prune;
                                smog_chdir("$workdir");
                            }
                        }
                        smog_rename( "$workdir/$file","$new_work/$file");
                    }
                }
                else {
		    # dir is of no interest
                    $in_set=0;
                    find (\&fndset,"$workdir/$file/");
                    if (!$in_set && !($opts & 0x01)) {
                        if ($^O eq "MSWin32") {
                            if (chdir("$workdir/$file")) {
                                unlink glob "* *.* .*";
                                smog_chdir("$workdir");
                                rmdir "$workdir/$file";
                            }

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

    closedir DIR;

    unless ($opts & 0x10) {
	# clean up a few extra remnants
        smog_chdir("$workdir");
        unlink glob ".* rfx.* smogplugin.*";
        unless (!defined($new_work)||$new_work 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)

    # final value would be 140003002

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

    my @soxvvz=split(/\./,(split(" ",$soxv))[2]);
    my $soxvv=$soxvvz[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;
    }
    else {
        $soxvv = $soxvv * 1000000 + $soxvvz[1] * 1000 + $soxvvz[2];
    }

    return $soxvv;
}


sub prune {
    # clean up old backup images etc in $workdir/$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 <workdir>/handle/lives-symlinks on whatever system

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

    my $curworkdir="$workdir/$handle";
    my $linksdir="$curworkdir/lives-symlinks/";

    smog_chdir($curworkdir);

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