« 2008年6月 | トップページ | 2008年8月 »

ミクの学園ラブソング

vocasim 0.0.4 を使っていただいたnobitan さんのオリジナル曲を紹介します。あのスクリプトからこの作品を作り上げるまでは大変だったと思います。

nobitanさんには、ブログに vocasim の改良点の示唆もいただいておりますので、以下に検討いたします。

http://vocaloid.cocolog-nifty.com/blog/

課題1 音量調節ができない
ボーカロイドを人間らしく歌わせるには、何より抑揚をきっちりつける必要があります。特に音量のバラツキの多いリンレンでは、必要不可欠な作業です。しかしながら、vocasim は人間歌唱のシミュレートとして、音符の並びからフレーズを自動解析し、フレーズごとに盛り上がる箇所を決定しています。つまり、一つ一つの音符に対しては自動で音量が決まってしまい、ユーザーが演出することができません。じゃあ、vocasim 通した後で修正すれば?という考え方もありますが、ここで問題となるのはvocasimの微細振動データ。人間音声の不安定感を出すために、微妙なPIT やDYN変化が続くため、音量調整などをしてしまうと、この微細振動のデータが消えてしまうのです。

課題1の解決方法は、「事前に調教済みのデータの場合にはフレーズ自動解析を止めて、調教済みのDYN/PITを目標値としてPID制御する」ことです。これによりユーザが調教済みのデータを生かし、なおかつvocasim固有の微細振動を加えることが可能となります。

課題2 はっきりと発音しすぎてしまう
vocasimは、「ぼかりす」や「ぼかんないんです」の技術を参考に開発されており、特に音の立ち上がりのPIT変化やDYN変化に、通常の調教では考えられないほどの振幅を描きます。プレパレーションとかオーバーシュート、とかいう部分です。ところがこいつがクセモノで、全ての音符に一律に掛かってしまうため、ここはレガートにしたいなーという箇所全てが明瞭な発音となってしまいます。全体的にスタッカートな感じになってしまうのです。

この解決方法は、現在検討中です。なにか簡単な手段があるような気がします。

課題3 ある程度調教済みのデータを通すと悲惨なことに。
調教をしていると、音符を分割するケースが割と頻繁に発生します。あたりまえですがvocasimは、こうした音符分割のデータを普通に分かれていると判断し、それぞれに立ち上がりの極端なカーブを作ってしまうので、結果調教が進んでいるデータであればあるほど妙なことになってしまいます。

この解決方法は、音符分割のパターンを内部に記憶して、それらを1つの音符として扱うことです。例えば「ぼおかろいど」をそのまま vocasim に通すと「ぼ・お・か・ろ・い・ど」に分割して発音しますが、母音が共通する部分の「ぼお」は1つの音符として一連称呼した方が自然になるかと思います。

課題4 ビブラートの設定ができない。
基本的にvocasimのビブラートの掛かり具合は、フレーズ解析により決定されているようで、フレーズの一番後ろで延ばしている音に対して深くかかります。ところが、歌い手は常にそういった歌い方をしているわけではなく、むしろここはビブラートかけたくないなぁとか、そういった箇所もあるわけです。これらをどうやってコントロールするのか・・・という点も課題の一つでした。

この解決方法は「ビブラートなしのオプションを設けて、Vocaloid editor 側でビブラートを掛けることです。現在の vocasim 0.0.8 に搭載されていますのでご利用ください。

余談ですが、nobitanさんのブログのタイトルを見て、「目指せP」というプロデューサー名称が付与されるに違いないと思ったのは自分だけでしょうか。

| | コメント (2) | トラックバック (0)

がくぼがきた日

動画を見て大笑いしてしまったので、備忘録に貼ります。自分的には「特盛りの姫」ってところで吹きました。らぶデス改造MEIKOに「姉さん特盛です」のコメントが付いて以来、MEIKO=特盛のイメージなのでしょうね。

| | コメント (0) | トラックバック (0)

VocaSim 0.0.8

vocasim 0.0.8 を作成しました。「課題」は解決出来ておらず、主としてバグフィックスですがご容赦ください。

「vocasim008.zip」をダウンロード

主たる変更点は以下のものです。

・EXE形式のファイルとPerlスクリプトが入っていますので、Perl や MIDI-Perl のインストール無しに動作しますし、細かく Perl スクリプトをチューニングしていただくこともできます。
・BPMがデフォルトの120以外の場合にビブラート周波数が変動してしまう不具合を修正しました。

