2018/04/14

音を鳴らしてみよう(3)

仕上げにサウンドドライバを作りました。
簡易的な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 (ドライバ本体)
#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 件のコメント:

  1. あなたのGBDKの記事はとても興味深いです。そして陰ながら応援しています。

    返信削除
    返信
    1. 遅くなりましたが、応援ありがとうございます。
      これからも宜しくお願いします。

      削除
    2. 返答遅くなりましたが、今後もゲームボーイ開発続けますか?
      これからも宜しくお願いします。

      削除
    3. 実は今もゲームボーイ開発しています。
      それを公開することは出来ませんが、開発によって得た情報は
      ここで公開していきたいと思っています。

      削除
    4. ありがとうございます。カラー・SGB対応のゲーム・プログラムなども製作しますか?

      削除
    5. カラーは大変そうなので今の所は対応予定がありません。
      ごめんなさい。

      削除
    6. そうですか
      しかしGBDKでサウンドドライバ作るのとBGM鳴らすの大変じゃないですか?
      音楽の知識ないとつらそうですが...

      削除
    7. 私自身は音楽の知識があるので何とかなってますが、音楽の知識がないと大変ですよね。
      ここに公開したサウンドドライバを元にMMLをデータ化して再生するものを作りましたが
      あまりに独自仕様になってしまったので公開するのは止めました。

      削除