TALKを使って歌わせる方法


 TALK命令ではピッチや速度を調整可能であり、それを上手く調整すれば歌わせることができます。歌わせる時には感情切り替えのパラメータ@E16を指定すれば良いのですが、それ以外をどのように調整していけば良いのかを書いていきます。

長文を読むのが面倒ならば音長、休符においては最後に書いている「まとめ」の部分だけ目を通してもらえれば問題ありません。


《 TALKの注意点 》

 TALKはBEEPと違って複数同時に発音できない
 TALKは発音までにタイムラグがある

これらの注意点を把握して効果的に使えばTALKは歌だけではなくゲームに華を添えることができます。


音程の調整


 最も重要な音程(ピッチ)の指定ですが、これはマニュアルに参考として1オクターブ分の数字が書かれています。下記の表はそのピッチを表すパラメータ@Nの指定値が半音ごとにどのように変わっているかの差分も追加したものとなっています。

ピッチ
  ド(C3)@N1447--
 #ド   @N151053
  レ   @N155949
 #レ   @N162667
  ミ   @N168660
  ファ  @N174559
 #ファ  @N179247
  ソ   @N185866
 #ソ   @N191658
  ラ   @N197155
 #ラ   @N203160
  シ   @N209362
  ド(C4)@N213744

 これを見てのように半音ごとのピッチ指定の差が異なっており単純計算式で表すことはできません。そのためこの値をTALK文に直接書き込んだりこの値を配列変数などに格納してそれを参照する必要があります。
 このように一定ではないのは感情値や話者(声質)によって音程が変化するためであり、言い換えればこの表の値も1つの目安にすぎないということです。
したがって、厳密なピッチ指定の値が欲しければ自分の耳で確認しながらその値をメモっていきその値をTALK文で使用するしかありません。そうすれば、この表の範囲外の音程でも歌わせることが可能になります。しかし、これは絶対音感がない人にとっては不可能です。

 そこで多少アバウトでもより現実な選択肢としては上記の表の値を簡単な式で表すことです。上記の表の値をプロットすればほぼ一次式で表すことが可能になります。その式は次のようなものです。

 @Nの値=1447+(C3と比べての音程差)×58

 つまり、半音ごとに58変わるというものです。上記では44〜66までバラつきがありますが、58で統一してもその誤差は1オクターブの100分の1以下(上記表が完璧なものだったとしてもそれと比べて3〜13セント程度のずれ)に止まるため一般人の耳だと音程がずれていることにほぼ気づかないため問題ないと思われます。それに気づくような絶対音感のある人であれば半音ごとに指定する値を厳密に指定すれば良いだけの話です。


音長の調整


 TALK命令では指定の時間発音させるということはできませんが音長を表すパラメータ@Tの指定値を変えることで発音する時間を変えることができます。同じ音を発音する場合は@Tの値にほぼ比例した時間だけ発音させることができます。TALK "@E16@T200ア"TALK "@E16@T100ア"の約2倍の時間発音するというわけです。

 ただし、これだけでは、曲のテンポに合わせて発音する場合の基準の時間が分からないのでパラメータ@Tの値がいくつならば何秒間だけ発音するのかということを知っておかなくてはなりません。ここで厳密な時間を測定するためTALKCHK()関数でTALK命令による発音状態を取得して発音終了までにかかる時間をMAINCNTLのカウンタで60分の1秒単位で計測するという方法がありますが、実はTALKCHK関数は発音が終了してもその状態を返すまではかなりのタイムラグがあるのです。(恐らくこれはバッファが完全解放された時点で終了を返すためだと思われる)
 したがって、ストップウォッチ等で発音時間を計測することになるのですが、これによって分かったことは同じ発話速度を指定しても発音する文字によって発音時間が異なるということです。
 例えば、TALK "@E16@T200"TALK "@E16@T200" では発音時間が異なるのです。
 発話速度が速ければあまり差は感じませんが@T999の場合はそれぞれ2.3秒と1.3秒になっており無視できる量ではありません。ちなみにその時間は下記のA-E間のものを示します。C-D間だと「バ」も「カ」もそこまで大きな発音時間の差はない(C-D間の発音時間は概ね@Tの指定値が1につき1ミリ秒程度)のですが、TALK命令の発話タイムラグを無効化することはできないためこれを込みで計測する必要があります。

TALK命令で1音発音した時のおおざっぱな波形
(縦軸が音量、横軸が時間)
   ____
 _/    \___
 A B C    D E  F

