サウンド3

サウンド3はsample buffer(Wave RAM)を持つチャンネルです。4ビット単位の波形を32か64個用意して音を出力します。波形をサウンド1,2に合わせて同じにすることも可能です。エンベロープはありません。このページはgba-docs-jaさんの内容をそのまま転記しています。(^^;

準備

REG_SOUNDCNT_X = 0x80;		// turn on sound circuit
REG_SOUNDCNT_L = 0x4477;	// full volume, enable sound 3 to left and right
REG_SOUNDCNT_H = 2;		// Overall output ratio - Full

REG_SOUND3CNT_L

4000070h - SOUND3CNT_L (NR30) - Channel 3 Stop/Wave RAM select (R/W)
  Bit        Expl.
  0-4   -    Not used
  5     R/W  Wave RAM Dimension   (0=One bank/32 digits, 1=Two banks/64 digits)
  6     R/W  Wave RAM Bank Number (0-1, see below)
  7     R/W  Sound Channel 3 Off  (0=Stop, 1=Playback)
  8-15  -    Not used
The currently selected Bank Number (Bit 6) will be played back, while reading/writing to/from wave RAM will address the other (not selected) bank. When dimension is set to two banks, output will star t by replaying the currently selected bank.

現在bit6で選択されているバンク番号が再生され、Wave RAMへの読み書きは、もう一方の(選択されていない)バンクをアドレスとします。ディメンションが2バンク(bit5が1)に設定されている場合、出力は現在選択されているバンクの再生から始まります。

REG_SOUND3CNT_H

4000072h - SOUND3CNT_H (NR31, NR32) - Channel 3 Length/Volume (R/W)
  Bit        Expl.
  0-7   W    Sound length; units of (256-n)/256s  (0-255)
  8-12  -    Not used.
  13-14 R/W  Sound Volume  (0=Mute/Zero, 1=100%, 2=50%, 3=25%)
  15    R/W  Force Volume  (0=Use above, 1=Force 75% regardless of above)
The Length value is used only if Bit 6 in NR34 is set.

長さの値(bit0-7)は、NR34のbit6が設定されている場合にのみ使用されます。

REG_SOUND3CNT_X

4000074h - SOUND3CNT_X (NR33, NR34) - Channel 3 Frequency/Control (R/W)
  Bit        Expl.
  0-10  W    Sample Rate; 2097152/(2048-n) Hz   (0-2047)
  11-13 -    Not used
  14    R/W  Length Flag  (1=Stop output when length in NR31 expires)
  15    W    Initial      (1=Restart Sound)
  16-31 -    Not used
The above sample rate specifies the number of wave RAM digits per second, the actual tone frequency depends on the wave RAM content, for example:

サンプルレートはbit0-10の値をnとすると、2097152/(2048-n) Hzとなります。このサンプルレートは、1秒あたりのWave RAMの桁数を指定するもので、実際のトーン周波数はWave RAMの内容に依存します。次に例を示します。

  Wave RAM, single bank 32 digits   Tone Frequency
  FFFFFFFFFFFFFFFF0000000000000000  65536/(2048-n) Hz
  FFFFFFFF00000000FFFFFFFF00000000  131072/(2048-n) Hz
  FFFF0000FFFF0000FFFF0000FFFF0000  262144/(2048-n) Hz
  FF00FF00FF00FF00FF00FF00FF00FF00  524288/(2048-n) Hz
  F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0  1048576/(2048-n) Hz

WAVE_RAM

4000090h - WAVE_RAM0_L - Channel 3 Wave Pattern RAM (W/R)
4000092h - WAVE_RAM0_H - Channel 3 Wave Pattern RAM (W/R)
4000094h - WAVE_RAM1_L - Channel 3 Wave Pattern RAM (W/R)
4000096h - WAVE_RAM1_H - Channel 3 Wave Pattern RAM (W/R)
4000098h - WAVE_RAM2_L - Channel 3 Wave Pattern RAM (W/R)
400009Ah - WAVE_RAM2_H - Channel 3 Wave Pattern RAM (W/R)
400009Ch - WAVE_RAM3_L - Channel 3 Wave Pattern RAM (W/R)
400009Eh - WAVE_RAM3_H - Channel 3 Wave Pattern RAM (W/R)
This area contains 16 bytes (32 x 4bits) Wave Pattern data which is output by channel 3.
Data is played back ordered as follows: MSBs of 1st byte, followed by LSBs of 1st byte,
followed by MSBs of 2n d byte, and so on - this results in a confusing ordering when filling Wave RAM in units of 16bit data
- ie. samples would be then located in Bits 4-7, 0-3, 12-15, 8-11.

In the GBA, two Wave Patterns exists (each 32 x 4bits), either one may be played (as selected in NR30 register),
the other bank may be accessed by the users. After all 32 samples have been played, ou tput of the same bank
(or other bank, as specified in NR30) will be automatically restarted.

Internally, Wave RAM is a giant shift-register, there is no pointer which is addressing the currently played digit.
Instead, the entire 128 bits are shifted, and the 4 least significant bits are out put.
Thus, when reading from Wave RAM, data might have changed its position.
And, when writing to Wave RAM all data should be updated
(it'd be no good idea to assume that old data is still located at the  same position where it has been written to previously).

このエリアには、チャンネル3が出力する16バイト(32×4bits)のWave Patternデータが格納されています。データは、第1バイトのMSB、第1バイトのLSB、第2バイトのMSB......という順序で再生されますが、16ビットデータ単位でWave RAMを埋めていくと、4-7、0-3、12-15、8-11ビットにサンプルが配置されるなど、混乱した順序になってしまいます。GBAでは、2つのWaveパターン(各32×4bits)が存在し、どちらか一方を再生し(NR30レジスタで選択)、もう一方のバンクにユーザーがアクセスすることができます。32個のサンプルがすべて再生された後、同じバンク(またはNR30で指定された他のバンク)の出力が自動的に再開されます。内部的には、Wave RAMは巨大なシフトレジスタであり、現在再生されている桁を指すポインタは存在しません。その代わり、128ビット全体がシフトされ、最下位4ビットが出力されます。 そのため、Wave RAMからデータを読み出す際には、データの位置が変わっている可能性があります。また、Wave RAMに書き込む際には、すべてのデータを更新する必要があります(古いデータが、以前に書き込まれたのと同じ位置にあると考えるのはよくありません)。

テスト

1.png
#include "lib/gba.h"
#include "irq.arm.h"
#include "key.h"
#include "mode3.h"

//---------------------------------------------------------------------------

typedef struct {
	s32 cur;	// 現在値
	s32 min;
	s32 max;
	s32 adj;	// 増減値
} ST_PARAM;

//---------------------------------------------------------------------------

const u32 wave[8*13] = { 
	// 0 ?
	0x02468ace,0xfdb97531,0x02468ace,0xfdb97531,
	0x2064a8ec,0xdf9b5713,0x2064a8ec,0xdf9b5713,
	// 1 double saw
	0x01234567,0x89abcdef,0x01234567,0x89abcdef,
	0x10325476,0x98badcfe,0x10325476,0x98badcfe,
	// 2 ?
	0x88808fff,0x88808880,0x00080888,0x80008000,
	0x8808f8ff,0x88088808,0x00808088,0x08000800,
	// 3 ?
	0x34455667,0x899aabbc,0x34455667,0x899aabbc,
	0x43546576,0x98a9bacd,0x43546576,0x98a9bacb,
	// 4 ?
	0x23345667,0x899abcde,0x23345667,0x899abcde,
	0x32436576,0x98a9cded,0x32436576,0x98a9cded,
	// 5 ?
	0xacddda48,0x3602cf16,0x2c04e52c,0xacddda48,
	0xcaddad84,0x6320fc61,0xc2405ec2,0xcaddad84,
	// 6 triangular
	0x10325476,0x67452301,0xefcdab89,0x98badcfe,
	0x01234567,0x76543210,0xfedcba98,0x89abcdef,
	// 7 ?
	0x01234567,0x89abcdef,0xffffffff,0xfedcba98,
	0x10325376,0x98badcfe,0xffffffff,0xefcdab89,
	// 8 ?
	0xf0eeca86,0x0a468ace,0xffeeca86,0x0a468ace,
	0x0feeac68,0xa064a8ec,0xffeeac68,0xa064a8ec,
	// 9 ?
	0x7ec9cea7,0xcfe8ab72,0x8d757203,0x85136317,
	0xe79cec7a,0xfc8eba27,0xd8572730,0x58313671,
	// 10 ?
	0xfedcba98,0x76543210,0x76543210,0x44002200,
	0xefcdab89,0x67452301,0x67452301,0x44002200,
	// 11 ?
	0x02468ace,0xffffffff,0xeca86420,0x00448844,
	0x2064a8ec,0xffffffff,0xce8a4602,0x00448844,
	// 12 ?
	0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000,
	0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF,
};

const u8 score[32*3] = {
	// 0
	19,8,15,8,19,15,8,15,19,15,8,15,19,8,15,19,5,12,17,12,5,17,12,17,5,17,12,17,5,12,17,5,
	// 1
	19,19,24,19,24,24,19,27,19,24,19,26,24,19,24,24,17,17,24,17,24,24,17,12,24,12,19,24,12,19,24,24,
	// 2
	31,31,36,31,36,36,31,39,31,36,31,38,36,31,36,36,29,29,36,29,36,36,29,24,36,24,31,36,24,31,36,36,
};

const u16 freq[12*6] = {
	// C      C+       D      D+       E       F      F+       G      G+       A      A+       B
	  44,    156,    262,    363,    457,    547,    631,    710,    786,    854,    923,    986,   // o3
	1046,   1102,   1155,   1205,   1253,   1297,   1339,   1379,   1417,   1452,   1486,   1517,   // o4
	1546,   1575,   1602,   1627,   1650,   1673,   1694,   1714,   1732,   1750,   1767,   1783,   // o5
	1798,   1812,   1825,   1837,   1849,   1860,   1871,   1881,   1890,   1899,   1907,   1915,   // o6
	1923,   1930,   1936,   1943,   1949,   1954,   1959,   1964,   1969,   1974,   1978,   1982,   // o7
	1985,   1988,   1992,   1995,   1998,   2001,   2004,   2006,   2009,   2011,   2013,   2015,   // o8
};

//---------------------------------------------------------------------------
void LoadWave(ST_PARAM* p, u32 select)
{
	u32 inst = p[0].cur * 8 + select * 4;

	DMA_Copy(3, (u32)&wave[inst], (u32)WAVE_RAM, DMA_SRC_INC | DMA_DST_INC | DMA32 | 4);

	// bank number
	if(p[1].cur == 0)
	{
		p[7].cur = inst;
	}
	else
	{
		p[8].cur = inst;
	}

	p[1].cur = (p[1].cur == 0) ? 1 : 0;
	REG_SOUND3CNT_L^= SOUND3_SETBANK(1);
}
//---------------------------------------------------------------------------
void DrawParam(ST_PARAM* p)
{
	// wave, bank number
	Mode3DrawPrintf(1,  6, "%02X", p[0].cur);
	Mode3DrawPrintf(1,  7, "%02X", p[1].cur);

	// bank mode
	if(p[2].cur == 0) Mode3DrawPrintf(1,  8, "1x32");
	else              Mode3DrawPrintf(1,  8, "1x64");

	// length, pattern, pan, note
	Mode3DrawPrintf(1,  9, "%02X", p[3].cur);
	Mode3DrawPrintf(1, 10, "%02X", p[4].cur);
	Mode3DrawPrintf(1, 11, "%02X", p[5].cur);
	Mode3DrawPrintf(1, 12, "%02X", p[6].cur);

	if(REG_SOUND3CNT_L & SOUND3_SETBANK(1))
	{
		Mode3DrawPrintf(1, 13, "1");
	}
	else
	{
		Mode3DrawPrintf(1, 13, "0");
	}
}
//---------------------------------------------------------------------------
void DrawWave(ST_PARAM* p)
{
	u32 inst1 = p[7].cur;
	u32 inst2 = p[8].cur;

	// bank0 wave
	Mode3DrawPrintf(10, 1, "%08X", wave[inst1+0]);
	Mode3DrawPrintf(10, 2, "%08X", wave[inst1+1]);
	Mode3DrawPrintf(10, 3, "%08X", wave[inst1+2]);
	Mode3DrawPrintf(10, 4, "%08X", wave[inst1+3]);

	// bank1 wave
	Mode3DrawPrintf(29, 1, "%08X", wave[inst2+0]);
	Mode3DrawPrintf(29, 2, "%08X", wave[inst2+1]);
	Mode3DrawPrintf(29, 3, "%08X", wave[inst2+2]);
	Mode3DrawPrintf(29, 4, "%08X", wave[inst2+3]);
}
//---------------------------------------------------------------------------
int main(void)
{
	REG_WSCNT = 0x4317;

	Mode3Init();
	IrqInit();
	KeyInit();


	ST_PARAM param[9];

	_Memset(&param, 0x00, sizeof(param));

	param[0].cur = 0x1;		// wave
	param[1].cur = 0x0;		// bank number
	param[2].cur = 0x0;		// dimension
	param[3].cur = 0xeb;	// sound length
	param[4].cur = 0x0;		// sound pattern
	param[5].cur = 0x0;		// pan
	param[6].cur = 0x0;		// tone
	param[7].cur = 0x0;		// bank0 wave
	param[8].cur = 0x0;		// bank1 wave

	param[0].min = 0x0;
	param[1].min = 0x0;
	param[2].min = 0x0;
	param[3].min = 0x0;
	param[4].min = 0x0;
	param[5].min = 0x0;
	param[6].min = 0x0;

	param[0].max = 0xc;
	param[1].max = 0x1;
	param[2].max = 0x1;
	param[3].max = 0xff;
	param[4].max = 0x2;
	param[5].max = 0x1;
	param[6].max = 0x1f;

	param[0].adj = 0x1;
	param[1].adj = 0x1;
	param[2].adj = 0x1;
	param[3].adj = 0x1;
	param[4].adj = 0x1;
	param[5].adj = 0x1;
	param[6].adj = 0x1;

	Mode3DrawStr(1,  0, "-----Bank 0------  -----Bank 1------");
	Mode3DrawStr(1,  1, "WAVERAM0:          WAVERAM0:        ");
	Mode3DrawStr(1,  2, "WAVERAM1:          WAVERAM1:        ");
	Mode3DrawStr(1,  3, "WAVERAM2:          WAVERAM2:        ");
	Mode3DrawStr(1,  4, "WAVERAM3:          WAVERAM3:        ");

	Mode3DrawStr(8,  6, "L/R : Change Wave");
	Mode3DrawStr(8,  7, "A   : Wave RAM Bank Number");
	Mode3DrawStr(8,  8, "B   : Wave RAM Dimension");
	Mode3DrawStr(8,  9, "U/D : Change Sound Length");
	Mode3DrawStr(8, 10, "L/R : Change Sound Pattern");
	Mode3DrawStr(8, 11, "STA : Toggle Stereo Auto-Pan");
	Mode3DrawStr(8, 12, "    : Note");
	Mode3DrawStr(8, 13, "    : Playback Bank Number");
	Mode3DrawStr(8, 14, "      (SOUND3CNT_L Wave Select)");

	REG_SOUNDCNT_X = 0x80;		// turn on sound circuit
	REG_SOUNDCNT_L = 0x4477;	// full volume, enable sound 3 to left and right
	REG_SOUNDCNT_H = 2;			// Overall output ratio - Full

	REG_SOUND3CNT_L = SOUND3_PLAY | SOUND3_STEP32 | SOUND3_SETBANK(1);
	REG_SOUND3CNT_H = TRILENVOL_100;
	REG_SOUND3CNT_X = TRIFREQ_RESET;

	LoadWave(param, 0);
	LoadWave(param, 0);

	DrawWave(param);
	DrawParam(param);

	u32 PanMask = 0x4000;
	u32 waitVblank = 0;

	bool isWave = FALSE;
	bool isBank = FALSE;
	bool isDimension = FALSE;

	for(;;)
	{
	    VBlankIntrWait();

		DrawWave(param);
		DrawParam(param);

		KeyExec();
		u16 rep = KeyGetRep();
		u16 trg = KeyGetTrg();

		if(trg & KEY_L)
		{
			if(param[0].cur >  param[0].min)
			{
				param[0].cur--;
			}
			else
			{
				param[0].cur = param[0].max;
			}

			isWave = TRUE;
		}

		if(trg & KEY_R)
		{
			if(param[0].cur <  param[0].max)
			{
				param[0].cur++;
			}
			else
			{
				param[0].cur = param[0].min;
			}

			isWave = TRUE;
		}

		if(trg & KEY_A)
		{
			isBank = TRUE;
		}

		if(trg & KEY_B)
		{
			isDimension = TRUE;
		}

		if(rep & KEY_UP  && param[3].cur <  param[3].max)
		{
			param[3].cur++;
		}

		if(rep & KEY_DOWN && param[3].cur >  param[3].min)
		{
			param[3].cur--;
		}

		if(trg & KEY_RIGHT  && param[4].cur <  param[4].max)
		{
			param[4].cur++;
		}

		if(trg & KEY_LEFT && param[4].cur >  param[4].min)
		{
			param[4].cur--;
		}

		if(trg & KEY_START)
		{
			param[5].cur = (param[5].cur == 0) ? 1 : 0;
		}


		// wait vblank
		if(++waitVblank < 7)
		{
			continue;
		}
		waitVblank = 0;


		// length
		REG_SOUND3CNT_H = TRILENVOL_100 | param[3].cur;

		// pattern + note
		u32 t = param[4].cur * 32 + param[6].cur;
		u32 s = score[t];
		_ASSERT(t < 32*3 && s < 12*6);
		REG_SOUND3CNT_X = freq[s] | TRIFREQ_TIMED | TRIFREQ_RESET;

		// pan
		if(param[5].cur == 1) REG_SOUNDCNT_L  = (REG_SOUNDCNT_L & 0xBBFF) | PanMask;
		else                  REG_SOUNDCNT_L |= 0x4400;
		PanMask^=0x4400;

		// bank number
		if(isBank == TRUE)
		{
			REG_SOUND3CNT_L ^= SOUND3_SETBANK(1);

			param[1].cur = (param[1].cur == 0) ? 1 : 0;
			isBank = FALSE;
		}

		// dimension
		if(isDimension == TRUE)
		{
			REG_SOUND3CNT_L^= SOUND3_STEP64;

			param[2].cur = (param[2].cur == 0) ? 1 : 0;
			isDimension = FALSE;
		}

		// wave
		if(isWave == TRUE)
		{
			REG_SOUND3CNT_L &= ~SOUND3_PLAY;

			if(param[2].cur == 0)
			{
				// 0 = One bank/32 digits
				LoadWave(param, 0);
			}
			else
			{
				// 1 = Two banks/64 digits
				LoadWave(param, 0);
				LoadWave(param, 1);
			}

			REG_SOUND3CNT_L |= SOUND3_PLAY;
			REG_SOUND3CNT_X |= TRIFREQ_RESET;

			isWave = FALSE;
		}


		// note++
		if(param[6].cur++ >= param[6].max)
		{
			param[6].cur = 0;
		}
	}
}

履歴


添付ファイル: file1.png 18件 [詳細]

トップ   差分 履歴 リロード   一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2023-05-29 (月) 12:14:04