解析が全然進まないー。
とグチばっかり言ってても仕方ないので、仮版で作り始めました。
○CPUとのやり取りや、出力などの制御タイミングなどは再現する。
○分かっている範囲で再現する。が...
・フィルタのパラメータはcisc.さん作の『μPD7752 風味の音声合成エンジン』から一部拝借。
・ノイズ音源の回路は適当なPN回路にする。
・補間回路は実現しない。
で、作ってみました。
D/Aコンバータは、AY-3-8910で使っていた1bit ΔΣ変調回路を使っています。その出力を、AY-3-8910の出力とORを取って、外部に出力します。
レイアウトして、TALK文や前に作った歌うプログラムなどを走らせてみました。
...何か発声はしているようだが、似ても似つかないものになった(^^;;;
さらに歌うプログラムでは、ナゼかハングアップ。
ハングアップの原因は、CPUI/Fとのタイミングと、MODE="11"(データシートではDisableとなっている)の挙動がおかしいせいでした。
実機で、MODE="11"を試すと、MODE="00"(Speed = Normal)と同じ動きでした(データシートのうそつき...)。
声がヘンなのは、何が原因かまだ特定できていません。
また、全く別の問題が...
レイアウト後のゲート使用率が、97% までなってきました。
ALTERA の場合、100% になっても、大抵問題なくレイアウトできるんですが、それでも回路の整理をする必要がありそうです。
SR は、今の基板では、今のままでは回路規模的に無理そうです。
2013年02月10日
2013年02月08日
音声合成の設計(5)の追記
2013年02月05日
音声合成の設計(6)
音声合成回路(uPD7752)の解析中ですが...2ヶ月ほどこればっかりしているんですが、全然進みません(;_;)
大体の構成は分かっていて、さらに cisc. さん作の『μPD7752 風味の音声合成エンジン』のおかげで、かなりの部分が分かっています。
ただ、肝心のフィルタの詳細がよく分かりません...
大体これかなー、というのを作ってシミュレーションをして、実機の実行結果と比較しているんですが、微妙に違う。
フィルタが完全にエミュレート出来ていないので、その後の補間とか乱数回路の解析まで進めません。
単なるグチでした...
大体の構成は分かっていて、さらに cisc. さん作の『μPD7752 風味の音声合成エンジン』のおかげで、かなりの部分が分かっています。
ただ、肝心のフィルタの詳細がよく分かりません...
大体これかなー、というのを作ってシミュレーションをして、実機の実行結果と比較しているんですが、微妙に違う。
フィルタが完全にエミュレート出来ていないので、その後の補間とか乱数回路の解析まで進めません。
単なるグチでした...
2012年12月16日
音声合成の設計(5)
音声合成の設計(4)のサンプルプログラムは、両方とも割り込み禁止をしていました。それは、音声データが0x0000からのRAM領域にあるために、切り替えるのが大変だったからです。
そこで、0x0000からの音声データを0xD000に転送させてから、割り込みを許可して音声(歌声)を鳴らすようにしました。
この方法だと、割り込みが有効なために、直前でPLAY文で音楽を鳴らすと、音声(歌声)と音楽が重ねられます。
songtest2.zip
これがそのプログラムです。速度は2を指定すると何となくタイミングが合います。
しかし、タイミングがちゃんと合っていないと、歌声+伴奏には到底聞こえません。
uPD7752 の動作タイミングを把握して、それに合わせる必要があります。
外部データの音声再生時の入出力タイミングと入出力データの詳細を以下に示します。
まず、外部データの音声再生時の動作の詳細です。
1)CPU から、mode (速度)を書き込む。
2)CPU から、外部データコマンド(0xFE)を書き込む。
3−1)uPD7752 が BUSY=L→H、REQ=L→H にする。
3−2)CPU から、ホルマントデータの1組目の1バイト目を書き込む。
3−3)uPD7752 が REQ=H→L にする。
3−4)uPD7752 が REQ=L→H にする。
3−5)CPU から、ホルマントデータの1組目の2バイト目を書き込む。
3−6)uPD7752 が REQ=H→L にする。
3−7)uPD7752 が REQ=L→H にする。
3−8)CPU から、ホルマントデータの1組目の3バイト目を書き込む。
3−9)uPD7752 が REQ=H→L にする。
3−10)uPD7752 が REQ=L→H にする。
3−11)CPU から、ホルマントデータの1組目の4バイト目を書き込む。
3−12)uPD7752 が REQ=H→L にする。
3−13)uPD7752 が REQ=L→H にする。
3−14)CPU から、ホルマントデータの1組目の5バイト目を書き込む。
3−15)uPD7752 が REQ=H→L にする。
3−16)uPD7752 が REQ=L→H にする。
3−17)CPU から、ホルマントデータの1組目の6バイト目を書き込む。
3−18)uPD7752 が REQ=H→L にする。
3−19)uPD7752 が REQ=L→H にする。
3−20)CPU から、ホルマントデータの1組目の7バイト目を書き込む。
3−21)uPD7752 が REQ=H→L にする。
3−22)uPD7752 内の初期設定の処理が行われる(ちょっと時間がかかる)。
4−1)uPD7752 が REQ=L→H にする。
4−2)CPU から、ホルマントデータの2組目の1バイト目を書き込む。
4−3)uPD7752 が REQ=H→L にする。
4−4)uPD7752 が REQ=L→H にする。
4−5)CPU から、ホルマントデータの2組目の2バイト目を書き込む。
4−6)uPD7752 が REQ=H→L にする。
:
: 3−7)〜3−19)と同様
:
4−19)uPD7752 が REQ=L→H にする。
4−20)CPU から、ホルマントデータの2組目の7バイト目を書き込む。
4−21)uPD7752 が REQ=H→L にする。
4−22)uPD7752 内で1組目のホルマントデータの再生が完了するまで待つ。
4−1)〜4−22)の間に、3−1)〜3−21)で設定した1組目のデータが再生される。
5−1)uPD7752 が REQ=L→H にする。
5−2)CPU から、ホルマントデータの3組目の1バイト目を書き込む。
5−3)uPD7752 が REQ=H→L にする。
:
: 3−4)〜3−19)と同様
:
5−19)uPD7752 が REQ=L→H にする。
5−20)CPU から、ホルマントデータの3組目の7バイト目を書き込む。
5−21)uPD7752 が REQ=H→L にする。
5−22)uPD7752 内で2組目のホルマントデータの再生が完了するまで待つ。
5−1)〜5−22)の間に、4−1)〜4−21)で設定した2組目のデータが再生される。
以下、上記の内容が繰り返されます。
最終バイトは、送るデータが違うだけです。
?−1)uPD7752 が REQ=L→H にする。
?−2)CPU から、ホルマントデータの最終組の1バイト目(0x00)を書き込む。
?−3)uPD7752 が REQ=H→L にする。
?−4)uPD7752 が REQ=L→H にする。
?−5)CPU から、ホルマントデータの最終組の2バイト目(0x00:ダミー)を書き込む。
?−6)uPD7752 が REQ=H→L にする。
:
: 3−5)〜3−19)と同様。書き込むのは、(0x00:ダミー)のデータ
:
?−19)uPD7752 が REQ=L→H にする。
?−20)CPU から、ホルマントデータの最終組の2バイト目(0x00:ダミー)を書き込む。
?−21)uPD7752 が REQ=H→L にする。
?−22)uPD7752 内で(最終−1)組目のホルマントデータの再生が完了するまで待つ。
?−1)〜?−22)の間に、一つ前で設定したデータが再生される。
TIME に2以上を設定した場合は、以下のようになります。
例えばTIME=3 の場合、
a−1)uPD7752 が REQ=L→H にする。
a−2)CPU から、ホルマントデータのn組目の1バイト目を書き込む。
a−3)uPD7752 が REQ=H→L にする。
a−4)uPD7752 が REQ=L→H にする。
a−5)CPU から、ホルマントデータのn組目の2バイト目を書き込む。
a−6)uPD7752 が REQ=H→L にする。
:
: 3−7)〜3−19)と同様
:
a−19)uPD7752 が REQ=L→H にする。
a−20)CPU から、ホルマントデータのn組目の7バイト目を書き込む。
a−21)uPD7752 が REQ=H→L にする。
a−22)uPD7752 内で一つ前のホルマントデータの再生が完了するまで待つ。
a−1)〜a−22)の間、一つ前で設定したデータが再生される。
b−1)uPD7752 が REQ=L→H にする。
b−2)CPU から、ホルマントデータのn組目の8バイト目を書き込む。
b−3)uPD7752 が REQ=H→L にする。
b−4)uPD7752 内で一つ前のホルマントデータの再生が完了するまで待つ。
b−1)〜b−4)の間、a−1)〜a−22)で設定したデータが再生される。
c−1)uPD7752 が REQ=L→H にする。
c−2)CPU から、ホルマントデータのn組目の9バイト目を書き込む。
c−3)uPD7752 が REQ=H→L にする。
c−4)uPD7752 内で一つ前のホルマントデータの再生が完了するまで待つ。
c−1)〜c−4)の間、b−1)〜b−22)で設定したデータが再生される。
上記のタイミングチャートは以下の通りです。