A TALK命令実行開始(TALKCHKで1を返す)
B 発音開始
C-D発音中
E 発音終了
F TALKCHKで0を返す

 歌わせる場合には長い音は長音(プチコンではマイナスの符号が長音として扱われている)をいくつか並べてTALK "@E16@T100アーーーーーーー" のようにして歌わせる必要があるのですが、この場合は「ア」と「ー」(長音)では発音時間が異なるため長音を9個付けたからといって元の10倍の長さ発音してくれるわけではありません。長音1つで概ね上記のC-D間に相当する時間ほど発音ができます。つまり、普通に1音分発音するより短い時間であり、BーC間が大きい音の場合は1音当たりの時間を比例計算したものと比べて大きな誤差が発生するわけです。そのため「八分音符=長音無し、四分音符=長音1つ、二分音符=長音3つ」などのようにおおざっぱに長さを指定しておいて後は実際に音を発音させて発話速度のパラメータ@Tによって微調整をする必要があります。
 上記では「バ」と「カ」は発音時間に大きな差があることを書きましたが、このように長音を並べて記述すれば全体的にみると長音を発音している時間が多いため「バ」と「カ」の発音時間の差は相対的には小さくなります。

 ここで「発話速度を遅くすれば長音をいくつも並べなくても良いのでは?」と思うかもしれませんが、発話速度を遅くした場合には上記のB-E間が単純に拡大されるため立ち上がり部分であるB-C間も拡大されてしまうので単に音の立ち上がりが遅い音になってしまいます。(音の性質からいって基本的に濁音や半濁音はB-C間が長目になっている)
 それに音ごとに違う発音時間の差が拡大されるため微調整のための手間も増えてしまいます。スローテンポを表現するために発話速度を遅くするなどの意図が無い限り長い発声には向かないため長音を並べて長い音を出す方が良いでしょう。

 また、上記は1音の発音させた場合について書きましたがこれを1行に入るだけ記述する、文字変数に入るだけ入れるなど一度にたくさん発音させる場合はA-B間のタイムラグが増える(文字数が増えればバッファに格納するのに時間がかかっているためだと思われる)ため注意が必要です。BGMPLAYなどを伴奏によって歌わせる場合にはTALKのタイムラグによって全体的に音がずれたような感じになってしまう場合もあるため曲の頭に短めの休符を入れた方が良いと思います。
 複数の音を1つのTALKで発音する場合には前の音のD-E間と次の音のB-C間が上手く合成されるため「1音発声するのにかかる時間」であるA-E間の時間を単純加算した場合の長さとは異なっています。したがって、正確な発音時間は実際に鳴らしてみないと分かりません。

《 まとめ 》

・おおざっぱな調整は長音の数で行う

 (※長音を付けない基準音が16分音符ならば四分音符を発音する場合には長音を3つ付けて音を伸ばす)

・テンポ120ならば@Tの値は100くらいが基本
 (※これは基準音が16分音符の場合なので基準音が8分音符ならば200くらいが基本になる)

・複数の音を1つのTALKで発音する場合は1音ずつ@Tの値を変えて微調整をする。
 (※長音が多めの場合は@Tの値をやや多めにする)


休符の調整


休符を表現するには下記の3つの方法があります。

(1) TALKコマンドの1つであるポーズ「_(アンダーバー)」を使用する
(2) ボリューム指定のパラメータ@Vで0を指定して音量0で発音する
(3) WAIT命令もしくはVSYNC命令を使う

(1)の方法は最も単純ですが、ポーズ1つの分解能が低い(概ね1.5〜2音分に相当する時間止まる)ために微調整が別途必要になります。(概ね@Tの指定値が1につき2ミリ秒程度となり、長音1つ発音する時間の約2倍になっている)
(2)の方法は発音している音と同じ時間だけ無音(休符)が行えるため調節しやすいのはいいのですが、音量0でも微妙に発音されているためそれが気になる人もいるかもしれません。

 最後に残るのは(3)ですが、実際は(3)だけで休符を表現するためには休符の度に複数のTALK命令に分割して実行する必要があり、休符の多い歌の場合は必要以上に煩雑になってしまいます。
 そのため(1)もしくは(2)と併用しつつ(3)を使うのがベターでしょう。というのも(1)(2)だけでは誤差もありBGMPLAY命令などによる伴奏で歌わせる場合にはその誤差が蓄積されてしまい大きな音ずれが発生してしまうからそれをWAITもしくはVSYNCによってリセットする必要があるわけです。ただし、TALK命令の処理には1フレーム以上の時間がかかるため微妙な音ずれを避けたい場合はWAITよりもVSYNCの方が良いと思われます。(※WAITとVSYNCの違いについてはプチコン講座 第2回のコラム参照)

 WAIT命令やVSYNC命令が必要な最も大きい理由はTALK命令はバッファに蓄積されたらすぐ次の命令に移るためです。歌詞すべてを1行で記述するのは無理であるため複数行に分割(複数データに分割)すると思われますが、その場合はWAIT命令を間に挟まないとTALK命令の後にすぐに次のTALK命令が実行されてしまいます。冒頭でTALKの注意点として書いているようにTALK命令はBEEPと違って重ね合わせができません。次のTALK命令を実行した場合には前に実行していたTALK命令による発音は強制的に終了してしまうためWAIT命令などによって時間待ちをしておかないと最後に実行されるTALK命令のみ発音されることになります。

 テンポ120の4/4拍子の曲を1小節単位で記述する場合には1つの区切りで四分音符4つ分となり、テンポ120の場合は四分音符1つが0.5秒(=WAIT 30)であるためWAIT 120をTALK命令の後に実行すればOKです。これによってBGMPLAYなどを伴奏にして歌わせる場合には少なくともTALK命令の最初に発音する音だけは発音のタイミングが合うことになります。