・ビブラートなし、ビブラート弱・中・強が選択可能となりました。

・トラック毎に乱数でビブラート周波数と位相が選択されるようになりました。これでコーラスに対応できるようになりました。

http://piapro.jp/content/ut3xxornucrad4hs

PiaPro にアップロードした試作品の「トロイカ」のコーラスです。

| | コメント (6) | トラックバック (0)

ICチップの商標の不正使用

パチスロICチップ商標偽装、メーカー幹部ら7人逮捕
http://www.yomiuri.co.jp/national/news/20080729-OYT1T00675.htm?from=main3

まるっきり、短答式筆記試験の過去問のようですね。いや、その過去問自体が実際に起きた事件を元にしているのですから、またも同様な事件が繰り返し起きたというべきでしょうね。

ここで論点になるのは、パチスロICチップの商標は、パチスロ機器にICが組み込まれた状態であっても、商標法上の使用の概念にあたるかという点です。

商標は2条に定義されています。

商標法2条
この法律で「商標」とは、文字、図形、記号若しくは立体的形状若しくはこれらの結合又はこれら倒し記載との結合であって、次に掲げるものをいう。

そして、商標の使用の概念は2条3項1号に規定されています。

この法律で標章について「使用」とは、次に掲げる行為をいう。
一 商品又は商品の包装に標章を付する行為。

 ICチップ単体を流通段階に置いたならば、商品に標章を付する行為であり、視認性を有することから商標の機能を発揮することは言うまでもありませんが、いったんパチスロ機器に組み込まれた段階では一般需要者はそれを視認することができず、よって商標の機能は発揮できず、よって商標の使用にはあたらないかのようにも思われます。
 しかし、パチスロ機器の購入者が、パチスロ機器内部を視認してその品質を確かめる商慣習があったならば、パチスロ機器の需要者が視認する対象となり、よって不正な商標の使用により誤認混同が発生し、よって商標権の侵害が発生することになります。

| | コメント (0) | トラックバック (0)

工業所有権法逐条解説17版

 この前、知財学会の帰りに買ったのですが、学会論文予稿集がただでさえ重いのに、これを序でに買ったら、腕がしびれて感覚がなくなりそうでした。

 7月28日時点では中古書店にはまだ並んでいないようですし、yahooオークションにもまだ出品されていませんね。
 ちょっとだけ安く買う秘訣は「Wセミナー購買部で買う」ことです。ご存知ない方はお試しください。但し、校舎によっては適用される割引価格が異なるかもしれません。「ちょっとだけ安く」買えなかった場合にはご容赦ください。

http://www.w-seminar.co.jp/

さっそく分冊して、じっくりと読んでみたいところです。

| | コメント (0) | トラックバック (0)

Perl スクリプトのEXE化

 Perlスクリプトは実行環境としてのハードルが高いようなので、なんとか自分でExe化を試みてみましたので、備忘録+情報共有としてここに貼ります。
まずは、"perl exe" で検索してトップに来た記事を参照しました。

[PAR] perlをexe化
http://hamachiya.com/junk/memo_PAR.html

いや・・・自分の環境はActivePerl 5.8.8 だし、バージョンを落とすのも面倒くさいなあと思いつつ、なんか解決案がないかとこの手順通りにやってみました。

C:\> ppm-shell
ppm> rep add bribes http://www.bribes.org/perl/ppm

まず、www.bribes.org をレポジトリに追加、ここにしか存在しないPPMパッケージがあるようです。続いてPARに必要なパッケージを次々とインストールします。

ppm> install File-Temp
ppm> install Compress-Zlib
ppm> upgrade Compress-Zlib -install -precious
ppm> install Archive-Zip
ppm> install Module-ScanDeps
ppm> install PAR-Dist
ppm> install Parse-Binary
ppm> install Win32-Exe
ppm> install Digest-SHA
ppm> install Module-Signature
ppm> install PAR

とりあえず、pp はインストールできたようです。
さて、サンプル用Perl プログラムを作成します。hello.pl がいいかな。

print "hello,world\n";

そして、pp で実行ファイル化するため、コマンドラインから以下をタイプインします。

C\:>pp -o hello.exe hello.pl

Pperr

 やっぱりエラーが出ましたね、でもここで止まってはいけない。エラーメッセージの要部"Perl_sv_2iv_flags perl58.dll" をGoogleで検索してみましょう。
 きっと、先人も同様な箇所で嵌ったのち、何らかの解決をしている筈ですし、そのときの様子をWEBに公開してくれているはずです。
 もっとも参考になったのは、ここのページでした。

