FM CSM Speech Test


img 人間の音声がPCで再生されるという事実が驚きを持って受け止められる時代がありました。
クリアでなくとも、それっぽく聞こえるだけでハイレベルな技術やハイスペックの証明でした。
そんな中でも一際異彩を放っていたのが、FM音源による音声合成でしょう。
「宇宙の帝王ザカリテ」「ひ゛め゛さ゛ま゛が〜」「洗面器に顔をつけて喋ったような声」で有名ですね。

当時からどうやって実現するのか不思議だったのですが、他所で使われたという記憶もなく
現在に至るまで詳しい説明を見かけなかったので、何となくもやもやした感じで覚えていました。

近年、あの手の音声が脚光を浴びるに至って、長年の疑問を払拭するいい機会と
88+FM音源で音声合成の実験を試みることに。





■ FM CSM Speech Test

ディスクイメージをセットしてV2モードでリセットしてください。音源はOPNで。
カーソルキーのどれかを押すと再生しなおします。テストなのでこれだけです。
耳障りな音が出るので音量を絞ってお試しください。

・ CSM音声合成テスト 20090729版 (D88イメージとソースコード)

下に解説を書きましたが、実装では以下の点で解説と変えてあります。

・CSMモードを使っていません。効果音モードで駆動しています。割り込み間隔の事情によるものです。
・CH3だけでなくCH1,2も使って合計6個のサイン波で再生します。
・周波数成分の解析後のデータから、適当にフィルターを通したデータを採用しています。思いっきり手動調整。

音声データはフリーのものを使用させていただきました。



■ PC-8800 Side CSM音声合成モードとは?

まず、FM源側の説明を。
CSM音声合成モードは、YM-2203(OPN)やYM-2608(OPNA)に搭載されています。他のチップにも搭載例があったかもしれません。
その「CSM」は「Composite Sinusoidal Model」の略であり、複合正弦波モデルということらしいです。
立派な名前が付いているのですが、OPNに搭載された機能としては意外と単純です。

・FM音源のch3のオペレータ4つに、独立してF-Numberが設定できる。
・Timer-Aのオーバーフローでch.3は4オペレータが一括キーオンされる。

これだけです。
OPN/OPNAにはもう一つ「効果音モード」というモードがありますが、
これは2番目「Timer-Aによるキーオン」が無いというだけの違いです。

そもそもOPN/OPNAは4オペレータFM音源であり、1つ1つのオペレータは正弦波しか出力できません。
以下の図はアルゴリズム7、各オペレータが何の変調も受けず/及ぼさずに正弦波がそのまま出る組み合わせ(アルゴリズム)です。
CSM音声合成は、このch.3でアルゴリズム7を使います。



F-Numberとは、正弦波の再生周波数を与える数値で(後述)、正弦波の1周期が速い=周波数が高い=音程が高い、ということになります。
正弦波単体では「ポー」という音しか出せませんが、これで4オペレータ個別に音程を変えられるわけです。
通常モードではch毎(=op4個毎)にF-Numberは1つしか設定できないので、ここが最大の違いと言っていいでしょう。

また、FM音源の元からの仕様によって、4つのオペレータそれぞれにトータルレベルを設定できます。
何も変調しないアルゴリズム7では、そのまま正弦波の振幅が変わります。すなわち音量(0-127:ただし対数)となって現れます。

音程と音量が個別に変えられるということはch3だけで4つの個別の音が出せると言うことで(音色は「ポー」固定ですが)
これが効果音モードとして、いくつかの音源ドライバで楽曲の厚みのあるパート用に使われたりするのをご存じかもしれません。

で、肝心のCSMモードですが、この「4つの個別に設定できる正弦波」をTimer-Aに設定した値がオーバーフローした時点で
一斉にキーオンします。一定の間隔で頭から正弦波4つを再生するということです。

PCMの再生をやったことがあれば分かると思いますが、8bit-22050Hz-モノラルのPCMデータを再生するには、
1/22050秒毎に0〜255(8bit)のデータを1つ(モノラル)DA部に出力する作業が必要になります。
同様にCSMモードでは、4オペレータのF-Numberとトータルレベルを一定の間隔で書き換えることで音声を再生します。
つまりTimer-Aの間隔で割り込み処理をして、データを順次流し込みます。

「正弦波4つがヒトの声に聞こえるまで」が結構大変ですが、ともかくもCSMモードというのは基本的にこれだけ。
正弦波の出力が、データ圧縮の観点と人間の音声を再生する目的に近いという点がミソなのですが、これは次のセクションで。

余談ですが、それだけの仕組みなので特別にCSMモードを持たないOPMやOPLLでも音声合成はできます。
各チャンネルに正弦波1つだけ使うアルゴリズム・パラメータを設定し、割り込みを使って
F-Number・トータルレベルとキーオンの管理をすれば良いだけです。OPN/OPNAは少しだけ便利なんですね。



