« VocaSim.0.0.4 | トップページ | VocaSim の比較動画とEXE化 »

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__

|

« VocaSim.0.0.4 | トップページ | VocaSim の比較動画とEXE化 »

初音ミク」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/110295/41751080

この記事へのトラックバック一覧です: vocasim 0.0.4 (source):

« VocaSim.0.0.4 | トップページ | VocaSim の比較動画とEXE化 »