1 /* Clarue Flower sound driver.
2 Initial version was based on the Wiping sound driver, which was based on the old namco.c sound driver.
3 */
4 
5 #include "burnint.h"
6 #include "flower.h"
7 #include <stddef.h>
8 
9 static const INT32 samplerate = 48000;
10 
11 struct flower_sound_channel
12 {
13 	UINT32 start;
14 	UINT32 pos;
15 	UINT16 freq;
16 	UINT8 volume;
17 	UINT8 voltab;
18 	UINT8 oneshot;
19 	UINT8 active;
20 	UINT8 effect;
21 	UINT32 ecount;
22 };
23 
24 static flower_sound_channel m_channel_list[8];
25 static flower_sound_channel *m_last_channel;
26 
27 /* global sound parameters */
28 static const UINT8 *m_sample_rom;
29 static const UINT8 *m_volume_rom;
30 
31 /* mixer tables and internal buffers */
32 static INT16 *m_mixer_table;
33 static INT16 *m_mixer_lookup;
34 static INT16 *m_mixer_buffer;
35 
36 static UINT8 m_soundregs1[0x40];
37 static UINT8 m_soundregs2[0x40];
38 
39 /* build a table to divide by the number of voices; gain is specified as gain*16 */
make_mixer_table(INT32 voices,INT32 gain)40 static void make_mixer_table(INT32 voices, INT32 gain)
41 {
42 	INT32 count = voices * 128;
43 
44 	/* allocate memory */
45 	m_mixer_table = (INT16 *)BurnMalloc(sizeof(INT16) * 256 * voices);
46 
47 	/* find the middle of the table */
48 	m_mixer_lookup = m_mixer_table + (128 * voices);
49 
50 	/* fill in the table - 16 bit case */
51 	for (INT32 i = 0; i < count; i++)
52 	{
53 		INT32 val = i * gain * 16 / voices;
54 		if (val > 32767) val = 32767;
55 		m_mixer_lookup[ i] = val;
56 		m_mixer_lookup[-i] =-val;
57 	}
58 }
59 
60 //-------------------------------------------------
61 //  device_start - device-specific startup
62 //-------------------------------------------------
63 
flower_sound_init(UINT8 * rom_sample,UINT8 * rom_volume)64 void flower_sound_init(UINT8 *rom_sample, UINT8 *rom_volume)
65 {
66 	m_mixer_buffer = (INT16 *)BurnMalloc(sizeof(INT16) * 2 * samplerate);
67 	make_mixer_table(8, 48);
68 
69 	m_sample_rom = rom_sample;
70 	m_volume_rom = rom_volume;
71 
72 	m_last_channel = m_channel_list + 8;
73 }
74 
flower_sound_exit()75 void flower_sound_exit()
76 {
77 	BurnFree(m_mixer_buffer);
78 	BurnFree(m_mixer_table);
79 }
80 
flower_sound_scan()81 void flower_sound_scan()
82 {
83 	SCAN_VAR(m_channel_list);
84 	SCAN_VAR(m_soundregs1);
85 	SCAN_VAR(m_soundregs2);
86 }
87 
88 //-------------------------------------------------
89 //  device_reset - device-specific reset
90 //-------------------------------------------------
91 
flower_sound_reset()92 void flower_sound_reset()
93 {
94 	/* reset all the voices */
95 	for (INT32 i = 0; i < 8; i++)
96 	{
97 		flower_sound_channel *voice = &m_channel_list[i];
98 
99 		voice->freq = 0;
100 		voice->pos = 0;
101 		voice->volume = 0;
102 		voice->voltab = 0;
103 		voice->effect = 0;
104 		voice->ecount = 0;
105 		voice->oneshot = 1;
106 		voice->active = 0;
107 		voice->start = 0;
108 	}
109 
110 	memset(m_soundregs1, 0, 0x40);
111 	memset(m_soundregs2, 0, 0x40);
112 }
113 
114 /********************************************************************************/
115 /* register functions (preliminary):
116 offset: cccrrr      c=channel, r=register
117 
118 set 1:
119 R  76543210
120 0  xxxxxxxx         frequency (which nibble?)
121 1  xxxxxxxx         *
122 2  xxxxxxxx         *
123 3  xxxxxxxx         *
124 4  ...x....         one-shot sample
125 5  ...x....         ??? same as R4?
126 6  ........         unused
127 7  xxxx....         volume
128 
129 set 2:
130 R  76543210
131 0  ....xxxx         start address
132 1  ....xxxx         *
133 2  ....xxxx         *
134 3  ....xxxx         *
135 4  xxxx             assume it's channel pitch/volume effects
136        xxxx         start address
137 5  x...             ???
138        xxxx         start address
139 6  ........         unused
140 7  ......xx         volume table + start trigger
141 
142 */
143 
flower_sound1_w(UINT16 offset,UINT8 data)144 void flower_sound1_w(UINT16 offset, UINT8 data)
145 {
146 	flower_sound_channel *voice = &m_channel_list[offset >> 3 & 7];
147 	INT32 c = offset & 0xf8;
148 	UINT8 *base1 = m_soundregs1;
149 
150 	base1[offset] = data;
151 
152 	// recompute voice parameters
153 	voice->freq = (base1[c+2] & 0xf) << 12 | (base1[c+3] & 0xf) << 8 | (base1[c+0] & 0xf) << 4 | (base1[c+1] & 0xf);
154 	voice->volume = base1[c+7] >> 4;
155 }
156 
flower_sound2_w(UINT16 offset,UINT8 data)157 void flower_sound2_w(UINT16 offset, UINT8 data)
158 {
159 	flower_sound_channel *voice = &m_channel_list[offset >> 3 & 7];
160 	INT32 i, c = offset & 0xf8;
161 	UINT8 *base1 = m_soundregs1;
162 	UINT8 *base2 = m_soundregs2;
163 
164 	base2[offset] = data;
165 
166 	// reg 7 is start trigger!
167 	if ((offset & 7) != 7)
168 		return;
169 
170 	voice->voltab = (base2[c+7] & 3) << 4;
171 	voice->oneshot = (~base1[c+4] & 0x10) >> 4;
172 	voice->effect = base2[c+4] >> 4;
173 	voice->ecount = 0;
174 	voice->pos = 0;
175 	voice->active = 1;
176 
177 	// full start address is 6 nibbles
178 	voice->start = 0;
179 	for (i = 5; i >= 0; i--)
180 		voice->start = (voice->start << 4) | (base2[c+i] & 0xf);
181 }
182 
update_effects()183 static void update_effects()
184 {
185 	flower_sound_channel *voice;
186 
187 	for (voice = m_channel_list; voice < m_last_channel; voice++)
188 		voice->ecount += (voice->ecount < (1<<22));
189 }
190 
191 //-------------------------------------------------
192 //  sound_stream_update - handle a stream update
193 //-------------------------------------------------
194 
flower_sound_update(INT16 * outputs,INT32 samples_len)195 void flower_sound_update(INT16 *outputs, INT32 samples_len)
196 {
197 	INT16 *mix;
198 
199 	// compute # of samples @ soundcore native (48khz) rate
200 	INT32 samples = (((((samplerate*1000) / nBurnFPS) * samples_len) / nBurnSoundLen)) / 10;
201 
202 	if (samples > samplerate) samples = samplerate;
203 
204 	/* zap the contents of the mixer buffer */
205 	memset(m_mixer_buffer, 0, samples * sizeof(INT16));
206 
207 	update_effects(); // once per frame
208 
209 	/* loop over each voice and add its contribution */
210 	for (flower_sound_channel *voice = m_channel_list; voice < m_last_channel; voice++)
211 	{
212 		INT32 f = voice->freq;
213 		INT32 v = voice->volume;
214 
215 		if (!voice->active)
216 			continue;
217 
218 		// effects
219 		// bit 0: volume slide down?
220 		if (voice->effect & 1 && !voice->oneshot)
221 		{
222 			// note: one-shot samples are fixed volume
223 			v -= (voice->ecount >> 4);
224 			if (v < 0) v = 0;
225 		}
226 		// bit 1: used often, but hard to figure out what for
227 		// bit 2: probably pitch slide
228 		if (voice->effect & 4)
229 		{
230 			f -= (voice->ecount << 7);
231 			if (f < 0) f = 0;
232 		}
233 		// bit 3: not used much, maybe pitch slide the other way?
234 
235 		v |= voice->voltab;
236 
237 		mix = m_mixer_buffer;
238 
239 		for (INT32 i = 0; i < samples; i++)
240 		{
241 			// add sample
242 			if (voice->oneshot)
243 			{
244 				UINT8 sample = m_sample_rom[(voice->start + voice->pos) >> 7 & 0x7fff];
245 				if (sample == 0xff)
246 				{
247 					voice->active = 0;
248 					break;
249 				}
250 				else
251 					*mix++ += m_volume_rom[v << 8 | sample] - 0x80;
252 			}
253 			else
254 			{
255 				UINT8 sample = m_sample_rom[(voice->start >> 7 & 0x7e00) | (voice->pos >> 7 & 0x1ff)];
256 				*mix++ += m_volume_rom[v << 8 | sample] - 0x80;
257 			}
258 
259 			// update counter
260 			voice->pos += f;
261 		}
262 	}
263 
264 	// resample native (48khz) -> our rate
265 	for (INT32 j = 0; j < samples_len; j++)
266 	{
267 		INT32 k = (((((samplerate*1000) / nBurnFPS) * j) / nBurnSoundLen)) / 10;
268 
269 		INT32 lr = (INT32)(m_mixer_lookup[m_mixer_buffer[k]] * 0.50);
270 
271 		outputs[0] = BURN_SND_CLIP(lr);
272 		outputs[1] = BURN_SND_CLIP(lr);
273 		outputs += 2;
274 	}
275 
276 }
277