summaryrefslogtreecommitdiffstats
path: root/lib/rbcodec/codecs/libgme/gb_oscs.h
blob: 3c8dfef51f15b9615aacf6e5fbac232892399fe9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Private oscillators used by Gb_Apu

// Gb_Snd_Emu 0.1.4
#ifndef GB_OSCS_H
#define GB_OSCS_H

#include "blargg_common.h"
#include "blip_buffer.h"

#ifndef GB_APU_OVERCLOCK
	#define GB_APU_OVERCLOCK 1
#endif

#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
	#error "GB_APU_OVERCLOCK must be a power of 2"
#endif

enum { clk_mul  = GB_APU_OVERCLOCK };
enum { dac_bias = 7 };

struct Gb_Osc {
	struct Blip_Buffer*    outputs [4];// NULL, right, left, center
	struct Blip_Buffer*    output;     // where to output sound
	uint8_t* regs;       // osc's 5 registers
	int             mode;       // mode_dmg, mode_cgb, mode_agb
	int             dac_off_amp;// amplitude when DAC is off
	int             last_amp;   // current amplitude in Blip_Buffer

	struct Blip_Synth* synth;
	
	int         delay;      // clocks until frequency timer expires
	int         length_ctr; // length counter
	unsigned    phase;      // waveform phase (or equivalent)
	bool        enabled;    // internal enabled flag	
};

// 11-bit frequency in NRx3 and NRx4
static inline int Osc_frequency( struct Gb_Osc* this ) { return (this->regs [4] & 7) * 0x100 + this->regs [3]; }

void Osc_clock_length( struct Gb_Osc* this );
void Osc_reset( struct Gb_Osc* this );

// Square

enum { period_mask = 0x70 };
enum { shift_mask  = 0x07 };

struct Gb_Square {
	struct Gb_Osc osc;
	
	int  env_delay;
	int  volume;
	bool env_enabled;
	
	// Sweep square
	int  sweep_freq;
	int  sweep_delay;
	bool sweep_enabled;
	bool sweep_neg;
};

void Square_run( struct Gb_Square* this, blip_time_t, blip_time_t );
void Square_clock_envelope( struct Gb_Square* this );
	 
static inline void Square_reset( struct Gb_Square* this )
{
	this->env_delay = 0;
	this->volume    = 0;
	Osc_reset( &this->osc );
	this->osc.delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
}
// Frequency timer period
static inline int Square_period( struct Gb_Square* this ) { return (2048 - Osc_frequency( &this->osc )) * (4 * clk_mul); }
static inline int Square_dac_enabled( struct Gb_Square* this) { return this->osc.regs [2] & 0xF8; }
static inline int Square_reload_env_timer( struct Gb_Square* this )
{
	int raw = this->osc.regs [2] & 7;
	this->env_delay = (raw ? raw : 8);
	return raw;
}

// Sweep square

void clock_sweep( struct Gb_Square* this );
	
static inline void Sweep_reset( struct Gb_Square* this )
{
	this->sweep_freq    = 0;
	this->sweep_delay   = 0;
	this->sweep_enabled = false;
	this->sweep_neg     = false;
	
	this->env_delay = 0;
	this->volume    = 0;
	Osc_reset( &this->osc );
	this->osc.delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
}

// Noise 

enum { period2_mask = 0x1FFFF };
	
struct Gb_Noise {
	struct Gb_Osc osc;
	
	int  env_delay;
	int  volume;
	bool env_enabled;
	
	int divider; // noise has more complex frequency divider setup
};

void Noise_run( struct Gb_Noise* this, blip_time_t, blip_time_t );
	
static inline void Noise_reset( struct Gb_Noise* this )
{
	this->divider = 0;
	
	this->env_delay = 0;
	this->volume    = 0;
	Osc_reset( &this->osc );
	this->osc.delay = 4 * clk_mul; // TODO: remove?
}
	
void Noise_clock_envelope( struct Gb_Noise* this );
	
// Non-zero if DAC is enabled
static inline int Noise_dac_enabled( struct Gb_Noise* this) { return this->osc.regs [2] & 0xF8; }
static inline int Noise_reload_env_timer( struct Gb_Noise* this )
{
	int raw = this->osc.regs [2] & 7;
	this->env_delay = (raw ? raw : 8);
	return raw;
}

static inline int period2_index( struct Gb_Noise* this ) { return this->osc.regs [3] >> 4; }
static inline int period2( struct Gb_Noise* this, int base ) { return base << period2_index( this ); }
static inline unsigned lfsr_mask( struct Gb_Noise* this ) { return (this->osc.regs [3] & 0x08) ? ~0x4040 : ~0x4000; }

// Wave

enum { bank40_mask = 0x40 };
enum { wave_bank_size   = 32 };
	
struct Gb_Wave {
	struct Gb_Osc osc;
	
	int sample_buf;      // last wave RAM byte read (hardware has this as well)
	
	int agb_mask;        // 0xFF if AGB features enabled, 0 otherwise
	uint8_t* wave_ram;   // 32 bytes (64 nybbles), stored in APU
};

void Wave_run( struct Gb_Wave* this, blip_time_t, blip_time_t );

static inline void Wave_reset( struct Gb_Wave* this )
{
	this->sample_buf = 0;
	Osc_reset( &this->osc );
}

// Frequency timer period
static inline int Wave_period( struct Gb_Wave* this ) { return (2048 - Osc_frequency( &this->osc )) * (2 * clk_mul); }
	
// Non-zero if DAC is enabled
static inline int Wave_dac_enabled( struct Gb_Wave* this ) { return this->osc.regs [0] & 0x80; }
	
static inline uint8_t* wave_bank( struct Gb_Wave* this ) { return &this->wave_ram [(~this->osc.regs [0] & bank40_mask) >> 2 & this->agb_mask]; }
	
// Wave index that would be accessed, or -1 if no access would occur
int wave_access( struct Gb_Wave* this, int addr );

// Reads/writes wave RAM		
static inline int Wave_read( struct Gb_Wave* this, int addr )
{
	int index = wave_access( this, addr );
	return (index < 0 ? 0xFF : wave_bank( this ) [index]);
}

static inline void Wave_write( struct Gb_Wave* this, int addr, int data )
{
	int index = wave_access( this, addr );
	if ( index >= 0 )
		wave_bank( this ) [index] = data;;
}

#endif