diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/helpers | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/helpers')
-rwxr-xr-x | src/helpers/rosegarden-audiofile-importer | 270 | ||||
-rwxr-xr-x | src/helpers/rosegarden-lilypondview | 395 | ||||
-rwxr-xr-x | src/helpers/rosegarden-project-package | 839 |
3 files changed, 1504 insertions, 0 deletions
diff --git a/src/helpers/rosegarden-audiofile-importer b/src/helpers/rosegarden-audiofile-importer new file mode 100755 index 0000000..8cbaa45 --- /dev/null +++ b/src/helpers/rosegarden-audiofile-importer @@ -0,0 +1,270 @@ +#!/bin/bash +# +# Helper application to convert and import audio files for Rosegarden. +# Copyright 2005-2008 Chris Cannam. Distributed under the GNU General +# Public License. +# +# Can take audio files of various kinds as input, always produces WAV +# files that are compatible with Rosegarden as output. +# +# Not actually specific to Rosegarden in any way, except that +# Rosegarden needs to know it can rely on its presence and calling +# interface. +# +# Usage: +# +# rosegarden-audiofile-importer -t [<version>] +# rosegarden-audiofile-importer --conftest [<version>] +# -- Exit successfully if the importer is available and working +# [and is of at least version <version>]. If some of the +# required helper applications are missing, also print to +# stdout a line saying "Required: application1, application2", +# listing the missing programs. +# +# rosegarden-audiofile-importer -l [<version>] +# -- List known file extensions (e.g. ogg wav flac). Return code +# is same as for -t (so can combine two calls into one) +# +# rosegarden-audiofile-importer [-r <rate>] -w <inputfile> +# -- Test whether any work is needed to convert <inputfile>, either +# because it isn't in a Rosegarden-compatible format or because +# its samplerate differs from <rate>. Exit successfully if no +# work is required; return error code 2 if conversion required, +# 3 if resampling required, 4 if both required, 1 for other error. +# +# rosegarden-audiofile-importer [-r <rate>] -c <inputfile> <outputfile> +# -- Convert <inputfile> [and resample if necessary] and write to +# <outputfile> as a WAV file +# +# N.B. <outputfile> must not already exist -- this program will not +# overwrite it, but will fail instead if it does + +version=1 + +OTHER_ERROR=1 +CONVERSION_REQD=2 +RESAMPLE_REQD=3 +BOTH_REQD=4 + +echo "rosegarden-audiofile-importer: $@" 1>&2 + +conftest() { + # ssrc is our favourite resampler, but it only works for certain + # samplerates, so we have to have at least one other just in case + if [ -x "`type -path sndfile-resample`" ]; then + return 0 + fi + if [ -x "`type -path sox`" ] && sox -h 2>&1 | grep -q polyphase; then + return 0 + fi + echo "Required: sox OR sndfile-resample" + echo "rosegarden-audiofile-importer: ERROR: No resampler available, failing configuration test" 1>&2 + return 1 +} + +[ -x "`type -path oggdec`" ] && have_oggdec=1 +[ -x "`type -path flac`" ] && have_flac=1 +[ -x "`type -path mpg321`" ] && have_mpg321=1 +[ -x "`type -path sndfile-convert`" -a -x "`type -path sndfile-info`" ] && have_sndfile=1 + +if [ -n "$have_mpg321" ]; then + if ! mpg321 --help 2>&1 | grep -q 'wav N'; then + have_mpg321= + fi +fi + +case "$1" in + -t|--conftest) conftest; exit $?;; + -l) conftest || exit $? + { + [ -n "$have_oggdec" ] && echo ogg + [ -n "$have_flac" ] && echo flac + [ -n "$have_mpg321" ] && echo mp3 + [ -n "$have_sndfile" ] && sndfile-convert --help 2>&1 | \ + grep '^ *[a-z][a-z]* *: ' | awk '{ print $1; }' + } | sort | uniq | fmt -1000 + exit 0;; + -r) rate="$2"; shift; shift;; +esac + +case "$1" in + -w) test_work=1;; + -c) convert=1;; + *) exit 2;; +esac + +conftest || exit $OTHER_ERROR + +infile="$2" +if [ ! -r "$infile" ]; then + echo "rosegarden-audiofile-importer: ERROR: input file \"$infile\" cannot be opened" 1>&2 + exit $OTHER_ERROR +fi + +if [ -n "$convert" ]; then + outfile="$3" + if [ -z "$outfile" ]; then + echo "rosegarden-audiofile-importer: ERROR: output file not specified" 1>&2 + exit $OTHER_ERROR + fi + if [ -f "$outfile" ]; then + echo "rosegarden-audiofile-importer: ERROR: output file \"$outfile\" already exists, not overwriting" 1>&2 + exit $OTHER_ERROR + fi +fi + +base=`basename "$infile"` +stem=${base%.*} +extension=${base##*.} + +extension=`echo $extension | tr '[A-Z]' '[a-z]'` + +# If the file ends in ogg, mp3, wav or flac, we believe it. Otherwise +# use file to see whether it's actually something else + +case "$extension" in + ogg|mp3|wav|flac) ;; + *) case "`file $infile`" in + *Ogg*Vorbis*) extension=ogg;; + *MP3*) extension=mp3;; + *FLAC*) extension=flac;; + esac;; +esac + +case "$extension" in + ogg) converter=oggdec + [ -x "`type -path ogginfo`" ] && filerate=`ogginfo "$infile" | grep '^Rate: ' | awk '{ print $2; }'`;; + mp3) converter=mpg321 + # don't have a helper program to work out rate + ;; + *) filerate=`sndfile-info "$infile" | grep '^Sample Rate :' | awk '{ print $4; }'`;; +esac + +case "$extension" in + wav) ;; + flac) converter=flac; convert_reqd=1;; +ogg|mp3) convert_reqd=1;; + *) converter=sndfile-convert; convert_reqd=1;; +esac + +echo "Extension is $extension, converter is $converter, reqd is $convert_reqd" + +case "$filerate" in [0-9]*);; *) filerate=; intermediate=1;; esac + +[ -n "$filerate" ] && echo "File samplerate is $filerate (requested: $rate)" + +[ -n "$filerate" -a -n "$rate" ] && [ "$filerate" -ne "$rate" ] && resample_reqd=1 +[ -n "$resample_reqd" -a -n "$convert_reqd" ] && intermediate=1 + +[ -n "$intermediate" ] && echo "Using intermediate file" + +if [ -z "$convert" ]; then # -w option + if [ -n "$convert_reqd" ]; then + [ -n "$intermediate" -o -n "$resample_reqd" ] && exit $BOTH_REQD + exit $CONVERSION_REQD; + else + [ -n "$intermediate" -o -n "$resample_reqd" ] && exit $RESAMPLE_REQD + exit 0; + fi +fi + +target="$outfile" +if [ -n "$intermediate" ]; then + target="$outfile.tmp.wav" + if [ -f "$target" ]; then + echo "rosegarden-audiofile-importer: ERROR: intermediate file \"$target\" already exists, not overwriting" 1>&2 + exit $OTHER_ERROR + fi + trap "rm -f \"$target\"" 0 + trap "rm -f \"$target\" \"$outfile\"" 1 2 9 11 15 +else + trap "rm -f \"$outfile\"" 1 2 9 11 15 +fi + +resample() { + _source=$1; shift + _target=$1; shift + simple="" + case "$filerate" in + 192000|176400|96000|88200|48000|44100|24000|22050|12000|11025) + case "$rate" in + 192000|176400|96000|88200|48000|44100|24000|22050|12000|11025) + simple=1;; + esac + esac + done="" + if [ -x "`type -path ssrc`" -a -n "$simple" ]; then + echo "Resampling using ssrc..." 1>&2 + ssrc --rate "$rate" --twopass --dither 4 --profile standard "$_source" "$_target" && done=1 + fi + if [ -z "$done" ]; then + if [ -x "`type -path sndfile-resample`" ]; then + echo "Resampling using sndfile-resample..." 1>&2 + sndfile-resample -to "$rate" -c 0 "$_source" "$_target" || return 4 + else + echo "Resampling using sox..." 1>&2 + sox "$_source" -r "$rate" "$_target" polyphase || return 4 + fi + fi + return 0 +} + +if [ -n "$convert_reqd" ]; then + case "$converter" in + flac) [ -z "$have_flac" -a -n "$have_sndfile" ] && converter=sndfile-convert;; + esac + case "$converter" in + oggdec) + [ -n "$have_oggdec" ] || exit $OTHER_ERROR + oggdec --output "$target" "$infile" || exit $OTHER_ERROR + ;; + mpg321) + [ -n "$have_mpg321" ] || exit $OTHER_ERROR + mpg321 --wav "$target" "$infile" || exit $OTHER_ERROR + ;; + flac) + [ -n "$have_flac" ] || exit $OTHER_ERROR + flac --decode --output-name="$target" "$infile" || exit $OTHER_ERROR + ;; + *) + [ -n "$have_sndfile" ] || exit $OTHER_ERROR + sndfile-convert -pcm16 "$infile" "$target" || exit $OTHER_ERROR + ;; + esac +elif [ -n "$resample_reqd" ]; then + resample "$infile" "$target" || exit $OTHER_ERROR + resample_reqd= +fi + +if [ ! -f "$target" ]; then + echo "rosegarden-audiofile-importer: ERROR: target file not found" 1>&2 + exit $OTHER_ERROR +fi + +if [ -z "$intermediate" ]; then + echo "rosegarden-audiofile-importer: Done" 1>&2 + exit 0 +fi + +if [ -n "$intermediate" -a -n "$rate" -a -z "$filerate" ]; then + filerate=`sndfile-info "$target" | grep '^Sample Rate :' | awk '{ print $4; }'` + case "$filerate" in + [0-9]*) if [ "$filerate" -ne "$rate" ]; then + resample_reqd=1 + fi;; + *) echo "rosegarden-audiofile-importer: ERROR: failed to extract samplerate of intermediate file" 1>&2 + rm "$target" + exit $OTHER_ERROR + ;; + esac +fi + +if [ -n "$resample_reqd" ]; then + resample "$target" "$outfile" || exit $OTHER_ERROR + rm "$target" +else + mv "$target" "$outfile" +fi + +echo "rosegarden-audiofile-importer: Done" 1>&2 +exit 0 diff --git a/src/helpers/rosegarden-lilypondview b/src/helpers/rosegarden-lilypondview new file mode 100755 index 0000000..95cc25e --- /dev/null +++ b/src/helpers/rosegarden-lilypondview @@ -0,0 +1,395 @@ +#!/bin/bash +# +# Helper program used by Rosegarden to preview and print Lilypond output. +# Copyright 2004-2008 Chris Cannam and Fervent Software Ltd. +# Copyright 2006-2008 Heikki Junes. +# Distributed under the GNU General Public License. + +tmpdir=/tmp/$$_lilypondview +mkdir "$tmpdir" || exit 1 + +trap "rm -rf \"$tmpdir\"" 0 + +# Requirements, actual or potential: +# +# lilypond (that actually runs, of course) +# mktemp OR tempfile +# kpdf OR kghostview OR gpdf OR xpdf OR evince OR acroread +# kdialog [for graphical mode only] +# kprinter OR gtklp + +prog_lilypond="" +prog_temp_file="" +prog_kdialog="" +prog_pdf_view="" +prog_printer="" + +set eval -- `getopt -n$0 --longoptions="graphical,pdf,print,version,conftest" "gpPv" "$@"` + +input_files="" +pdf_view="" +printing="" +graphical="" +while [ $# -gt 0 ] +do + # Parse options + case "$1" in + -g|--graphical) graphical=true ;; + -p|--print) printing=true ;; + -P|--pdf) pdf_view=true ;; + -v|--version) print_version=true ;; + --conftest) conftest=true ;; + esac + + # Check and list the listed LilyPond input files + # getopt adds quotation marks ('): "'input.ly'" + if [ "`expr match $1 '.*.ly'`" == "`expr length $1 - 1`" -o \ + "`expr match $1 '.*.ly.gz'`" == "`expr length $1 - 1`" ]; then + input="${1:1:`expr length "$1" - 2`}" + if [ ! -f "$input" ]; then + echo "Error: Can't open \"$input\" for reading" 1>&2 + exit 1 + fi + input_files="$input_files +$input" + fi + shift +done + +#echo "input files: $input_files" + +if [ -n "$print_version" ]; then + echo "rosegarden-lilypondview v1.6" 1>&2 + exit 0 +fi + +if [ -x "`type -path lilypond`" ]; then + lilypond_version="`lilypond --version | grep LilyPond | head -1 | sed 's/^.* //'`" + case "$lilypond_version" in + 1.*|2.[0-5].*) + echo "rosegarden-lilypondview: Unsupported LilyPond version ($lilypond_version)" 1>&2 + echo "Required: LilyPond v2.6 or newer";; + *) + prog_lilypond=lilypond;; + esac +else + echo "rosegarden-lilypondview: LilyPond unavailable" 1>&2 + echo "Required: LilyPond" +fi + +reqd= + +for x in mktemp tempfile; do + if [ -x "`type -path $x`" ]; then + prog_temp_file=$x + break; + fi +done + +if [ -z "$prog_temp_file" ]; then + reqd="mktemp OR tempfile, $reqd" + echo "rosegarden-lilypondview: No temporary file utility found" 1>&2 +fi + +if [ -x "`type -path kdialog`" ]; then + prog_kdialog=kdialog +fi + +for x in acroread kpdf kghostview gpdf xpdf evince; do + if [ -x "`type -path $x`" ]; then + prog_pdf_view=$x + break; + fi +done + +if [ -z "$prog_pdf_view" ]; then + reqd="kpdf OR kghostview OR gpdf OR xpdf OR evince OR acroread, $reqd" + echo "rosegarden-lilypondview: No PDF viewer found" 1>&2 +fi + +for x in kprinter gtklp; do + if [ -x "`type -path $x`" ]; then + case "$x" in kprinter) x="$x --stdin";; esac + prog_printer=$x + break; + fi +done + +if [ -z "$prog_printer" ]; then + reqd="kprinter OR gtklp, $reqd" + echo "rosegarden-lilypondview: No printing program found" 1>&2 +fi + +if [ -n "$conftest" ]; then + if [ -n "$reqd" ]; then + echo "Required: "${reqd%%, } + fi +fi + +[ -z "$prog_lilypond" ] && exit 1 +[ -z "$prog_pdf_view" ] && exit 1 +[ -z "$prog_temp_file" ] && exit 1 + +if [ -n "$conftest" ]; then + echo "LilyPond version: $lilypond_version" + exit 0 +fi + +if [ -z "$prog_kdialog" ]; then + # can't do graphical mode + echo "rosegarden-lilypondview: Graphical progress dialog requires kdialog utility" 1>&2 + graphical="" +fi + +if [ -z "$input" ]; then +cat <<End-of-Usage-message +Process LilyPond files to PDF files and print/view them. + +Usage: rosegarden-lilypondview [OPTION] file.ly ... + +Options: + -g, --graphical : show Lilypond progress in a graphical dialog + -P, --pdf : view created PDF files (DEFAULT) + -p, --print : print created PDF files; together with '--pdf', viewing starts first + -v, --version : print version of rosegarden-lilypondview + --conftest : check that all dependencies are present + +example: rosegarden-lilypondview -g file.ly + +End-of-Usage-message + exit 2 +fi + +for x in $input_files; do + case "$x" in + *.ly|*.ly.gz) + input="$x" + cp "$input" "$tmpdir/" || exit 1 + +( + dir=`dirname "$input"` + base=`basename "$input"` + cd "$tmpdir" + + if [ -n "$graphical" ]; then + dcopRef=`kdialog --title "Processing" --icon "rosegarden" --progressbar "Processing LilyPond file..." 100` + fi + + send_dcop() { + if [ -n "$graphical" ]; then + dcop $dcopRef "$@" + fi + } + + trap "send_dcop close" 0 + + send_dcop showCancelButton true + + if file "$base" | grep -q gzip; then + gunzip "$base" + base=`basename "$base" .gz` + fi + + includes=`\ + grep '\\include ' "$base" | \ + sed -e 's/\\include//' \ + -e 's/^.*[^a-zA-Z0-9-]\([a-zA-Z0-9-][a-zA-Z0-9-]*.ly\).*$/\1/'` + + for include in $includes; do + if [ -r "$dir/$include" ]; then + cp "$dir/$include" . + elif [ -r "$dir/$include.gz" ]; then + gunzip -c "$dir/$include" > ./"$include" + fi + done + + fileversion=`grep '\\version ' "$base" | head -1 | \ + sed -e 's/\\version //' -e 's/[^0-9.]//g'` + + args="" + convert="" +echo "LilyPond version is $lilypond_version, file version is $fileversion" + case "$lilypond_version" in + 2.6.*) + args="--format=pdf" + case "$fileversion" in + 1.*|2.[0-5].*) convert=true;; + esac;; + 2.8.*) + args="--format=pdf" + case "$fileversion" in + 1.*|2.[0-7].*) convert=true;; + esac;; + 2.10.*) + args="--format=pdf" + case "$fileversion" in + 1.*|2.[0-9].*) convert=true;; + esac;; + esac + + logfile="lilypond-output.log" + cat </dev/null >"$logfile" + + if [ -n "$convert" ]; then + echo "File version is $fileversion against LilyPond version $lilypond_version -- converting..." 1>&2 + send_dcop setLabel "Updating LilyPond file..." + for srcfile in "$base" $includes; do + if [ ! -f "$srcfile" ]; then continue; fi + case "$fileversion" in + 1.*|2.[012345].*) + grep -v override-auto-beam-setting "$srcfile" > "${srcfile}_tmp" + mv "${srcfile}_tmp" "$srcfile" + ;; + esac + if [ -n "$graphical" ]; then + convert-ly "$srcfile" > "${srcfile}_converted" 2> "$logfile" && mv "${srcfile}_converted" "$srcfile" + else + convert-ly "$srcfile" > "${srcfile}_converted" && mv "${srcfile}_converted" "$srcfile" + fi + done + send_dcop setLabel "Processing LilyPond file..." + fi + + if [ -n "$graphical" ] ; then + # special bar comment syntax RG exports -- hopefully benign if absent + bars=`grep '^%% [0-9][0-9]*$' "$base" | sort -k 1 -n | tail -1 | awk '{ print $2; }'` + + if [ -z "$bars" ]; then + staffs=`grep 'Staff *[=<]' "$base" | wc -l` + [ "$staffs" -eq 0 ] && staffs=1 + bars=`grep -v '^ *%' "$base" | wc -l` + bars=$(($bars / $staffs)) + fi + + bars=$(($bars + 5)) + count=$(($bars * 7 / 3)) + + indev=/dev/pts/0 + if [ ! -c "$indev" ]; then indev=/dev/ptya0; fi + + PROGRESS=`$prog_temp_file` + +# echo "Running $prog_lilypond $args \"$base\"" + $prog_lilypond $args "$base" <$indev 2>&1 | tee -a "$logfile" | \ + perl -e ' + $| = 1; + print "0\n"; + $state = 0; + $n = ""; + $base = 0; + while (defined ($key = getc)) { + if ($key eq "[") { # bar number start mark + $state = 1; + } elsif ($key eq "]") { # bar number end mark + $state = 2; + $val = int( ($base + $n) * 100 / '$count' ); + if ($val > 100) { $val = 100; } + print " $val"; + print "\n"; + $n = ""; + } elsif ($key eq "\n") { + if ($state == 2) { + $base = $base + '$bars'; $state = 0; + } + } elsif ($state == 1) { # bar number + $n = $n . $key; + } + } + print "end\n" + ' >& $PROGRESS & + + PROCESSINGSTAGE=0 + # + # Stages: + # 0 -- Process LilyPond file + # 1 -- Create PDF output + # 2 -- Finished + # + until [ "$PROCESSINGSTAGE" != "0" ]; do + sleep 0.2 + PROGRESSVALUE=`tail -c 4 $PROGRESS` + ## Debugging code: + # cat $PROGRESS + # echo "= $PROGRESSVALUE ==" + if [ "$PROGRESSVALUE" == "end" ]; then + # + # Processing was terminated: + # - either the number of bars was not known, + # - or there appeared an error during processing. + # + send_dcop setProgress 100 + PROCESSINGSTAGE=2 + elif [ "$PROGRESSVALUE" == "100" ]; then + PROCESSINGSTAGE=1 + # + # Note: percentage is 100 only after PDF has been created. + # + send_dcop setProgress 99 + send_dcop setLabel "Creating PDF output..." + else + send_dcop setProgress $PROGRESSVALUE + fi + if [ "true" == `send_dcop wasCancelled` ]; then + send_dcop close + rm $PROGRESS + exit 1; + fi + done + until [ "$PROCESSINGSTAGE" == "2" ]; do + sleep 0.2 + if [ "true" == `send_dcop wasCancelled` ]; then + send_dcop close + rm $PROGRESS + exit 1; + fi + PROGRESSVALUE=`tail -c 4 $PROGRESS` + if [ "$PROGRESSVALUE" == "end" ]; then + PROCESSINGSTAGE=2 + send_dcop setProgress 100 + fi + done + ( sleep 2 ; send_dcop close ) & + rm $PROGRESS + else +# echo "running $prog_lilypond $args \"$base\"..." + $prog_lilypond $args "$base" + fi + + target="${base%.*}.pdf" + + if [ -f "$target" ]; then + if [ -z "$printing" ]; then + $prog_pdf_view "$target" + else + if [ -n "$pdf_view" ]; then + $prog_pdf_view "$target" + fi + if [ -n "$printing" ]; then + $prog_printer < "$target" + fi + fi + exit 0 + elif [ -n "$graphical" ]; then + cat $logfile 1>&2 + echo 1>&2 + echo "LilyPond failed" 1>&2 + LOGGINGFILE=`$prog_temp_file` + if [ -n "$graphical" ]; then + ( echo + echo " ERROR: LilyPond processing failed." + echo " LilyPond output follows:" + echo + cat "$logfile" ) > $LOGGINGFILE + $prog_kdialog --textbox $LOGGINGFILE 600 200 + fi + rm $LOGGINGFILE + else + echo 1>&2 + echo "LilyPond processing failed." 1>&2 + fi + exit 1 +) + ;; + *) ;; + esac +done; diff --git a/src/helpers/rosegarden-project-package b/src/helpers/rosegarden-project-package new file mode 100755 index 0000000..d9f2cb8 --- /dev/null +++ b/src/helpers/rosegarden-project-package @@ -0,0 +1,839 @@ +#!/bin/bash +# +# Check Perl requirements before executing the program proper +if [ "$1" = "--conftest" ]; then + reqd= + if ! type -path perl >/dev/null; then + reqd="Perl 5" + else + for x in Getopt::Long File::Copy File::Basename XML::Twig; do + if ! perl -e "require $x" 2>/dev/null; then + reqd="$reqd, Perl module $x" + fi + done + fi + if [ -n "$reqd" ]; then + echo "Required: ${reqd#, }" + exit 1 + fi +fi +exec perl -x "$0" "$@" + +#!/usr/bin/perl -w +# -*- cperl-indent-level: 4 -*- +# +# Rosegarden Project file packager +# Copyright 2005-2008 Chris Cannam and Fervent Software Ltd. +# Distributed under the GNU General Public License. +# +# This program converts between Rosegarden (.rg) files and Rosegarden +# Project (.rgp) files. +# +# A Rosegarden file (.rg) is a single file (in gzipped XML format) +# that defines a Rosegarden composition in terms of events, segments, +# programs, plugin data, references to audio files, and so on. Some +# of these types of information depend on the presence of external +# files such as the audio files or plugin preset data, without which +# the file cannot be used. +# +# A Rosegarden Project (.rgp) file is a bundle of files, including the +# Rosegarden file itself, as well as any audio file or other external +# data it requires, in compressed form. A Rosegarden Project file is +# intended to be portable and self-contained, but the Rosegarden +# program itself does not (at the time of writing) understand these +# files directly. +# +# This program can take a Rosegarden file, examine it to find out +# which other data files it uses, and pack it to create the +# corresponding Rosegarden Project file. Conversely, it can also take +# a Rosegarden Project file and unpack it as a project directory for +# use with Rosegarden. + +# TODO: +# - quiet +# - versioning +# - graceful recovery from running out of disk space +# - mucho tidying + +use strict; + +use XML::Twig; +use File::Copy; +use File::Basename; +use Getopt::Long; + +print STDERR "Rosegarden Project Packager v0.25 (c) Fervent Software Ltd 2005-2008\n"; +print STDERR "This program is Free Software under the GNU General Public License.\n"; + +my $dcop = undef; +sub usage { + print STDERR "Usage: rosegarden-project-package [--quiet] [--pack] file.rg [file.rgp]\n"; + print STDERR " rosegarden-project-package [--quiet] --unpack file.rgp\n"; + print STDERR " rosegarden-project-package [--quiet] --rg file.rgp\n"; + print STDERR " rosegarden-project-package --conftest\n"; + if (defined $dcop) { + `dcop "$dcop" close`; + } + exit 2; +} + +my $kdialog = "kdialog --title \"Rosegarden Project\" --icon \"rosegarden\""; + +my $pack = 0; +my $unpack = 0; +my $conftest = 0; +my $rg = 0; +my $quiet = 0; +my $result = GetOptions("pack" => \$pack, + "unpack" => \$unpack, + "conftest" => \$conftest, + "rg" => \$rg, + "quiet" => \$quiet); +if ($result eq "") { + usage; +} + +if (!$conftest) { + $dcop = `kdialog --title \"Rosegarden Project Progress\" --icon \"rosegarden\" --progressbar "Please wait..." 100`; + chomp $dcop; +} + +my @temporaries = (); +$SIG{__DIE__} = sub { + my $error = shift; + $error =~ s/ at .*$/./; + `dcop "$dcop" close`; + map { system qq{rm -rf $_} } @temporaries; + system qq{kdialog --error "$error"}; + return 1; +}; + +sub conftest { + my @required = (); + system "kdialog -v >/dev/null" and push @required, "kdialog"; + system "flac --help >/dev/null" and push @required, "flac"; + # sndfile-convert --help always returns 1 + system "which sndfile-convert >/dev/null" and push @required, "sndfile-convert"; +# system "oggenc -h >/dev/null 2>&1" and push @required, "oggenc"; +# system "oggdec -h >/dev/null 2>&1" and push @required, "oggdec"; + system "dcop --help >/dev/null" and push @required, "dcop"; + return @required; +} + +sub canonicalise { + my $b = shift; + chomp $b; + my $p = shift; + chomp $p; + $p =~ s,^~/,$ENV{"HOME"}/,; + $p =~ s,^~$,$ENV{"HOME"},; + $p =~ s,^([^/]),$b/$1,; + $p =~ s,/./,/,g; + $p =~ s,/$,,; + return $p; +} + +sub relativise { + my $b = shift; + chomp $b; + my $p = shift; + chomp $p; + if ($p =~ m,^$b/,) { + $p =~ s,^$b/,,; + } + return $p; +} + +sub name { + my $rgFile = shift; + my $n = shift; + my $base = basename $rgFile; + $base =~ s/\.rg$//i; + + if ($n =~ m,^RG-AUDIO-\d+\.wav(.pk)?$,i or + $n =~ m,/RG-AUDIO-\d+\.wav(.pk)?$,i) { + + $n =~ s,RG-AUDIO-(\d+)\.wav,$base-rg-$1.wav,; + + } elsif ($n =~ m,^rg-[0-9-]+.wav(.pk)?$, or + $n =~ m,/rg-[0-9-]+.wav(.pk)?$,) { + + $n =~ s,rg-([0-9-]+).wav,$base-rg-$1.wav,; + } + + return $n; +} + +sub convertType { + my $file = shift; + if ($file =~ m/\.(wav|w64)$/i) { + return 'flac'; + } else { + return 'copy'; + } +} + +sub contrib { # Approximate amount of contribution to overall processing time + my $file = shift; + my $size = (stat $file)[7]; + if (!defined $size) { + return 10.0; + } + if (convertType($file) eq 'flac' or convertType($file) eq 'ogg') { + return $size / 1000.0; + } else { + return $size / 50000.0; + } +} + +sub locate { + my $file = shift; + if (-f $file) { + return $file; + } + my $code = system qq{$kdialog --warningyesnocancel 'File not found:\n$file\nDo you want to browse for this file now?'}; + if ($code == 512) { # cancel + die "File $file not found."; + } elsif ($code == 256) { # no + return ""; + } + my $dir = dirname $file; + if (! -d $dir) { $dir = $ENV{'HOME'}; } + $file = basename $file; + $file = `$kdialog --getopenfilename "$dir" "$file"`; + chomp $file; + return $file; +} + +sub convert { + my $source = shift; + my $target = shift; + print STDERR "$source -> $target\n"; + $source = locate $source; + return if ($source eq ""); + my $d = dirname $target; + print STDERR "Creating directory $d\n"; + system qq{mkdir -p "$d"} and die "Failed to create directory $d: $!"; + print STDERR "Convert type is " . convertType($source) . "\n"; + if (convertType($source) eq 'flac') { + print STDERR "Running flac -o $target.rgp.flac $source\n"; + system qq{flac -s -o "$target.rgp.flac" "$source"} and do { + # Conversion failed. First let's assume that's because the + # input is a type of WAV file flac doesn't like. + system qq{sndfile-convert -pcm24 "$source" "$target.rgp.wav"} and do { + die "Failed to convert $source to PCM-24 intermediate file: $!"; + }; + system qq{flac -s -o "$target.rgp.flac" "$target.rgp.wav"} and do { + # OK, that didn't help. + system qq{rm -f "$target.rgp.wav"}; + die "Failed to convert PCM-24 intermediate file $target.rgp.wav to $target.rgp.flac: $!"; # warn via dialog & recover + }; + # Looks good, remove intermediate file + system qq{rm -f "$target.rgp.wav"}; + }; + } elsif (convertType($source) eq 'ogg') { + print STDERR "Running oggenc -o $target $source\n"; + system qq{oggenc -o "$target.rgp.ogg" "$source"} and + die "Failed to convert $source to $target.rgp.ogg"; # warn via dialog & recover + } else { + print STDERR "Copying $source to $target\n"; + copy $source, $target or die "Failed to copy $source to $target: $!"; + } + return 1; +} + +sub rgPack { + + my ($d, $an, $pn); + $an = 0; + $pn = 0; + my $projectDir = ""; + my %audioFiles = (); + my %unusedAudioFiles = (); + my @possibles = (); + my @indices = (); + + my $rgFile = shift; + my $projectFile = shift; + + $rgFile = canonicalise `pwd`, $rgFile; + $projectFile = canonicalise `pwd`, $projectFile; + + if (-d $projectFile) { + die "Project file $projectFile exists and is a directory -- not overwriting it"; + } + + if (-f $projectFile) { + if (system qq{$kdialog --warningyesno "Project file \"$projectFile\" already exists. Overwrite it?"}) { + die "Not overwriting existing project file $projectFile"; + } + } + + my $targetDir = $projectFile . ".d"; + if (-f $targetDir or -d $targetDir) { + system qq{$kdialog --sorry "Packaging directory \"`pwd`/$targetDir\" already exists.\nCannot continue to create project file."}; + die "Not overwriting existing packaging directory $targetDir"; + } + + `dcop "$dcop" setLabel "Reading Rosegarden file..."`; + + my $dir = '.'; + if ($rgFile =~ m,/,) { + $dir = $rgFile; + $dir =~ s,[^/]*$,,; + } + + if ($dir =~ m,^[^/],) { + my $wd = `pwd`; + chomp $wd; + $dir = "$wd/$dir"; + } + + $dir =~ s,/\.?/,/,g; + + print STDERR "dir is $dir\n"; + + my $twig = XML::Twig->new(); + + open INZIP, "-|", "gunzip -c $rgFile"; + + eval { + $twig->parse(\*INZIP); + }; + + close INZIP; + + my $root = $twig->root; + if (!defined $root) { die "No root"; } + + if ($root->gi ne 'rosegarden-data') { + die "Not a Rosegarden file"; + } + + my $audioFilesNode = $root->first_child('audiofiles'); + + my $audioPathNode = $audioFilesNode->first_child('audioPath'); + $projectDir = canonicalise $dir, $audioPathNode->att('value'); + + my @audioNodes = $audioFilesNode->children('audio'); + foreach my $audioNode (@audioNodes) { + my $file = $audioNode->att('file'); + my $id = $audioNode->att('id'); + my $done = 0; + foreach my $segment ($root->children('segment')) { + if (defined $segment->att('type') and + $segment->att('type') eq "audio" and + $segment->att('file') == $id) { + $audioFiles{$id} = $file; + $done = 1; + last; + } + } + if (!$done) { $unusedAudioFiles{$id} = $file; } + } + + if (%unusedAudioFiles) { + my @args; + map { + my $id = $_; + my $file = $unusedAudioFiles{$id}; + + my $haveElsewhere = 0; + foreach my $usedFile (values %audioFiles) { + if ($usedFile eq $file) { + $haveElsewhere = 1; + last; + } + } + + if (!$haveElsewhere) { + + my $fn = canonicalise $projectDir, $file; + my $desc = `file "$fn"`; + my $sz = `wc -c "$fn"`; + chomp $desc; + chomp $sz; + $sz =~ s,\s+.*,,; + $desc =~ s,^$fn: ,,; + $desc =~ s,\([^\)]*\)\s+,,g; + $fn =~ s,^$projectDir/,[PROJECT]/,; + push @args, ($id, "\"$fn - $sz bytes - $desc\"", "off"); + } + + } sort { $a <=> $b } keys %unusedAudioFiles; + + print STDERR "args are @args\n"; + my $result = `$kdialog --checklist "The following audio files are referred to in the Rosegarden file,\nbut are not used in any segment.\n\nBy default these will not be included in the project file.\n\nPlease select any that you wish to include." @args`; + if ($? == 256) { + die "Operation cancelled"; + } + chomp $result; + print STDERR "result is $result\n"; + @indices = + map { s/^"([^"]*)"$/$1/; $_ } + split '\s', $result; + } + + map { $audioFiles{$_} = $unusedAudioFiles{$_} } @indices; + @indices = (); + + my @peakFiles = (); + map { + my $peakFile = canonicalise($projectDir, $_) . ".pk"; + -f $peakFile and push @peakFiles, $peakFile; + } values %audioFiles; + + my %possibleMap; + + my $studioNode = $root->first_child('studio'); + my @deviceNodes = $studioNode->children('device'); + foreach my $deviceNode (@deviceNodes) { + my @instrumentNodes = $deviceNode->children('instrument'); + foreach my $instrumentNode (@instrumentNodes) { + my @pluginNodes = $instrumentNode->children('plugin'); + push @pluginNodes, $instrumentNode->children('synth'); + my @configureNodes; + map { push @configureNodes, $_->children('configure') } @pluginNodes; + map { + my $possible = $_->att('value'); + my $loc = canonicalise $projectDir, $possible; + if (-f $loc && -r $loc) { + $possibleMap{$possible} = 1; + } + } @configureNodes; + } + } + + @possibles = sort keys %possibleMap; + + `dcop "$dcop" setProgress 5`; + + print STDERR "Possibles: @possibles\n"; + + if (@possibles) { + print STDERR "$#possibles possibles\n"; + my @args; + my $n = 0; + map { + my $possible = $_; + my $fn = canonicalise $projectDir, $possible; + my $desc = `file "$fn"`; + my $sz = `wc -c "$fn"`; + my $status = "off"; + chomp $desc; + chomp $sz; + $sz =~ s,\s+.*,,; + $desc =~ s,^$fn: ,,; + $desc =~ s,\([^\)]*\)\s+,,g; + if ($fn =~ m,^$projectDir/[^/]*$,) { + $status = "on"; + } + $fn =~ s,^$projectDir/,(PROJECT)/,; + push @args, ($n, "\"$fn - $sz bytes - $desc\"", $status); + ++$n; + } @possibles; + print STDERR "args are @args\n"; + my $result = `$kdialog --checklist "The following files may be required for use by plugins used in this composition.\n\nPlease select any that you wish to include in the project file." @args`; + if ($? == 256) { + die "Operation cancelled"; + } + chomp $result; + print STDERR "result is $result\n"; + @indices = + map { s/^"([^"]*)"$/$1/; $_ } + split '\s', $result; + } + + mkdir $targetDir or die "Cannot create packaging directory $targetDir: $!"; + push @temporaries, $targetDir; + + my @sourceFiles = sort values %audioFiles; + push @sourceFiles, @peakFiles; + map { push @sourceFiles, $possibles[$_] } @indices; + + print STDERR "Source files: @sourceFiles\n"; + + `dcop "$dcop" setProgress 10`; + + my %fileMap; + + my $totalContrib = 0.0; + map { $totalContrib += contrib canonicalise $projectDir, $_; } @sourceFiles; + if ($totalContrib == 0) { + $totalContrib = 10; + } + + my $contribCount = 0.0; + + print STDERR "Total contribution: $totalContrib\n"; + + my $newProjectDir = basename $rgFile; + $newProjectDir =~ s/.rg$//i; + if ($newProjectDir eq basename $rgFile) { + $newProjectDir = $newProjectDir . ".d"; + } + + foreach my $origSourceFile (@sourceFiles) { + my $sourceCanonical = canonicalise $projectDir, $origSourceFile; + my $sourceRelative = relativise $projectDir, $sourceCanonical; + my $renamedRelative = name $rgFile, $sourceRelative; + if ($sourceRelative =~ m,^/,) { + $renamedRelative = basename $renamedRelative; + } + my $sourceFile = canonicalise $projectDir, $sourceRelative; + my $targetFile = canonicalise($targetDir . "/" . $newProjectDir, $renamedRelative); + print STDERR "first guess: $sourceFile -> $targetFile (renamedRelative is $renamedRelative)\n"; + my $count = 1; + while (-f $targetFile or -d $targetFile) { + print STDERR "$targetFile exists... "; + if ($renamedRelative =~ m,\.([^.]+)$,) { + $renamedRelative =~ s,^(.*)\.([^.]+)$,$1_$count.$2,; + } else { + $renamedRelative = $renamedRelative . "_$count"; + } + $targetFile = canonicalise($targetDir . "/" . $newProjectDir, $renamedRelative); + ++$count; + print STDERR "trying $targetFile\n"; + } + if (convertType($sourceFile) eq 'flac' or + convertType($sourceFile) eq 'ogg') { + `dcop "$dcop" setLabel "Converting $renamedRelative..."`; + } else { + `dcop "$dcop" setLabel "Including $renamedRelative..."`; + } + $fileMap{$origSourceFile} = $renamedRelative; + convert $sourceFile, $targetFile; + $contribCount += contrib $sourceFile; + my $progress = int(10.0 + $contribCount * 82.0 / $totalContrib); + `dcop "$dcop" setProgress $progress`; + } + + my $code; + while (($code = system qq{$kdialog --yesno "Do you want to include any additional files in this project?"}) == 0) { + my $file = `$kdialog --getopenfilename "$dir" "*"`; + chomp $file; + my $targetFile = canonicalise($targetDir . "/" . $newProjectDir, basename $file); + convert $file, $targetFile; + } + + `dcop "$dcop" setLabel "Converting Rosegarden file..."`; + + $audioPathNode->set_att('value', $newProjectDir); + + foreach my $audioNode (@audioNodes) { + if (exists $audioFiles{$audioNode->att('id')}) { + if (exists $fileMap{$audioNode->att('file')}) { + my $before = $audioNode->att('file'); + my $after = $fileMap{$before}; + print STDERR "Renaming $before to $after in XML\n"; + $audioNode->set_att('file', $after); + } + } else { + $audioNode->cut; + } + } + + foreach my $deviceNode (@deviceNodes) { + my @instrumentNodes = $deviceNode->children('instrument'); + foreach my $instrumentNode (@instrumentNodes) { + my @pluginNodes = $instrumentNode->children('plugin'); + push @pluginNodes, $instrumentNode->children('synth'); + my @configureNodes; + map { push @configureNodes, $_->children('configure') } @pluginNodes; + foreach my $configureNode (@configureNodes) { + if (exists $fileMap{$configureNode->att('value')}) { + my $before = $configureNode->att('value'); + my $after = $fileMap{$before}; + print STDERR "Renaming $before to $after in configure XML\n"; + $configureNode->set_att('value', $after); + } + } + } + } + + my $targetRgFile = $targetDir . "/" . basename $rgFile; + my $targetRgXml = $targetRgFile . ".xml"; + + print STDERR "out is $targetRgFile\n"; + + open OUT, "| gzip -c > $targetRgFile" or die "Failed to open $targetRgFile for writing via gzip"; + + $twig->print(\*OUT); + + close OUT; + + `dcop "$dcop" setLabel "Packaging..."`; + `dcop "$dcop" setProgress 95`; + + system qq{rm -f "$projectFile"} and die "Overwriting old project file failed: $!"; + + my $baseTarget = basename $targetDir; + my $baseRg = basename $targetRgFile; + my $baseProject = basename $projectFile; + print STDERR "dir is $targetDir\nbaseTarget is $baseTarget\nbaseRg is $baseRg\nnewProjectDir is $newProjectDir\n"; + system qq{mkdir -p "$targetDir/$newProjectDir"}; + system qq{cd "$targetDir"; tar cf - "$baseRg" "$newProjectDir" | gzip --fast -c > "$baseProject" ; mv "$baseProject" ..} and die "Making tarball failed"; + + `dcop "$dcop" setLabel "Done"`; + `dcop "$dcop" setProgress 100`; + + system qq{rm -r "$targetDir"} and die "Cleaning up packaging directory failed: $!"; + + sleep 1; + + my $completeProject = canonicalise $targetDir, "../$baseProject"; + system qq{$kdialog --msgbox "Packaging complete."}; + + `dcop "$dcop" close`; + + return 1; +} + +sub rgUnpack { + + my $rgFile = shift; + my $projectFile = shift; + + $rgFile = canonicalise `pwd`, $rgFile; + $projectFile = canonicalise `pwd`, $projectFile; + + if (! -f $projectFile) { + die "Project file $projectFile not found"; + } + + $rgFile =~ /.rg$/i or $rgFile = "$rgFile.rg"; + + # directory containing target rg file + my $rgDir = dirname $rgFile; + + my $origRgFile = basename $projectFile; + $origRgFile =~ s/\.rg\.rgp$/.rg/i; + $origRgFile =~ s/\.rgp$/.rg/i; + + if (-d $rgFile) { + die "Target Rosegarden file $rgFile exists and is a directory -- not overwriting"; + } + + if (-f $rgFile) { + if (system qq{$kdialog --warningyesno "Rosegarden file \"$rgFile\" already exists. Overwrite it?"}) { + die "Not overwriting existing Rosegarden file $rgFile"; + } + } + + my $projectDir = $rgFile; + $projectDir =~ s/\.[^\.\/]*$//; + if ($projectDir eq $rgFile) { + $projectDir = "$projectDir.d"; + } + # projectDir is now absolute or relative to cwd + + if (-f $projectDir) { + + die "Project directory $projectDir already exists and is a file -- not overwriting it"; + + } elsif (-d $projectDir) { + + if (system qq{$kdialog --warningyesno "Project directory \"$projectDir\" already exists. Overwrite any duplicate files?"}) { + die "Not overwriting existing project directory $projectDir"; + } + + } else { + push @temporaries, $projectDir; + system qq{mkdir -p "$projectDir"} and die "Failed to create target project directory $projectDir"; + } + + my %dirs; + map { chomp; s,/.*$,,; $dirs{$_} = 1; } `gunzip -c "$projectFile" | tar tf -`; + + my $origProjectDir = ""; + for my $targetDir (keys %dirs) { + if ($targetDir =~ /.rg$/ and !($targetDir =~ m,/,)) { + $origRgFile = $targetDir; + next; + } + if ($targetDir eq $origRgFile) { + next; + } + if ($origProjectDir eq "") { + $origProjectDir = $targetDir; + next; + } + } + # origProjectDir is now relative to the base path + # origRgFile is a base name only (no path) + + # We have: + # rgFile -> target to unpack rg file to + # projectFile -> original project file + # rgDir -> target directory to contain rg file + # origRgFile -> original rg file (virtual) in same dir as project file + # projectDir -> target to unpack project files to (rgFile.d) + # origProjectDir -> original name of project directory (probably origRgFile.d) + + # procedure: + # create a temporary directory tmpdir + # go there and unpack the original project file + # this will create original rg file and original project dir in tmpdir + # move original project dir from tmpdir to rgDir + # unpack audio files in new project dir + # move original rg file from tmpdir to rgFile + # if the project dir basename differs from the original project dir basename, + # edit the rgFile (just the audioPath node) appropriately. + # !!! extra above: show error and exit if rgFile exists and is a directory + + my $tmpdir = "rgp_tmp_$$"; ##!!! clean up + if (-e $tmpdir) { die "Temporary directory $tmpdir already exists, abandoning"; } + system qq{ mkdir "$tmpdir" } and die "Failed to create temporary directory $tmpdir"; + + push @temporaries, $tmpdir; + + `dcop "$dcop" setLabel "Unpacking..."`; + `gunzip -c "$projectFile" | ( cd "$tmpdir"; tar xf - )`; + + `dcop "$dcop" setProgress 20`; + + for my $item (glob "$tmpdir/$origProjectDir/*") { + system qq{mv "$item" "$projectDir/"} and die "Failed to move $item to $projectDir"; + } + + my @encFiles = `ls $projectDir/*.rgp.flac $projectDir/*.rgp.ogg 2>/dev/null`; + my $count = 0; + foreach my $encFile (@encFiles) { + chomp $encFile; + my $origFile = $encFile; + $origFile =~ s/\.rgp\.(flac|ogg)$//; + my $decoder = 'flac -s -f -d'; + if ($encFile =~ /ogg$/) { + $decoder = 'oggdec'; + } + if (system qq{$decoder -o "$origFile" "$encFile"}) { + die "Failed to unpack $origFile"; # should warn & continue + } else { + system qq{rm "$encFile"}; + } + my $progress = @encFiles; + $progress = int(25 + $count * 75 / $progress); + ++$count; + `dcop "$dcop" setProgress $progress`; + } + + # we now have the target project directory set up correctly: + # all we need is the rg file + + open INZIP, "-|", "gunzip -c $tmpdir/$origRgFile" or die "Failed to open original Rosegarden file $tmpdir/$origRgFile"; + open OUTZIP, "| gzip -c > $rgFile" or die "Failed to open target Rosegarden file $rgFile for writing"; + my $encProjDir = basename $projectDir; + while (<INZIP>) { + if (/<audioPath\s/) { + s/<audioPath\s+value=\"[^\"]*"\s*\/>/<audioPath value="$encProjDir"\/>/; + } + print OUTZIP; + } + close INZIP; + close OUTZIP; + + `dcop "$dcop" setLabel "Done"`; + `dcop "$dcop" setProgress 100`; + + sleep 1; + + system qq{rm -rf "$tmpdir"}; + `dcop "$dcop" close`; + + @temporaries = (); + return 1; +} + + +if (!$pack && !$unpack && !$conftest && !$rg) { + if (exists $ARGV[0]) { + if ($ARGV[0] =~ /.rg$/i) { + $pack = 1; + } elsif ($ARGV[0] =~ /.rgp$/i) { + $unpack = 1; + } else { + usage; + } + } else { + usage; + } +} + +my @required = conftest; +if (@required) { + print STDERR "The following additional packages are required but not available in the PATH:\n"; + print STDERR join ' ', @required; + print STDERR "\n"; + if ($conftest) { + print "Required: "; + print join ', ', @required; + print "\n"; + exit 1; + } else { + die "Configuration requirements not met\nThe following helper applications were not found: " . join(', ', @required); + } +} + +if ($conftest) { + exit 0; +} + +if ($pack) { + + if ($unpack || $conftest || $rg) { usage; } + + my ($rgFile, $projectFile); + + if (exists $ARGV[0]) { + $rgFile = $ARGV[0]; + chomp $rgFile; + } + if (! -f $rgFile) { + die "$rgFile: No such file or directory\n"; + } + + if (exists $ARGV[1]) { + $projectFile = $ARGV[1]; + chomp $projectFile; + } else { + $projectFile = $rgFile; + $projectFile =~ s/\.rg$/.rgp/; + if ($projectFile eq $rgFile) { + $projectFile = $rgFile . ".rgp"; + } + } + + rgPack $rgFile, $projectFile; + +} elsif ($unpack || $rg) { + + if ($pack || $conftest) { usage; } + if ($unpack && $rg) { usage; } + + my $projectFile; + + if (exists $ARGV[0]) { + $projectFile = $ARGV[0]; + chomp $projectFile; + } + if (! -f $projectFile) { + die "$projectFile: No such file or directory\n"; + } + + my $rgFile; + if (exists $ARGV[1]) { + $rgFile = $ARGV[1]; + chomp $rgFile; + } else { + $rgFile = $projectFile; + $rgFile =~ s/\.rg\.rgp$/.rg/i; + $rgFile =~ s/\.rgp$/.rg/i; + } + + rgUnpack $rgFile, $projectFile; + + if ($rg) { + system qq{rosegarden "$rgFile"}; + } +} + |