簡易的な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データをテーブル化して、曲番号をリクエストして
再生できるようにすると良いでしょう。