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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
/* gameplaySP
*
* Copyright (C) 2006 Exophase <exophase@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SOUND_H
#define SOUND_H
#define BUFFER_SIZE 65536
#define GBA_XTAL 16777216.0f
#define GBA_60HZ_RATE 16853760.0f /* 228*(272+960)*60 */
#if !defined(PSP_BUILD) && !defined(POLLUX_BUILD)
// run GBA at 60Hz (~0.5% faster) to better match host display
#define GBC_BASE_RATE GBA_60HZ_RATE
#else
#define GBC_BASE_RATE GBA_XTAL
#endif
typedef enum
{
DIRECT_SOUND_INACTIVE,
DIRECT_SOUND_RIGHT,
DIRECT_SOUND_LEFT,
DIRECT_SOUND_LEFTRIGHT
} direct_sound_status_type;
typedef enum
{
DIRECT_SOUND_VOLUME_50,
DIRECT_SOUND_VOLUME_100
} direct_sound_volume_type;
typedef struct
{
s8 fifo[32];
u32 fifo_base;
u32 fifo_top;
fixed8_24 fifo_fractional;
// The + 1 is to give some extra room for linear interpolation
// when wrapping around.
u32 buffer_index;
direct_sound_status_type status;
direct_sound_volume_type volume;
u32 last_cpu_ticks;
} direct_sound_struct;
typedef enum
{
GBC_SOUND_INACTIVE,
GBC_SOUND_RIGHT,
GBC_SOUND_LEFT,
GBC_SOUND_LEFTRIGHT
} gbc_sound_status_type;
typedef struct
{
u32 rate;
fixed16_16 frequency_step;
fixed16_16 sample_index;
fixed16_16 tick_counter;
u32 total_volume;
u32 envelope_initial_volume;
u32 envelope_volume;
u32 envelope_direction;
u32 envelope_status;
u32 envelope_step;
u32 envelope_ticks;
u32 envelope_initial_ticks;
u32 sweep_status;
u32 sweep_direction;
u32 sweep_ticks;
u32 sweep_initial_ticks;
u32 sweep_shift;
u32 length_status;
u32 length_ticks;
u32 noise_type;
u32 wave_type;
u32 wave_bank;
u32 wave_volume;
gbc_sound_status_type status;
u32 active_flag;
u32 master_enable;
s8 *sample_data;
} gbc_sound_struct;
extern direct_sound_struct direct_sound_channel[2];
extern gbc_sound_struct gbc_sound_channel[4];
extern s8 square_pattern_duty[4][8];
extern u32 gbc_sound_master_volume_left;
extern u32 gbc_sound_master_volume_right;
extern u32 gbc_sound_master_volume;
extern u32 gbc_sound_buffer_index;
extern u32 gbc_sound_last_cpu_ticks;
extern u32 sound_frequency;
extern u32 sound_on;
extern u32 global_enable_audio;
extern u32 enable_low_pass_filter;
extern u32 audio_buffer_size_number;
extern SDL_mutex *sound_mutex;
void sound_timer_queue8(u32 channel, u8 value);
void sound_timer_queue16(u32 channel, u16 value);
void sound_timer_queue32(u32 channel, u32 value);
void sound_timer(fixed8_24 frequency_step, u32 channel);
void sound_reset_fifo(u32 channel);
void update_gbc_sound(u32 cpu_ticks);
void init_sound(int need_reset);
void sound_write_mem_savestate(file_tag_type savestate_file);
void sound_read_savestate(file_tag_type savestate_file);
#ifdef IN_MEMORY_C
#define gbc_sound_tone_control_low(channel, address) \
{ \
u32 initial_volume = (value >> 12) & 0x0F; \
u32 envelope_ticks = ((value >> 8) & 0x07) * 4; \
gbc_sound_channel[channel].length_ticks = 64 - (value & 0x3F); \
gbc_sound_channel[channel].sample_data = \
square_pattern_duty[(value >> 6) & 0x03]; \
gbc_sound_channel[channel].envelope_direction = (value >> 11) & 0x01; \
gbc_sound_channel[channel].envelope_initial_volume = initial_volume; \
gbc_sound_channel[channel].envelope_volume = initial_volume; \
gbc_sound_channel[channel].envelope_initial_ticks = envelope_ticks; \
gbc_sound_channel[channel].envelope_ticks = envelope_ticks; \
gbc_sound_channel[channel].envelope_status = (envelope_ticks != 0); \
gbc_sound_channel[channel].envelope_volume = initial_volume; \
gbc_sound_update = 1; \
address16(io_registers, address) = value; \
} \
#define gbc_sound_tone_control_high(channel, address) \
{ \
u32 rate = value & 0x7FF; \
gbc_sound_channel[channel].rate = rate; \
gbc_sound_channel[channel].frequency_step = \
float_to_fp16_16(((131072.0 / (2048 - rate)) * 8.0) / sound_frequency); \
gbc_sound_channel[channel].length_status = (value >> 14) & 0x01; \
if(value & 0x8000) \
{ \
gbc_sound_channel[channel].active_flag = 1; \
gbc_sound_channel[channel].sample_index -= float_to_fp16_16(1.0 / 12.0); \
gbc_sound_channel[channel].envelope_ticks = \
gbc_sound_channel[channel].envelope_initial_ticks; \
gbc_sound_channel[channel].envelope_volume = \
gbc_sound_channel[channel].envelope_initial_volume; \
} \
\
gbc_sound_update = 1; \
address16(io_registers, address) = value; \
} \
#define gbc_sound_tone_control_sweep() \
{ \
u32 sweep_ticks = ((value >> 4) & 0x07) * 2; \
gbc_sound_channel[0].sweep_shift = value & 0x07; \
gbc_sound_channel[0].sweep_direction = (value >> 3) & 0x01; \
gbc_sound_channel[0].sweep_status = (value != 8); \
gbc_sound_channel[0].sweep_ticks = sweep_ticks; \
gbc_sound_channel[0].sweep_initial_ticks = sweep_ticks; \
gbc_sound_update = 1; \
address16(io_registers, 0x60) = value; \
} \
#define gbc_sound_wave_control() \
{ \
gbc_sound_channel[2].wave_type = (value >> 5) & 0x01; \
gbc_sound_channel[2].wave_bank = (value >> 6) & 0x01; \
if(value & 0x80) \
{ \
gbc_sound_channel[2].master_enable = 1; \
} \
else \
{ \
gbc_sound_channel[2].master_enable = 0; \
} \
\
gbc_sound_update = 1; \
address16(io_registers, 0x70) = value; \
} \
static u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 };
#define gbc_sound_tone_control_low_wave() \
{ \
gbc_sound_channel[2].length_ticks = 256 - (value & 0xFF); \
if((value >> 15) & 0x01) \
{ \
gbc_sound_channel[2].wave_volume = 12288; \
} \
else \
{ \
gbc_sound_channel[2].wave_volume = \
gbc_sound_wave_volume[(value >> 13) & 0x03]; \
} \
gbc_sound_update = 1; \
address16(io_registers, 0x72) = value; \
} \
#define gbc_sound_tone_control_high_wave() \
{ \
u32 rate = value & 0x7FF; \
gbc_sound_channel[2].rate = rate; \
gbc_sound_channel[2].frequency_step = \
float_to_fp16_16((2097152.0 / (2048 - rate)) / sound_frequency); \
gbc_sound_channel[2].length_status = (value >> 14) & 0x01; \
if(value & 0x8000) \
{ \
gbc_sound_channel[2].sample_index = 0; \
gbc_sound_channel[2].active_flag = 1; \
} \
gbc_sound_update = 1; \
address16(io_registers, 0x74) = value; \
} \
#define gbc_sound_noise_control() \
{ \
u32 dividing_ratio = value & 0x07; \
u32 frequency_shift = (value >> 4) & 0x0F; \
if(dividing_ratio == 0) \
{ \
gbc_sound_channel[3].frequency_step = \
float_to_fp16_16(1048576.0 / (1 << (frequency_shift + 1)) / \
sound_frequency); \
} \
else \
{ \
gbc_sound_channel[3].frequency_step = \
float_to_fp16_16(524288.0 / (dividing_ratio * \
(1 << (frequency_shift + 1))) / sound_frequency); \
} \
gbc_sound_channel[3].noise_type = (value >> 3) & 0x01; \
gbc_sound_channel[3].length_status = (value >> 14) & 0x01; \
if(value & 0x8000) \
{ \
gbc_sound_channel[3].sample_index = 0; \
gbc_sound_channel[3].active_flag = 1; \
gbc_sound_channel[3].envelope_ticks = \
gbc_sound_channel[3].envelope_initial_ticks; \
gbc_sound_channel[3].envelope_volume = \
gbc_sound_channel[3].envelope_initial_volume; \
} \
gbc_sound_update = 1; \
address16(io_registers, 0x7C) = value; \
} \
#define gbc_trigger_sound_channel(channel) \
gbc_sound_master_volume_right = value & 0x07; \
gbc_sound_master_volume_left = (value >> 4) & 0x07; \
gbc_sound_channel[channel].status = ((value >> (channel + 8)) & 0x01) | \
((value >> (channel + 11)) & 0x03) \
#define gbc_trigger_sound() \
{ \
gbc_trigger_sound_channel(0); \
gbc_trigger_sound_channel(1); \
gbc_trigger_sound_channel(2); \
gbc_trigger_sound_channel(3); \
address16(io_registers, 0x80) = value; \
} \
#define trigger_sound() \
{ \
timer[0].direct_sound_channels = (((value >> 10) & 0x01) == 0) | \
((((value >> 14) & 0x01) == 0) << 1); \
timer[1].direct_sound_channels = (((value >> 10) & 0x01) == 1) | \
((((value >> 14) & 0x01) == 1) << 1); \
direct_sound_channel[0].volume = (value >> 2) & 0x01; \
direct_sound_channel[0].status = (value >> 8) & 0x03; \
direct_sound_channel[1].volume = (value >> 3) & 0x01; \
direct_sound_channel[1].status = (value >> 12) & 0x03; \
gbc_sound_master_volume = value & 0x03; \
\
if((value >> 11) & 0x01) \
sound_reset_fifo(0); \
if((value >> 15) & 0x01) \
sound_reset_fifo(1); \
address16(io_registers, 0x82) = value; \
} \
#define sound_on() \
if(value & 0x80) \
{ \
if(sound_on != 1) \
{ \
sound_on = 1; \
} \
} \
else \
{ \
u32 i; \
for(i = 0; i < 4; i++) \
{ \
gbc_sound_channel[i].active_flag = 0; \
} \
sound_on = 0; \
} \
address16(io_registers, 0x84) = \
(address16(io_registers, 0x84) & 0x000F) | (value & 0xFFF0); \
#define sound_update_frequency_step(timer_number) \
timer[timer_number].frequency_step = \
float_to_fp8_24(GBC_BASE_RATE / (timer_reload * sound_frequency)) \
#endif // IN_MEMORY_C
void reset_sound();
void sound_exit();
#endif
|