読者です 読者をやめる 読者になる 読者になる

DCSGのノイズについて


DCSGを使い倒すには
何か,超クールなアイコンなんですが...それは置いといて.
AY-3-8910と同じだろうと思って,すっかりノーマークだったDCSG.実はかなり違うっぽい.
DCSGってのは,TIのPSG音源チップSN76489系の総称...だったと思う.あんまり自信が無い.とりあえず,SegaMasterSystem/メガドラ/GameGear辺りの音源と言っておけば,当たり障りは無いと思う.
で,AY-3-8910ってのは,一般的にPSG音源と言われている奴で,PC6001/FM7/MSXとかに乗ってたTI製音源チップ.自分は小学生の頃,FM7のPLAY文/SOUND文でDTMに入った人間なので,非常に馴染み深い.

AY-3-8910とSN76489の違い

wikipediaのPSGの項によると,ノイズチャンネルの独立とだけ書いてあるので,それを鵜呑みにしてた.けど,件のエントリを読んでちょっと気になったので,いつものようにMAMEソース(sn76496.c)とデータシートを眺めてたら,そんな一言で片付くものじゃないっぽい.

  • ノイズチャンネルの独立
  • ノイズ周波数を音階で指定可能(後述)
  • PeriodicNoiseという名のパルス波が出せる
  • エンベロープ無し
  • 音量がノンリニア(訂正AY-3-8910もノンリニア)

総じて,AYよりシンプルで表現力が格段に高くなっている.実質的にAY-3-8910のノイズは音楽で使えなかったので,ノイズチャンネル独立による表現力の向上は非常に大きい.また,PSG特有の奇妙なHWエンベロープが無くなって,ソフトウェア側でエンベロープを制御する割り切った設計になっている.併せて,音量がdB表現なので矩形派の癖に減衰音の表現が自然なのも聞いてて面白い.

DCSGのノイズチャンネルについて

で,件のエントリでも取り上げているSN76489のノイズチャンネルだけど,すげー面白い仕様になってる.残念ながら資料が少なく,テストもエミュのみで実機を触った事は無いので,合ってるか判らないけど.
まず,DCSGのノイズチャンネルではホワイトノイズの他に"PeriodicNoise"という名前のノイズ...というか資料を読む限りDuty比6.25%(or6.67%)の矩形波を出すことができる.コントロールレジスタの4MSB=0xEの場合,bit2でPeriodic/Whiteを選択して,bit1,0で周波数モードを設定する.また,4MSB=0xFの場合,bit3,2,1,0で音量を設定する.
また,ホワイトノイズ用擬似乱数は,各チップで微妙に違うみたいで,MAMEソースによると,

  • SN76489は,15bitLFSRで14bitと15bitのXORをフィードバック(多分ファミコンと同じ).
  • SN76489A/94/96は,16bitLFSRで14bitと16bitのXORをフィードバック.
  • MasterSystemIII/メガドラ/GameGearは,16bitLFSRで13bitと16bitのXORをフィードバック.

という感じらしい.

DCSGのノイズチャンネルの周波数について

問題のノイズ周波数の設定なんだけど,データシートを読む限り,周波数モード=3の場合(レジスタ値=0xE3(PeriodicNoise)/0xE7(WhiteNoise)),矩形波チャンネルの3番の周波数でLFSRをシフトできるらしい.これって他の音源チップでは見たことの無いすごくアグレッシブな仕様だと思う.今一英語理解に自信が無いので,MAMEソースも確認してみたけど,やっぱりそういう仕様らしい.
つまり,DCSGでは,少しトリッキーな事をすれば,50%矩形波とホワイトノイズ以外に,6.25%矩形波を音色として使えるという事になる.件のエントリで挙がってたSMS Power! - Music - Sega 8-bit Music Competition 2005の1位の作品はこれを使ってると思われる.

vgzデータのバイナリハック

そこで,この作品データの最初の512バイトだけバイナリ解析してみた.作品データはvgzフォーマット.vgzファイルはvgmデータをzip圧縮したものなので,普通のアーカイバで展開するとvgmデータになる.vgmデータは"register write"と"wait"他いくつかだけの非常にシンプルなフォーマットなので,実際はバイナリハックって程の事でも無くて,比較的簡単に直読する事ができる.以下,最初の512+64Bytes.

