summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/sound/qsound.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/sound/qsound.c')
-rw-r--r--trunk/src/emu/sound/qsound.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/qsound.c b/trunk/src/emu/sound/qsound.c
new file mode 100644
index 00000000000..cc08776aca6
--- /dev/null
+++ b/trunk/src/emu/sound/qsound.c
@@ -0,0 +1,402 @@
+/***************************************************************************
+
+ Capcom System QSound(tm)
+ ========================
+
+ Driver by Paul Leaman (paul@vortexcomputing.demon.co.uk)
+ and Miguel Angel Horna (mahorna@teleline.es)
+
+ A 16 channel stereo sample player.
+
+ QSpace position is simulated by panning the sound in the stereo space.
+
+ Register
+ 0 xxbb xx = unknown bb = start high address
+ 1 ssss ssss = sample start address
+ 2 pitch
+ 3 unknown (always 0x8000)
+ 4 loop offset from end address
+ 5 end
+ 6 master channel volume
+ 7 not used
+ 8 Balance (left=0x0110 centre=0x0120 right=0x0130)
+ 9 unknown (most fixed samples use 0 for this register)
+
+ Many thanks to CAB (the author of Amuse), without whom this probably would
+ never have been finished.
+
+ If anybody has some information about this hardware, please send it to me
+ to mahorna@teleline.es or 432937@cepsz.unizar.es.
+ http://teleline.terra.es/personal/mahorna
+
+***************************************************************************/
+
+#include "emu.h"
+#include "qsound.h"
+
+/*
+Debug defines
+*/
+#define LOG_WAVE 0
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/* 8 bit source ROM samples */
+typedef INT8 QSOUND_SRC_SAMPLE;
+
+
+#define QSOUND_CLOCKDIV 166 /* Clock divider */
+#define QSOUND_CHANNELS 16
+typedef stream_sample_t QSOUND_SAMPLE;
+
+struct QSOUND_CHANNEL
+{
+ INT32 bank; /* bank (x16) */
+ INT32 address; /* start address */
+ INT32 pitch; /* pitch */
+ INT32 reg3; /* unknown (always 0x8000) */
+ INT32 loop; /* loop address */
+ INT32 end; /* end address */
+ INT32 vol; /* master volume */
+ INT32 pan; /* Pan value */
+ INT32 reg9; /* unknown */
+
+ /* Work variables */
+ INT32 key; /* Key on / key off */
+
+ INT32 lvol; /* left volume */
+ INT32 rvol; /* right volume */
+ INT32 lastdt; /* last sample value */
+ INT32 offset; /* current offset counter */
+};
+
+typedef struct _qsound_state qsound_state;
+struct _qsound_state
+{
+ /* Private variables */
+ sound_stream * stream; /* Audio stream */
+ struct QSOUND_CHANNEL channel[QSOUND_CHANNELS];
+ int data; /* register latch data */
+ QSOUND_SRC_SAMPLE *sample_rom; /* Q sound sample ROM */
+ UINT32 sample_rom_length;
+
+ int pan_table[33]; /* Pan volume table */
+ float frq_ratio; /* Frequency ratio */
+
+ FILE *fpRawDataL;
+ FILE *fpRawDataR;
+};
+
+INLINE qsound_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == QSOUND);
+ return (qsound_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+/* Function prototypes */
+static STREAM_UPDATE( qsound_update );
+static void qsound_set_command(qsound_state *chip, int data, int value);
+
+static DEVICE_START( qsound )
+{
+ qsound_state *chip = get_safe_token(device);
+ int i;
+
+ chip->sample_rom = (QSOUND_SRC_SAMPLE *)*device->region();
+ chip->sample_rom_length = device->region()->bytes();
+
+ memset(chip->channel, 0, sizeof(chip->channel));
+
+ chip->frq_ratio = 16.0;
+
+ /* Create pan table */
+ for (i=0; i<33; i++)
+ {
+ chip->pan_table[i]=(int)((256/sqrt(32.0)) * sqrt((double)i));
+ }
+
+ LOG(("Pan table\n"));
+ for (i=0; i<33; i++)
+ LOG(("%02x ", chip->pan_table[i]));
+
+ {
+ /* Allocate stream */
+ chip->stream = device->machine().sound().stream_alloc(
+ *device, 0, 2,
+ device->clock() / QSOUND_CLOCKDIV,
+ chip,
+ qsound_update );
+ }
+
+ if (LOG_WAVE)
+ {
+ chip->fpRawDataR=fopen("qsoundr.raw", "w+b");
+ chip->fpRawDataL=fopen("qsoundl.raw", "w+b");
+ }
+
+ /* state save */
+ for (i=0; i<QSOUND_CHANNELS; i++)
+ {
+ device->save_item(NAME(chip->channel[i].bank), i);
+ device->save_item(NAME(chip->channel[i].address), i);
+ device->save_item(NAME(chip->channel[i].pitch), i);
+ device->save_item(NAME(chip->channel[i].loop), i);
+ device->save_item(NAME(chip->channel[i].end), i);
+ device->save_item(NAME(chip->channel[i].vol), i);
+ device->save_item(NAME(chip->channel[i].pan), i);
+ device->save_item(NAME(chip->channel[i].key), i);
+ device->save_item(NAME(chip->channel[i].lvol), i);
+ device->save_item(NAME(chip->channel[i].rvol), i);
+ device->save_item(NAME(chip->channel[i].lastdt), i);
+ device->save_item(NAME(chip->channel[i].offset), i);
+ }
+}
+
+static DEVICE_STOP( qsound )
+{
+ qsound_state *chip = get_safe_token(device);
+ if (chip->fpRawDataR)
+ {
+ fclose(chip->fpRawDataR);
+ }
+ chip->fpRawDataR = NULL;
+ if (chip->fpRawDataL)
+ {
+ fclose(chip->fpRawDataL);
+ }
+ chip->fpRawDataL = NULL;
+}
+
+WRITE8_DEVICE_HANDLER( qsound_w )
+{
+ qsound_state *chip = get_safe_token(device);
+ switch (offset)
+ {
+ case 0:
+ chip->data=(chip->data&0xff)|(data<<8);
+ break;
+
+ case 1:
+ chip->data=(chip->data&0xff00)|data;
+ break;
+
+ case 2:
+ qsound_set_command(chip, data, chip->data);
+ break;
+
+ default:
+ logerror("%s: unexpected qsound write to offset %d == %02X\n", device->machine().describe_context(), offset, data);
+ break;
+ }
+}
+
+READ8_DEVICE_HANDLER( qsound_r )
+{
+ /* Port ready bit (0x80 if ready) */
+ return 0x80;
+}
+
+static void qsound_set_command(qsound_state *chip, int data, int value)
+{
+ int ch=0,reg=0;
+ if (data < 0x80)
+ {
+ ch=data>>3;
+ reg=data & 0x07;
+ }
+ else
+ {
+ if (data < 0x90)
+ {
+ ch=data-0x80;
+ reg=8;
+ }
+ else
+ {
+ if (data >= 0xba && data < 0xca)
+ {
+ ch=data-0xba;
+ reg=9;
+ }
+ else
+ {
+ /* Unknown registers */
+ ch=99;
+ reg=99;
+ }
+ }
+ }
+
+ switch (reg)
+ {
+ case 0: /* Bank */
+ ch=(ch+1)&0x0f; /* strange ... */
+ chip->channel[ch].bank=(value&0x7f)<<16;
+#ifdef MAME_DEBUG
+ if (!(value & 0x8000))
+ popmessage("Register3=%04x",value);
+#endif
+
+ break;
+ case 1: /* start */
+ chip->channel[ch].address=value;
+ break;
+ case 2: /* pitch */
+ chip->channel[ch].pitch=value * 16;
+ if (!value)
+ {
+ /* Key off */
+ chip->channel[ch].key=0;
+ }
+ break;
+ case 3: /* unknown */
+ chip->channel[ch].reg3=value;
+#ifdef MAME_DEBUG
+ if (value != 0x8000)
+ popmessage("Register3=%04x",value);
+#endif
+ break;
+ case 4: /* loop offset */
+ chip->channel[ch].loop=value;
+ break;
+ case 5: /* end */
+ chip->channel[ch].end=value;
+ break;
+ case 6: /* master volume */
+ if (value==0)
+ {
+ /* Key off */
+ chip->channel[ch].key=0;
+ }
+ else if (chip->channel[ch].key==0)
+ {
+ /* Key on */
+ chip->channel[ch].key=1;
+ chip->channel[ch].offset=0;
+ chip->channel[ch].lastdt=0;
+ }
+ chip->channel[ch].vol=value;
+ break;
+
+ case 7: /* unused */
+#ifdef MAME_DEBUG
+ popmessage("UNUSED QSOUND REG 7=%04x",value);
+#endif
+
+ break;
+ case 8:
+ {
+ int pandata=(value-0x10)&0x3f;
+ if (pandata > 32)
+ {
+ pandata=32;
+ }
+ chip->channel[ch].rvol=chip->pan_table[pandata];
+ chip->channel[ch].lvol=chip->pan_table[32-pandata];
+ chip->channel[ch].pan = value;
+ }
+ break;
+ case 9:
+ chip->channel[ch].reg9=value;
+/*
+#ifdef MAME_DEBUG
+ popmessage("QSOUND REG 9=%04x",value);
+#endif
+*/
+ break;
+ }
+ LOG(("QSOUND WRITE %02x CH%02d-R%02d =%04x\n", data, ch, reg, value));
+}
+
+
+static STREAM_UPDATE( qsound_update )
+{
+ qsound_state *chip = (qsound_state *)param;
+ int i,j;
+ int rvol, lvol, count;
+ struct QSOUND_CHANNEL *pC=&chip->channel[0];
+ stream_sample_t *datap[2];
+
+ datap[0] = outputs[0];
+ datap[1] = outputs[1];
+ memset( datap[0], 0x00, samples * sizeof(*datap[0]) );
+ memset( datap[1], 0x00, samples * sizeof(*datap[1]) );
+
+ for (i=0; i<QSOUND_CHANNELS; i++)
+ {
+ if (pC->key)
+ {
+ QSOUND_SAMPLE *pOutL=datap[0];
+ QSOUND_SAMPLE *pOutR=datap[1];
+ rvol=(pC->rvol*pC->vol)>>8;
+ lvol=(pC->lvol*pC->vol)>>8;
+
+ for (j=samples-1; j>=0; j--)
+ {
+ count=(pC->offset)>>16;
+ pC->offset &= 0xffff;
+ if (count)
+ {
+ pC->address += count;
+ if (pC->address >= pC->end)
+ {
+ if (!pC->loop)
+ {
+ /* Reached the end of a non-looped sample */
+ pC->key=0;
+ break;
+ }
+ /* Reached the end, restart the loop */
+ pC->address = (pC->end - pC->loop) & 0xffff;
+ }
+ pC->lastdt=chip->sample_rom[(pC->bank+pC->address)%(chip->sample_rom_length)];
+ }
+
+ (*pOutL) += ((pC->lastdt * lvol) >> 6);
+ (*pOutR) += ((pC->lastdt * rvol) >> 6);
+ pOutL++;
+ pOutR++;
+ pC->offset += pC->pitch;
+ }
+ }
+ pC++;
+ }
+
+ if (chip->fpRawDataL)
+ fwrite(datap[0], samples*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataL);
+ if (chip->fpRawDataR)
+ fwrite(datap[1], samples*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataR);
+}
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+DEVICE_GET_INFO( qsound )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(qsound_state); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( qsound ); break;
+ case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( qsound ); break;
+ case DEVINFO_FCT_RESET: /* Nothing */ break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Q-Sound"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Capcom custom"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}
+
+/**************** end of file ****************/
+
+DEFINE_LEGACY_SOUND_DEVICE(QSOUND, qsound);