http://www.mail-archive.com/par@perl.org/msg03013.html

参考訳
ActiveState build 822をお使いの際は、PARを直接ここからダウンロードしてください。 http://backpan.perl.org/authors/id/S/SM/SMUELLER/PAR-Packer-0.976-MSWin32-x86-multi-thread-5.8.8.par

そして、コマンドラインから直接以下のコマンドでインストールしてください。
perl -MPAR::Dist -e"install_par( 'PAR-Packer-0.976-MSWin32-x86-multi-thread-5.8.8.par' )"

これで無事に hello.pl や vocasim が EXE化できるようになりました。

| | コメント (0) | トラックバック (0)

vocasimによる合唱のテスト #3

ステレオにして、1トラック目のミクから3トラック目のミクまで左から順番にならべてみました。

http://piapro.jp/content/ia77zsxhu09usc7c

まだロボットくささが抜けないですね。人間の合唱についてもう少し検討が必要なようです。

| | コメント (0) | トラックバック (0)

vocasimによる合唱のテスト #2

ちょっと修正したものをピアプロにアップロードしました。

http://piapro.jp/content/o609axflj4rm5e9h

3人の初音ミクそれぞれのビブラートの位相に乱数を導入して、
歌い始め、終りのばらつきは、Vocaloid Editor 上で編集しました。

| | コメント (3) | トラックバック (0)

vocasim による合唱のテスト

いろいろなミク3人の合唱です。
ビブラート周波数と微細振動の周波数と音量と音高をトラックごとに乱数で生成して、トラック毎のハーモニーが聞こえないかと考えたものです。

http://piapro.jp/content/u7ov7ez31abxyxvj
 
でも、いまいち合唱っぽくないですね。まだミクそれぞれのキャラが立ってないんじゃないかと思います。他にもパラメータを変動させないとダメかな?

| | コメント (0) | トラックバック (0)

MIDI-Perl のインストール

ActivePerl のインストール方法は、kent-web に画面遷移が詳細に説明されています。説明されているバージョンは 5.6.1 ですが、現在のバージョンでもほぼ同様とおもいます。
http://www.kent-web.com/www/anhttpd/perl_inst.html

ActivePerl のインストールが終わったならば、MIDI-Perl のインストールです。MIDI-Perl はMIDIファイルを読み書きするためのモジュールです。

①インターネット接続環境において、Administrator アカウントでログインしてください。日本語のログイン名だとインストールできない場合があります。
②次に、スタートメニューの「すべてのプログラム」⇒「アクセサリ」から、「コマンドプロンプト」を起動してください。

③ppm-shell を起動して、 install MIDI-Perl とタイプしてください。以下の画面になったらインストール成功です。
Midiperl

この作業によって、C:\Perl\site\lib フォルダ下に、MIDI.pm ファイル及び MIDI フォルダが作られ、C:\Perl\site\lib\midi フォルダ下に Event.pm Opus.pm Score.pm Simple.pm Track.pm が作られます。

| | コメント (1) | トラックバック (0)

ActivePerlのダウンロード方法

Perl は初めてという方への ActivePerl のダウンロード方法のご紹介です。

①まず最初に ActiveState社のサイトにアクセスします。
http://www.activestate.com/index.mhtml

②一番下の「Alll Downloads」をクリックします。
Perl_dl_01

③ActivePerl の項目右にある「Get Current Release」をクリックします。
Perl_dl_02_2

④ActivePerl の Download を選択します。
Perl_dl_03

⑤名前やメールアドレス等の入力画面が表示されますが、これはオプションなのでそのまま Continue ボタンを押して問題ありません。

Perl_dl_04

⑥ActivePerl 5.8.8.822 から、オペレーティングシステムを選択してインストールします。通常は "Download ActivePerl 5.8.8.822 for Windows(x86)" の MSI で問題ありません。
Perl_dl_05
⑦ファイルのダウンロード画面が表示されたら、保存ボタンをクリックして適切なフォルダを選択します。
Perl06

| | コメント (0) | トラックバック (0)

VocaSim の比較動画とEXE化

Closebox and Openpod の松尾さんが「ベタ打ち」「VocaSim」「MikuMikuVoice」の比較動画を発表されました。改変も再配布も自由ですので、心ゆくまで触っていただければ幸いです。