56 67 6D 20 7A 75 01 00 01 01 00 00 99 9E 36 00  00 00 00 00 42 74 01 00 ED 34 83 00 00 00 00 00 
00 00 00 00 3C 00 00 00 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
4F FF 50 87 50 35 50 AA 50 01 50 CA 50 06 50 E4  50 90 50 B9 50 DF 50 F4 50 E3 62 50 89 50 38 50 
97 50 BA 50 F1 62 50 BB 50 F2 62 50 8F 50 3B 50  BC 50 F3 62 50 BD 50 F4 62 50 88 50 3F 50 BE 50 
F5 62 50 BF 50 F6 62 50 87 50 35 50 C5 50 03 50  E4 50 BB 50 F5 62 50 E3 50 98 50 BC 50 F1 62 50 
99 50 BD 50 F2 62 50 9A 50 BE 50 F3 62 50 9B 50  BF 50 F4 62 50 9C 50 F5 62 50 9D 50 F6 62 50 CA 
50 06 50 E4 50 90 50 B9 50 F4 62 50 89 50 38 50  E3 50 97 50 BD 50 F1 62 50 BF 50 F2 62 50 8F 50 
3B 50 F3 62 50 F4 62 50 88 50 3F 50 F5 62 50 F6  62 50 87 50 35 50 E4 50 B9 50 F5 62 50 E3 50 98 
50 BA 50 F1 62 50 99 50 BB 50 F2 62 50 9A 50 BC  50 F3 62 50 9B 50 BD 50 F4 62 50 9C 50 BE 50 F5 
62 50 9D 50 BF 50 F6 62 50 8A 50 21 50 C7 50 07  50 E5 50 90 50 B9 50 F1 62 50 8C 50 25 50 E4 50 
97 50 BD 62 50 BF 50 F2 62 50 86 50 2A 50 F3 62  50 F4 62 50 8F 50 2C 50 F5 62 50 F6 62 50 87 50 
35 50 E4 50 90 50 B9 50 F5 62 50 89 50 38 50 E3  50 97 50 BA 50 F1 62 50 BB 50 F2 62 50 8F 50 3B 
50 BC 50 F3 62 50 BD 50 F4 62 50 88 50 3F 50 BE  50 F5 62 50 BF 50 F6 62 50 87 50 35 50 CA 50 06 
50 E4 50 B9 50 F4 62 50 E3 50 98 50 BD 50 F1 62  50 99 50 BF 50 F2 62 50 9A 50 F3 62 50 9B 50 F4 
62 50 9C 50 F5 62 50 9D 50 F6 62 50 E4 50 90 50  B9 50 F5 62 50 89 50 38 50 E3 50 97 50 BA 50 F1 
62 50 F2 62 50 8F 50 3B 50 F3 62 50 F4 62 50 BB  50 F5 62 50 98 50 F6 62 50 E4 50 F4 62 50 E3 50 
F1 62 50 BC 50 F2 62 50 99 50 F3 62 50 F4 62 50  F5 62 50 BD 50 F6 62 50 8A 50 23 50 E6 50 90 50 
B9 50 F1 62 50 80 50 28 50 E5 50 97 50 BA 62 50  BB 50 F2 62 50 8F 50 2C 50 BC 50 F3 62 50 BD 50 

この内最初の64Bytesはヘッダなので削除,64Byte目の"4F FF"はGameGear対応の決まり文句なので無視.詳細は省略するけど,このデータは通常よりさらにシンプルで,「レジスタ書き込み命令」"50 dd"と「PALで1フレーム進める命令」"62"しか使ってない.66Byte目から読んで行くと"50 87 50 35 50 AA 50 01 ..."なのでこれは,レジスタに87 35 AA 01を次々に書き込んでるって事になる.90Byte目に"62"があるのでこのタイミングでシーケンスが1フレーム進む.以下繰り返し.
後はレジスタ値の意味さえ頭に入っていれば,読み進めることができる.オタクちゃんならこれだけで十分なんだけど,ボクハイッパンジンナノデヨクワカリマセン.ので,もうちょっと読みやすくアセンブラ風に書き直す.以下,エディタの正規表現文字列置換だけで作った即席逆アセンブラで変換したもの.命令は"(t[1-3]|nz)f"で,矩形波1-3ch及びノイズchの周波数設定,"(t[1-3]|nz)a"で各チャンネルの音量設定,"wait"で1フレーム進める.

