X-Git-Url: http://gb7djk.dxcluster.net/gitweb/gitweb.cgi?a=blobdiff_plain;f=mscore-halve;h=7272a7657c61e9a22bff88ed17074d62bda15ff1;hb=5e28fd50bbdeea5a0c68ccefb79894ca80a632dc;hp=4d92702e1058eccea5761c8d7ef17bfc93deb4e3;hpb=e45fffbae918fe0de13ea71124e7cb38352ab987;p=music.git diff --git a/mscore-halve b/mscore-halve index 4d92702..7272a76 100755 --- a/mscore-halve +++ b/mscore-halve @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl # # A program for processing Musescore XML files and halving the times of all the notes # together with anything else that may be relevant (eg Time Sig, rests, trailing @@ -12,63 +12,118 @@ # use strict; + +require 5.10.1; + use XML::LibXML; use File::Basename; +use File::Temp qw{ :mktemp }; use IO::File; - use v5.10; +use utf8; + +our $VERSION = "1.0"; our %half = ( # decode from one note length to its half - # there may be mispellings, as I can't be bothered - # to look at the code, as I use this for early music - qw( + maxima long long breve breve whole whole half half quarter quarter eighth - eighth sixteenth - sixteenth thirtysecond - thirtysecond sixtyfourth + eighth 16th + 16th 32nd + 32nd 64th + 64th 128th + 128th 256th + 256th 512th + 512th 1024th ) ); our %yesno = ( qw(yes 1 no 0) ); # used for turning translating yes/no text values -our $dbg = 1; # show debugging +our $dbg = 0; # show debugging our $removebeam = 1; # if set remove any BeamMode clauses usage() unless @ARGV; +binmode STDOUT, "utf8"; + foreach my $fn (@ARGV) { - my ($name, $path, $suffix) = fileparse($fn, qr/\.[^.]*/); - my ($ifn, $ofn); - if ($suffix eq ".mscx") { - $ifn = $fn; - $ofn = $path . $name . "-halved" . $suffix; + + if ($fn =~ /^-\w/) { + usage() if $fn =~ /^\-+[\?h]/i; + $dbg ^= 1 if $fn =~ /^\-+x/; + $removebeam ^= 1 if $fn =~ /^\-+b/; } else { - usage(); - } + my ($ifn, $ofn, $tfn); - process($ifn, $ofn); + my ($name, $path, $suffix) = fileparse($fn, qr/\.[^.]*/); + if ($suffix eq ".mscx" || $suffix eq ".mscz") { + $ifn = $fn; + $ofn = $path . $name . "-halved.mscx"; + + # extract out the zipped up .mscx file from an .mscz archive + if ($suffix eq '.mscz') { + $tfn = mktemp("/tmp/msczXXXXXXX"); + my $xifn = $ifn; + $xifn =~ s/z$/x/; + system("unzip -p $ifn $xifn > $tfn"); + $ifn = $tfn; # the tmp file is the actual input. + } + } else { + usage("Only Musescore .mscx or .mscz files allowed (got: $fn)"); + } + + process($ifn, $ofn, $fn); + unlink $tfn if $tfn; + } } exit 0; sub process { - my ($ifn, $ofn) = @_; + my ($ifn, $ofn, $fn) = @_; - my $of = IO::File->new(">$ofn") or die "Cannot open $ofn $!\n"; my $p = XML::LibXML->new(); - my $doc = $p->load_xml(location=>$ifn); + my $doc = eval { $p->load_xml(location=>$ifn) }; + + usage("Invalid Musescore file detected (in $fn) $@") unless $doc; + + my $version; + + my ($muse) = $doc->findnodes('/museScore'); + if ($muse) { + my ($v) = $muse->findnodes('./@version'); + $version = $v->to_literal if $v; + } + if (!$version || $version < 2) { + $version ||= "Unknown"; + usage("Version $version detected in $fn, this program will only work with MuseScore 2 (or greater) files"); + } + + my $of = IO::File->new(">$ofn") or usage("Cannot open $ofn $!"); foreach my $staff ($doc->findnodes('/museScore/Score/Staff')) { my ($sigN, $sigD); # current time sig values (may be needed later) my $syllabic = 0; # track syllabic mode (whether we are in the middle of a word in lyrics). + display($staff) if $dbg; + foreach my $measure ($staff->findnodes('./Measure')) { + my $lens; + + # obtain the measure no and any len attr. Change the len attribute + my ($l) = $measure->findnodes('./@len'); + if ($l) { + my ($t,$b) = split m{/}, $l->to_literal; + $b *= 2; + $lens = "$t/$b"; + $l->setValue($lens); + } # process nodes foreach my $node ($measure->findnodes('./*')) { @@ -82,7 +137,7 @@ sub process my ($nz) = $node->findnodes('./duration/@z'); my ($nn) = $node->findnodes('./duration/@n'); my $was = $nn->to_literal; - my $now = $sigD || $was * 2; + my $now = $was * 2; my $z = $nz->to_literal; display($staff, $measure, $node, "$type $z/$was -> $z/$now") if $dbg; $nn->setValue($now); @@ -117,14 +172,14 @@ sub process } # determine where we are in a word and if there is a - # clause, and it is necessary, add an appropriate one + # clause, note its value (which is "in word" or "not in word") # # This is for dealing with musicxml imports where there is no - # explicit detection of trailing '-' signs, if there are and - # there is no add one of the correct sort and remove + # explicit detection of trailing '-' signs, if there are such signs and + # there is no clause, add one of the correct sort and remove # any trailing '-' from the text. # - # Sadly, it's too much hard work to deal with trailing '_' 'cos + # Sadly, it's too much hard work to deal with any trailing '_' 'cos # mscore calulates the distance in advance because they appear # to be too lazy to have another state to deal with # it. Manual edit will therefore be required. Hopefully, not @@ -146,11 +201,11 @@ sub process my $newv; my $newstate; my $newtext = $v; - if ($v =~ /-$/) { + if ($v =~ /[-–]$/) { $newv = 'begin' unless $syllabic; $newv = 'middle' if $syllabic; $newstate = 1; - $newtext =~ s/\-+$//; + $newtext =~ s/[-–]+$//; } else { $newv = 'end' if $syllabic; $newstate = 0; @@ -212,6 +267,26 @@ sub display sub usage { - say "$0: usage ..."; + my $s = shift; + my ($name, $path, $suffix) = fileparse($0, qr/\.[^.]*/); + $name = "$name$suffix: "; + + if ($s) { + say "\n${name}$s\n"; + $name = "\t"; + } + say "${name}version $VERSION usage: [-b] [-x] ...\n"; + say "\tA program to halve the note values of a MuseScore 2.x file.\n"; + say "\tThis designed to be used to convert 'early music' note values"; + say "\tinto something more 'modern'. It will also sort out things like"; + say "\tinter-syllablic hyphenation if it comes across trailing hyphens"; + say "\tsuch as come from imported Finale musicxml files."; + say "\n\tfilenames: 'a.mscz' (or 'a.mscx') will be written to 'a-halved.mscx'."; + say "\tYou can do several files at a time!\n"; + say "\n\tArguments:"; + say "\t-b - normally any beaming is converted to auto, use this to retain beaming info"; + say "\t-x - enable debugging (actually more a stream of conscienceness)"; + say; + exit 1; }