Mashpod さんは、VocaSim をEXEファイルにしていただきました。恐らく ActivePerl のインストール無しに動作するとおもいます。どうも有難うございました。

http://mashpod.vox.com/library/post/vocasim004pl%E3%81%AEexe%E5%8C%96%E6%9A%AB%E5%AE%9A.html

| | コメント (3) | トラックバック (0)

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__

| | コメント (0) | トラックバック (0)

VocaSim.0.0.4

VocalSIMulator –ボーカル歌唱のシミュレーション手法の提案

1、目的:
 VocalSimulator(以下 VocaSim)は、ヤマハのVOCALOIDシリーズのVSQファイルを入力として、音量や音高を自動調整して滑舌処理・オーバーシュートやビブラート及び微細変動処理を加えたVSQファイルを出力するものです。

VocaSim004.pl の実行結果 「星の界」
http://piapro.jp/content/bgxqrywgfuhums4s

2、従来技術との比較
 従来のVocaloid2 Editorは全てマウスで描画しており、オーバーシュートなどの細かな調整には膨大な工数が必要でした。唯一ビブラートのみは自動化されています。
 VocaListerner, MikuMikuVoice のアプローチは、肉声から音量と音高を抽出して、VSQファイルに反映させるというものです。肉声のニュアンスを緻密に反映できる特徴を有します。
 VocaSim のアプローチは、ボーカルをフィードバック制御システムとしてモデル化し、制御理論で元にPC内でシミュレーションするというものです。元歌不要でボーカルに似せた調整を掛けること可能です。

3、使用方法
(1)動作チェック環境
OS:WindowsXP
Perl処理系:ActivePerl 5.8.8
使用モジュール:MIDI-Perl v0.81
(2)インストール方法
ActivePerl 5.8.x をインストールしたのち、コマンドラインからppm-shell を起動します。
C:\hogehoge> ppm-shell
次に MIDI-Perl をインストールしてください。
 ppm> install MIDI-Perl
これで準備は完了です。
 そして、「vocasim004.pl」を右クリックしてダウンロードし、処理したいVSQと同じフォルダに入れてください。

(3)使い方
コマンドプロンプトを起動して、以下入力してください。出力VSQ名を省略したならば a_out.vsqに出力されます。
 C:\hogehoge> vocasim004.pl test.入力vsq test出力.vsq
のんびりとした感じの調教したかったら、3番目引数に「のんびりと」と記載願います。
 C:\hogehoge> vocasim004.pl test.入力vsq test出力.vsq のんびりと

4、処理の概要
(1)フレーズ処理
 発音要素ごとの時間間隔が一定値未満($interval_time 未満)ならば、そこでフレーズが終了すると見做しました。
 滑舌を明瞭にあらわす為、発音時間長が一定値以上($interval_time 以上)かつフレーズの末尾以外ならば、発音時間の末尾($prologue_time)で音量と音高が初期値に戻るよう制御しました。
 フレーズを理解して歌っている様をあらわす為、フレーズの包絡線が山なりの音量となるように制御しました。
(2)発音要素の処理
 ボーカルのフィードバック制御システムを以下にモデル化し、音量と音高それぞれをシミュレートします。原則として音量と音高は相関関係を有するよう制御します。
Vocasim_system_diagram

 ボーカルの脳は、耳で測定して目標値と比較するまでに応答遅延(8mSEC程度)を経たのち、目標値との差分をPID制御し、声帯にフィードバック指示します。
 声帯は、脳からの指示をもとに音声を出力します。このとき9Hz近傍のビブラートや、20Hz近傍の微細振動が外乱として加わります。なお、ビブラートはボーカルが故意に発振の大きさを調整可能とします。
 PID制御の式は以下のもので、これに応答遅延時間Dが加わります。

 制御指示 = Κp・(目標値-測定値)+Κi・Σ(目標値-測定値)

 応答遅延時間Dはボーカル固有であり、定数と考えられますので、立ち上がり波形の制御は、Κp・Κiのパラメータにのみ依存します。
 巧いボーカルをシミュレートしたい場合には臨界制動になるように制御し、ちょっと音痴なボーカルをシミュレートしたい場合にはオーバーシュートさせて制御します。 元気よい歌い方はΚp・Κiともに大きく、おとなしい歌い方はΚp・Κiともに小さくしてください。(VocaSim004.pl は、ちょっとオーバーシュート気味です)
 なお、VocaSim004.plでは「得意とするキー」の70から外れたならば力が入りすぎてオーバーシュートするようコーディングし、発音要素間の音量の谷間のときは、Κp・Κiを大きくしました。発声時の音の立ち上がりより、発音しなくなる場合の方が応答が早いと考えた為です。
 外乱として常に細かなビブラートと微細変動を付与しています。これは通常のボーカルの音声波形を見ても同様になっています。フレーズ末尾においてはビブラートの振幅を増大させて、ビブラートが明瞭に認識されるようにしました。

