Making「鼻歌」その1

Home PC-6001mkII Program etc

 このページでは、計画から音声出力の部分的成功までの過程を書いています。

Making「鼻歌」 Top


2015/01/xx:計画
 PC-6001mkIIで音声出力の研究を始めて早くも6年を超す。ノウハウもそれなりに貯まってきたし、そろそろ作れるのではなかろうか、リアルタイム音声合成が。

 理論上は、これまでHSPで作成していた音声データ生成処理をZ80のマシン語に落とし込めばいい。ただ、生成処理が間に合わないとそもそも音声が鳴らないんだよな。毎度のことながらだが、処理を軽くする方法をまず考えないといけない。

 ここで、音声合成ROMに送る7バイトデータをおさらいする。F1〜F5が符号付き5bit範囲、B1〜B5とPitchが符号付き3bit範囲の差分データとなる。FB2倍フラグが立っているときは、F1〜F5とB1〜B5の差分値が倍になる。
 bit7bit6bit5bit4bit3bit2bit1bit0
1バイト目繰り返し数FB2倍???T|N
2バイト目F1B1
3バイト目F2B2
4バイト目F3B3
5バイト目F4B4
6バイト目F5B5
7バイト目AmpT&NPitch

 ???部分は、資料によると選択的補完情報...らしいが、正直どんな効果があるのかよくわからない。
 この他に繰り返し数部分が2以上の場合の特殊な書式があるが、必須という訳ではないし、処理が面d…複雑になるので、まずは考えない事にする。

 という事で、この7バイトデータをZ80でどうやって作成するかが肝であり、大きな関門でもある。

2015/01/xx:処理概要 その1
 大まかに処理内容を決める。

 まず、加工元の音声データは、HSPのプログラムで使用した以下の16バイト1組のフォーマットを流用する。
 項目値範囲
1バイト目F1 実際値0〜255?
2バイト目B1 実際値0〜255?
3バイト目F2 実際値0〜255?
4バイト目B2 実際値0〜255?
5バイト目F3 実際値0〜255?
6バイト目B3 実際値0〜255?
7バイト目F4 実際値0〜255?
8バイト目B4 実際値0〜255?
9バイト目F5 実際値0〜255?
10バイト目B5 実際値0〜255?
11バイト目Pitch 実際値0〜255?
12バイト目Amp0〜15
13バイト目Tone/Noizeフラグ0:Tone, 1:Noize, 3:Tone+Noize
14〜16バイト目未使用

 後は、F,B,Pitchについて実際値の差分を求めて7バイトデータとして表せば良い。と言いたいところだが、あいにく差分値の範囲には限界がある。差分情報を、実際に使える差分範囲に丸めるための変換テーブルがあった方が良いのだろうか?

F値用に5bit範囲の丸め、B値とPitch値用に3bit範囲の丸め、これにFB2倍フラグが絡むと…、えーと、どういう変換テーブルにすればいいかよくわからなくなってきた。

2015/04/xx:処理概要 その2
 月日が流れて、早くも4月。また少しやる気が出てきたので、処理内容の続きを考える。

 7バイトデータにするときの差分情報の範囲に制限がある以上、差分で表現できる範囲を超えることはできない。つまり、差分範囲を超える変化があった場合は、差分処理を複数回に分ける必要がある。

 という事で、本来出力したい値を目標値、現在の値を現在値、最終的に出力するデータを次回値として、以下の処理を考える。
  1. F、B, Pitchについて 差分値 = 目標値−現在値 を求める
  2. 差分値を各値の差分範囲内になるよう加工する
  3. 次回値 = 差分値 + 現在値を計算
  4. 差分値を元に7バイトデータの生成
 おおむねこんな感じかな。これに2倍フラグが絡むと、話が面倒になってくるんだろうな。

 ひとまず次回値の計算部分を途中まで作成するが、うまく動くかどうかはまだわからない。