それぞれのタイミングチャートの中での、フレームの長さや設定完了までの制限時間は、音声の再生速度に依存します。

制限時間までにホルマントデータを設定しないと、エラーになります。
uPD7752 の入出力タイミングが分かると、それに合わせてAY-3-8910を制御すれば、歌声に伴奏が出来る事になります。
実際に作ったのがこれです。
kohan.zip
プログラムにデータを持たせましたので、mk2、66、のどちらでも動作可能です。
MODE=5、PAGE=2 で起動して下さい。
エミュレータと実機で音量が違うようなので、PSG の方の音量を変更可能にしました。
10ms/Normal の設定(速度2)の時は、一つのデータの処理が10ms なので、これに合わせて音を出しています。
そのため、音声の再生速度を変更すると、伴奏の速度も合わせて変わります。
実行結果〜♪
実機(mk2)で、速度 = 1、VOL-A = 8、VOL-B = 8、として再生したものです。
音楽の才能がないので、イマイチですが...
そこで、0x0000からの音声データを0xD000に転送させてから、割り込みを許可して音声(歌声)を鳴らすようにしました。
この方法だと、割り込みが有効なために、直前でPLAY文で音楽を鳴らすと、音声(歌声)と音楽が重ねられます。
songtest2.zip
これがそのプログラムです。速度は2を指定すると何となくタイミングが合います。
しかし、タイミングがちゃんと合っていないと、歌声+伴奏には到底聞こえません。
uPD7752 の動作タイミングを把握して、それに合わせる必要があります。
外部データの音声再生時の入出力タイミングと入出力データの詳細を以下に示します。
まず、外部データの音声再生時の動作の詳細です。
1)CPU から、mode (速度)を書き込む。
2)CPU から、外部データコマンド(0xFE)を書き込む。
3−1)uPD7752 が BUSY=L→H、REQ=L→H にする。
3−2)CPU から、ホルマントデータの1組目の1バイト目を書き込む。
3−3)uPD7752 が REQ=H→L にする。
3−4)uPD7752 が REQ=L→H にする。
3−5)CPU から、ホルマントデータの1組目の2バイト目を書き込む。
3−6)uPD7752 が REQ=H→L にする。
3−7)uPD7752 が REQ=L→H にする。
3−8)CPU から、ホルマントデータの1組目の3バイト目を書き込む。
3−9)uPD7752 が REQ=H→L にする。
3−10)uPD7752 が REQ=L→H にする。
3−11)CPU から、ホルマントデータの1組目の4バイト目を書き込む。
3−12)uPD7752 が REQ=H→L にする。
3−13)uPD7752 が REQ=L→H にする。
3−14)CPU から、ホルマントデータの1組目の5バイト目を書き込む。
3−15)uPD7752 が REQ=H→L にする。
3−16)uPD7752 が REQ=L→H にする。
3−17)CPU から、ホルマントデータの1組目の6バイト目を書き込む。
3−18)uPD7752 が REQ=H→L にする。
3−19)uPD7752 が REQ=L→H にする。
3−20)CPU から、ホルマントデータの1組目の7バイト目を書き込む。
3−21)uPD7752 が REQ=H→L にする。
3−22)uPD7752 内の初期設定の処理が行われる(ちょっと時間がかかる)。
4−1)uPD7752 が REQ=L→H にする。
4−2)CPU から、ホルマントデータの2組目の1バイト目を書き込む。
4−3)uPD7752 が REQ=H→L にする。
4−4)uPD7752 が REQ=L→H にする。
4−5)CPU から、ホルマントデータの2組目の2バイト目を書き込む。
4−6)uPD7752 が REQ=H→L にする。
:
: 3−7)〜3−19)と同様
:
4−19)uPD7752 が REQ=L→H にする。
4−20)CPU から、ホルマントデータの2組目の7バイト目を書き込む。
4−21)uPD7752 が REQ=H→L にする。
4−22)uPD7752 内で1組目のホルマントデータの再生が完了するまで待つ。
4−1)〜4−22)の間に、3−1)〜3−21)で設定した1組目のデータが再生される。
5−1)uPD7752 が REQ=L→H にする。
5−2)CPU から、ホルマントデータの3組目の1バイト目を書き込む。
5−3)uPD7752 が REQ=H→L にする。
:
: 3−4)〜3−19)と同様
:
5−19)uPD7752 が REQ=L→H にする。
5−20)CPU から、ホルマントデータの3組目の7バイト目を書き込む。
5−21)uPD7752 が REQ=H→L にする。
5−22)uPD7752 内で2組目のホルマントデータの再生が完了するまで待つ。
5−1)〜5−22)の間に、4−1)〜4−21)で設定した2組目のデータが再生される。
以下、上記の内容が繰り返されます。
最終バイトは、送るデータが違うだけです。
?−1)uPD7752 が REQ=L→H にする。
?−2)CPU から、ホルマントデータの最終組の1バイト目(0x00)を書き込む。
?−3)uPD7752 が REQ=H→L にする。
?−4)uPD7752 が REQ=L→H にする。
?−5)CPU から、ホルマントデータの最終組の2バイト目(0x00:ダミー)を書き込む。
?−6)uPD7752 が REQ=H→L にする。
:
: 3−5)〜3−19)と同様。書き込むのは、(0x00:ダミー)のデータ
:
?−19)uPD7752 が REQ=L→H にする。
?−20)CPU から、ホルマントデータの最終組の2バイト目(0x00:ダミー)を書き込む。
?−21)uPD7752 が REQ=H→L にする。
?−22)uPD7752 内で(最終−1)組目のホルマントデータの再生が完了するまで待つ。
?−1)〜?−22)の間に、一つ前で設定したデータが再生される。
TIME に2以上を設定した場合は、以下のようになります。
例えばTIME=3 の場合、
a−1)uPD7752 が REQ=L→H にする。
a−2)CPU から、ホルマントデータのn組目の1バイト目を書き込む。
a−3)uPD7752 が REQ=H→L にする。
a−4)uPD7752 が REQ=L→H にする。
a−5)CPU から、ホルマントデータのn組目の2バイト目を書き込む。
a−6)uPD7752 が REQ=H→L にする。
:
: 3−7)〜3−19)と同様
:
a−19)uPD7752 が REQ=L→H にする。
a−20)CPU から、ホルマントデータのn組目の7バイト目を書き込む。
a−21)uPD7752 が REQ=H→L にする。
a−22)uPD7752 内で一つ前のホルマントデータの再生が完了するまで待つ。
a−1)〜a−22)の間、一つ前で設定したデータが再生される。
b−1)uPD7752 が REQ=L→H にする。
b−2)CPU から、ホルマントデータのn組目の8バイト目を書き込む。
b−3)uPD7752 が REQ=H→L にする。
b−4)uPD7752 内で一つ前のホルマントデータの再生が完了するまで待つ。
b−1)〜b−4)の間、a−1)〜a−22)で設定したデータが再生される。
c−1)uPD7752 が REQ=L→H にする。
c−2)CPU から、ホルマントデータのn組目の9バイト目を書き込む。
c−3)uPD7752 が REQ=H→L にする。
c−4)uPD7752 内で一つ前のホルマントデータの再生が完了するまで待つ。
c−1)〜c−4)の間、b−1)〜b−22)で設定したデータが再生される。
上記のタイミングチャートは以下の通りです。
それぞれのタイミングチャートの中での、フレームの長さや設定完了までの制限時間は、音声の再生速度に依存します。
制限時間までにホルマントデータを設定しないと、エラーになります。
uPD7752 の入出力タイミングが分かると、それに合わせてAY-3-8910を制御すれば、歌声に伴奏が出来る事になります。
実際に作ったのがこれです。
kohan.zip
プログラムにデータを持たせましたので、mk2、66、のどちらでも動作可能です。
MODE=5、PAGE=2 で起動して下さい。
エミュレータと実機で音量が違うようなので、PSG の方の音量を変更可能にしました。
10ms/Normal の設定(速度2)の時は、一つのデータの処理が10ms なので、これに合わせて音を出しています。
そのため、音声の再生速度を変更すると、伴奏の速度も合わせて変わります。
実行結果〜♪
実機(mk2)で、速度 = 1、VOL-A = 8、VOL-B = 8、として再生したものです。
音楽の才能がないので、イマイチですが...
2012年12月15日
音声合成の設計(4)の追記2
音声合成の設計(4)でのサンプルプログラム(歌う方)で、一部バグがありましたので、修正しました。
songtest.zip
実行は同じく、66 で MODE=5、PAGE=2 です。
音符長の分だけ引き伸ばしている箇所で、引き伸ばし数が0x00 の場合がありました。
この時は、引き伸ばさない、が正解でした。
songtest.zip
実行は同じく、66 で MODE=5、PAGE=2 です。
音符長の分だけ引き伸ばしている箇所で、引き伸ばし数が0x00 の場合がありました。
この時は、引き伸ばさない、が正解でした。
2012年12月14日
音声合成の設計(4)の追記
追記です。
サンプルプログラムで速度を1にした場合ですが、PC6001VW だと、なぜか速度2よりも遅くなります。どうもPC6001VWのバグのようです。
PC6001V や実機では、ちゃんと速く喋ります。参考まで。
サンプルプログラムで速度を1にした場合ですが、PC6001VW だと、なぜか速度2よりも遅くなります。どうもPC6001VWのバグのようです。
PC6001V や実機では、ちゃんと速く喋ります。参考まで。
音声合成の設計(4)
音声合成の設計(3)で説明しているホルマントパラメータですが、実際には何を与えていいか分からないと思います。
そのため、とりあえず TALK 文で音声を鳴らし、そのデータを使う事にします。
この辺りの情報は、Oh!PC 1985年1月号で解析されています。
BASICのTALK文では、
1)命令を解釈し、
2)VOICEROM から音声データを読み込み、
3)そのデータから、uPD7752に出力するデータを組み立て、
4)組み立てたデータを、0x0000〜のRAM領域に書き込みます。
そのため、VRAMの1ページ分が使えない(上書きされる)仕様になっています。
TALK文で音声を鳴らした後は、RAM上にその音声データが残っています。
残っている場所は、mk2と66 でアドレスが違います。さらに66の場合は、通常の音声と、歌声でフォーマットが若干違います。
○通常の音声データ
mk2
0x01B3、0x01B4:データバイト数
0x01B5〜:音声データ
66
0x03E6、0x03E7:データバイト数
0x03E8〜:音声データ
TALK "f2 aiueo." などと実行した時のホルマントデータが、0x01B5〜(0x03E8〜)に格納されます。また、その時のデータの総バイト数が、0x01B3、0x01B4(0x03E6、0x03E7)です。
音声データ自体には、速度(例えば、TALK"f2 aiueo." の場合は、2になる)の情報はありません。これは、ポート0xE2の動作速度を使っているためです。
10ms/20ms と、Normal/Slow/Fast の組み合わせ2×3の6通り分、速度を変えられます。TALK文の速度が1〜6と丁度対応しています。
voicetest.zip
サンプルプログラムです。MODE=5、PAGE=2、で実行して下さい。
サンプルプログラムでは、TALK文で音声をしゃべらせてから、RAM上に残っているデータをそのまま再生しています。当然、そのデータを別の場所に移して、使用する事も可能です。
○歌声データ
66
0x000D:0x01の時、歌声データ、0x00の時、通常の音声データ
0x0011:音符長データ(0x0019〜)の個数
0x0019〜:音符長データ、引き伸ばした時の音声データ
0x03E6、0x03E7:データバイト数
0x03E8〜:音声データ(+音階データ)
0x03E6以降は、通常の音声データと同じですが、音階のデータも含まれたデータになっています。ただし、音節の長さのデータがないため、そのまま再生すると、短い音節のデータで再生されます。
それぞれの音節を引き伸ばす時のデータ、及びそれぞれの音節の長さのデータが、0x0019以降に格納されています。5バイトで1つの固まりのデータで、データ数が0x0011に格納されています。
例えば、talk "f2#doremi:cde" を実行した時、RAM のデータは以下のようになります。
0x000D:01 :1の時、歌声
0x0011:04 :0x0019からのデータ数
0x0019〜
33 00 17 09 a0 :1音節目のデータ
31 00 0f 09 d0 :2音節目のデータ
2f 00 11 09 b0 :3音節目のデータ
01 00 0e 00 00 :4音節目のデータ
それぞれの音節データは、
1バイト目:追加するデータのデータ長
2バイト目:未使用
3バイト目:0x03E8からの音節の長さ
4バイト目:追加するデータの1バイト目のデータ
5バイト目:追加するデータの7バイト目のデータ
0x03E6:ea 01 :0x03E8からのデータのバイト数
0x03E8:〜
08 02 7b 12 21 df 03 :00(ここから1音節目)
08 00 23 00 00 00 03 :01
08 00 00 00 00 00 03 :02
08 00 00 00 00 00 00 :03
08 00 00 00 00 00 00 :04
08 00 00 00 00 00 00 :05
08 00 00 00 00 00 00 :06
08 00 00 00 00 00 00 :07
08 00 00 00 00 00 00 :08
08 00 00 00 00 00 00 :09
08 00 00 00 00 00 00 :0A
0d 06 b9 fd f5 bd 30 :0B
09 07 79 f0 ef ff 60 :0C
09 00 78 00 00 00 70 :0D
09 00 08 00 00 00 60 :0E
09 00 00 00 00 00 50 :0F
09 00 00 10 00 00 40 :10
09 00 88 00 20 00 60 :11
09 00 b8 f0 f0 30 b0 :12
09 00 00 00 f0 30 c0 :13
09 00 10 00 f0 00 b0 :14
09 00 20 00 f0 f0 a0 :15
09 00 30 00 f0 00 a0 :16(ここまで1音節目)
09 00 f8 00 08 00 a7 :00(ここから2音節目)
09 00 f8 00 08 08 a0 :01
以下、省略。
実際の歌声の再生時には、データを組み立てています。
1)1音節目のデータを出力。(0x0019+2)の内容の7倍のバイト数、出力する。上の例だと、0x03E8からの 00 〜 16 を出力。
2)1音節目の後に、追加でデータを出力。追加するデータは、
1バイト目は、(0x0019+3)の内容
2〜6バイト目は、0x00
7バイト目は、(0x0019+4)の内容
の7バイトを、(0x0019)回出力する。
上の例だと、09 00 00 00 00 00 a0 を 0x33 回出力する。
3)2音節目のデータを出力する。
4)2音節目の後に追加でデータを出力。追加するデータは、
09 00 00 00 00 00 d0
5)以降、0x0011 で書かれている個数分、繰り返す。
最終は、必ず 00 00 00 00 00 00 00 が出力されます。
通常の音声データと同じように、歌声データも速度の情報はなく、ポート0xE2 の出力値で変更しています。
songtest.zip
サンプルプログラムです。66 で MODE=5、PAGE=2、で実行して下さい。
サンプルプログラムは、上記アルゴリズムの通りに歌声を再生しています。データをファイルに落とせば、mk2 上でも実行できます。
そのため、とりあえず TALK 文で音声を鳴らし、そのデータを使う事にします。
この辺りの情報は、Oh!PC 1985年1月号で解析されています。
BASICのTALK文では、
1)命令を解釈し、
2)VOICEROM から音声データを読み込み、
3)そのデータから、uPD7752に出力するデータを組み立て、
4)組み立てたデータを、0x0000〜のRAM領域に書き込みます。
そのため、VRAMの1ページ分が使えない(上書きされる)仕様になっています。
TALK文で音声を鳴らした後は、RAM上にその音声データが残っています。
残っている場所は、mk2と66 でアドレスが違います。さらに66の場合は、通常の音声と、歌声でフォーマットが若干違います。
○通常の音声データ
mk2
0x01B3、0x01B4:データバイト数
0x01B5〜:音声データ
66
0x03E6、0x03E7:データバイト数
0x03E8〜:音声データ
TALK "f2 aiueo." などと実行した時のホルマントデータが、0x01B5〜(0x03E8〜)に格納されます。また、その時のデータの総バイト数が、0x01B3、0x01B4(0x03E6、0x03E7)です。
音声データ自体には、速度(例えば、TALK"f2 aiueo." の場合は、2になる)の情報はありません。これは、ポート0xE2の動作速度を使っているためです。
10ms/20ms と、Normal/Slow/Fast の組み合わせ2×3の6通り分、速度を変えられます。TALK文の速度が1〜6と丁度対応しています。
voicetest.zip
サンプルプログラムです。MODE=5、PAGE=2、で実行して下さい。
サンプルプログラムでは、TALK文で音声をしゃべらせてから、RAM上に残っているデータをそのまま再生しています。当然、そのデータを別の場所に移して、使用する事も可能です。
○歌声データ
66
0x000D:0x01の時、歌声データ、0x00の時、通常の音声データ
0x0011:音符長データ(0x0019〜)の個数
0x0019〜:音符長データ、引き伸ばした時の音声データ
0x03E6、0x03E7:データバイト数
0x03E8〜:音声データ(+音階データ)
0x03E6以降は、通常の音声データと同じですが、音階のデータも含まれたデータになっています。ただし、音節の長さのデータがないため、そのまま再生すると、短い音節のデータで再生されます。
それぞれの音節を引き伸ばす時のデータ、及びそれぞれの音節の長さのデータが、0x0019以降に格納されています。5バイトで1つの固まりのデータで、データ数が0x0011に格納されています。
例えば、talk "f2#doremi:cde" を実行した時、RAM のデータは以下のようになります。
0x000D:01 :1の時、歌声
0x0011:04 :0x0019からのデータ数
0x0019〜
33 00 17 09 a0 :1音節目のデータ
31 00 0f 09 d0 :2音節目のデータ
2f 00 11 09 b0 :3音節目のデータ
01 00 0e 00 00 :4音節目のデータ
それぞれの音節データは、
1バイト目:追加するデータのデータ長
2バイト目:未使用
3バイト目:0x03E8からの音節の長さ
4バイト目:追加するデータの1バイト目のデータ
5バイト目:追加するデータの7バイト目のデータ
0x03E6:ea 01 :0x03E8からのデータのバイト数
0x03E8:〜
08 02 7b 12 21 df 03 :00(ここから1音節目)
08 00 23 00 00 00 03 :01
08 00 00 00 00 00 03 :02
08 00 00 00 00 00 00 :03
08 00 00 00 00 00 00 :04
08 00 00 00 00 00 00 :05
08 00 00 00 00 00 00 :06
08 00 00 00 00 00 00 :07
08 00 00 00 00 00 00 :08
08 00 00 00 00 00 00 :09
08 00 00 00 00 00 00 :0A
0d 06 b9 fd f5 bd 30 :0B
09 07 79 f0 ef ff 60 :0C
09 00 78 00 00 00 70 :0D
09 00 08 00 00 00 60 :0E
09 00 00 00 00 00 50 :0F
09 00 00 10 00 00 40 :10
09 00 88 00 20 00 60 :11
09 00 b8 f0 f0 30 b0 :12
09 00 00 00 f0 30 c0 :13
09 00 10 00 f0 00 b0 :14
09 00 20 00 f0 f0 a0 :15
09 00 30 00 f0 00 a0 :16(ここまで1音節目)
09 00 f8 00 08 00 a7 :00(ここから2音節目)
09 00 f8 00 08 08 a0 :01
以下、省略。
実際の歌声の再生時には、データを組み立てています。
1)1音節目のデータを出力。(0x0019+2)の内容の7倍のバイト数、出力する。上の例だと、0x03E8からの 00 〜 16 を出力。
2)1音節目の後に、追加でデータを出力。追加するデータは、
1バイト目は、(0x0019+3)の内容
2〜6バイト目は、0x00
7バイト目は、(0x0019+4)の内容
の7バイトを、(0x0019)回出力する。
上の例だと、09 00 00 00 00 00 a0 を 0x33 回出力する。
3)2音節目のデータを出力する。
4)2音節目の後に追加でデータを出力。追加するデータは、
09 00 00 00 00 00 d0
5)以降、0x0011 で書かれている個数分、繰り返す。
最終は、必ず 00 00 00 00 00 00 00 が出力されます。
通常の音声データと同じように、歌声データも速度の情報はなく、ポート0xE2 の出力値で変更しています。
songtest.zip
サンプルプログラムです。66 で MODE=5、PAGE=2、で実行して下さい。
サンプルプログラムは、上記アルゴリズムの通りに歌声を再生しています。データをファイルに落とせば、mk2 上でも実行できます。
2012年12月13日
音声合成の設計(3)
内部ROMデータだと固定語しか出せないので、通常は外部データを使う事になります。
外部データを使う場合、ホルマントデータを与える必要がありますが、そのフォーマットに関してです。
ホルマントデータは、基本的に7バイト単位で与えます(例外あり。後述)。

