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