2018/04/14

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

今度は音程を変えましょう。
周波数のレジスタに音程ごとの周波数をセットします。
周波数の値についてはここを参考にしました。

プログラムはこんな感じです。
#include <stdio.h>
#include <gb.h>

// タイルデータ
#include "Ascii_misaki.c"

// 音程に対応した周波数
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,// 2
 1046,1102,1155,1205,1253,1297,1339,1379,1417,1452,1486,1517,// 3
 1546,1575,1602,1627,1650,1673,1694,1714,1732,1750,1767,1783,// 4
 1798,1812,1825,1837,1849,1860,1871,1881,1890,1899,1907,1915,// 5
 1923,1930,1936,1943,1949,1954,1959,1964,1969,1974,1978,1982,// 6
 1985,1988,1992,1995,1998,2001,2004,2006,2009,2011,2013,2015,// 7
 2017
};

UBYTE input_on, input_old, input_edge, input_trg;// キー入力用

UBYTE scale;// 音程
UBYTE length;// 音長
UBYTE play_flg;// 再生フラグ

void bg_num_disp(UWORD n, UBYTE x, UBYTE y, UBYTE d);
void snd_reg_init();

// 変数初期化
void var_init()
{
 scale = 0;// 音程
 length = 0;// 音長
 play_flg = 0;// 再生フラグ
}

// サウンド処理
void proc_snd()
{
 UWORD frequency;
 // ファースト処理
 if (play_flg == 1) {
  // チャンネル制御
  // 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 = 0xFF;
  play_flg = 2;
  scale = 0;// 音程
  length = 0;// 音長
 }
 // 再生開始
 if (length == 0) {
  frequency = scale_frequency[scale];
  // チャンネル1(矩形波スイープあり)
  // b6-4:スイープ時間=0 / b3:スイープ方向=0(上) / b2-0:スイープ変化量=0
  NR10_REG = 0x00;
  // b7-6:デューティ比=3 / b5-0:音長カウンタ=0
  NR11_REG = 0xC0;
  // b7-4:初期音量=15 / b3:エンベロープ増減=0(減) / b2-0:エンベロープ単位時間=0
  NR12_REG = 0xF0;
  // b7-0:周波数(11ビットの下位8ビット)
  NR13_REG = (UBYTE)(frequency & 0x00FF);
  // b7:開始フラグ=1 / b6:カウンタ再生フラグ=0 / b2-0:周波数(11ビットの上位3ビット)
  NR14_REG = (UBYTE)((frequency >> 8) & 0x00FF) + 0x80;
  gotoxy(5, 3);
  printf("SCALE=");
  bg_num_disp(scale, 11, 3, 2);// BGへ数字描画
 }

 if (length++ > 15) {
  length = 0;
  if (scale++ >= 72) {
   scale = 0;
  }
 }
}

void proc()
{
 if (input_trg & J_A) {
  gotoxy(5, 2);
  print("PLAY");
  play_flg = 1;
  NR10_REG = 0x00;
  NR11_REG = 0xC0;
  NR12_REG = 0xF0;
  NR13_REG = 0x0A;
  NR14_REG = 0x86;
 }

 if (input_trg & J_B) {
  gotoxy(5, 2);
  print("INIT");
  snd_reg_init();// サウンドレジスタ初期化
  var_init();// 変数初期化
  gotoxy(5, 3);
  print("        ");
 }

 if (play_flg != 0) {
  proc_snd();// サウンド処理
 }

}

// 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();
}

// BGへ数字描画
// n=表示する数値 x=描画X座標(左端) y=描画Y座標 d=描画桁数
// 10で除算をやってるので処理が重いかも
void bg_num_disp(UWORD n, UBYTE x, UBYTE y, UBYTE d)
{
 UBYTE f = 1;// 最初の0は表示する
 do {
  d--;
  gotoxy(x+d, y);
  if (f == 0 && n == 0)
   setchar(0);// 空白
  else
   setchar(0x30U+(n%10));// 数字
  f = 0;
  if (n != 0) n /= 10;
 } while(d != 0);
}

// サウンド関連の全レジスタを初期化
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 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();// サウンドレジスタ初期化
 var_init();// 変数初期化

 // メインループ(無くても良いけど何となく)
 while(1) {
  delay(1000UL);
 }
}

チャンネル1を鳴らしている所にチャンネル2・3も音程を変えて
鳴らせば和音になります。

0 件のコメント:

コメントを投稿