vocasim 0.0.4 (source)
vocasim004.pl のソースコードです。
#!/perl -w
########################################################################
#
# Vocal Simulator Ver.0.0.4
# vocasim004.pl
#
# Programmed by A.Izumi 5/Jul/2008
#
use strict;
use MIDI;
use MIDI::Simple;use constant {
PHRASE_BEGIN => 1,
PHRASE_END => 2,
};
use constant {
INTERVAL_TIME => 120,
EPILOGUE_TIME => 50,
};my $fname = shift;
if( !defined($fname) ) {
print ("使い方:vocasim004.pl in_vsq out_vsq mode\n");
print ("mode = 元気よく,のんびりと \n");
die;
}
my $o = MIDI::Opus->new({ "from_file" => $fname || die });
my @tracks = $o->tracks;my $vsqname = shift;
if( !defined($vsqname) ) {
$vsqname = "a_out.vsq";
}
my $trainingMode = shift;
if( !defined($vsqname) ) {
$trainingMode = "元気よく";
}
my $vibratoCycle = 110;
my $deltawaveCycle = 47;
my $delay_time = 8;#
# 「元気よく」がデフォルト
#
my $Kp = 0.03;
my $Ki = 0.0003;
if( $trainingMode eq "のんびりと" ) {
$Kp = 0.01;
$Ki = 0.0001;
}#
# 各トラック毎の処理
#
my $ref_array_track = [0];
my $j;
for(my $i = 0; $i < scalar(@tracks); $i++) {
my @thisEvents = ();
my $text = "";
# テキスト部分の抽出
foreach my $events ($tracks[$i]->events) {
if ($events->[0] eq "text_event"){
$text .= $events->[2];
}
}
# テキスト部分から DM:数字4桁を削除する。
my @trackText =();
my $my_miku_phonetics="";
$text =~ s/DM:[0-9]{4}://go;my @eventListSection = ();
#
# いったんセクション毎に分解する
#
my @sections = ();
@sections = split(/\[/,$text);
shift @sections;
for(my $k = 0; $k < scalar(@sections); $k++) {
$sections[$k] = "\[".$sections[$k];
}for(my $k = 0; $k < scalar(@sections); $k++) {
if( index($sections[$k],"\[EventList\]") == 0 ) {
@eventListSection = split(/\n/,$sections[$k]);
}
}
shift @eventListSection;
pop @eventListSection;my @hId = ();
my $idx_event = 0;
foreach my $thisEvent (@eventListSection) {
my $hash = {};
my @event_elements = ();
@event_elements = split(/=/,$thisEvent);
$hash->{"Timing"} = $event_elements[0];
$hash->{"id_section"} = $event_elements[1];
#
# イベントに対応した、IDセクションの抽出
#
my $idSection = "";
foreach my $thisSection0 (@sections) {
if( index($thisSection0,$hash->{id_section} ) == 1 ) {
$idSection = $thisSection0;
}
}
if( $idSection eq "" ) {
next;
}
my @id_elements = ();
@id_elements = split(/\n/,$idSection);
foreach my $thisElement (@id_elements) {
my $equal = index($thisElement,"=");
if( 0 < $equal ) {
my @terms = split(/=/,$thisElement);
$hash->{$terms[0]} = $terms[1];
}
}
#
# IDセクションに対応した、hセクションの抽出
#
if( !defined($hash->{"LyricHandle"}) ) {
next;
}
my $hSection = "";
foreach my $thisSection1 (@sections) {
if( index($thisSection1,$hash->{"LyricHandle"} ) == 1 ) {
$hSection = $thisSection1;
}
}
if( $hSection eq "" ) {
next;
}
my @h_elements = ();
@h_elements = split(/\n/,$hSection);
foreach my $thisElement1 (@h_elements) {
my $equal = index($thisElement1,"=");
if( 0 < $equal ) {
my @terms = split(/=/,$thisElement1);
$hash->{$terms[0]} = $terms[1];
}
}
push(@hId,$hash);
$idx_event++;
}
#
# フレーズ内の発音要素の判断
#
my $timing_last = 0;
for($j = 0; $j < scalar(@hId); $j++) {
$hId[$j]->{"Interval"} = $hId[$j]->{"Timing"} - $timing_last;
$hId[$j]->{"Location"} = 0;
if( $j == 0 ) {
$hId[$j]->{"Location"} = PHRASE_BEGIN;
} else {
if( INTERVAL_TIME < ($hId[$j]->{"Timing"} - $timing_last)) {
$hId[$j-1]->{"Location"} |= PHRASE_END;
$hId[$j]->{"Location"} = PHRASE_BEGIN;
}
}
if($j == scalar(@hId)-1){
$hId[$j]->{"Location"} = PHRASE_END;
}
$timing_last = $hId[$j]->{"Timing"} + $hId[$j]->{"Length"};
}my $timing_phrase_begin = 0;
for($j = 0; $j < scalar(@hId); $j++) {
if( ($hId[$j]->{"Location"} & PHRASE_BEGIN) || $j==0 ) {
$timing_phrase_begin = $hId[$j]->{"Timing"};
}
$hId[$j]->{"PhraseBegin"} = $timing_phrase_begin;
}my $timing_phrase_end = 0;
for($j = scalar(@hId)-1; $j >= 0; $j--) {
if( ($hId[$j]->{"Location"} & PHRASE_END) || $j == scalar(@hId) - 1 ) {
$timing_phrase_end = $hId[$j]->{"Timing"} + $hId[$j]->{"Length"};
}
$hId[$j]->{"PhraseEnd"} = $timing_phrase_end;
}
for($j = 0; $j < scalar(@hId); $j++) {
printf("%s: %d : %d : %d :%d: %d:%d: %s\n",
$hId[$j]->{"Timing"},
$hId[$j]->{"Length"},
$hId[$j]->{"Note#"},
$hId[$j]->{"Interval"},
$hId[$j]->{"Location"},
$hId[$j]->{"PhraseBegin"},
$hId[$j]->{"PhraseEnd"},
$hId[$j]->{"L0"});
}
my $myDynamicsBPList = "\[DynamicsBPList\]\n";
my $myPitchBendBPList = "\[PitchBendBPList\]\n";
my $myPitchBendSensBPList = "\[PitchBendSensBPList\]\n";my $currentDynamics = 0.0;
my $lastDynamics = -1;
my $proportionalDynamics = 0.0;
my $integralDynamics = 0.0;
my $targetDynamics = 0.0;
my $KpDynamics = 0;
my $KiDynamics = 0;
my $thisDynamics = 0;my $currentPitchbend = -1.0;
my $lastPitchbend = -8192;
my $proportionalPitchbend = 0.0;
my $integralPitchbend = 0.0;
my $targetPitchbend = -1.0;
my $KpPitchbend = 0;
my $KiPitchbend = 0;
my $thisPitchbend = 0;my $phraseVibrato = 0.0;
my $swingVibratoPit = 0.1;
my $swingVibratoDyn = 0.02;for($j = 0; $j < scalar(@hId); $j++) {
if( ($hId[$j]->{"Location"} & PHRASE_BEGIN) ) {
$currentDynamics = 0.0;
$currentPitchbend = -1.0;
}
$integralDynamics = 0.0;
$integralPitchbend = 0.0;
my @proportionalDynamicsAry = ();
my @proportionalPitchbendAry = ();for(my $k=0; $k<$hId[$j]->{"Length"}; $k++) {
my $thisTiming = $hId[$j]->{"Timing"} + $k;
my $thisPhraseDynamics =
sin(3.141592*
(($thisTiming - $hId[$j]->{"PhraseBegin"})
/($hId[$j]->{"PhraseEnd"} - $hId[$j]->{"PhraseBegin"})*90+45)/180.0);
#
# PID制御の目標値の設定
#
$targetDynamics = 1.0;
$targetPitchbend = 0.0;
my $vibratoDynamics = 0;
my $vibratoPitchbend = 0;
#
# 外乱の算出
#
$phraseVibrato = ($k*2.0*3.141592)/$vibratoCycle;
if( ($hId[$j]->{"Location"} & PHRASE_END) ) {
$swingVibratoPit = 0.008 + 0.016 * $phraseVibrato/(2.0*3.141592*2.0);
if( 0.03 < $swingVibratoPit ) {
$swingVibratoPit = 0.03;
}
$swingVibratoDyn = 0.004 + 0.001 * $phraseVibrato/(2.0*3.141592*2.0);
if( 0.01 < $swingVibratoDyn ) {
$swingVibratoDyn = 0.01;
}
} else {
$swingVibratoPit = 0.004;
$swingVibratoDyn = 0.002;
}
$vibratoDynamics = $swingVibratoDyn*sin($phraseVibrato);
$vibratoPitchbend = $swingVibratoPit*sin($phraseVibrato);my $phraseDeltawave = ($k*2.0*3.141592)/$deltawaveCycle;
my $deltawavePitchbend = 0.008*sin($phraseDeltawave)/3.0;
my $deltawaveDynamics = 0.004*sin($phraseDeltawave)/3.0;#
# 発音と発音の間の無音期間の設定と
# 比例積分制御のパラメータ設定
#
my $noteDiff = abs($hId[$j]->{"Note#"} - 70);
if( 5 < $noteDiff ) {
$noteDiff = 5;
}
$KpDynamics = $Kp *(1.0 + 0.2* $noteDiff);
$KiDynamics = $Ki *(1.0 + 0.2* $noteDiff);
$KpPitchbend = $Kp *(1.0 + 0.2* $noteDiff);
$KiPitchbend = $Ki *(1.0 + 0.2* $noteDiff);
if( !($hId[$j]->{"Location"} & PHRASE_END) and INTERVAL_TIME < $hId[$j]->{"Length"} and $hId[$j]->{"Length"} - EPILOGUE_TIME < $k ) {
if( $targetDynamics != 0.0 ) {
$integralDynamics = 0.0;
$integralPitchbend = 0.0;
$phraseVibrato = 0.0;
}
$targetDynamics = 0.0;
$targetPitchbend = -1.0;$KpDynamics = $Kp*3.0;
$KiDynamics = $Ki*3.0;
$KpPitchbend = $Kp*3.0;
$KiPitchbend = $Ki*3.0;
}
#
# DYN のPID制御
#
$proportionalDynamicsAry[$k] = $targetDynamics - $currentDynamics;
if( $k < $delay_time ) {
$proportionalDynamics = 0;
} else {
$proportionalDynamics = $proportionalDynamicsAry[$k - $delay_time];
}
$integralDynamics += $proportionalDynamics;
$currentDynamics += $proportionalDynamics*$KpDynamics + $integralDynamics*$KiDynamics + $vibratoDynamics + $deltawaveDynamics;
$thisDynamics = int(($currentDynamics*50)*$thisPhraseDynamics+15);
if( $thisDynamics < 0 ) {
$thisDynamics = 0;
} elsif( 127 <= $thisDynamics ) {
$thisDynamics = 127;
}
#
# PIT のPID制御
#
$proportionalPitchbendAry[$k] = $targetPitchbend - $currentPitchbend;
if( $k < $delay_time ) {
$proportionalPitchbend = 0;
} else {
$proportionalPitchbend = $proportionalPitchbendAry[$k - $delay_time];
}
$integralPitchbend += $proportionalPitchbend;
$currentPitchbend += $proportionalPitchbend*$KpPitchbend + $integralPitchbend*$KiPitchbend + $vibratoPitchbend + $deltawavePitchbend;
$thisPitchbend = int(($currentPitchbend)*(4096+2048));
if( $thisPitchbend < -8191 ) {
$thisPitchbend = -8191;
} elsif( 8191 <= $thisPitchbend ) {
$thisPitchbend = 8191;
}if( $lastDynamics != $thisDynamics ) {
$myDynamicsBPList .= int($thisTiming). "=". $thisDynamics. "\n";
}
$lastDynamics = $thisDynamics;
if( $lastPitchbend != $thisPitchbend ) {
$myPitchBendBPList .= int($thisTiming). "=". $thisPitchbend. "\n";
}
$lastPitchbend = $thisPitchbend;
if( $myPitchBendSensBPList eq "\[PitchBendSensBPList\]\n" ) {
$myPitchBendSensBPList .= int($thisTiming). "=1";
}
}
if( ($hId[$j]->{"Location"} & PHRASE_END)) {
$currentDynamics = 0.0;
$currentPitchbend = -1.0;
$thisDynamics = 20;
$thisPitchbend = -4096 - 2048;
my $thisTiming = $hId[$j]->{"Timing"} + $hId[$j]->{"Length"};
$myDynamicsBPList .= int($thisTiming). "=". $thisDynamics. "\n";
$myPitchBendBPList .= int($thisTiming). "=". $thisPitchbend. "\n";
}
}for(my $k = 0; $k < scalar(@sections); $k++) {
if( index($sections[$k],"\[PitchBendBPList\]") == 0 ) {
} elsif( index($sections[$k],"\[DynamicsBPList\]") == 0 ) {
} elsif( index($sections[$k],"\[PitchBendSensBPList\]") == 0 ) {
} else {
$my_miku_phonetics .= $sections[$k];
}
}
if( 0 < scalar(@sections) ) {
$my_miku_phonetics .= $myPitchBendBPList;
$my_miku_phonetics .= $myDynamicsBPList;
$my_miku_phonetics .= $myPitchBendSensBPList;
#print "\n\n";
#print $my_miku_phonetics;
}# テキスト部分をtext_event に再構成する。
foreach my $events ($tracks[$i]->events) {
if ($events->[0] eq "track_name"){
push @thisEvents,$events;
}
}
for(my $j=0; $j*119<length($my_miku_phonetics); $j++) {
push @thisEvents, ['text_event', 0,
sprintf("DM:%04d:",$j).substr($my_miku_phonetics,$j*119,119) ];
}
if( $i==0 ) {
foreach my $events ($tracks[$i]->events) {
if( $events->[0] ne "track_name"
&& $events->[0] ne "text_event" ){
push @thisEvents,$events;
}
}
}
push @thisEvents,["end_track",0];
my $this_track = MIDI::Track->new({ 'events' => \@thisEvents });
$ref_array_track->[$i] = $this_track;
}
#
# マスタートラックとVoice1トラックからVSQファイルを生成する。
#
my $opus = MIDI::Opus->new(
{ 'format' => 1, 'ticks' => 480, 'tracks' => $ref_array_track } );
$opus->write_to_file( $vsqname );
exit(0);
1;
__END__
| 固定リンク
「初音ミク」カテゴリの記事
- MikuMikuTalk ver.0.2.1c(2012.04.20)
- MikuMikuTalk ver.0.2.1(2012.04.13)
- Tda式Appendミク・ver.βのMMM対応パッチ(2012.04.08)
- Tell Your World MMD-PV(2012.04.04)
- Tda式Appendミクさんの物理演算対応(2012.02.22)
この記事へのコメントは終了しました。
コメント