5、今後の課題
(1)プレパレーションの付与
 音程変化直前に観測される、音程変化とは逆方向の瞬時的な変化をプレパレーションと言います。現在のVocaSim にはプレパレーションが付与されていませんが、これを付与することで更に自然な発生が可能となると思われます。

(2)実際の発声を分析してフィードバック制御
 現在のVocaSimのフィードバックシステムは、VSQファイルを生成するところまでが対象です。実際のVocaloid2システムはDYN/PITに対して非線形かつ独自な応答をしますので、VocaSimが出力した音量・音高と実際の発声は異なっています。よって、Vocaloid2システムが出力する音声の音量と音高をフィードバック制御することで、ボーカル・システムを更に正確にシミュレーション可能となります。
(3)リアルタイムにΚp・Κiを変化
 現在のVocaSimは、曲の途中でΚp・Κiを変更できませんが、曲の途中でユーザがリアルタイムにΚp・Κiを制御することで、ユーザの演出意図を反映した音声シミュレーションが可能となります。

6、ライセンスについて
 当ソフトウエアの改変と再配布は自由で、特に作者への通知も必要ありません。但し、ウイルス・コード埋め込み、及びスパイウエアコードの埋め込み、その他法律に抵触する態様への改変を禁じます。


参考データと参考ソフトウエア
Dearest.vsq:ぼかんないんです>< yuukiss さん

【ニコニコ動画】【本気MEIKO】 Dearest (pf mix) 【カバー曲】

MikuMikuVoice:樋口優さん
http://www.geocities.jp/higuchuu4/index.htm
Ripples Ver.0.0.8:zhuoさん
http://www.geocities.jp/zhuoware/ripples/ripples_top.html

参考文献(敬称略)
VocaListenerユーザ歌唱を真似る歌声合成パラメータを自動推定するシステムの提案
中野倫靖, 後藤真孝(産業技術総合研究所) 2008/05
http://staff.aist.go.jp/t.nakano/PRESENTATION/pSIGMUS200805nakano.pdf

歌声知覚・生成機構の解明に向けた歌声合成システム構築に関する研究
斎藤毅 北陸先端科学技術大学院大学 情報科学研究科 2006/3
http://www.jaist.ac.jp/library/thesis/is-doctor-2006/abstract/t-saitou/jabstract.pdf

歌声における F0動的変動成分の抽出とF0制御モデルに関する研究
斎藤毅 北陸先端科学技術大学院大学 情報科学研究科 2002/2/15
http://www.jaist.ac.jp/library/thesis/is-master-2002/abstract/t-saitou/jabstract.pdf

歌声に含まれる基本周波数の微細変動成分の知覚に関する研究
北風裕教 北陸先端科学技術大学院大学 情報科学研究科 2000/2/15
http://www.jaist.ac.jp/library/thesis/is-master-2000/abstract/kitakaze/jabstract.pdf

| | コメント (2) | トラックバック (2)

VocaSim 0.0.3

今日は、日曜日の学会参加の分の代休ですので、VocaSim のコーディングをおこない、テスト用に「かもめのすいへいさん」の音楽データをピアプロにアップロードいたしました。

http://piapro.jp/content/vwczc758nihw5ee7

「かもめのすいへいさん」をミクがアカペラで歌うというものです。処理前と処理後を連結しましたので、簡単に処理の効果を比較いただけるかとおもいます。

「かもめのすいへいさん」の作詞家 武内俊子さん(1945年4月7日死去)作曲家 川村光陽(1946年死去)ともに死後50年が経過しており、よって著作権は消滅しています。

今回はコーディングよりも、「著作権消滅曲」をちゃんと探し当てるのが大変だったりします。(笑) 「とんぼのめがね」も「あかとんぼ」も「メリーさんの羊」もまだ著作権が消滅していないのですね。著作権が存在しない外国民謡に日本の作詞家が歌詞をつけるというパターンが多く、この場合には殆どが著作権が存続していますので、気をつけないといけませんね。

| | コメント (0) | トラックバック (0)

« 2008年6月 | トップページ | 2008年8月 »