Gameboy Development Forum

Discussion about software development for the old-school Gameboys, ranging from the "Gray brick" to Gameboy Color
(Launched in 2008)

You are not logged in.

#1 2020-12-08 17:35:54

New member
Registered: 2020-12-08
Posts: 2

Wave Channel Help

I am new to Gameboy development and use C with gbdk. I am trying to use the wave channel to play at least a triangle wave. However, I can't find any good tutorials or examples and can't get it working myself. Can anyone explain in-depth about how to use the wave channel or give an example.

This is my current code so far:

Code:

```#include <gb/gb.h>
#include <gb/cgb.h>
#include <stdio.h>

typedef enum {
C3, Cd3, D3, Dd3, E3, F3, Fd3, G3, Gd3, A3, Ad3, B3,
C4, Cd4, D4, Dd4, E4, F4, Fd4, G4, Gd4, A4, Ad4, B4,
C5, Cd5, D5, Dd5, E5, F5, Fd5, G5, Gd5, A5, Ad5, B5,
C6, Cd6, D6, Dd6, E6, F6, Fd6, G6, Gd6, A6, Ad6, B6,
C7, Cd7, D7, Dd7, E7, F7, Fd7, G7, Gd7, A7, Ad7, B7,
C8, Cd8, D8, Dd8, E8, F8, Fd8, G8, Gd8, A8, Ad8, B8,
SILENCE
} pitch;

const UWORD frequencies[] = {
44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986,
1046, 1102, 1155, 1205, 1253, 1297, 1339, 1379, 1417, 1452, 1486, 1517,
1546, 1575, 1602, 1627, 1650, 1673, 1694, 1714, 1732, 1750, 1767, 1783,
1798, 1812, 1825, 1837, 1849, 1860, 1871, 1881, 1890, 1899, 1907, 1915,
1923, 1930, 1936, 1943, 1949, 1954, 1959, 1964, 1969, 1974, 1978, 1982,
1985, 1988, 1992, 1995, 1998, 2001, 2004, 2006, 2009, 2011, 2013, 2015,
0
};

typedef enum {
SQUARE,
TRIANGLE
} instrument;

const UBYTE samples[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10
};

main() {
NR52_REG = 0x80;
NR51_REG = 0x11;
NR50_REG = 0x77;

NR51_REG |= 0x33;
NR30_REG = 0x00;
NR31_REG = 0xFF;
NR32_REG = 0x60;
NR33_REG = (UBYTE)frequencies[C4];
NR34_REG = ((UWORD)frequencies[C4]>>8);
for (int i = 0; i < 16; i++) {
unsigned char *addr = (unsigned char *)(0xFF30u + i);
}
NR30_REG |= 0x80;
NR34_REG |= 0x80;
while(1) {}
return 1;
}```

Offline

#2 2020-12-08 19:40:29

nitro2k01
Registered: 2008-02-22
Posts: 245

Re: Wave Channel Help

Let's go through the code line by line:

Code:

`    NR52_REG = 0x80;`

Turn on the APU. This is ok.

Code:

`    NR51_REG = 0x11;`

Select which audio channels are connected to left and right. Not ok. This is a bit mask so the value 1 = binary 0001. This routes channel 1 to the left and right outputs.

Code:

`        NR50_REG = 0x77;`

Set left and right to maximum volume. This is ok.

Code:

`    NR51_REG |= 0x33;`

Select which audio channels are connected to left and right, again. This value was 0x11 and it's bitwise OR'ed with 0x33. But 3=0011 in binary, this enables channel 1 and 2, and channel 3 (the wave channel) is not enabled. You should do generally or it with 0x44 to enable the wave channel if you might have other channels playing at the same time, or just set it to 0x44 directly if you want to enable only the wave channel.

Code:

`    NR30_REG = 0x00;`

Disable the wave channel. This is needed to change the waveform, so this is correct.

Code:

`    NR31_REG = 0xFF;`

Set the length that the channel is played. Not ok. Two things:
1) The formula is (256-t1)*(1/256) seconds so 0xFF is actually the shortest duration the channel can play. (256-0xff)*(1/256)=(256-255)*(1/256)=1*(1/256)
2) This value is only used if bit 6 of NR34 is set, so no need to care about this value

Code:

`    NR32_REG = 0x60;`

Set the "volume" of the wave channel. Not ok. Two things:
1) This actually lowers the volume by shifting the value of each sample to the right, so you lose one bit of resolution for each step you lower the volume with this function. You probably want to use max volume in your example to get full quality.
2) Even though 0x60 sets the correct two bits of this setting to 1, this is not the highest setting. The highest setting (in binary) is 01. So this should probably be set to 0x20 in your example. From highest to lowest the values are 0x20, 0x40, 0x60, 0x00.

Code:

```    NR33_REG = (UBYTE)frequencies[C4];
NR34_REG = ((UWORD)frequencies[C4]>>8);```

This is ok in itself, but:
1) This should be set after enabling the wave channel.
2) You should set the top bit of NR30 to start playing the sound. See below.

Code:

```    for (int i = 0; i < 16; i++) {
unsigned char *addr = (unsigned char *)(0xFF30u + i);
}```

This looks ok in principle, but it could be more optimized. For example TRIANGLE*16 must be calculated each time, which is hard on the poor 8 bit CPU.

Code:

`    NR30_REG |= 0x80;`

Enable the wave channel. Ok.

Code:

`    NR34_REG |= 0x80;`

Start playback. Not ok. NR34 can't be read back so this actually sets the upper bits of the frequency to garbage. (The |= operator reads the memory location and then writes it back.)

So here's a new version you can try. (Untested because I don't have GBDK set up atm.

Code:

```main() {
NR52_REG = 0x80; // Enable APU.
NR51_REG = 0x44; // Enable wave channel to left and right output.
NR50_REG = 0x77; // Set maximum master volume.

NR30_REG = 0x00; // Disable wave channel for reload.
NR32_REG = 0x40; // Set wave channel "volume" to max.
unsigned char *dst = (unsigned char *)(0xFF30u); // Create pointer to the wave RAM.
unsigned char *src = &samples[TRIANGLE*16]; // Create pointer to the waveform.
unsigned char length = 16; // Number of bytes to copy.

while (length--) {
*dst++ = *src++;
}
NR30_REG |= 0x80; // Enable wave channel.
NR33_REG = (UBYTE)frequencies[C4]; // Set lower byte of frequency.
NR34_REG = ((UWORD)frequencies[C4]>>8)|0x80; // Set higher byte of frequency and start playback.

while(1) {}
return 1;
}```

Blog: Gameboy Genius
"A journey of a thousand miles begins with one small step"
Old Chinese Proverb

Offline

#3 2020-12-08 21:16:07

New member
Registered: 2020-12-08
Posts: 2

Re: Wave Channel Help

Thanks, it does work now

Offline