nicotakuya.hatenablog.com
前回の続きです。HuCを使ったPCエンジン のプログラミングについて紹介します。
ここまで順調に進んできましたが、今回の課題は超苦手分野、、、サウンド 出力 です。
困ったことにHuCにはサウンド 出力の機能が入ってません。
ドキュメントを見ても
「PSG player is started, and will be available soon(近いうちに公開) 」
とあります。これが書かれたのが2005年ですが、更新は2020年現在まで止まっています。
PSG関連の関数が存在する? ライブラリが対応していないと、どうしようもありません。ただ、不思議なことにライブラリのソースを見ると、それらしい関数の名前(PSG_~)は存在しています。もしかして、ソースを少し修正するだけで使えるようになるかもしれません。
GitHub にサウンド ライブラリを発見あと、いろいろ検索すると、HuC関連のサンプルプログラムがヒットしました。
https://github.com/uli/huc
これを見ると「st.c」「st.h」というサウンド のライブラリが存在しています。ローカルのhucフォルダ内にはこのファイルはありません。知らないところでアップデートしてるのでしょうか。
しかし、このライブラリは自前のhucではコンパイル できませんでした。なぜできないのか、原因がわかりません。
ここで、完全に手詰まりになってしまったのですが、hucのdocフォルダを漁ってみると、「PSG.txt」 というテキストファイルを発見しました。
サウンド 系レジスタ の解説今まで「資料がない」と愚痴ってましたが、、、反省です。
この「PSG.txt」を読むと、PCエンジン のサウンド 系のレジスタ について書かれています。
番地やビットの配置がわかりましたので、次のプログラムを作ってみました。
#include "huc.h"
const char waveform[]={
18 ,22 ,24 ,26 ,28 ,28 ,30 ,30 ,30 ,30 ,28 ,28 ,26 ,24 ,22 ,18 ,
12 , 8 , 6 , 4 , 2 , 2 , 0 , 0 , 0 , 0 , 2 , 2 , 4 , 6 , 8 ,12
};
#define FREQ12BIT 254
char *psgch;
char *psgmvol;
char *psgfreql;
char *psgfreqh;
char *psgctrl;
char *psglrvol;
char *psgdata;
main()
{
int i;
psgch = 0x800 ;
psgmvol = 0x801 ;
psgfreql= 0x802 ;
psgfreqh= 0x803 ;
psgctrl = 0x804 ;
psglrvol= 0x805 ;
psgdata = 0x806 ;
disp_on();
cls();
vsync();
*psgch = 0 ;
*psgctrl = (1 <<6 );
*psgctrl = (0 <<6 );
for (i=0 ;i<32 ;i++){
*psgdata = waveform[i];
}
*psgfreql = FREQ12BIT & 0xff ;
*psgfreqh = FREQ12BIT >> 8 ;
*psglrvol = 0xff ;
*psgmvol = 0xee ;
*psgctrl = (1 <<7 )+ 0x1f ;
put_string("PSG TEST 440Hz" ,3 ,3 );
while (1 ){
vsync();
}
}
こんな感じです。世界一シンプルなサウンド ドライバです。
使用しているチャンネルは0番。周波数は440Hzです。
波形データ 波形のデータはPSG.txtからコピペしました。波形は32バイトで表現します。振幅の範囲はPSG.txtに0~32と書かれていますが、正しくは0~31でしょうか。
実行結果。440Hzの音を鳴らし続けます ビルドして、ROMイメージをPCエンジン エミュレータ で実行すると、「ポー」という感じの音が鳴り続けます。
これはサウンド 系のレジスタ を直接叩くという、過激なプログラムです。まだ安全性が確認できていません。
もっと良い方法があるかもしれないので、引き続き検討してみたいです。
(追記2020/12/05)
ノート番号を変えたり、左右の音量を変えられるようにしてみました。
表計算 ソフトでレジスタ 値を計算表計算 ソフトのpower関数を使ってノート番号から周波数を計算して、
そこからさらにレジスタ の値を計算してみました。
周波数設定のレジスタ の値は(3580000/32)/周波数で計算できます。
#include "huc.h"
const char waveform[]={
18 ,22 ,24 ,26 ,28 ,28 ,30 ,30 ,30 ,30 ,28 ,28 ,26 ,24 ,22 ,18 ,
12 , 8 , 6 , 4 , 2 , 2 , 0 , 0 , 0 , 0 , 2 , 2 , 4 , 6 , 8 ,12
};
const int freqtbl[]={
428 ,
404 ,
381 ,
360 ,
339 ,
320 ,
302 ,
285 ,
269 ,
254 ,
240 ,
227
};
char *psgch;
char *psgmvol;
char *psgfreql;
char *psgfreqh;
char *psgctrl;
char *psglrvol;
char *psgdata;
char wave_cnt;
wave_init()
{
int i;
psgch = 0x800 ;
psgmvol = 0x801 ;
psgfreql= 0x802 ;
psgfreqh= 0x803 ;
psgctrl = 0x804 ;
psglrvol= 0x805 ;
psgdata = 0x806 ;
vsync();
*psgch = 0 ;
*psgctrl = (1 <<6 );
*psgctrl = (0 <<6 );
for (i=0 ;i<32 ;i++){
*psgdata = waveform[i];
}
*psgmvol = 0xee ;
wave_cnt = 0 ;
}
wave_play(note, volume, length)
char note, volume, length;
{
int reg;
reg = freqtbl[note % 12 ];
note -= 60 ;
while (note < 0 ){
note += 12 ;
reg <<= 1 ;
}
while (note >= 12 ){
note -= 12 ;
reg >>= 1 ;
}
*psgfreql = reg & 0xff ;
*psgfreqh = reg >> 8 ;
*psglrvol = volume;
*psgctrl = (1 <<7 ) + 0x1f ;
wave_cnt = length;
}
main()
{
char test,note,volume;
wave_init();
disp_on();
cls();
put_string("PSG TEST" ,2 ,2 );
put_string("NOTE NUMBER: 0x" ,4 ,4 );
put_string(" L/R VOLUME: 0x" ,4 ,6 );
test = 0 ;
while (1 ){
vsync();
if (wave_cnt > 0 ){
wave_cnt--;
if (wave_cnt==0 ){
*psgctrl = (0 <<7 ) + 0x0 ;
}
}
if (wave_cnt==0 ){
note = 60 +(test % 12 );
if (test < 12 ){
volume = 0xff ;
}else if (test < 24 ){
volume = 0x0f ;
}else {
volume = 0xf0 ;
}
put_hex(note , 2 , 20 ,4 );
put_hex(volume, 2 , 20 ,6 );
wave_play(note, volume, 100 );
test++;
if (test >= 12 *3 ) test=0 ;
}
}
}
ノート番号と左右の音量を変更 実行するとノート番号60~71番までを順番に鳴らします。音の出力は左右、右のみ、左のみの順番で切り替わります。
音が止まる時にプツンという短い音が出てしまいます。どこかプログラムが間違っていると思います。