■ 音声変換のしくみ

音声というのは、楽器の音でもヒトの声でも、時間軸に沿った波形の強弱です。
一見すると複雑・ランダムに推移して分析などできそうもありませんが、ここで以下の仮定をします。

・音声や画像などの一見ランダムな離散データも、細かく区切って見ると周期性があるように見なせる。

ちなみに、人間の音声に関しては、それなりに根拠があるそうです。
さて、ここで離散フーリエ変換(DFT : Discrete Fourier Transform)の出番です。
詳しい説明は検索すれば沢山出てくるので、かいつまんで言うと、

・周期性のある関数は三角関数の和で表すことが出来る。

というものです。
先ほどの、細かく区切った一見ランダムでも周期性がありそうにも見える短時間のデータを一つの関数と考えるわけです。
そうすると、離散フーリエ変換(正確には短時間フーリエ変換=SDFT)を通すと、それが三角関数に分解されることになります。



ああ、数式が出てくると途端にわけがわかりませんね。
プログラムで書くとこんな感じです。


     for (int i = 0; i < size; i++)
     {
         for (int j = 0; j < size; j++)
         {
             outRe[i] += inRe[j] * Math.Cos(2.0 * Math.PI * (double)j * (double)i / (double)size);  
             outIm[i] -= inRe[j] * Math.Sin(2.0 * Math.PI * (double)j * (double)i / (double)size);  
         }
     }

ちょっと意味が変わってしましましたが、データをsizeで区切った各区間における三角関数の係数が
配列に収まっているのが分かると思います。

「データをsizeで区切った各区間」とは何を意味するのでしょうか。
たとえば44100Hzでサンプリングされた音声を2048個集めると、約0.0464秒間のデータになります。
この 0.0464秒のデータをフーリエ変換にかけると、「秒(時間域)」が「周波数域」に変換されるのです。
すなわち、44100 / 2048 = 21.533Hz を最小単位(基調波)として、その 1〜2048倍(=44100Hz)までの周波数の成分量が
sin/cosの係数として現れてきます。

数式の方を見ると分かりますが、sinの方に虚数 √-1 が入っているのでそのままでは複素数となって扱いづらいです。
プログラムの方でも、実数部(Re=Real)と虚数部(Im=Imaginary)に分けて格納しています。
これは二乗平均を取ることでパワースペクトルとして強弱を得ることができるようになるので無問題です。
いわゆるスペアナはこれを表示しているわけですね。


     Spectrum[i] = Math.Sqrt(Re[i] * Re[i] + Im[i] * Im[i]);   

さて、ここまでで次のことが分かるとおもいます。

・音声データをフーリエ変換にかけると、どの周波数成分が強いかが分かる。



上の図は 1Hzと5Hzの矩形波を2つ合体させたものですが、この波形がどの周波数成分を多く含むかを
調べる為にフーリエ変換を使うと 1Hzと5Hzにパワースペクトルのピークが現れてくる、という具合です。
人間の声なども同じで、変換を使うと特徴的なピーク周波数の動きが現れてきます。
これを特にフォルマントと呼んでいるようです。


ところで、先ほど波形を三角関数に分解しましたが、逆に三角関数を寄せ集めれば元の波形になります。
これはフーリエ逆変換と言って、元の波形に戻すことが出来る変換です。

さて、三角波・元に戻す、となるとようやく CSM音声合成の出番です。
フーリエ変換でどの周波数が強いかが分かるわけですから、解析後の周波数を強い順にソートして、
上位いくつかをピックアップして正弦波で再現してやれば、少なくとも元の音のもっとも特徴的な部分は
再現できることになります。
FM音源CSMでは4つのオペレータ(正弦波発生器)で元の波形を再現しようとするので結構無理がありそうですが、
それでもわりと聞こえる音になるのはご承知の通り。

というわけで、これで変換の説明は終わりです。


説明が冗長になるのを避けるため、書いていない部分が沢山あります。
FFTとか窓関数とか不確定性原理とか標本化定理とか離散サイン/コサイン変換とかウェーブレット変換etc,etc...
そもそも素人の筆者が書いても間違いだらけになる可能性が高いので他所で調べていただくことを強くオススメ。



■ 再び PC-8800 side

概要は上に書いた通りですが、8bitマシンとして嬉しいことといえばサイズとスピードの問題です。

仮に 8192Hzのサンプルを 256ポイントの DFT(離散フーリエ変換、以下同)に通したとすると、
データの個数は1秒間に8192個→32個になります。
正確には周波数データで2byte、強弱のデータで1byte、それが4オペレータ分なので 32*3*4=384 個になるわけですが、
ともかくデータサイズが縮むことは確かです。