《 まとめ 》

・フレーズ単位もしくは小節単位で区切りWAIT命令などで時間調整する

 (※区切りの末尾の休符においてはWAITによる時間調整があるため省略可能)

・休符の長さはポーズコマンドで行い音符の長さと同じく1つずつ微調整をする
 (※ポーズコマンド1つで概ね長音2つ分の時間に相当する)


お手軽に歌わせるためには


 音程にしろ、音長にしろ、休符にしろ正確に歌わせるためには1音ずつの微調整が必要不可欠になります。これは実際に自分の耳で何度も聞いて確かめるしかありません。例えば童謡「春が来た」の前半部分をBGMPLAYの伴奏によってTALK命令で歌わせる場合には下記のようになります。

BGMPLAY "T120R16O3G4E8F8G4A4G4E8F8G4A4G4E4.C8D2.R4

TALK "@E16@T100@N1858ハ---@N1686ル-@N1745ガ-@N1858キ---@N1971タ---"
WAIT 120
TALK "@E16@T100@N1858ハ---@N1686ル-@N1745ガ-@N1858キ---@N2137タ---"
WAIT 120
TALK "@E16@T100@N1971ド---@N1858コ---@N1686ニ-----@N1510キ-@N1559タ---------"
WAIT 240

 「ハーーールーガーキーーーターーー」は全部で16音分なので1つの音が100m秒ならば1小節は1600m秒となります。これはあくまで長音基準で計算した場合であり、発音した音によって変わるため16音で約2000m秒(約2秒)となっているためWAIT 120で問題なくウエイトが行われています。
 しかし、これは長音の数によって調整を行っているだけで発話速度を変える微調整は行っていないため微妙に音ずれがあります。1音ずつ微調整をしていない分だけリストは短くなっているのですが、それでも、この長さだし、曲のテンポや歌詞を変えることになったらまた調整しなおす必要性が出てきます。これではあまりに大変です。
 プチコン講座の第3回で書いたようにBEEP命令においてはMML演奏プログラムを自作することができました。TALK命令でも1音ずつ直接パラメータの値を記述するのではなくMMLを元にして歌唱できるプログラムがあれば便利そうです。ということで、「無いものは作る」の原則に従って私が作ったのがMMLを用意すれば微調整をせずにその音符通りの高さ・長さで自動的に歌ってくれる棒歌ロイドOSPです。

 ピッチの計算式は若干アバウトですが、上記のものを採用するとして問題は音長です。MMLに合わせた長さ発音するためには発音する時間が正確に分からなくてはなりません。あらかじめ清音、濁音、拗音すべての発音時間を正確に測定(B-C、C-D、D-E間を別々に測定)して配列変数に入れておけばこの問題はほぼ解決なのですが、さすがにそれも大変なのでより簡単な方法でほぼ正確な時間鳴らせる方法をとっています。


 「清音はすべて同じ時間発音する」という前提で考えてこれに濁音、半濁音、拗音の場合や長音の場合にはそれを元に補正を行うことで正確な時間だけ発音が可能になります。
 ただし、清音がすべて同じ時間発音されるというわけではないためこの大前提は正しくありません。しかし、実際に歌わせる場合には長音をたくさん重ねて発音されるためこの長音の時間さえ正確に求めておけばほぼ誤差の範囲内に収めることが可能になります。仮に清音に30%の誤差があっても長音を8つ重ねれば全体で見ると誤差は3〜4%まで軽減できます。
 つまり、長い音符ほど正確な時間発音できるということです。(この補正アルゴリズムはOSPの初期バージョンでは導入されず「2」で導入された)

 しかし、長音のみ正確な時間発音する場合は発音時間の短い音は不正確になりやすいことになります。確かにそうなのですが、それは1音単位で発音することでその誤差がリセットされるため全く問題はありません。
 その代わり1音単位で発音するとどうしてもブツ切れな発音になりやすいのすが、それは短い音は正確な長さよりやや長目に発音することで軽減しています。言い換えると事実上の基準音は音の長さで変わっており、長い音の基準音は長音であり、短い音の基準音は短い発音をする清音(「ア」「カ」など)になっているわけです。それが正しい長さで発音できるようにしていて誤差をうまく隠蔽しているというわけです。

 これを参考にして自動歌唱プログラムを作ってみようと思う人もいるかもしれないですがOSPよりキレイに歌わせようとするならば1音単位の発音である限りは難しいです。したがって、より良く歌わせたい場合にはフレーズ単位まとめて発音する必要があります。
 ただし、その場合には上記のように微調整がほぼ必要不可欠となってしまいますので自動化はかなり難しいでしょう。私は、OSPでもフレーズ単位での歌唱を試みてみましたが、歌詞によって補正量を変える必要があり、ある歌では上手く歌えても別の歌だと音ずれしまくるという状況になってしまい断念しました。

 結局、「お手軽さ」をとるか「手間がかかっても1音ずつ微調整をする」という方法をとるのかという二択になると思います。


RETURN/RETURN *MAIN

inserted by FC2 system