・1バイト目のbit7-3:TIME
繰り返し回数。通常は1("00001")を設定。0("00000")の時は、データが終了した事を意味します。
・1バイト目のbit2:QMAG
"1"の時は、2〜6バイト目のf、b の値を2倍にします。
・1バイト目のbit1:S.I.
"1"の時は、F、B、の補間を行わない。
・1バイト目のbit0:V/UV
7バイト目のFVと併せて、音源を選択する。
V/UV = "0"、FV="0" の時、無声音源(摩擦音源)
V/UV = "0"、FV="1" の時、使用禁止
V/UV = "1"、FV="0" の時、有声音源
V/UV = "1"、FV="1" の時、有声摩擦音源(有声音源+無声音源)
・2バイト目のbit7-3:f1
1段目のホルマントフィルタの周波数情報用の差分データ
・2バイト目のbit2-0:b1
1段目のホルマントフィルタの帯域幅用の差分データ
・3バイト目のbit7-3:f2
2段目のホルマントフィルタの周波数情報用の差分データ
・3バイト目のbit2-0:b2
2段目のホルマントフィルタの帯域幅用の差分データ
・4バイト目のbit7-3:f3
3段目のホルマントフィルタの周波数情報用の差分データ
・4バイト目のbit2-0:b3
3段目のホルマントフィルタの帯域幅用の差分データ
・5バイト目のbit7-3:f4
4段目のホルマントフィルタの周波数情報用の差分データ
・5バイト目のbit2-0:b4
4段目のホルマントフィルタの帯域幅用の差分データ
・6バイト目のbit7-3:f5
5段目のホルマントフィルタの周波数情報用の差分データ
・6バイト目のbit2-0:b5
5段目のホルマントフィルタの帯域幅用の差分データ
・7バイト目のbit7-4:AMP
音源からフィルタへ入力する際の音量値用データ
・7バイト目のbit3:FV
1バイト目のV/UV と併せて、音源を選択する。
・7バイト目のbit2-0:p
有声音源用の周波数を決定する値の差分データ
それぞれのパラメータの詳細については、後ほど。
1バイト目のTIME は、繰り返し回数を示し、2以上の場合は7バイト単位ではなく、7バイト+αになります。
この時、7バイト目を繰り返します。
例えば、TIME に3を設定した場合は、
1バイト目
2バイト目
3バイト目
4バイト目
5バイト目
6バイト目
7バイト目
7バイト目その2
7バイト目その3
になります。その2、その3のデータを処理する時は、1〜6バイト目が同じ値として扱います。
1〜6バイト目を何度も与えなくて済むので、データの削減が図れるのが狙いなようです。
外部データを使う場合、ホルマントデータを与える必要がありますが、そのフォーマットに関してです。
ホルマントデータは、基本的に7バイト単位で与えます(例外あり。後述)。
・1バイト目のbit7-3:TIME
繰り返し回数。通常は1("00001")を設定。0("00000")の時は、データが終了した事を意味します。
・1バイト目のbit2:QMAG
"1"の時は、2〜6バイト目のf、b の値を2倍にします。
・1バイト目のbit1:S.I.
"1"の時は、F、B、の補間を行わない。
・1バイト目のbit0:V/UV
7バイト目のFVと併せて、音源を選択する。
V/UV = "0"、FV="0" の時、無声音源(摩擦音源)
V/UV = "0"、FV="1" の時、使用禁止
V/UV = "1"、FV="0" の時、有声音源
V/UV = "1"、FV="1" の時、有声摩擦音源(有声音源+無声音源)
・2バイト目のbit7-3:f1
1段目のホルマントフィルタの周波数情報用の差分データ
・2バイト目のbit2-0:b1
1段目のホルマントフィルタの帯域幅用の差分データ
・3バイト目のbit7-3:f2
2段目のホルマントフィルタの周波数情報用の差分データ
・3バイト目のbit2-0:b2
2段目のホルマントフィルタの帯域幅用の差分データ
・4バイト目のbit7-3:f3
3段目のホルマントフィルタの周波数情報用の差分データ
・4バイト目のbit2-0:b3
3段目のホルマントフィルタの帯域幅用の差分データ
・5バイト目のbit7-3:f4
4段目のホルマントフィルタの周波数情報用の差分データ
・5バイト目のbit2-0:b4
4段目のホルマントフィルタの帯域幅用の差分データ
・6バイト目のbit7-3:f5
5段目のホルマントフィルタの周波数情報用の差分データ
・6バイト目のbit2-0:b5
5段目のホルマントフィルタの帯域幅用の差分データ
・7バイト目のbit7-4:AMP
音源からフィルタへ入力する際の音量値用データ
・7バイト目のbit3:FV
1バイト目のV/UV と併せて、音源を選択する。
・7バイト目のbit2-0:p
有声音源用の周波数を決定する値の差分データ
それぞれのパラメータの詳細については、後ほど。
1バイト目のTIME は、繰り返し回数を示し、2以上の場合は7バイト単位ではなく、7バイト+αになります。
この時、7バイト目を繰り返します。
例えば、TIME に3を設定した場合は、
1バイト目
2バイト目
3バイト目
4バイト目
5バイト目
6バイト目
7バイト目
7バイト目その2
7バイト目その3
になります。その2、その3のデータを処理する時は、1〜6バイト目が同じ値として扱います。
1〜6バイト目を何度も与えなくて済むので、データの削減が図れるのが狙いなようです。
音声合成の設計(2)
まず、uPD7752 の使い方から。
どうも uPD7752 の使い方に関する解説はほとんど知られていないようで、TALK文以外の使い方をしている人は皆無だと思います。
資料としては、
・NEC技報1981年10月号
・uPD7752 データシート
ぐらいしかなさそうです。
また、BASIC のTALK文の解析は、
・Oh!PC 1985年1月号
で、多少詳しく解析されています(ただし、この記事は、uPD7752 の仕様を知らずに解析しているようです)。
アクセスは、I/O ポートの0xE0〜0xE3 を使います。
○0xE0:リード:ステータス読み出し
(0xE0〜0xE3 のどれを読み出しても同じ)
bit7:BUSY
bit6:REQ
bit5:nINT/EXT
bit4:Error
bit3-0:"0"
BUSY が"1" の時は、音声データを出力している時(uPD7752が動作している時)です。
REQ が"1" の時は、ホルマントデータの入力を要求している時です。
nINT/EXT は、"0" 時:内部ROMデータ、"1" 時:外部データで音声を再生している事を示します。
Error は、
・REQ が"0" の時に、ホルマントデータを書き込まれた
・REQ が"1" になってから、所定時間以内に、ホルマントデータが書き込まれなかった。
時に、"1" にセットされます。
・uPD7752 がリセットされた。
・新しいコマンドが書き込まれた。
時に、"0" にリセットされます。
○0xE0:ライト:ホルマントデータ書き込み
ややこしいので後ほど。
○0xE1:ライト:禁止(未使用)
このポートは使えません。
○0xE2:ライト:モード書き込み
動作する速度を設定します。
bit2: "0"時、10ms/frame、"1"時、20ms/frame です。
bit1-0:
"00"時、Normal speed
"01"時、Slow(×1.2)
"10"時、Fast(×0.8)
"11"時、Disable となります。
bit7-3:未使用
○0xE3:ライト:コマンド書き込み
"11111111" 時、音声合成を停止します。
"11111110" 時、ホルマントデータを外部データにします。
それ以外は、全て内部ROMデータになります。
bit5-0: 内部ROMデータの番号(0-63)
bit7-6:未使用
P6では、内部ROMデータは、0-4 しか実装されていないようです。
使い方としては、
・内部ROMデータを使う場合
1)0xE2 に出力し、速度を設定する。
2)0xE3 に再生するROM番号を出力し、音声を再生する。
・外部データを使う場合
1)0xE2 に出力し、速度を設定する。
2)0xE3 に 0xFE を出力し、外部データを選択する。
3)0xE0 のbit6 を確認し、"1" になっていたら、ホルマントデータを1バイト書き込む。
4)出力すべきホルマントデータがなくなるまで、3)に戻る。
内部ROMデータを使う場合は、すごく簡単です。
外部データを使う場合は、常に REQ になっているかを監視する必要があるので、ちょっと面倒です。
REQ が"1" になってから、所定時間以内にデータをセットしないとエラーになるので、注意が必要です。
BASIC の TALK文では、REQ をずっと監視し続けるために、他の処理が出来ないようになっています。
どうも uPD7752 の使い方に関する解説はほとんど知られていないようで、TALK文以外の使い方をしている人は皆無だと思います。
資料としては、
・NEC技報1981年10月号
・uPD7752 データシート
ぐらいしかなさそうです。
また、BASIC のTALK文の解析は、
・Oh!PC 1985年1月号
で、多少詳しく解析されています(ただし、この記事は、uPD7752 の仕様を知らずに解析しているようです)。
アクセスは、I/O ポートの0xE0〜0xE3 を使います。
○0xE0:リード:ステータス読み出し
(0xE0〜0xE3 のどれを読み出しても同じ)
bit7:BUSY
bit6:REQ
bit5:nINT/EXT
bit4:Error
bit3-0:"0"
BUSY が"1" の時は、音声データを出力している時(uPD7752が動作している時)です。
REQ が"1" の時は、ホルマントデータの入力を要求している時です。
nINT/EXT は、"0" 時:内部ROMデータ、"1" 時:外部データで音声を再生している事を示します。
Error は、
・REQ が"0" の時に、ホルマントデータを書き込まれた
・REQ が"1" になってから、所定時間以内に、ホルマントデータが書き込まれなかった。
時に、"1" にセットされます。
・uPD7752 がリセットされた。
・新しいコマンドが書き込まれた。
時に、"0" にリセットされます。
○0xE0:ライト:ホルマントデータ書き込み
ややこしいので後ほど。
○0xE1:ライト:禁止(未使用)
このポートは使えません。
○0xE2:ライト:モード書き込み
動作する速度を設定します。
bit2: "0"時、10ms/frame、"1"時、20ms/frame です。
bit1-0:
"00"時、Normal speed
"01"時、Slow(×1.2)
"10"時、Fast(×0.8)
"11"時、Disable となります。
bit7-3:未使用
○0xE3:ライト:コマンド書き込み
"11111111" 時、音声合成を停止します。
"11111110" 時、ホルマントデータを外部データにします。
それ以外は、全て内部ROMデータになります。
bit5-0: 内部ROMデータの番号(0-63)
bit7-6:未使用
P6では、内部ROMデータは、0-4 しか実装されていないようです。
使い方としては、
・内部ROMデータを使う場合
1)0xE2 に出力し、速度を設定する。
2)0xE3 に再生するROM番号を出力し、音声を再生する。
・外部データを使う場合
1)0xE2 に出力し、速度を設定する。
2)0xE3 に 0xFE を出力し、外部データを選択する。
3)0xE0 のbit6 を確認し、"1" になっていたら、ホルマントデータを1バイト書き込む。
4)出力すべきホルマントデータがなくなるまで、3)に戻る。
内部ROMデータを使う場合は、すごく簡単です。
外部データを使う場合は、常に REQ になっているかを監視する必要があるので、ちょっと面倒です。
REQ が"1" になってから、所定時間以内にデータをセットしないとエラーになるので、注意が必要です。
BASIC の TALK文では、REQ をずっと監視し続けるために、他の処理が出来ないようになっています。
2012年12月12日
音声合成の設計(1)
私はツイッターはしていないんですが、P6botパピ子ちゃんだけは読んでいます。
初代P6 でサイバースティックを動かしている人もいるようで、役に立てているようです。
で、気になったつぶやきなんですが...
『YM2203の音声合成モードとuPD7752を同時に喋らすことってできるんだろか』
YM2203 の音声合成モードって何かなー、と思ったらどうもPCM再生と同じようなものでした(似て非なるものですが)。
(bookwormさんのトコに書いてました。世間は狭いな...)
で、私の中の結論は、AY-3-8910 のPCM音声再生と uPD7752 とは同時に再生が出来る、です。
ただ、やるのは大変なのと、実際に同時に再生してもあまり面白くなさそうなので、試しません(^^;
今回試そうとしているのは、AY-3-8910 に伴奏させて、uPD7752 に歌わせるというもの。
これなら利用価値がありそうですよね。
実現は割と簡単そうなんですが、音楽データを作るのが面倒だという話も。
私の知っている限りではないんですが、既に実現しているソフトがあるかも知れません。
初代P6 でサイバースティックを動かしている人もいるようで、役に立てているようです。
で、気になったつぶやきなんですが...
『YM2203の音声合成モードとuPD7752を同時に喋らすことってできるんだろか』
YM2203 の音声合成モードって何かなー、と思ったらどうもPCM再生と同じようなものでした(似て非なるものですが)。
(bookwormさんのトコに書いてました。世間は狭いな...)
で、私の中の結論は、AY-3-8910 のPCM音声再生と uPD7752 とは同時に再生が出来る、です。
ただ、やるのは大変なのと、実際に同時に再生してもあまり面白くなさそうなので、試しません(^^;
今回試そうとしているのは、AY-3-8910 に伴奏させて、uPD7752 に歌わせるというもの。
これなら利用価値がありそうですよね。
実現は割と簡単そうなんですが、音楽データを作るのが面倒だという話も。
私の知っている限りではないんですが、既に実現しているソフトがあるかも知れません。
2012年11月05日
音声合成LSI解析用ボード
音声合成のLSIの解析のため、データ転送用のボードを作りました。
...というか、RS-232C ボードの作成のきっかけも、音声合成のLSIの解析をしようと思っていたからでした。
というわけで、解析用のボードを作りました。
音声合成LSIは、CPUインタフェースからパラメータを設定すると、音声データを出力します。この出力は2種類あって、1つはアナログデータで、そのまま音声データとして使えます。
もう一つは、アナログデータをD/Aする前のデジタルデータが出力されています。今回はこれを取り込んで解析に使用します。
デジタルデータはシリアルで出力されます。タイミングは下図の通りです。

パルス幅などが書かれていないのは手抜きです。VCK の立下りの間隔は、3.6MHz クロックで6クロック分です(1.67μS)。
他の人が使う事がないと思いますが、参考のために回路図とAVRのプログラムを載せておきます。
今回は AVR を使用しました。AVR のテスト用に購入した AT90USB162 が搭載されているデータを使用しました。

音声合成ボード(AVRプログラム)
ちなみにボードの詳細はここ。
http://strawberry-linux.com/products/at90usb162/spec.php
USB からプログラムを書き換えられるので便利かなーと思ったので、これにしました。
ヒューズビットの読み書きをした場合には、ISPライタが必要になります。このボードを使っている分には必要ないかとは思います。
私は、LPTから接続できるISPライタを自作しました。
プログラムの構成自体は簡単で、VCK の立下りで DVO を取り込み1ビットシフト、VSTB の立ち上がりでデータを確定させてデータを送信するだけです。
これだけなんですが、VCK の立下りがくる間隔が 1.67μs 毎です。
これは、16MHz で数えると26クロックぐらいなので、 AVR では結構厳しいものがあります。
ですので、AVR のプログラムは、割り込みルーチンをアセンブラで書いています。さらに、SREG 待避とデータ取り込み専用のレジスタを設けています(メインプログラムからは使われない)。
取り込んだデータは USART を通して、RS-232C経由でパソコンに送信します。計算上は、ボーレートが250Kbps もあれば十分なはずなんですが、なぜか安定しませんでした。500Kbps にすれば安定的に送信できましたので、今の値になっています。
...というか、RS-232C ボードの作成のきっかけも、音声合成のLSIの解析をしようと思っていたからでした。
というわけで、解析用のボードを作りました。
音声合成LSIは、CPUインタフェースからパラメータを設定すると、音声データを出力します。この出力は2種類あって、1つはアナログデータで、そのまま音声データとして使えます。
もう一つは、アナログデータをD/Aする前のデジタルデータが出力されています。今回はこれを取り込んで解析に使用します。
デジタルデータはシリアルで出力されます。タイミングは下図の通りです。
パルス幅などが書かれていないのは手抜きです。VCK の立下りの間隔は、3.6MHz クロックで6クロック分です(1.67μS)。
他の人が使う事がないと思いますが、参考のために回路図とAVRのプログラムを載せておきます。
今回は AVR を使用しました。AVR のテスト用に購入した AT90USB162 が搭載されているデータを使用しました。
音声合成ボード(AVRプログラム)
ちなみにボードの詳細はここ。
http://strawberry-linux.com/products/at90usb162/spec.php
USB からプログラムを書き換えられるので便利かなーと思ったので、これにしました。
ヒューズビットの読み書きをした場合には、ISPライタが必要になります。このボードを使っている分には必要ないかとは思います。
私は、LPTから接続できるISPライタを自作しました。
プログラムの構成自体は簡単で、VCK の立下りで DVO を取り込み1ビットシフト、VSTB の立ち上がりでデータを確定させてデータを送信するだけです。
これだけなんですが、VCK の立下りがくる間隔が 1.67μs 毎です。
これは、16MHz で数えると26クロックぐらいなので、 AVR では結構厳しいものがあります。
ですので、AVR のプログラムは、割り込みルーチンをアセンブラで書いています。さらに、SREG 待避とデータ取り込み専用のレジスタを設けています(メインプログラムからは使われない)。
取り込んだデータは USART を通して、RS-232C経由でパソコンに送信します。計算上は、ボーレートが250Kbps もあれば十分なはずなんですが、なぜか安定しませんでした。500Kbps にすれば安定的に送信できましたので、今の値になっています。
2012年06月28日
音声合成回路の設計(1)
タイトルは「音声合成回路の設計」ですが、実際には設計ではなくて解析です。
まず、音声合成回路(μPD7752)について、いろいろと情報を探しました。
以下のものを参考に出来そうです。
1)μPD7752 データシート
外部からの使い方が一通り記載されています。
2)NEC技報1981-10
内部の構造が大体記載されています。
ただ、具体的な数値とかが書かれていないので、自分で調べる必要があります。
3)ciscさん作、μPD7752風味 音声合成エンジン http://retropc.net/cisc/sound/
これ、すごいですね。内部構成をどうやって調べたんだろ?
4)KAWさんの「Making「音声出力」」 http://kaw.my-sv.net/index.htm
直にμPD7752を使う時の方法がわかります。
テーブルが一部不完全ですが、上記のNEC技報に内容が記載されています。
上のものより内部回路を再現していくつもりです。
内部構成は、大体2)に記載されていますが、変換テーブルやパラメータの初期値など、
実現するのに必須なものの具体的な数値の記載はありません。
外部からうまくパラメータを与えて、内部を調べるしかなさそうです...
μPD7752 の出力には、スピーカーにつながっているアナログ出力以外に、
D/A前のデジタル出力があるので、これを解析していくことになります。
シリアル出力が面倒なので、S/P変換した後にモニタリングします。
まず、音声合成回路(μPD7752)について、いろいろと情報を探しました。
以下のものを参考に出来そうです。
1)μPD7752 データシート
外部からの使い方が一通り記載されています。
2)NEC技報1981-10
内部の構造が大体記載されています。
ただ、具体的な数値とかが書かれていないので、自分で調べる必要があります。
3)ciscさん作、μPD7752風味 音声合成エンジン http://retropc.net/cisc/sound/
これ、すごいですね。内部構成をどうやって調べたんだろ?
4)KAWさんの「Making「音声出力」」 http://kaw.my-sv.net/index.htm
直にμPD7752を使う時の方法がわかります。
テーブルが一部不完全ですが、上記のNEC技報に内容が記載されています。
上のものより内部回路を再現していくつもりです。
内部構成は、大体2)に記載されていますが、変換テーブルやパラメータの初期値など、
実現するのに必須なものの具体的な数値の記載はありません。
外部からうまくパラメータを与えて、内部を調べるしかなさそうです...
μPD7752 の出力には、スピーカーにつながっているアナログ出力以外に、
D/A前のデジタル出力があるので、これを解析していくことになります。
シリアル出力が面倒なので、S/P変換した後にモニタリングします。