t1f 0x7 35 
t2f 0xA 01 
t3f 0xA 06 
nzf 0x4 
t1a 0x0 
t2a 0x9 
t3a 0xF 
nza 0x4 
nzf 0x3 
wait
t1f 0x9 38 
t1a 0x7 
t2a 0xA 
nza 0x1 
wait
t2a 0xB 
nza 0x2 
wait
t1f 0xF 3B 
t2a 0xC 
nza 0x3 
wait
t2a 0xD 
nza 0x4 
...

この中から,3番チャンネルとノイズチャンネルだけ抽出.

t3f 0xA 06 
nzf 0x4 
t3a 0xF 
nza 0x4 
nzf 0x3 
wait
nza 0x1 
wait
nza 0x2 
wait
nza 0x3 
wait
nza 0x4 
wait
nza 0x5 
wait
nza 0x6 
wait
t3f 0x5 03 
nzf 0x4 
nza 0x5 
wait
...

5行目"wait"から16行目"nza 0x6"までを見ると,1フレごとに音量を1下げてる(nza値↑で音量↓)ので,単なる減衰エンベロープだとわかる.ので,減衰エンベロープは省略して,これらは"release 6"とまとめる事にする.これで"release"が登場するまでが1音って事になるのでそこで区切って記述する.ついでに1音内のレジスタ値の意味についてコメントも付けておく.

t3f 0xA 06  // set 3ch freqency (base pitch = 0xA06)
nzf 0x4     // ???
t3a 0xF     // set 3ch volume minimum
nza 0x4     // set volume 4
nzf 0x3     // set periodic noise andfreq.sync with 3ch (base tone)
release 7

t3f 0x5 03  // set 3ch freqency (base pitch = 0x503)
nzf 0x4     // set white noise with fixed highest freq.
nza 0x5     // set volume 5
wait        // wait 20 msec
nzf 0x3     // set periodic noise and freq.sync with 3ch (base tone)
nza 0x1     // set volume 1
release 6

t3f 0xA 06  // set 3ch freqency (base pitch = 0xA06)
nzf 0x4     // set white noise with fixed highest freq.
nza 0x4     // set volume 4
wait        // wait 20 msec
nzf 0x3     // set periodic noise and freq.sync with 3ch (base tone)
nza 0x1     // set volume 1
release 6

nzf 0x4     // set white noise with fixed highest freq.
nza 0x5     // set volume 5
wait        // wait 20 msec
nzf 0x3     // set periodic noise and freq.sync with 3ch (base tone)
nza 0x1     // set volume 1
release 6

t3f 0x7 07  // set 3ch freqency (base pitch = 0x707)
nzf 0x5     // set white noise with fixed middle freq.
nza 0x1     // set volume 1
wait        // wait 20 msec
nzf 0x4     // set white noise with fixed highest freq.
release 6   // (This note has no pitch.->Snare note)

nzf 0x4     // set white noise with fixed highest freq.
nza 0x5     // set volume 5
wait        // wait 20 msec
nzf 0x3     // set periodic noise and freq.sync with 3ch (base tone)
nza 0x1     // set volume 1
release 6

t3f 0xA 06  // set 3ch freqency (base pitch = 0xA06)
nzf 0x4     // set white noise with fixed highest freq.
nza 0x4     // set volume 4
wait        // wait 20 msec
nzf 0x3     // set periodic noise and freq.sync with 3ch (base tone)
nza 0x1     // set volume 1
release 6
...

以上が,ノイズチャンネルと3番チャンネルのシーケンス.
簡単に説明すると,3行目で3chの音量をオフにして,以降3chの周波数(t3f ddd)を使ってperiodic noiseの周波数をコントロールしている(nzf 0x3).また各音の最初20msec(1フレーム)だけホワイトノイズを鳴らす事でアタックを表現している(nzf 0x4).31行目から36行目の音はホワイトノイズのみで3chの周波数を参照していないので,スネア音を表現しているって事になる.


すっげー...他の曲と比べてもレベルが違いすぎる...

OPMエミュでDCSGのコンセプトを表現するために必要な拡張

  • PeriodicNoiseの再現
  • ノイズチャンネルの周波数指定
  • ノンリニアな音量

大きくみるとこの3つを実装する必要がある.PeriodicNoiseはVRC6エミュで代用が可能.ノイズチャンネルの周波数指定もVRC6エミュであればそのまま使える.また,そもそもOPMエミュの音量がノンリニアなのでここも問題ない.
結局,DCSGはPSG音源の表現として相当なポテンシャルを持っているものの,それに対応するための拡張は特に必要ないっぽい.