NScripterでボイス作成
もしかしたら、荘大な無駄な努力かも知れん。
このエントリーの最終目標
NScripterで、ボイスファイルをつなぎ合わせてその場でセリフを作成すること。
もっとわかりやすく言うならば、NScripterで
- http://cncc.hp.infoseek.co.jp/(「ゆっくり」で有名)
- UTAU(重音テトで有名)
- 「くまうた」ディスク交換のお願い | プレイステーション® オフィシャルサイト(一部で有名)
- 阿久女イク(いろんな意味で有名)
のようなことをやれないかな、と言う話。
応用
- ボイスにあわせて文字速度を変える。 - 永字八法をもっと楽に。
- キャラクターが頻繁に使う定番の台詞と、それに含まれる別キャラクターの名前などを別録りすることで、労力・容量の削減に。
- システムカスタマイズした時の「読み上げモード」の実装に。
方法論
ボイスを作成すると言うが、つまるところ「こんにちは」と言うセリフを言わせるために、「こ.wav」「ん.wav」「に.wav」「ち.wav」「わ.wav」を順繰りに再生する、そういう仕組みを作るだけの話である。
ただし、これをプログラム的に表現すると「こ.wavを再生する。それが終わればん.wavを再生する。それが終われば(以下略」となるが、問題になるのは「終われば」と言う、演奏の終了判定である。
automode命令によるオートスキップから、NScripterの内部ではこの終了判定が可能なことは推定できるが、マニュアルを見る限りそれを開発者やユーザーが使用する方法はないようだ。(将来的にこれが可能になれば、このエントリーで書かれたことは全てゴミクズと化す。きゃほー)
次善の策として、演奏したいファイルの長さを事前に取得することが考えられる。演奏開始からカウントしてそれと同じ時間が経過した時、実際の演奏状況に関係なく終了とみなして次の処理に進む訳だ。
では、ファイルの演奏時間を取得する方法について考えてみるが、ボイスにあわせて文字速度を変える。 - 永字八法では、事前も事前、作成段階で取得しておくようにしていたが、正直それはどうなんよと言わざるを得ない。データの持ち方としてはあまり美しくもない訳だし、スクリプターにとっても負担が大きい。
ので、ファイルから直接演奏時間を取得することを考えた。
その結果、後述のvoice_len.exeが完成し、wavに限定してだが、その演奏時間をミリ秒単位で取得することに成功した。
後は、これをNScripterからDLLを介さずに他の言語で作られたアプリを使う。 - 永字八法を応用してNScripterから扱えるようにして、サンプルプログラムを作成した。
voice_len.exeの中身。
Windowsのwavの仕様は見つけたので、それに沿ってファイルヘッダーを解析するプログラムをPerlで組んでみた。wavのファイルヘッダーの中に、「データ部分の長さ」と「一秒あたりのバイト数」があるので、前者を後者で割り、1,000倍すると演奏時間(ミリ秒)になる。端数は切り捨てた。
それをNScripterからDLLを介さずに他の言語で作られたアプリを使う。 - 永字八法に適合するように整えて、ppでアプリ化して完成である。
open ( IN, 'command.txt' ); my @file = <IN>; close ( IN ); map { chomp $_ } @file; map { chomp $_ } @file; open ( OUT, '>result.txt' ); if ( 0 == scalar @file ) { print OUT ''; exit(0); } my $line = ''; my @type = qw ( RIFF SIZE-8 WAVE fmt 16 format channel SAMPLING Bit/Sec Byte/S Bit/S ); my %hack = ( 'RIFF'=>'A4', 'SIZE-8'=>'L', 'WAVE'=>'A4', 'fmt'=>'A4', '16'=>'L', 'format'=>'S', 'channel'=>'S', 'SAMPLING'=>'L', 'Bit/Sec'=>'L', 'Byte/S'=>'S', 'Bit/S'=>'S', ); my %data = (); my $str = ''; map { $str .= $hack{$_} } @type; my @result = (); my $length = 0; my @output = (); foreach my $file ( @file ) { $length = 0; if ( open ( IN, $file ) ) { binmode ( IN ); read ( IN, $line, 36 ); %data = (); @result = unpack $str, $line; map { $data{$_} = shift @result } @type; unless ( $data{'16'} == 16 ) { read ( IN, $line, 2 ); my $count = unpack 'S', $line; $count and read ( IN, $line, $count ); } read ( IN, $line, 8 ); my ( $data_chunk, $data_size ) = unpack 'A4L', $line; $data{'DATA'} = $data_chunk; $data{'DATA_SIZE'} = $data_size; $length = int ( 1000 * $data{'DATA_SIZE'} / $data{'Bit/Sec'} ); close ( IN ); } push @output, '"'.$file.'",'.$length; } print OUT join ( "\n", @output );
00.txtの中身
NScripter側は、このように出迎えた。
*start voice_len %1,"JNGL001.wav" voice_len %2,"JNGL002.wav" voice_len %3,"JNGL003.wav" voice_len %4,"JNGL004.wav" ファイル1:%1ミリ秒 dwave 0,"JNGL001.wav" resettimer waittimer %1 ファイル2:%2ミリ秒 dwave 0,"JNGL002.wav" resettimer waittimer %2 ファイル3:%3ミリ秒 dwave 0,"JNGL003.wav" resettimer waittimer %3 ファイル4:%4ミリ秒 dwave 0,"JNGL004.wav" resettimer waittimer %4 演奏終了\ end *define mov %0,100 numalias voice_len_file,%0:inc %0 numalias voice_len_len,%0:inc %0 defsub voice_len game *voice_len ; パラメータの取得 getparam i%voice_len_len,$voice_len_file ; 対象のファイルの有無のチェック mov %%voice_len_len,-1 ; 初期化 fileexist %voice_len_file,$voice_len_file if %voice_len_file=0 return ; 存在しないなら何もしない。 ; 前回の実行結果を削除。 fileremove "result.txt" ; 書き込み csvopen "command.txt","w" csvwrite $voice_len_file csvclose ; 実行 winexec "voice_len.exe",0 ; 結果が出来上がるまで待つ。 wait 10 fileexist %voice_len_file,"result.txt" if %voice_len_file=0 skip -2 csvopen "result.txt","r" csvread $voice_len_file,%%voice_len_len csvclose return ; wavファイルは、「音楽工房」よりコピーしています。 ; http://www.ddnj.com/product/sound/ok/index.html
今後の課題
でもまあ、こういう外部アプリの使い方は究極ダサいよね。やっぱdllを作って、execdll命令から使ってみたいよね。
と、言う訳で、誰かこの仕組みをCとかC++で書きなおして、リプレースしてくれないかなと他力本願で終わる。
おまえはマイノリティなんだよ!
目算を誤る - 永字八法の続き。
100円ショップに空気読まずメルフォ書いた結果が出た。
お問合せの収納袋の件ですが、
あいにくA5サイズは文庫本やコミックなどと異なり
シリーズでの収納用途が少なく、取扱いがございません。
つまり、エロ漫画は日陰の本である、と。……おのれっ! マイノリティなめんな!
おまえはマイノリティなんだよ!
目算を誤る - 永字八法の続き。
100円ショップに空気読まずメルフォ書いた結果が出た。
お問合せの収納袋の件ですが、
あいにくA5サイズは文庫本やコミックなどと異なり
シリーズでの収納用途が少なく、取扱いがございません。
つまり、エロ漫画は日陰の本である、と。……おのれっ! マイノリティなめんな!
NScripterでボイス作成
もしかしたら、荘大な無駄な努力かも知れん。
このエントリーの最終目標
NScripterで、ボイスファイルをつなぎ合わせてその場でセリフを作成すること。
もっとわかりやすく言うならば、NScripterで
- http://cncc.hp.infoseek.co.jp/(「ゆっくり」で有名)
- UTAU(重音テトで有名)
- 「くまうた」ディスク交換のお願い | プレイステーション® オフィシャルサイト(一部で有名)
- 阿久女イク(いろんな意味で有名)
のようなことをやれないかな、と言う話。
応用
- ボイスにあわせて文字速度を変える。 - 永字八法をもっと楽に。
- キャラクターが頻繁に使う定番の台詞と、それに含まれる別キャラクターの名前などを別録りすることで、労力・容量の削減に。
- システムカスタマイズした時の「読み上げモード」の実装に。
方法論
ボイスを作成すると言うが、つまるところ「こんにちは」と言うセリフを言わせるために、「こ.wav」「ん.wav」「に.wav」「ち.wav」「わ.wav」を順繰りに再生する、そういう仕組みを作るだけの話である。
ただし、これをプログラム的に表現すると「こ.wavを再生する。それが終わればん.wavを再生する。それが終われば(以下略」となるが、問題になるのは「終われば」と言う、演奏の終了判定である。
automode命令によるオートスキップから、NScripterの内部ではこの終了判定が可能なことは推定できるが、マニュアルを見る限りそれを開発者やユーザーが使用する方法はないようだ。(将来的にこれが可能になれば、このエントリーで書かれたことは全てゴミクズと化す。きゃほー)
次善の策として、演奏したいファイルの長さを事前に取得することが考えられる。演奏開始からカウントしてそれと同じ時間が経過した時、実際の演奏状況に関係なく終了とみなして次の処理に進む訳だ。
では、ファイルの演奏時間を取得する方法について考えてみるが、ボイスにあわせて文字速度を変える。 - 永字八法では、事前も事前、作成段階で取得しておくようにしていたが、正直それはどうなんよと言わざるを得ない。データの持ち方としてはあまり美しくもない訳だし、スクリプターにとっても負担が大きい。
ので、ファイルから直接演奏時間を取得することを考えた。
その結果、後述のvoice_len.exeが完成し、wavに限定してだが、その演奏時間をミリ秒単位で取得することに成功した。
後は、これをNScripterからDLLを介さずに他の言語で作られたアプリを使う。 - 永字八法を応用してNScripterから扱えるようにして、サンプルプログラムを作成した。
voice_len.exeの中身。
Windowsのwavの仕様は見つけたので、それに沿ってファイルヘッダーを解析するプログラムをPerlで組んでみた。wavのファイルヘッダーの中に、「データ部分の長さ」と「一秒あたりのバイト数」があるので、前者を後者で割り、1,000倍すると演奏時間(ミリ秒)になる。端数は切り捨てた。
それをNScripterからDLLを介さずに他の言語で作られたアプリを使う。 - 永字八法に適合するように整えて、ppでアプリ化して完成である。
open ( IN, 'command.txt' ); my @file = <IN>; close ( IN ); map { chomp $_ } @file; map { chomp $_ } @file; open ( OUT, '>result.txt' ); if ( 0 == scalar @file ) { print OUT ''; exit(0); } my $line = ''; my @type = qw ( RIFF SIZE-8 WAVE fmt 16 format channel SAMPLING Bit/Sec Byte/S Bit/S ); my %hack = ( 'RIFF'=>'A4', 'SIZE-8'=>'L', 'WAVE'=>'A4', 'fmt'=>'A4', '16'=>'L', 'format'=>'S', 'channel'=>'S', 'SAMPLING'=>'L', 'Bit/Sec'=>'L', 'Byte/S'=>'S', 'Bit/S'=>'S', ); my %data = (); my $str = ''; map { $str .= $hack{$_} } @type; my @result = (); my $length = 0; my @output = (); foreach my $file ( @file ) { $length = 0; if ( open ( IN, $file ) ) { binmode ( IN ); read ( IN, $line, 36 ); %data = (); @result = unpack $str, $line; map { $data{$_} = shift @result } @type; unless ( $data{'16'} == 16 ) { read ( IN, $line, 2 ); my $count = unpack 'S', $line; $count and read ( IN, $line, $count ); } read ( IN, $line, 8 ); my ( $data_chunk, $data_size ) = unpack 'A4L', $line; $data{'DATA'} = $data_chunk; $data{'DATA_SIZE'} = $data_size; $length = int ( 1000 * $data{'DATA_SIZE'} / $data{'Bit/Sec'} ); close ( IN ); } push @output, '"'.$file.'",'.$length; } print OUT join ( "\n", @output );
00.txtの中身
NScripter側は、このように出迎えた。
*start voice_len %1,"JNGL001.wav" voice_len %2,"JNGL002.wav" voice_len %3,"JNGL003.wav" voice_len %4,"JNGL004.wav" ファイル1:%1ミリ秒 dwave 0,"JNGL001.wav" resettimer waittimer %1 ファイル2:%2ミリ秒 dwave 0,"JNGL002.wav" resettimer waittimer %2 ファイル3:%3ミリ秒 dwave 0,"JNGL003.wav" resettimer waittimer %3 ファイル4:%4ミリ秒 dwave 0,"JNGL004.wav" resettimer waittimer %4 演奏終了\ end *define mov %0,100 numalias voice_len_file,%0:inc %0 numalias voice_len_len,%0:inc %0 defsub voice_len game *voice_len ; パラメータの取得 getparam i%voice_len_len,$voice_len_file ; 対象のファイルの有無のチェック mov %%voice_len_len,-1 ; 初期化 fileexist %voice_len_file,$voice_len_file if %voice_len_file=0 return ; 存在しないなら何もしない。 ; 前回の実行結果を削除。 fileremove "result.txt" ; 書き込み csvopen "command.txt","w" csvwrite $voice_len_file csvclose ; 実行 winexec "voice_len.exe",0 ; 結果が出来上がるまで待つ。 wait 10 fileexist %voice_len_file,"result.txt" if %voice_len_file=0 skip -2 csvopen "result.txt","r" csvread $voice_len_file,%%voice_len_len csvclose return ; wavファイルは、「音楽工房」よりコピーしています。 ; http://www.ddnj.com/product/sound/ok/index.html
今後の課題
でもまあ、こういう外部アプリの使い方は究極ダサいよね。やっぱdllを作って、execdll命令から使ってみたいよね。
と、言う訳で、誰かこの仕組みをCとかC++で書きなおして、リプレースしてくれないかなと他力本願で終わる。
おまえはマイノリティなんだよ!
目算を誤る - 永字八法の続き。
100円ショップに空気読まずメルフォ書いた結果が出た。
お問合せの収納袋の件ですが、
あいにくA5サイズは文庫本やコミックなどと異なり
シリーズでの収納用途が少なく、取扱いがございません。
つまり、エロ漫画は日陰の本である、と。……おのれっ! マイノリティなめんな!
NScripterでボイス作成
もしかしたら、荘大な無駄な努力かも知れん。
このエントリーの最終目標
NScripterで、ボイスファイルをつなぎ合わせてその場でセリフを作成すること。
もっとわかりやすく言うならば、NScripterで
- http://cncc.hp.infoseek.co.jp/(「ゆっくり」で有名)
- UTAU(重音テトで有名)
- 「くまうた」ディスク交換のお願い | プレイステーション® オフィシャルサイト(一部で有名)
- 阿久女イク(いろんな意味で有名)
のようなことをやれないかな、と言う話。
応用
- ボイスにあわせて文字速度を変える。 - 永字八法をもっと楽に。
- キャラクターが頻繁に使う定番の台詞と、それに含まれる別キャラクターの名前などを別録りすることで、労力・容量の削減に。
- システムカスタマイズした時の「読み上げモード」の実装に。
方法論
ボイスを作成すると言うが、つまるところ「こんにちは」と言うセリフを言わせるために、「こ.wav」「ん.wav」「に.wav」「ち.wav」「わ.wav」を順繰りに再生する、そういう仕組みを作るだけの話である。
ただし、これをプログラム的に表現すると「こ.wavを再生する。それが終わればん.wavを再生する。それが終われば(以下略」となるが、問題になるのは「終われば」と言う、演奏の終了判定である。
automode命令によるオートスキップから、NScripterの内部ではこの終了判定が可能なことは推定できるが、マニュアルを見る限りそれを開発者やユーザーが使用する方法はないようだ。(将来的にこれが可能になれば、このエントリーで書かれたことは全てゴミクズと化す。きゃほー)
次善の策として、演奏したいファイルの長さを事前に取得することが考えられる。演奏開始からカウントしてそれと同じ時間が経過した時、実際の演奏状況に関係なく終了とみなして次の処理に進む訳だ。
では、ファイルの演奏時間を取得する方法について考えてみるが、ボイスにあわせて文字速度を変える。 - 永字八法では、事前も事前、作成段階で取得しておくようにしていたが、正直それはどうなんよと言わざるを得ない。データの持ち方としてはあまり美しくもない訳だし、スクリプターにとっても負担が大きい。
ので、ファイルから直接演奏時間を取得することを考えた。
その結果、後述のvoice_len.exeが完成し、wavに限定してだが、その演奏時間をミリ秒単位で取得することに成功した。
後は、これをNScripterからDLLを介さずに他の言語で作られたアプリを使う。 - 永字八法を応用してNScripterから扱えるようにして、サンプルプログラムを作成した。
voice_len.exeの中身。
Windowsのwavの仕様は見つけたので、それに沿ってファイルヘッダーを解析するプログラムをPerlで組んでみた。wavのファイルヘッダーの中に、「データ部分の長さ」と「一秒あたりのバイト数」があるので、前者を後者で割り、1,000倍すると演奏時間(ミリ秒)になる。端数は切り捨てた。
それをNScripterからDLLを介さずに他の言語で作られたアプリを使う。 - 永字八法に適合するように整えて、ppでアプリ化して完成である。
open ( IN, 'command.txt' ); my @file = <IN>; close ( IN ); map { chomp $_ } @file; map { chomp $_ } @file; open ( OUT, '>result.txt' ); if ( 0 == scalar @file ) { print OUT ''; exit(0); } my $line = ''; my @type = qw ( RIFF SIZE-8 WAVE fmt 16 format channel SAMPLING Bit/Sec Byte/S Bit/S ); my %hack = ( 'RIFF'=>'A4', 'SIZE-8'=>'L', 'WAVE'=>'A4', 'fmt'=>'A4', '16'=>'L', 'format'=>'S', 'channel'=>'S', 'SAMPLING'=>'L', 'Bit/Sec'=>'L', 'Byte/S'=>'S', 'Bit/S'=>'S', ); my %data = (); my $str = ''; map { $str .= $hack{$_} } @type; my @result = (); my $length = 0; my @output = (); foreach my $file ( @file ) { $length = 0; if ( open ( IN, $file ) ) { binmode ( IN ); read ( IN, $line, 36 ); %data = (); @result = unpack $str, $line; map { $data{$_} = shift @result } @type; unless ( $data{'16'} == 16 ) { read ( IN, $line, 2 ); my $count = unpack 'S', $line; $count and read ( IN, $line, $count ); } read ( IN, $line, 8 ); my ( $data_chunk, $data_size ) = unpack 'A4L', $line; $data{'DATA'} = $data_chunk; $data{'DATA_SIZE'} = $data_size; $length = int ( 1000 * $data{'DATA_SIZE'} / $data{'Bit/Sec'} ); close ( IN ); } push @output, '"'.$file.'",'.$length; } print OUT join ( "\n", @output );
00.txtの中身
NScripter側は、このように出迎えた。
*start voice_len %1,"JNGL001.wav" voice_len %2,"JNGL002.wav" voice_len %3,"JNGL003.wav" voice_len %4,"JNGL004.wav" ファイル1:%1ミリ秒 dwave 0,"JNGL001.wav" resettimer waittimer %1 ファイル2:%2ミリ秒 dwave 0,"JNGL002.wav" resettimer waittimer %2 ファイル3:%3ミリ秒 dwave 0,"JNGL003.wav" resettimer waittimer %3 ファイル4:%4ミリ秒 dwave 0,"JNGL004.wav" resettimer waittimer %4 演奏終了\ end *define mov %0,100 numalias voice_len_file,%0:inc %0 numalias voice_len_len,%0:inc %0 defsub voice_len game *voice_len ; パラメータの取得 getparam i%voice_len_len,$voice_len_file ; 対象のファイルの有無のチェック mov %%voice_len_len,-1 ; 初期化 fileexist %voice_len_file,$voice_len_file if %voice_len_file=0 return ; 存在しないなら何もしない。 ; 前回の実行結果を削除。 fileremove "result.txt" ; 書き込み csvopen "command.txt","w" csvwrite $voice_len_file csvclose ; 実行 winexec "voice_len.exe",0 ; 結果が出来上がるまで待つ。 wait 10 fileexist %voice_len_file,"result.txt" if %voice_len_file=0 skip -2 csvopen "result.txt","r" csvread $voice_len_file,%%voice_len_len csvclose return ; wavファイルは、「音楽工房」よりコピーしています。 ; http://www.ddnj.com/product/sound/ok/index.html
今後の課題
でもまあ、こういう外部アプリの使い方は究極ダサいよね。やっぱdllを作って、execdll命令から使ってみたいよね。
と、言う訳で、誰かこの仕組みをCとかC++で書きなおして、リプレースしてくれないかなと他力本願で終わる。