1 /***************************************************************************
2
3 Wiping sound driver (quick hack of the Namco sound driver)
4
5 used by wiping and clshroad
6
7 ***************************************************************************/
8
9 #include "burnint.h"
10 #include "wiping.h"
11 #include <stddef.h>
12
13 static const INT32 samplerate = 48000;
14 static const INT32 defgain = 48;
15
16 /* 8 voices max */
17 #define MAX_VOICES 8
18
19 /* this structure defines the parameters for a channel */
20 struct sound_channel
21 {
22 INT32 frequency;
23 INT32 counter;
24 INT32 volume;
25 INT32 oneshot;
26 INT32 oneshotplaying;
27 const UINT8 *wave;
28 };
29
30 /* data about the sound system */
31 static sound_channel m_channel_list[MAX_VOICES];
32 static sound_channel *m_last_channel;
33
34 /* global sound parameters */
35 static UINT8 *m_sound_prom;
36 static UINT8 *m_sound_rom;
37 static INT32 m_num_voices;
38
39 /* mixer tables and internal buffers */
40 static INT16 *m_mixer_table;
41 static INT16 *m_mixer_lookup;
42 static INT16 *m_mixer_buffer;
43
44 static INT32 game_is_wiping = 0;
45
46 static UINT8 m_soundregs[0x4000];
47
48 static void make_mixer_table(INT32 voices, INT32 gain);
49
wipingsnd_scan(INT32,INT32 *)50 void wipingsnd_scan(INT32 , INT32 *)
51 {
52 for (INT32 i = 0; i < MAX_VOICES; i++) {
53 struct BurnArea ba;
54 char szName[16];
55 sprintf(szName, "Wiping Ch#%d", i);
56
57 memset(&ba, 0, sizeof(ba));
58 ba.Data = &m_channel_list[i];
59 ba.nLen = STRUCT_SIZE_HELPER(struct sound_channel, oneshotplaying);
60 ba.szName = szName;
61 BurnAcb(&ba);
62 }
63
64 SCAN_VAR(m_soundregs);
65 }
66
wipingsnd_init(UINT8 * rom,UINT8 * prom)67 void wipingsnd_init(UINT8 *rom, UINT8 *prom)
68 {
69 m_sound_prom = prom;
70 m_sound_rom = rom;
71
72 m_mixer_buffer = (INT16 *)BurnMalloc(sizeof(INT16) * 2 * samplerate);
73
74 /* build the mixer table */
75 make_mixer_table(8, defgain);
76 wipingsnd_reset();
77 }
78
wipingsnd_wipingmode()79 void wipingsnd_wipingmode() // lowers the volume of "that noise"
80 {
81 game_is_wiping = 1;
82 }
83
wipingsnd_exit()84 void wipingsnd_exit()
85 {
86 BurnFree(m_mixer_buffer);
87 BurnFree(m_mixer_table);
88 game_is_wiping = 0;
89 }
90
wipingsnd_reset()91 void wipingsnd_reset()
92 {
93 memset(m_channel_list, 0, sizeof(sound_channel)*MAX_VOICES);
94 memset(m_soundregs, 0, sizeof(UINT8)*0x4000);
95
96 sound_channel *voice;
97
98 m_num_voices = 8;
99 m_last_channel = m_channel_list + m_num_voices;
100
101 /* reset all the voices */
102 for (voice = m_channel_list; voice < m_last_channel; voice++)
103 {
104 voice->frequency = 0;
105 voice->volume = 0;
106 voice->wave = &m_sound_prom[0];
107 voice->counter = 0;
108 }
109 }
110
111 /* build a table to divide by the number of voices; gain is specified as gain*16 */
make_mixer_table(INT32 voices,INT32 gain)112 static void make_mixer_table(INT32 voices, INT32 gain)
113 {
114 INT32 count = voices * 128;
115
116 /* allocate memory */
117 m_mixer_table = (INT16 *)BurnMalloc(sizeof(INT16) * 256 * voices);
118
119 /* find the middle of the table */
120 m_mixer_lookup = m_mixer_table + (128 * voices);
121
122 /* fill in the table - 16 bit case */
123 for (INT32 i = 0; i < count; i++)
124 {
125 INT32 val = i * gain * 16 / voices;
126 if (val > 32767) val = 32767;
127 m_mixer_lookup[ i] = val;
128 m_mixer_lookup[-i] = -val;
129 }
130 }
131
wipingsnd_write(INT32 offset,UINT8 data)132 void wipingsnd_write(INT32 offset, UINT8 data)
133 {
134 sound_channel *voice;
135 INT32 base;
136
137 offset &= 0x3fff;
138
139 /* set the register */
140 m_soundregs[offset] = data;
141
142 /* recompute all the voice parameters */
143 if (offset <= 0x3f)
144 {
145 for (base = 0, voice = m_channel_list; voice < m_last_channel; voice++, base += 8)
146 {
147 voice->frequency = m_soundregs[0x02 + base] & 0x0f;
148 voice->frequency = voice->frequency * 16 + ((m_soundregs[0x01 + base]) & 0x0f);
149 voice->frequency = voice->frequency * 16 + ((m_soundregs[0x00 + base]) & 0x0f);
150
151 voice->volume = m_soundregs[0x07 + base] & 0x0f;
152
153 if (m_soundregs[0x5 + base] & 0x0f)
154 {
155 // hack :)
156 if (128 * (16 * (m_soundregs[0x5 + base] & 0x0f) + (m_soundregs[0x2005 + base] & 0x0f)) == 0x1800
157 && game_is_wiping) voice->volume /= 3;
158 // end of hack
159
160 voice->wave = &m_sound_rom[128 * (16 * (m_soundregs[0x5 + base] & 0x0f)
161 + (m_soundregs[0x2005 + base] & 0x0f))];
162 voice->oneshot = 1;
163 }
164 else
165 {
166 voice->wave = &m_sound_rom[16 * (m_soundregs[0x3 + base] & 0x0f)];
167 voice->oneshot = 0;
168 voice->oneshotplaying = 0;
169 }
170 }
171 }
172 else if (offset >= 0x2000)
173 {
174 voice = &m_channel_list[(offset & 0x3f)/8];
175 if (voice->oneshot)
176 {
177 voice->counter = 0;
178 voice->oneshotplaying = 1;
179 }
180 }
181 }
182
wipingsnd_update(INT16 * outputs,INT32 samples_len)183 void wipingsnd_update(INT16 *outputs, INT32 samples_len)
184 {
185 sound_channel *voice;
186
187 // compute # of samples @ soundcore native (48khz) rate
188 INT32 samples = (((((samplerate*1000) / nBurnFPS) * samples_len) / nBurnSoundLen)) / 10;
189
190 if (samples > samplerate) samples = samplerate;
191
192 /* zap the contents of the mixer buffer */
193 memset(m_mixer_buffer, 0, sizeof(INT16) * 2 * samplerate);
194
195 /* loop over each voice and add its contribution */
196 for (voice = m_channel_list; voice < m_last_channel; voice++)
197 {
198 INT32 f = 16*voice->frequency;
199 INT32 v = voice->volume;
200
201 /* only update if we have non-zero volume and frequency */
202 if (v && f)
203 {
204 const UINT8 *w = voice->wave;
205 INT32 c = voice->counter;
206
207 INT16 *mix = m_mixer_buffer;
208
209 /* add our contribution */
210 for (INT32 i = 0; i < samples; i++)
211 {
212 INT32 offs;
213
214 c += f;
215
216 if (voice->oneshot)
217 {
218 if (voice->oneshotplaying)
219 {
220 offs = (c >> 15);
221 if (w[offs>>1] == 0xff)
222 {
223 voice->oneshotplaying = 0;
224 }
225
226 else
227 {
228 /* use full byte, first the high 4 bits, then the low 4 bits */
229 if (offs & 1)
230 *mix++ += ((w[offs>>1] & 0x0f) - 8) * v;
231 else
232 *mix++ += (((w[offs>>1]>>4) & 0x0f) - 8) * v;
233 }
234 }
235 }
236 else
237 {
238 offs = (c >> 15) & 0x1f;
239
240 /* use full byte, first the high 4 bits, then the low 4 bits */
241 if (offs & 1)
242 *mix++ += ((w[offs>>1] & 0x0f) - 8) * v;
243 else
244 *mix++ += (((w[offs>>1]>>4) & 0x0f) - 8) * v;
245 }
246 }
247
248 /* update the counter for this voice */
249 voice->counter = c;
250 }
251 }
252
253 // resample native (48khz) -> our rate
254 for (INT32 j = 0; j < samples_len; j++)
255 {
256 INT32 k = (((((samplerate*1000) / nBurnFPS) * j) / nBurnSoundLen)) / 10;
257
258 INT32 lr = (INT32)(m_mixer_lookup[m_mixer_buffer[k]] * 0.50);
259
260 outputs[0] = BURN_SND_CLIP(lr);
261 outputs[1] = BURN_SND_CLIP(lr);
262 outputs += 2;
263 }
264
265 }
266