#
#
# vocalips2musicxml_01.pl
#
# Rev 0.00 Jan. 2, 2010 by zhuo : initial.
# Rev 0.01 Jan. 7, 2010 by zhuo : "remove-rest shorter than n" option added.
#
#
# input: resulting tab-separated file of vocalips 0.42 (in standard mode)
# output: music xml, similar to the example on sinsy.jp
#
# usage:
# perl vocalips2musicxml_00.pl [-b bpm] [-t template] [-r gap] < vocalips_txt > musicxml.xml
#
# -b bpm : Set the BPM to bpm; 120.0 by default.
# -t template : Use template as the template file, instead of the default
# ( musicxml_template.txt ).
# -r gap : remove the rests shorter than the gap (in second) by
# make the preceding note longer.
# vocalips_txt : the event list output of vocalis, in "-m e2" mode.
$tool_name = "vocalips2musicxml";
$tool_version = "0.01";
$encoding_date = "2010-01-08";
#
# set up params
#
$g_tpq = 480; # fixed; matched to the value in the template
$g_bpm = 120.0;
$g_template_name = "musicxml_template.txt";
$g_rest_gap = 0.0;
while ( 1 ) {
my $arg = $ARGV[0];
unless ( $arg =~ /^\-/ ) { last; }
shift;
if ( $arg eq "-b" ) {
$g_bpm = $ARGV[0];
shift;
if ( $g_bpm eq "" || ! ($g_bpm =~ /^[0-9]+$/) ) {
die "bpm not correct.\n";
}
next;
}
if ( $arg eq "-t" ) {
$g_template_name = $ARGV[0];
shift;
if ( $g_template_name eq "" || ! (-f $g_template_name ) ) {
die "template not specified. \n";
}
next;
}
if ( $arg eq "-r" ) {
$g_rest_gap = $ARGV[0];
shift;
if ( ! ($g_rest_gap =~ /^[0-9.]+$/) ) {
die "the gap must be a number in second.\n";
}
next;
}
}
#
# Read in the event list from stdin;
# the list is an output of vsqtxt2lipsync_051.pl, run in -e m2 mode,
# and is in the following format (so-called tab-separated text).
#
# ## begin eventlist
# 4.333 4.667 UTT 95 69 ど d o O
# 4.667 5.000 UTT 73 68 こ k o O
# 5.000 5.167 UTT 78 69 か k a A
# 5.167 5.333 UTT 64 69 あ a A
# 5.333 6.000 UTT 54 66 の n o O
# 6.000 7.000 SIL 112 66 や j a M
# 7.000 7.167 UTT 112 66 や j a A
# ## end eventlist
@g_pitchclass = ("C","C","D","D","E","F","F","G","G","A","A","B");
@g_accidental = ( 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0 );
sub generate_note {
my ( $beg, $end, $utt, $note, $lyric ) = @_;
$text = "\n";
if ( $utt eq "UTT" ) {
my ( $oct ) = int(($note-24)/12 );
my ( $pclass ) = $g_pitchclass[$note % 12];
$text .= "\n";
$text .= "$pclass\n";
if ( $g_accidental[$note%12] ) {
$text .= "1\n";
}
$text .= "$oct\n";
$text .= "\n";
} else { # SIL.
$text .= "\n";
}
$dur = int( ($end - $beg) * ($g_bpm / 60.0) * $g_tpq );
$text .= "$dur\n";
$text .= "1\n";
if ( $utt eq "UTT" ) {
$text .= "$lyric\n";
}
$text .= "\n";
return $text;
}
# skip the header part of the input
while (<>) {
last if ( $_ =~ /^\#\#\tbegin\teventlist/ );
}
# pass 1 : load eventlist to array, merging a short rest to the preceding notes
$g_events = ();
while ( <> ) {
last if ( $_ =~ /^\#\#\tend\teventlist/ );
my ( $beg, $end, $utt, $vel, $note, $lyric, @rest ) = split( /\t/, $_ );
if ( $utt eq 'SIL' && $end - $beg < $g_rest_gap && $#g_events >= 0 ) {
$g_events[ $#g_events ]->{'end'} = $end;
# note: Make the previous utterance longer, by
# replacing the 'end' value of the previous note
# with that of this rest; This takes place
# when a very short silence (rest) follows after a note.
} else {
push( @g_events, { 'beg'=>$beg, 'end'=>$end, 'utt'=>$utt, 'note'=>$note, 'lyric'=>$lyric } );
# note: you can't generate the note text here yet,
# as the 'end' value may be altered at the next iteration.
}
}
# pass 2 : generate events, based on the processed eventlist
foreach $pe (@g_events) {
$g_eventlist_text .= &generate_note( $pe->{'beg'}, $pe->{'end'}, $pe->{'utt'}, $pe->{'note'}, $pe->{'lyric'} );
}
# The event part of musicxml has got stored in $musicxml_text.
# Now, let's read the template, replacing the keywords with
# the contents generated, and write it out to stdout.
open( MUSICXML_TEMPLATE, $g_template_name ) || die "unable to open $g_template_name. \n";
while ( ) {
if ( /\%measures\%/ ) {
print $g_eventlist_text;
next;
}
s/\%tempo\%/$g_bpm/e;
print $_;
}
close MUSICXML_TEMPLATE;