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