2015/04/xx:基準長
 音声出力するにあたって、やはり音の長さも必要だろう。ひいては、テンポの概念も必要かな。

 鳴らす音声の長さをカウンタで管理するとして、カウンタの単位をどうするか考える。

 まず、カウンタの範囲は2バイトに抑えるようにする。音声出力が10ミリ秒単位のため、1カウンタ=10msにしたいところだが、それだとさすがに分解能が低いので、下位1バイトを小数点以下とする。これで分解能が10/256ミリ秒≒39マイクロ秒、最大の音の長さが約2.56秒となる。

 次に音の基準長だが、多くの音源ドライバが96分音符を基準長としている事から、それに習う事にする。
 という事で、96分音符の長さは10msを1フレームとして何フレームかを逆算せねば。えーと、こんな感じかな。
  • テンポ120の4分音符は0.5秒→500ms→50フレーム
  • 96分音符単位に直すと、50フレーム×4÷96=25/12フレーム
  • テンポが半分になると、同じ96分音符でも音の長さが2倍になるので、
    テンポはフレームと反比例
  • 120×25/12=250からテンポを割ると、96分音符のフレーム数が求まる
  • カウンタの小数点以下部分を整数化するため、全体を256倍する
  • ∴250×256=64000からテンポを割ると、96分音符のフレーム数×256が求まる
 被除数もうまい具合に2バイト範囲に収まりそうだ。

2015/07/28〜08/04:処理概要 その3
 さて、また月日が流れて、早くも7月。さて続きをするか。

 とはいえ、前に作った処理も忘れかけているので、再度処理を考え直す。
  1. F, B, Pitchの 目標値−現在値 を、差分値に入れる
    Ampとその他オプションは、現在値をそのまま差分値に入れる
  2. 2倍フラグOFF
  3. F1〜F5について、差分値を差分範囲×2(E0H-1FH)に収まるよう加工 F1〜F5の差分値が差分範囲外(10H-EFH)なら2倍フラグを立てる
  4. B1〜B5について、差分値を差分範囲×2(F8H-07H)に収まるよう加工 B1〜B5の差分値が差分範囲外(04H-FBH)なら2倍フラグを立てる
  5. Pitchについて、差分値を差分範囲×2(F8H-07H)に収まるよう加工
  6. 現在値+差分値を次回値に入れる
    ただし2倍フラグONのときは、事前にF1〜F5とB1〜B5を2の倍数になるよう値を切り捨てる
  7. 差分値を元に、7バイトデータを生成する。
    ただし2倍フラグONのときは、事前にF1〜F5とB1〜B5の差分値を1/2にしておく
 これらの部分と、テンポによる基準長計算を作成するが、どうもマシン語に落とすときにいろいろ間違えているようで、まともに動かない...。

2016/02/11〜23:音声出力
 さてさて、またもや月日が流れて、早くも年をまたいで2月...。さて、続きをするか。

 再開のきっかけになったのは、作りたいネタができたからだ。ネタのイメージはズバリ鼻歌だ。鼻歌だと「フン」という一音を加工する事でデータを使いまわせるだろうし、多少テンポや音程が狂ってもさほど問題にならない気がする。まあ、音が鳴らない事には何も始まらないが。

 まずは、昨年の8月に作った差分値や次回値の算出処理がちゃんと動くか、1フレーム分のデータを作成して検証する。

 想定する結果と算出結果を見比べつつ、プログラムを修正していく。検証用データでは、うまく動くようになったような気がする。

 さて次!7バイトデータの組み立て処理に移る。事前に考えていた通りにデータ出力処理を作成し、軽く確認する。ちゃんとこの7バイトデータで鳴るんだろうか?

 次!ここまで目標値となる音声データのアドレスは固定だったが、これをアドレス可変になるよう修正し、1フレーム分の音声出力後に目標値のアドレスを移動させる処理を追加する。また、7バイトデータを音声合成ROMに送り込むルーチンを、過去に作成したプログラムから移してくる。

 次!フラグ管理。とりあえず、以下の2種類のフラグを用意する。
フラグ内容
出力初回フラグ
(bit0)
初期値はON
ONのときは、E2H, E3Hポートへの出力を行う
E2H, E3Hポートへの出力完了後にOFFに変更
初回追いつきフラグ
(bit1)
初期値はON
ONの間は、音量0で出力
1音目のはじめに出すF値とB値に、内部管理の
F値とB値が追いついたらOFFに変更

 これで、音声が鳴るのに最低限必要な処理はできたかな。という事で、元となる音声データを作成し、試してみる。

 心配したほどの問題もなく、1音が鳴った。これでようやく第一歩を踏み出せた気がする。


Making「鼻歌」 その2