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