1 /***************************************************************************
2 
3   Capcom System QSound(tm)
4   ========================
5 
6   Driver by Paul Leaman (paul@vortexcomputing.demon.co.uk)
7 		and Miguel Angel Horna (mahorna@teleline.es)
8 
9   A 16 channel stereo sample player.
10 
11   QSpace position is simulated by panning the sound in the stereo space.
12 
13   Register
14   0	 xxbb	xx = unknown bb = start high address
15   1	 ssss	ssss = sample start address
16   2	 pitch
17   3	 unknown (always 0x8000)
18   4	 loop offset from end address
19   5	 end
20   6	 master channel volume
21   7	 not used
22   8	 Balance (left=0x0110  centre=0x0120 right=0x0130)
23   9	 unknown (most fixed samples use 0 for this register)
24 
25   Many thanks to CAB (the author of Amuse), without whom this probably would
26   never have been finished.
27 
28   If anybody has some information about this hardware, please send it to me
29   to mahorna@teleline.es or 432937@cepsz.unizar.es.
30   http://teleline.terra.es/personal/mahorna
31 
32 ***************************************************************************/
33 
34 #include <math.h>
35 #include "driver.h"
36 
37 /*
38 Two Q sound drivers:
39 DRIVER1 Based on the Amuse source
40 DRIVER2 Miguel Angel Horna (mahorna@teleline.es)
41 */
42 #define QSOUND_DRIVER1	  1
43 /*
44 I don't know whether this system uses 8 bit or 16 bit samples.
45 If it uses 16 bit samples then the sample ROM loading macros need
46 to be modified to work with non-intel machines.
47 */
48 #define QSOUND_8BIT_SAMPLES 1
49 
50 /*
51 Debug defines
52 */
53 #define LOG_WAVE	0
54 #define LOG_QSOUND  0
55 
56 /* Typedefs & defines */
57 
58 #define QSOUND_DRIVER2 !QSOUND_DRIVER1
59 
60 #if QSOUND_8BIT_SAMPLES
61 /* 8 bit source ROM samples */
62 typedef signed char QSOUND_SRC_SAMPLE;
63 #define LENGTH_DIV 1
64 #else
65 /* 8 bit source ROM samples */
66 typedef signed short QSOUND_SRC_SAMPLE;
67 #define LENGTH_DIV 2
68 #endif
69 
70 #define QSOUND_CLOCKDIV 166			 /* Clock divider */
71 #define QSOUND_CHANNELS 16
72 typedef INT16 QSOUND_SAMPLE;
73 
74 struct QSOUND_CHANNEL
75 {
76 	int bank;	   /* bank (x16)	*/
77 	int address;	/* start address */
78 	int pitch;	  /* pitch */
79 	int reg3;	   /* unknown (always 0x8000) */
80 	int loop;	   /* loop address */
81 	int end;		/* end address */
82 	int vol;		/* master volume */
83 	int pan;		/* Pan value */
84 	int reg9;	   /* unknown */
85 
86 	/* Work variables */
87 	int key;		/* Key on / key off */
88 
89 #if QSOUND_DRIVER1
90 	int lvol;	   /* left volume */
91 	int rvol;	   /* right volume */
92 	int lastdt;	 /* last sample value */
93 	int offset;	 /* current offset counter */
94 #else
95 	QSOUND_SRC_SAMPLE *buffer;
96 	int factor;		   /*step factor (fixed point 8-bit)*/
97 	int mixl,mixr;		/*mixing factor (fixed point)*/
98 	int cursor;		   /*current sample position (fixed point)*/
99 	int lpos;			 /*last cursor pos*/
100 	int lastsaml;		 /*last left sample (to avoid any calculation)*/
101 	int lastsamr;		 /*last right sample*/
102 #endif
103 };
104 
105 
106 /* Private variables */
107 static struct QSound_interface *intf;	/* Interface  */
108 static int qsound_stream;				/* Audio stream */
109 static struct QSOUND_CHANNEL qsound_channel[QSOUND_CHANNELS];
110 static int qsound_data;				  /* register latch data */
111 QSOUND_SRC_SAMPLE *qsound_sample_rom;	/* Q sound sample ROM */
112 
113 #if QSOUND_DRIVER1
114 static int qsound_pan_table[33];		 /* Pan volume table */
115 static float qsound_frq_ratio;		   /* Frequency ratio */
116 #endif
117 
118 #if LOG_WAVE
119 static FILE *fpRawDataL;
120 static FILE *fpRawDataR;
121 #endif
122 
123 /* Function prototypes */
124 void qsound_update( int num, INT16 **buffer, int length );
125 void qsound_set_command(int data, int value);
126 
127 #if QSOUND_DRIVER2
128 void setchannel(int channel,signed short *buffer,int length,int vol,int pan);
129 void setchloop(int channel,int loops,int loope);
130 void stopchan(int channel);
131 void calcula_mix(int channel);
132 #endif
133 
qsound_sh_start(const struct MachineSound * msound)134 int qsound_sh_start(const struct MachineSound *msound)
135 {
136 	int i;
137 
138 	if (Machine->sample_rate == 0) return 0;
139 
140 	intf = msound->sound_interface;
141 
142 	qsound_sample_rom = (QSOUND_SRC_SAMPLE *)memory_region(intf->region);
143 
144 	memset(qsound_channel, 0, sizeof(qsound_channel));
145 
146 #if QSOUND_DRIVER1
147 	qsound_frq_ratio = ((float)intf->clock / (float)QSOUND_CLOCKDIV) /
148 						(float) Machine->sample_rate;
149 	qsound_frq_ratio *= 16.0;
150 
151 	/* Create pan table */
152 	for (i=0; i<33; i++)
153 	{
154 		qsound_pan_table[i]=(int)((256/sqrt(32)) * sqrt(i));
155 	}
156 #else
157 	i=0;
158 #endif
159 
160 #if LOG_QSOUND
161 	log_cb(RETRO_LOG_DEBUG, LOGPRE "Pan table\n");
162 	for (i=0; i<33; i++)
163 		log_cb(RETRO_LOG_DEBUG, LOGPRE "%02x ", qsound_pan_table[i]);
164 #endif
165 	{
166 		/* Allocate stream */
167 #define CHANNELS ( 2 )
168 		char buf[CHANNELS][40];
169 		const char *name[CHANNELS];
170 		int  vol[2];
171 		name[0] = buf[0];
172 		name[1] = buf[1];
173 		sprintf( buf[0], "%s L", sound_name(msound) );
174 		sprintf( buf[1], "%s R", sound_name(msound) );
175 		vol[0]=MIXER(intf->mixing_level[0], MIXER_PAN_LEFT);
176 		vol[1]=MIXER(intf->mixing_level[1], MIXER_PAN_RIGHT);
177 		qsound_stream = stream_init_multi(
178 			CHANNELS,
179 			name,
180 			vol,
181 			Machine->sample_rate,
182 			0,
183 			qsound_update );
184 	}
185 
186 #if LOG_WAVE
187 	fpRawDataR=fopen("qsoundr.raw", "w+b");
188 	fpRawDataL=fopen("qsoundl.raw", "w+b");
189 	if (!fpRawDataR || !fpRawDataL)
190 	{
191 		return 1;
192 	}
193 #endif
194 
195 	return 0;
196 }
197 
qsound_sh_stop(void)198 void qsound_sh_stop (void)
199 {
200 	if (Machine->sample_rate == 0) return;
201 #if LOG_WAVE
202 	if (fpRawDataR)
203 	{
204 		fclose(fpRawDataR);
205 	}
206 	if (fpRawDataL)
207 	{
208 		fclose(fpRawDataL);
209 	}
210 #endif
211 }
212 
WRITE_HANDLER(qsound_data_h_w)213 WRITE_HANDLER( qsound_data_h_w )
214 {
215 	qsound_data=(qsound_data&0xff)|(data<<8);
216 }
217 
WRITE_HANDLER(qsound_data_l_w)218 WRITE_HANDLER( qsound_data_l_w )
219 {
220 	qsound_data=(qsound_data&0xff00)|data;
221 }
222 
WRITE_HANDLER(qsound_cmd_w)223 WRITE_HANDLER( qsound_cmd_w )
224 {
225 	qsound_set_command(data, qsound_data);
226 }
227 
READ_HANDLER(qsound_status_r)228 READ_HANDLER( qsound_status_r )
229 {
230 	/* Port ready bit (0x80 if ready) */
231 	return 0x80;
232 }
233 
qsound_set_command(int data,int value)234 void qsound_set_command(int data, int value)
235 {
236 	int ch=0,reg=0;
237 	if (data < 0x80)
238 	{
239 		ch=data>>3;
240 		reg=data & 0x07;
241 	}
242 	else
243 	{
244 		if (data < 0x90)
245 		{
246 			ch=data-0x80;
247 			reg=8;
248 		}
249 		else
250 		{
251 			if (data >= 0xba && data < 0xca)
252 			{
253 				ch=data-0xba;
254 				reg=9;
255 			}
256 			else
257 			{
258 				/* Unknown registers */
259 				ch=99;
260 				reg=99;
261 			}
262 		}
263 	}
264 
265 	switch (reg)
266 	{
267 		case 0: /* Bank */
268 			ch=(ch+1)&0x0f;	/* strange ... */
269 			qsound_channel[ch].bank=(value&0x7f)<<16;
270 			qsound_channel[ch].bank /= LENGTH_DIV;
271 #ifdef MAME_DEBUG
272 			if (!value & 0x8000)
273 				usrintf_showmessage("Register3=%04x",value);
274 #endif
275 
276 			break;
277 		case 1: /* start */
278 			qsound_channel[ch].address=value;
279 			qsound_channel[ch].address/=LENGTH_DIV;
280 			break;
281 		case 2: /* pitch */
282 #if QSOUND_DRIVER1
283 			qsound_channel[ch].pitch=(long)
284 					((float)value * qsound_frq_ratio );
285 			qsound_channel[ch].pitch/=LENGTH_DIV;
286 #else
287 			qsound_channel[ch].factor=((float) (value*(6/LENGTH_DIV)) /
288 									  (float) Machine->sample_rate)*256.0;
289 
290 #endif
291 			if (!value)
292 			{
293 				/* Key off */
294 				qsound_channel[ch].key=0;
295 			}
296 			break;
297 		case 3: /* unknown */
298 			qsound_channel[ch].reg3=value;
299 #ifdef MAME_DEBUG
300 			if (value != 0x8000)
301 				usrintf_showmessage("Register3=%04x",value);
302 #endif
303 			break;
304 		case 4: /* loop offset */
305 			qsound_channel[ch].loop=value/LENGTH_DIV;
306 			break;
307 		case 5: /* end */
308 			qsound_channel[ch].end=value/LENGTH_DIV;
309 			break;
310 		case 6: /* master volume */
311 			if (value==0)
312 			{
313 				/* Key off */
314 				qsound_channel[ch].key=0;
315 			}
316 			else if (qsound_channel[ch].key==0)
317 			{
318 				/* Key on */
319 				qsound_channel[ch].key=1;
320 #if QSOUND_DRIVER1
321 				qsound_channel[ch].offset=0;
322 				qsound_channel[ch].lastdt=0;
323 #else
324 				qsound_channel[ch].cursor=qsound_channel[ch].address <<8 ;
325 				qsound_channel[ch].buffer=qsound_sample_rom+
326 										 qsound_channel[ch].bank;
327 #endif
328 			}
329 			qsound_channel[ch].vol=value;
330 #if QSOUND_DRIVER2
331 			calcula_mix(ch);
332 #endif
333 			break;
334 
335 		case 7:  /* unused */
336 #ifdef MAME_DEBUG
337 				usrintf_showmessage("UNUSED QSOUND REG 7=%04x",value);
338 #endif
339 
340 			break;
341 		case 8:
342 			{
343 #if QSOUND_DRIVER1
344 			   int pandata=(value-0x10)&0x3f;
345 			   if (pandata > 32)
346 			   {
347 					pandata=32;
348 			   }
349 			   qsound_channel[ch].rvol=qsound_pan_table[pandata];
350 			   qsound_channel[ch].lvol=qsound_pan_table[32-pandata];
351 #endif
352 			   qsound_channel[ch].pan = value;
353 #if QSOUND_DRIVER2
354 			   calcula_mix(ch);
355 #endif
356 			}
357 			break;
358 		 case 9:
359 			qsound_channel[ch].reg9=value;
360 /*
361 #ifdef MAME_DEBUG
362 			usrintf_showmessage("QSOUND REG 9=%04x",value);
363 #endif
364 */
365 			break;
366 	}
367 #if LOG_QSOUND
368 	log_cb(RETRO_LOG_DEBUG, LOGPRE "QSOUND WRITE %02x CH%02d-R%02d =%04x\n", data, ch, reg, value);
369 #endif
370 }
371 
372 #if QSOUND_DRIVER1
373 
374 /* Driver 1 - based on the Amuse source */
375 
qsound_update(int num,INT16 ** buffer,int length)376 void qsound_update( int num, INT16 **buffer, int length )
377 {
378 	int i,j;
379 	int rvol, lvol, count;
380 	struct QSOUND_CHANNEL *pC=&qsound_channel[0];
381 	QSOUND_SRC_SAMPLE * pST;
382 	QSOUND_SAMPLE  *datap[2];
383 
384 	if (Machine->sample_rate == 0) return;
385 
386 	datap[0] = buffer[0];
387 	datap[1] = buffer[1];
388 	memset( datap[0], 0x00, length * sizeof(QSOUND_SAMPLE) );
389 	memset( datap[1], 0x00, length * sizeof(QSOUND_SAMPLE) );
390 
391 	for (i=0; i<QSOUND_CHANNELS; i++)
392 	{
393 		if (pC->key)
394 		{
395 			QSOUND_SAMPLE *pOutL=datap[0];
396 			QSOUND_SAMPLE *pOutR=datap[1];
397 			pST=qsound_sample_rom+pC->bank;
398 			rvol=(pC->rvol*pC->vol)>>(8*LENGTH_DIV);
399 			lvol=(pC->lvol*pC->vol)>>(8*LENGTH_DIV);
400 
401 			for (j=length-1; j>=0; j--)
402 			{
403 				count=(pC->offset)>>16;
404 				pC->offset &= 0xffff;
405 				if (count)
406 				{
407 					pC->address += count;
408 					if (pC->address >= pC->end)
409 					{
410 						if (!pC->loop)
411 						{
412 							/* Reached the end of a non-looped sample */
413 							pC->key=0;
414 							break;
415 						}
416 						/* Reached the end, restart the loop */
417 						pC->address = (pC->end - pC->loop) & 0xffff;
418 					}
419 					pC->lastdt=pST[pC->address];
420 				}
421 
422 				(*pOutL) += ((pC->lastdt * lvol) >> 6);
423 				(*pOutR) += ((pC->lastdt * rvol) >> 6);
424 				pOutL++;
425 				pOutR++;
426 				pC->offset += pC->pitch;
427 			}
428 		}
429 		pC++;
430 	}
431 
432 #if LOG_WAVE
433 	fwrite(datap[0], length*sizeof(QSOUND_SAMPLE), 1, fpRawDataL);
434 	fwrite(datap[1], length*sizeof(QSOUND_SAMPLE), 1, fpRawDataR);
435 #endif
436 }
437 
438 #else
439 
440 /* ----------------------------------------------------------------
441 		QSound Sample Mixer (Slow)
442 		Miguel Angel Horna mahorna@teleline.es
443 
444  ------------------------------------------------------------------ */
445 
446 
calcula_mix(int channel)447 void calcula_mix(int channel)
448 {
449 	int factl,factr;
450 	struct QSOUND_CHANNEL *pC=&qsound_channel[channel];
451 	int vol=pC->vol>>5;
452 	int pan=((pC->pan&0xFF)-0x10)<<3;
453 	pC->mixl=vol;
454 	pC->mixr=vol;
455 	factr=pan;
456 	factl=255-factr;
457 	pC->mixl=(pC->mixl * factl)>>8;
458 	pC->mixr=(pC->mixr * factr)>>8;
459 #if QSOUND_8BIT_SAMPLES
460 	pC->mixl<<=8;
461 	pC->mixr<<=8;
462 #endif
463 }
464 
qsound_update(int num,void ** buffer,int length)465 void qsound_update(int num,void **buffer,int length)
466 {
467 	int i,j;
468 	QSOUND_SAMPLE *bufL,*bufR, sample;
469 	struct QSOUND_CHANNEL *pC=qsound_channel;
470 
471 	memset( buffer[0], 0x00, length * sizeof(QSOUND_SAMPLE) );
472 	memset( buffer[1], 0x00, length * sizeof(QSOUND_SAMPLE) );
473 
474 	for(j=0;j<QSOUND_CHANNELS;++j)
475 	{
476 		  bufL=(QSOUND_SAMPLE *) buffer[0];
477 		  bufR=(QSOUND_SAMPLE *) buffer[1];
478 		  if(pC->key)
479 		  {
480 				for(i=0;i<length;++i)
481 				{
482 						   int pos=pC->cursor>>8;
483 						   if(pos!=pC->lpos)	/*next sample*/
484 						   {
485 								sample=pC->buffer[pos];
486 								pC->lastsaml=(sample*pC->mixl)>>8;
487 								pC->lastsamr=(sample*pC->mixr)>>8;
488 								pC->lpos=pos;
489 						   }
490 						   (*bufL++)+=pC->lastsaml;
491 						   (*bufR++)+=pC->lastsamr;
492 						   pC->cursor+=pC->factor;
493 						   if(pC->loop && (pC->cursor>>8) > pC->end)
494 						   {
495 								 pC->cursor=(pC->end-pC->loop)<<8;
496 						   }
497 						   else if((pC->cursor>>8) > pC->end)
498 								   pC->key=0;
499 				 }
500 		  }
501 		  pC++;
502 	 }
503 }
504 #endif
505 
506 
507 /**************** end of file ****************/
508