簡易的なMMLを実装しています。
snd_drv.h (レジスタとトラック構造体の定義)
#include <gb.h>
// 波形メモリ音源のレジスタ定義
#define VW30_REG (*(UBYTE *)0xFF30) /* Sound register */
#define VW31_REG (*(UBYTE *)0xFF31) /* Sound register */
#define VW32_REG (*(UBYTE *)0xFF32) /* Sound register */
#define VW33_REG (*(UBYTE *)0xFF33) /* Sound register */
#define VW34_REG (*(UBYTE *)0xFF34) /* Sound register */
#define VW35_REG (*(UBYTE *)0xFF35) /* Sound register */
#define VW36_REG (*(UBYTE *)0xFF36) /* Sound register */
#define VW37_REG (*(UBYTE *)0xFF37) /* Sound register */
#define VW38_REG (*(UBYTE *)0xFF38) /* Sound register */
#define VW39_REG (*(UBYTE *)0xFF39) /* Sound register */
#define VW3A_REG (*(UBYTE *)0xFF3A) /* Sound register */
#define VW3B_REG (*(UBYTE *)0xFF3B) /* Sound register */
#define VW3C_REG (*(UBYTE *)0xFF3C) /* Sound register */
#define VW3D_REG (*(UBYTE *)0xFF3D) /* Sound register */
#define VW3E_REG (*(UBYTE *)0xFF3E) /* Sound register */
#define VW3F_REG (*(UBYTE *)0xFF3F) /* Sound register */
#define SND_TRACK_MAX 4
#define SND_FLAG_VW 0x01
/*
// チャンネル制御
NR52_REG = 0x8F;// b7:全チャンネルON=1 / b3-0:チャンネル4~1再生フラグON=F(1111)
NR50_REG = 0x77;// b7:左VinOF=0F / b6-4:左音量=7 / b3:右VinOFF=0 / b2-0:右音量=7
NR51_REG = 0xFF;// b7-4:チャンネル4~1左出力ON=F(1111) / b3-0:チャンネル4~1右出力ON=F(1111)
// チャンネル1(矩形波スイープあり)
NR10_REG = 0x00;// b6-4:スイープ時間=0 / b3:スイープ方向=0(上) / b2-0:スイープ変化量=0
NR11_REG = 0xC0;// b7-6:デューティ比=3 / b5-0:音長カウンタ=0
NR12_REG = 0xF0;// b7-4:初期音量=15 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間=0
NR13_REG = 0x0A;// b7-0:周波数(11ビットの下位8ビット)
NR14_REG = 0x86;// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
// チャンネル2(矩形波)
NR21_REG = 0xC0;// b7-6:デューティ比=3 / b5-0:音長カウンタ=0
NR22_REG = 0xF0;// b7-4:初期音量=15 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間=0
NR23_REG = 0xB2;// b7-0:周波数(11ビットの下位8ビット)
NR24_REG = 0x86;// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
// チャンネル3(波形メモリ音源)
NR30_REG = 0x80;// b7:出力ON=1
NR31_REG = 0x00;// b7-0:音長=0
NR32_REG = 0x20;// b6-5:音量 100%出力=1 ※ミュート(0%出力)=0、50%出力=2、25%出力=3
NR33_REG = 0x72;// b7-0:周波数(11ビットの下位8ビット)
NR34_REG = 0x86;// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
// チャンネル4(ノイズ)
NR41_REG = 0x00;// b5-0:音長=0
NR42_REG = 0xF3;// b7-4:初期音量=15 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間=3
NR43_REG = 0x33;// b7-4:周波数シフト量=3 / b3:カウンタ単位(0:15/1:7)=0 / b2-0:周波数=3
NR44_REG = 0x80;// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0
*/
struct st_track {
UBYTE ch; // チャンネル(1~4)
UBYTE play; // 再生フラグ
UBYTE on; // ON/OFFフラグ(音符=1、休符=0)
UBYTE vol; // 音量(0~15)※チャンネル3の波形メモリ音源は0~3(ミュート(0%出力=0、100%出力=1、50%出力=2、25%出力=3)
UBYTE duty; // デューティ比(0~3)
UBYTE pan; // パンポット(0~3)消音=0、左=1、右=2、両方=3
UBYTE tmp; // テンポ(1~8)
UBYTE len; // デフォルト音長
UBYTE cnt; // カウンタ
UBYTE oct; // オクターブ(0~5)
UBYTE scl; // 音階(0~11)C,C+,D,D+,E,F,F+,G,G+,A,A+,B
UBYTE note; // オクターブ×音階(0~71)
UBYTE flag; // フラグ b0:波形メモリ転送フラグ
UBYTE *pp; // 再生ポインタ
UBYTE *lp; // ループポインタ
UWORD freq; // 周波数
};
snd_drv.c (ドライバ本体)
Sound.c
以下がMMLのマニュアルです。
音程コマンド
cdefgabr(+/-)数値
数値には音長を指定します。
音長は32、16、16.、8、8.、4、4.、2、2.、1の10通りです。
音長を省略した場合は基準音長(lコマンド)になります。
.(付点)のみを指定した場合は基準音長に付点を加えたものになりますが
基準音長が32と1の時は無効になります。
また、基準音長が付点音長の場合の動作は未確認です。
rは休符です。
チャンネル4(ノイズ)では音程ごとにscale_noiseの値を再生します。
頻度の高いものを割り当て直すと便利です。
基準音長コマンド
l数値
数値には音長を指定します。
タイコマンド
^数値
数値には音長を指定します。
音を延ばしたい時に指定します。音長の省略は不可です。
オクターブコマンド
o数値
オクターブの指定は0~5です。
>で現在のオクターブを1つ上げます。
<で現在のオクターブを1つ下げます。
テンポコマンド
t数値
テンポの指定は1~8です。
テンポ1の32分音符が1フレームになります。
テンポ1の4分音符が8フレームになります。
テンポ2の8分音符も8フレームになります。
テンポ4の16分音符も8フレームになります。
テンポ8の32分音符も8フレームになります。
テンポを音長をうまく指定すれば連符も表現できます。
音量コマンド
v数値
音量の指定は0~15です。
※マスター音量の指定コマンドは実装していません。
パンポットコマンド
p数値
パンポットの指定は0~3です。
0:消音
1:左
2:右
3:両方
音色コマンド
y数値
音色の指定は0~3です。
チャンネル1・2(矩形波)ではデューティ比の指定になります。
チャンネル3(波形メモリ)ではvoluntary_waveのプリセットになります。
チャンネル4(ノイズ)では無効になります。
ループポイント設定コマンド
L
MMLの最後まで再生してから指定のループポイントに戻ります。
Lを設定していなければループせずに再生を終了します。
#include "snd_drv.h"
// トラック
struct st_track snd_track[SND_TRACK_MAX];
// 音程ごとの周波数
UWORD scale_frequency[] = {
// C C+ D D+ E F F+ G G+ A A+ B // Oct
44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986,// 0
1046,1102,1155,1205,1253,1297,1339,1379,1417,1452,1486,1517,// 1
1546,1575,1602,1627,1650,1673,1694,1714,1732,1750,1767,1783,// 2
1798,1812,1825,1837,1849,1860,1871,1881,1890,1899,1907,1915,// 3
1923,1930,1936,1943,1949,1954,1959,1964,1969,1974,1978,1982,// 4
1985,1988,1992,1995,1998,2001,2004,2006,2009,2011,2013,2015,// 5
2017
};
// テンポごとの音長
UBYTE tempo_length[8][10] = {
// 32 16 16. 8 8. 4 4. 2 2. 1 // Tempo
{ 1-1, 2-1, 3-1, 4-1, 6-1, 8-1, 12-1, 16-1, 24-1, 32-1},// 1
{ 2-1, 4-1, 6-1, 8-1, 12-1, 16-1, 24-1, 32-1, 48-1, 64-1},// 2
{ 3-1, 6-1, 9-1, 12-1, 18-1, 24-1, 36-1, 48-1, 72-1, 96-1},// 3
{ 4-1, 8-1, 12-1, 16-1, 24-1, 32-1, 48-1, 64-1, 96-1,128-1},// 4
{ 5-1, 10-1, 15-1, 20-1, 30-1, 40-1, 60-1, 80-1,120-1,160-1},// 5
{ 6-1, 12-1, 18-1, 24-1, 36-1, 48-1, 72-1, 96-1,144-1,192-1},// 6
{ 7-1, 14-1, 21-1, 28-1, 42-1, 56-1, 84-1,112-1,168-1,224-1},// 7
{ 8-1, 16-1, 24-1, 32-1, 48-1, 64-1, 96-1,128-1,182-1,256-1},// 8
};
// チャンネルごとのパンポット設定
UBYTE ch_panpot[4][4] = {
// 消音 左 右 両方
{ 0x00, 0x10, 0x01, 0x11},// ch1
{ 0x00, 0x20, 0x02, 0x22},// ch2
{ 0x00, 0x40, 0x04, 0x44},// ch3
{ 0x00, 0x80, 0x08, 0x88},// ch4
};
// 音程ごとのノイズ設定(NR42(下位4ビット),NR43)
UWORD scale_noise[] = {
// C C+ D D+ E F F+ G G+ A A+ B // Oct
0x0016, 0x0026, 0x0036, 0x0046, 0x0043, 0x0053, 0x0063, 0x0073, 0x0070, 0x0080, 0x0090, 0x00A0,// 1
0x0116, 0x0126, 0x0136, 0x0146, 0x0143, 0x0153, 0x0163, 0x0173, 0x0170, 0x0180, 0x0190, 0x01A0,// 2
0x0216, 0x0226, 0x0236, 0x0246, 0x0243, 0x0253, 0x0263, 0x0273, 0x0270, 0x0280, 0x0290, 0x02A0,// 3
0x0316, 0x0326, 0x0336, 0x0346, 0x0343, 0x0353, 0x0363, 0x0373, 0x0370, 0x0380, 0x0390, 0x03A0,// 4
0x0416, 0x0426, 0x0436, 0x0446, 0x0443, 0x0453, 0x0463, 0x0473, 0x0470, 0x0480, 0x0490, 0x04A0,// 5
0x0516, 0x0526, 0x0536, 0x0546, 0x0543, 0x0553, 0x0563, 0x0573, 0x0570, 0x0580, 0x0590, 0x05A0,// 6
0x0616, 0x0626, 0x0636, 0x0646, 0x0643, 0x0653, 0x0663, 0x0673, 0x0670, 0x0680, 0x0690, 0x06A0,// 7
0x0716, 0x0726, 0x0736, 0x0746, 0x0743, 0x0753, 0x0763, 0x0773, 0x0770, 0x0780, 0x0790, 0x07A0,// 8
};
// 波形メモリ音源のプリセット
UBYTE voluntary_wave[4][16] = {
//
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},// 矩形波
{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},// ノコギリ波
{ 0x0b, 0xde, 0xef, 0xff, 0xff, 0xfe, 0xed, 0xb0, 0x04, 0x21, 0x10, 0x00, 0x00, 0x01, 0x12, 0x40},// 正弦波
{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},// 三角波
// { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//
};
// サウンド関連の全レジスタを初期化
void snd_reg_init()
{
NR10_REG = 0;
NR11_REG = 0;
NR12_REG = 0;
NR13_REG = 0;
NR14_REG = 0;
NR21_REG = 0;
NR22_REG = 0;
NR23_REG = 0;
NR24_REG = 0;
NR30_REG = 0;
NR31_REG = 0;
NR32_REG = 0;
NR33_REG = 0;
NR34_REG = 0;
NR41_REG = 0;
NR42_REG = 0;
NR43_REG = 0;
NR44_REG = 0;
NR50_REG = 0;
NR51_REG = 0;
NR52_REG = 0;
}
// トラックデータの初期化
void snd_track_init()
{
UBYTE i;
for (i = 0; i < SND_TRACK_MAX; i++) {
snd_track[i].ch = i+1; // チャンネル(1~4)
snd_track[i].play = 0; // 再生フラグ
snd_track[i].on = 0; // ON/OFFフラグ
snd_track[i].vol = 0; // 音量(0~15)※チャンネル3の波形メモリ音源は0~3(ミュート(0%出力=0、100%出力=1、50%出力=2、25%出力=3)
snd_track[i].duty = 0; // デューティ比(0~3)
snd_track[i].pan = 0; // パンポット(0~3)消音=0、左=1、右=2、両方=3
snd_track[i].tmp = 0; // テンポ(1~8)
snd_track[i].len = 0; // デフォルト音長
snd_track[i].cnt = 0; // カウンタ
snd_track[i].oct = 0; // オクターブ(0~5)
snd_track[i].scl = 0; // 音階(0~11)C,C+,D,D+,E,F,F+,G,G+,A,A+,B
snd_track[i].note = 0; // オクターブ×音階(0~71)
snd_track[i].flag = 0; // フラグ b0:波形メモリ転送フラグ
snd_track[i].pp = 0; // 再生ポインタ
snd_track[i].lp = 0; // ループポインタ
snd_track[i].freq = 0; // 周波数
}
}
// BGMリクエスト
void snd_req_bgm(UBYTE *ch1, UBYTE *ch2, UBYTE *ch3, UBYTE *ch4)
{
if (ch1 != 0) {
snd_track[0].ch = 1;
snd_track[0].play = 1;
snd_track[0].on = 0;
snd_track[0].vol = 15;
snd_track[0].duty = 0;
snd_track[0].pan = 3;
snd_track[0].tmp = 4;
snd_track[0].len = 5;
snd_track[0].cnt = 0;
snd_track[0].oct = 3;
snd_track[0].scl = 0;
snd_track[0].note = 0;
snd_track[0].flag = 0;
snd_track[0].pp = ch1;
snd_track[0].lp = 0;
snd_track[0].freq = 0;
}
if (ch2 != 0) {
snd_track[1].ch = 2;
snd_track[1].play = 1;
snd_track[1].on = 0;
snd_track[1].vol = 15;
snd_track[1].duty = 0;
snd_track[1].pan = 3;
snd_track[1].tmp = 4;
snd_track[1].len = 5;
snd_track[1].cnt = 0;
snd_track[1].oct = 3;
snd_track[1].scl = 0;
snd_track[1].note = 0;
snd_track[1].flag = 0;
snd_track[1].pp = ch2;
snd_track[1].lp = 0;
snd_track[1].freq = 0;
}
if (ch3 != 0) {
snd_track[2].ch = 3;
snd_track[2].play = 1;
snd_track[2].on = 0;
snd_track[2].vol = 15;
snd_track[2].duty = 0;
snd_track[2].pan = 3;
snd_track[2].tmp = 4;
snd_track[2].len = 5;
snd_track[2].cnt = 0;
snd_track[2].oct = 3;
snd_track[2].scl = 0;
snd_track[2].note = 0;
snd_track[2].flag = 0;
snd_track[2].pp = ch3;
snd_track[2].lp = 0;
snd_track[2].freq = 0;
}
if (ch4 != 0) {
snd_track[3].ch = 4;
snd_track[3].play = 1;
snd_track[3].on = 0;
snd_track[3].vol = 15;
snd_track[3].duty = 0;
snd_track[3].pan = 3;
snd_track[3].tmp = 4;
snd_track[3].len = 5;
snd_track[3].cnt = 0;
snd_track[3].oct = 3;
snd_track[3].scl = 0;
snd_track[3].note = 0;
snd_track[3].flag = 0;
snd_track[3].pp = ch4;
snd_track[3].lp = 0;
snd_track[3].freq = 0;
}
// チャンネル制御
// b7:全チャンネルON=1 / b3-0:チャンネル4~1再生中フラグは読込のみ
NR52_REG = 0x80;
// b7:左VinOF=0F / b6-4:左音量=7 / b3:右VinOFF=0 / b2-0:右音量=7
NR50_REG = 0x77;
// b7-4:チャンネル4~1左出力ON=F(1111) / b3-0:チャンネル4~1右出力ON=F(1111)
NR51_REG = 0x00;
}
void snd_voluntary_wave_set(UBYTE num) {
VW30_REG = voluntary_wave[num][0];
VW31_REG = voluntary_wave[num][1];
VW32_REG = voluntary_wave[num][2];
VW33_REG = voluntary_wave[num][3];
VW34_REG = voluntary_wave[num][4];
VW35_REG = voluntary_wave[num][5];
VW36_REG = voluntary_wave[num][6];
VW37_REG = voluntary_wave[num][7];
VW38_REG = voluntary_wave[num][8];
VW39_REG = voluntary_wave[num][9];
VW3A_REG = voluntary_wave[num][10];
VW3B_REG = voluntary_wave[num][11];
VW3C_REG = voluntary_wave[num][12];
VW3D_REG = voluntary_wave[num][13];
VW3E_REG = voluntary_wave[num][14];
VW3F_REG = voluntary_wave[num][15];
}
UBYTE snd_conv_len(char *lens)
{
UBYTE ret = 0;
UBYTE num = 0;
UBYTE dot = 0;
while (1) {
num = *lens++;
if (num == '\0') {
break;
}
if (num >= '0' && num <= '9') {
ret *= 10;
ret += (num - '0');
}
if (num == '.') {
dot = 1;
}
}
num = ret;
switch (num) {
case 32:
ret = 0;
break;
case 16:
ret = 1;
break;
case 8:
ret = 3;
break;
case 4:
ret = 5;
break;
case 2:
ret = 7;
break;
case 1:
ret = 9;
break;
default:
ret = 0;
}
if (dot == 1) {
ret++;
}
if (ret >= 10) {
ret = 9;
}
if (num == 0 && dot == 1) {
ret = 255; //付点のみ
}
return ret;
}
void snd_driver()
{
struct st_track *snd_trk;
UBYTE i;
UBYTE mml;
UBYTE note;
UBYTE len;
UBYTE tmp;
UBYTE tie;
UBYTE nr51;
char atoi_str[4];
for (i = 0; i < SND_TRACK_MAX; i++)
{
snd_trk = &snd_track[i];
// トラック再生中?
if (snd_trk->play == 0) {
continue;
}
// 音長あり?
snd_trk->cnt--;
if (snd_trk->cnt != 255) {
continue;
}
note = 0;
tie = 0;
len = snd_trk->len;
while (note == 0) {
mml = *snd_trk->pp++;
if (mml == '\0') {
if (snd_trk->lp != 0) {
snd_trk->pp = snd_trk->lp;
continue;
} else {
break;
}
}
if (mml >= 'a' && mml <= 'g') {
if (mml == 'c') {
snd_trk->scl = 0;
}
if (mml == 'd') {
snd_trk->scl = 2;
}
if (mml == 'e') {
snd_trk->scl = 4;
}
if (mml == 'f') {
snd_trk->scl = 5;
}
if (mml == 'g') {
snd_trk->scl = 7;
}
if (mml == 'a') {
snd_trk->scl = 9;
}
if (mml == 'b') {
snd_trk->scl = 11;
}
snd_trk->on = 1;
note = 1;
}
if (mml == 'r') {
snd_trk->on = 0;
note = 1;
}
if (mml == '^') {
snd_trk->on = 1;
tie = 1;
note = 1;
}
if (note == 0) {
if (mml == 'o') {
mml = *snd_trk->pp++;
atoi_str[0] = mml;
atoi_str[1] = '\0';
snd_trk->oct = atoi(atoi_str);
if (snd_trk->oct >= 6) {
snd_trk->oct = 5;
}
continue;
}
if (mml == '>') {
snd_trk->oct++;
if (snd_trk->oct >= 6) {
snd_trk->oct = 5;
}
continue;
}
if (mml == '<') {
snd_trk->oct--;
if (snd_trk->oct == 255) {
snd_trk->oct = 0;
}
continue;
}
if (mml == 'l') {
tmp = 0;
while (1) {
mml = *snd_trk->pp++;
if ((mml >= '0' && mml <= '9') || mml == '.') {
atoi_str[tmp++] = mml;
} else {
snd_trk->pp--;
break;
}
}
if (tmp > 0) {
atoi_str[tmp] = '\0';
len = snd_trk->len = snd_conv_len(atoi_str);
}
continue;
}
if (mml == 't') {
mml = *snd_trk->pp++;
atoi_str[0] = mml;
atoi_str[1] = '\0';
snd_trk->tmp = atoi(atoi_str);
if (snd_trk->tmp > 8) {
snd_trk->tmp = 8;
}
if (snd_trk->tmp == 0) {
snd_trk->tmp = 1;
}
continue;
}
if (mml == 'v') {
mml = *snd_trk->pp++;
atoi_str[0] = mml;
atoi_str[1] = '\0';
mml = *snd_trk->pp;
// 2桁目が数字かどうか
if (mml >= '0' && mml <= '9') {
atoi_str[1] = mml;
atoi_str[2] = '\0';
snd_trk->pp++;
}
snd_trk->vol = atoi(atoi_str);
if (snd_trk->vol > 15) {
snd_trk->vol = 15;
}
continue;
}
if (mml == 'y') {
mml = *snd_trk->pp++;
atoi_str[0] = mml;
atoi_str[1] = '\0';
snd_trk->duty = atoi(atoi_str);
if (snd_trk->ch != 3) {
if (snd_trk->duty > 3) {
snd_trk->duty = 3;
}
} else {
if (snd_trk->duty > 3) {
snd_trk->duty = 3;
}
snd_trk->flag |= SND_FLAG_VW;
}
continue;
}
if (mml == 'p') {
mml = *snd_trk->pp++;
atoi_str[0] = mml;
atoi_str[1] = '\0';
snd_trk->pan = atoi(atoi_str);
if (snd_trk->pan > 3) {
snd_trk->pan = 3;
}
continue;
}
if (mml == 'L') {
snd_trk->lp = snd_trk->pp;
continue;
}
}
else {
while(1) {
mml = *snd_trk->pp;
if (mml == '-') {
snd_trk->scl--;
snd_trk->pp++;
continue;
}
if (mml == '+') {
snd_trk->scl++;
snd_trk->pp++;
continue;
}
tmp = 0;
while (1) {
mml = *snd_trk->pp++;
if ((mml >= '0' && mml <= '9') || mml == '.') {
atoi_str[tmp++] = mml;
} else {
snd_trk->pp--;
break;
}
}
if (tmp > 0) {
atoi_str[tmp] = '\0';
tmp = snd_conv_len(atoi_str);
if (tmp == 255) {
len = snd_trk->len + 1;
} else {
len = tmp;
}
}
break;
}
if (snd_trk->on == 1) {
if (tie == 0) {
if (snd_trk->ch != 4) {
snd_trk->freq = scale_frequency[snd_trk->oct*12+snd_trk->scl];
} else {
snd_trk->freq = scale_noise[snd_trk->oct*12+snd_trk->scl];
}
}
} else {
snd_trk->freq = 0;
}
// 音長
snd_trk->cnt = tempo_length[snd_trk->tmp][len];
}
}
if (note == 1) {
// チャンネル1(矩形波スイープあり)
if (snd_trk->ch == 1) {
if (snd_trk->on == 1) {
// b6-4:スイープ時間=0 / b3:スイープ方向=0(上) / b2-0:スイープ変化量=0
NR10_REG = 0x00;
// b7-6:デューティ比=3 / b5-0:音長カウンタ=0
NR11_REG = snd_trk->duty << 6;
// b7-4:初期音量=15 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間=0
NR12_REG = snd_trk->vol << 4;
// b7-0:周波数(11ビットの下位8ビット)
NR13_REG = (UBYTE)(snd_trk->freq & 0x00FF);
// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
NR14_REG = (UBYTE)((snd_trk->freq >> 8) & 0x00FF) + 0x80;
// チャンネル毎の左右オン
tmp = ch_panpot[snd_trk->ch-1][snd_trk->pan];
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xee) ^ tmp;
} else {
NR13_REG = 0;
NR14_REG = 0;
// チャンネル毎の左右オフ
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xee);
}
}
// チャンネル2(矩形波)
if (snd_trk->ch == 2) {
if (snd_trk->on == 1) {
// b7-6:デューティ比=3 / b5-0:音長カウンタ=0
NR21_REG = snd_trk->duty << 6;
// b7-4:初期音量=15 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間=0
NR22_REG = snd_trk->vol << 4;
// b7-0:周波数(11ビットの下位8ビット)
NR23_REG = (UBYTE)(snd_trk->freq & 0x00FF);
// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
NR24_REG = (UBYTE)((snd_trk->freq >> 8) & 0x00FF) + 0x80;
// チャンネル毎の左右オン
tmp = ch_panpot[snd_trk->ch-1][snd_trk->pan];
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xdd) ^ tmp;
} else {
NR23_REG = 0;
NR24_REG = 0;
// チャンネル毎の左右オフ
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xdd);
}
}
// チャンネル3(波形メモリ音源)
if (snd_trk->ch == 3) {
if (snd_trk->on == 1) {
// 波形メモリ転送フラグ
if (snd_trk->flag & SND_FLAG_VW) {
tmp = SND_FLAG_VW ^ 0xFF;
snd_trk->flag &= tmp;
snd_voluntary_wave_set(snd_trk->duty);
}
// b7:出力ON=1
NR30_REG = 0x80;
// b7-0:音長=0
NR31_REG = 0x00;
// b6-5:音量 100%出力=1 ※ミュート(0%出力)=0、50%出力=2、25%出力=3
//NR32_REG = snd_trk->vol << 5;
tmp = 0;
if (snd_trk->vol > 0) {
tmp = 0x60;
}
if (snd_trk->vol > 4) {
tmp = 0x40;
}
if (snd_trk->vol > 8) {
tmp = 0x20;
}
NR32_REG = tmp;
// b7-0:周波数(11ビットの下位8ビット)
NR33_REG = (UBYTE)(snd_trk->freq & 0x00FF);
// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
NR34_REG = (UBYTE)((snd_trk->freq >> 8) & 0x00FF) + 0x80;
// チャンネル毎の左右オン
tmp = ch_panpot[snd_trk->ch-1][snd_trk->pan];
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xbb) ^ tmp;
} else {
NR30_REG = 0;
NR32_REG = 0;
NR33_REG = 0;
NR34_REG = 0;
// チャンネル毎の左右オフ
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xbb);
}
}
// チャンネル4(ノイズ)
if (snd_trk->ch == 4) {
if (snd_trk->on == 1) {
// b5-0:音長=0
NR41_REG = 0x00;
// b7-4:初期音量 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間
tmp = (UBYTE)((snd_trk->freq >> 8) & 0x00FF);
NR42_REG = tmp + (snd_trk->vol << 4);
// b7-4:周波数シフト量 / b3:カウンタ単位(0:15/1:7)=0 / b2-0:周波数
NR43_REG = (UBYTE)(snd_trk->freq & 0x00FF);
// b7:開始フラグ=1 / b6:カウンタ再生フラグ=0
NR44_REG = 0x80;
// チャンネル毎の左右オン
tmp = ch_panpot[snd_trk->ch-1][snd_trk->pan];
nr51 = NR51_REG;
NR51_REG = (nr51 & 0x77) ^ tmp;
} else {
NR42_REG = 0;
NR43_REG = 0;
NR44_REG = 0;
// チャンネル毎の左右オフ
nr51 = NR51_REG;
NR51_REG = (nr51 & 0x77);
}
}
} else {
snd_trk->play = 0;
// チャンネル1(矩形波スイープあり)
if (snd_trk->ch == 1) {
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xee);
}
// チャンネル2(矩形波)
if (snd_trk->ch == 2) {
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xdd);
}
// チャンネル3(波形メモリ音源)
if (snd_trk->ch == 3) {
nr51 = NR51_REG;
NR51_REG = (nr51 & 0xbb);
}
// チャンネル4(ノイズ)
if (snd_trk->ch == 4) {
nr51 = NR51_REG;
NR51_REG = (nr51 & 0x77);
}
}
}
}
Sound.c
#include <stdio.h>
#include <gb.h>
// サウンドドライバ
#include "snd_drv.c"
// タイルデータ
#include "Ascii_misaki.c"
UBYTE input_on, input_old, input_edge, input_trg;// キー入力用
UBYTE music1_1[] = {"t5p1v9y1 L l2o4c<b->e-.l4d- ce-a-b-e-2.e f2<b-.>c16d-16 e-2<a2> d-2l8c<b-a-gb-2a-2"};
UBYTE music1_2[] = {"t5p2v8y2 L l8o3a-e-a-e-ge-ge- a-e-a-e-b-e-b-e- a-e-b-e->c<a->d<a- gb-gb-gb-gb- gb-gb-ge-ge- a-e-a-e-e-ce-c fd-fd-d-d-d-d- d-e-d-e-c2"};
UBYTE music1_3[] = {"t5p3v8y2 L l2o2a->d- c<g l4a-gf>f l2e-<e- d->d- c<f <b->e-<l4a->a-<a-r"};
UBYTE music1_4[] = {"t5p3v7 L l4o1d>c<d8d8>c< d>c<d8d8>c< d>c<d8d8>c< d>c<d8d8>c8c8 >>c+<<c<d8d8>c< d>c<d8d8>c< d>c<d8d8>c8<d8 >>>c+<<<d>>>c+<<<d16d16d+8"};
void proc()
{
if (input_trg & J_A) {
gotoxy(5, 2);
print("PLAY");
// BGM再生
snd_req_bgm(music1_1, music1_2, music1_3, music1_4);
}
if (input_trg & J_B) {
gotoxy(5, 2);
print("INIT");
snd_reg_init();// サウンドレジスタ初期化
snd_track_init();
}
// サウンドドライバ
snd_driver();
}
// VBL割込で呼ばれる
void vbl_isr(void)
{
// キー入力
input_old = input_on;
input_on = joypad();
input_edge = input_on ^ input_old;
input_trg = input_on & input_edge;
proc();
}
void main()
{
DISPLAY_OFF;// 画面全体を非表示
HIDE_BKG;// BGを非表示
HIDE_SPRITES;// スプライトを非表示
print(" ");// ダミー描画
set_bkg_data(32, 64, AsciiBgLabel);// アスキー文字
// SHOW_SPRITES;// スプライトを表示
SHOW_BKG;// BGを表示
DISPLAY_ON;// 画面全体を表示
disable_interrupts();// 割込無効
add_VBL(vbl_isr);// VBL割込に追加
enable_interrupts();// 割込有効
set_interrupts(VBL_IFLAG);// VBL割込セット
snd_reg_init();// サウンドレジスタ初期化
snd_track_init();
// メインループ(無くても良いけど何となく)
while(1) {
delay(1000UL);
}
}
以下がMMLのマニュアルです。
音程コマンド
cdefgabr(+/-)数値
数値には音長を指定します。
音長は32、16、16.、8、8.、4、4.、2、2.、1の10通りです。
音長を省略した場合は基準音長(lコマンド)になります。
.(付点)のみを指定した場合は基準音長に付点を加えたものになりますが
基準音長が32と1の時は無効になります。
また、基準音長が付点音長の場合の動作は未確認です。
rは休符です。
チャンネル4(ノイズ)では音程ごとにscale_noiseの値を再生します。
頻度の高いものを割り当て直すと便利です。
基準音長コマンド
l数値
数値には音長を指定します。
タイコマンド
^数値
数値には音長を指定します。
音を延ばしたい時に指定します。音長の省略は不可です。
オクターブコマンド
o数値
オクターブの指定は0~5です。
>で現在のオクターブを1つ上げます。
<で現在のオクターブを1つ下げます。
テンポコマンド
t数値
テンポの指定は1~8です。
テンポ1の32分音符が1フレームになります。
テンポ1の4分音符が8フレームになります。
テンポ2の8分音符も8フレームになります。
テンポ4の16分音符も8フレームになります。
テンポ8の32分音符も8フレームになります。
テンポを音長をうまく指定すれば連符も表現できます。
音量コマンド
v数値
音量の指定は0~15です。
※マスター音量の指定コマンドは実装していません。
パンポットコマンド
p数値
パンポットの指定は0~3です。
0:消音
1:左
2:右
3:両方
音色コマンド
y数値
音色の指定は0~3です。
チャンネル1・2(矩形波)ではデューティ比の指定になります。
チャンネル3(波形メモリ)ではvoluntary_waveのプリセットになります。
チャンネル4(ノイズ)では無効になります。
ループポイント設定コマンド
L
MMLの最後まで再生してから指定のループポイントに戻ります。
Lを設定していなければループせずに再生を終了します。
プログラムではsnd_driver()を毎フレーム呼び出すようにして
snd_req_bgm()で4トラックのMML文字列の先頭アドレスを指定すれば
再生されます。トラック指定が不要な場合は0を指定します。
こんなところでしょうか。
エンベロープやスイープ、ディレイビブラート、デチューン、リピートなどの機能は
今回は実装しませんでした。また、効果音による上書き再生も実装しませんでした。
なお、MMLの解釈は処理に負荷がかかるため
MMLをサウンド用データにコンバートするツールを用意した方が良いです。
実機では動作確認していませんので、ノイズ対策等は行っていません。
ちなみにサンプルの曲はベートーヴェンのピアノソナタ第8番「悲愴」です。
MMLデータをテーブル化して、曲番号をリクエストして
再生できるようにすると良いでしょう。
実機では動作確認していませんので、ノイズ対策等は行っていません。
ちなみにサンプルの曲はベートーヴェンのピアノソナタ第8番「悲愴」です。
MMLデータをテーブル化して、曲番号をリクエストして
再生できるようにすると良いでしょう。