加えて 8192Hzのデータを再生するためにはその周期で割り込みを駆動しなければならず、鈍足な8bitマシンには
結構荷が重い作業になります。しかし、CSM音声合成なら 32Hzの割り込みで良いので負担は軽くなります。
…といってもFM音源レジスタへの書き込みに、かなりの手間と Wait が掛かるので楽というわけでは有りませんが。

反対に、実装上問題のある点としては…

・FM音源の制限上、再現できない周波数が結構ある。

F-Numberで表される正弦波の再生周波数は以下の式で表されます。

Frq = F-Number * (2 ^ (Block - 1)) * (3993600 / 72) / 2 ^ 10

これにより導き出される周波数は 0Hz〜6929.948Hzまでです。
元データのサンプリングレートがいくら高くても、再現できるのはこの辺りまでの周波数となるわけです。
刻み方も飛び飛びになっているところがあったりするので、DFTで得られた代表周波数と合致しない場合があり得ます。

しかし、人間の声に限って言えば、200Hz〜4000Hzが発音域であり、サンプリング定理によって大体8000Hzあればカバーすることができます。
(通常の喋っている時は狭く、歌う場合は音域が当然広がる。また発音域と音声の認識に必要な音域もまた異なる)
いずれにせよ、上記を念頭に置いて、元のデータのサンプリングレートとDFTにかけるポイント数をよく吟味すべきでしょう。

・FM音源のTimerは一長一短

CSMモードではTimer-Aでキーオンするのですが、そもそもTimer-Aは間隔が短いです。

Timer-A(Hz) = 7987200 / (72 * (1024 - 設定値))
間隔は 108.3333Hz 〜 110933.3Hz です。

Timer-B(Hz( = 7987200 / (1152 * (256 - 設定値))
間隔は 27.08333Hz 〜 6933.333Hz です。

8192Hzのデータを256ポイントのDFTに通したとすると、32Hzの割り込み処理が必要になるのですが、
どんなに頑張っても最長108Hzでキーオンしてしまうのでは使えません。
ということで、場合によってはCSMモードをやめて効果音モードで実装し、再生はTimer-B割り込みか
他の割り込みリソース、またはCPUによるポーリングなどを用いる必要出てくるでしょう。
この場合、当然キーオンはその都度 FM音源I/Oポートの0x28をアクセスすることになります。

とまあ、こんなところでしょう。

・応 用 編

で、結局ボーカロイドは出来るの?

むかし、PC88に歌声人(だったかな)という、SB2にため込んだ「あいうえお」等の単発ボイスのADPCMをつなげて
歌を歌わせるという、誰もが思いつくけど結果が容易に想像できて尻込みするという驚異のソフトがありました。

結局サンプリング音声を繋いでも速度を変えると音程が変わってしまい、継ぎ目も不自然だったりして、アラが目立ちすぎるんですよね。
イマドキの技術で補正してやれば、違った結果になるのかもしれませんが。

フォルマント発声では、そもそも周波数の集まりとしてデータを持っているので、音程を変えずに速度を変えたり
周波数成分毎の加工が出来るので、もっと応用を効かせる余地はあります。

この分野も随分研究されているようですし、一般レベルでかなりの実証とノウハウの蓄積が進んでいるでしょうから
目標地点としても随分ハードルは高くなってしまった感があります。

ま、それ以前に素材が無いとどうしようもないんですけどね。HAHAHA



■ 反 省

直接的なきっかけとなったのは↓の動画でした。



MIDI音源の音色 Sine Wave を8個使って音声合成をするというもので、
これを見た時は、その発想は無かった!まさに膝ポンでした。
音声として十分聞き取れるレベルで、データのチューニング等、色々工夫されたのだろうと思います。
MIDIの転送レートでこれだけ出来ると言うことは、データをバルクダンプしているのでしょうか。凄いです。

参考資料…あまりに多い上に理解できたとは恥ずかしくて言えないような有様なので敢えて挙げないことにします。
海外では Sine Wave Synthesis などの名称で研究されているようですね。さすがにFM音源を使ったものはなさそうでした。

そういえば、ザカリテ等のしゃべるルーチンでは、1周期再生の度にTimer-Aに違う値をいれているようです。
ひょっとして今で言うところの可変ビットレートみたいな感じだったのでしょうか。謎。

全体として、要調整というところですね。まだまだ形にすらなっていないと言うことで。猛省。
DFTだけでなく、DSTなどの方法も試してみたり窓サイズを調整してみたりいろいろやってみたのですが、いまひとつ。
FM音源のスペックに沿ったうまいやり方をアドバイスいただければ幸いです。できれば易しく。


▲ TOP