diff options
Diffstat (limited to 'src/emu/sound/qsound.c')
-rw-r--r-- | src/emu/sound/qsound.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/emu/sound/qsound.c b/src/emu/sound/qsound.c new file mode 100644 index 00000000000..1eabe0759b0 --- /dev/null +++ b/src/emu/sound/qsound.c @@ -0,0 +1,411 @@ +/*************************************************************************** + + 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 <math.h> +#include "sndintrf.h" +#include "streams.h" +#include "qsound.h" + +/* +Debug defines +*/ +#define LOG_WAVE 0 +#define LOG_QSOUND 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 */ +}; + +struct qsound_info +{ + /* Private variables */ + const struct QSound_interface *intf; /* Interface */ + 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 */ + + #if LOG_WAVE + FILE *fpRawDataL; + FILE *fpRawDataR; + #endif +}; + +/* Function prototypes */ +void qsound_update( void *param, stream_sample_t **inputs, stream_sample_t **outputs, int length ); +void qsound_set_command(struct qsound_info *chip, int data, int value); + +static void *qsound_start(int sndindex, int clock, const void *config) +{ + struct qsound_info *chip; + int i; + + chip = auto_malloc(sizeof(*chip)); + memset(chip, 0, sizeof(chip)); + + chip->intf = config; + + chip->sample_rom = (QSOUND_SRC_SAMPLE *)memory_region(chip->intf->region); + chip->sample_rom_length = memory_region_length(chip->intf->region); + + 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)) * sqrt(i)); + } + +#if LOG_QSOUND + logerror("Pan table\n"); + for (i=0; i<33; i++) + logerror("%02x ", chip->pan_table[i]); +#endif + { + /* Allocate stream */ + chip->stream = stream_create( + 0, 2, + clock / QSOUND_CLOCKDIV, + chip, + qsound_update ); + } + +#if LOG_WAVE + chip->fpRawDataR=fopen("qsoundr.raw", "w+b"); + chip->fpRawDataL=fopen("qsoundl.raw", "w+b"); + if (!chip->fpRawDataR || !chip->fpRawDataL) + { + return NULL; + } +#endif + + /* state save */ + for (i=0; i<QSOUND_CHANNELS; i++) + { + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].bank); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].address); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].pitch); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].loop); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].end); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].vol); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].pan); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].key); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].lvol); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].rvol); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].lastdt); + state_save_register_item("QSound", sndindex*QSOUND_CHANNELS+i, chip->channel[i].offset); + } + + return chip; +} + +static void qsound_stop (void *_chip) +{ +#if LOG_WAVE + if (chip->fpRawDataR) + { + fclose(chip->fpRawDataR); + } + if (chip->fpRawDataL) + { + fclose(chip->fpRawDataL); + } +#endif +} + +WRITE8_HANDLER( qsound_data_h_w ) +{ + struct qsound_info *chip = sndti_token(SOUND_QSOUND, 0); + chip->data=(chip->data&0xff)|(data<<8); +} + +WRITE8_HANDLER( qsound_data_l_w ) +{ + struct qsound_info *chip = sndti_token(SOUND_QSOUND, 0); + chip->data=(chip->data&0xff00)|data; +} + +WRITE8_HANDLER( qsound_cmd_w ) +{ + struct qsound_info *chip = sndti_token(SOUND_QSOUND, 0); + qsound_set_command(chip, data, chip->data); +} + +READ8_HANDLER( qsound_status_r ) +{ + /* Port ready bit (0x80 if ready) */ + return 0x80; +} + +void qsound_set_command(struct qsound_info *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; + } +#if LOG_QSOUND + logerror("QSOUND WRITE %02x CH%02d-R%02d =%04x\n", data, ch, reg, value); +#endif +} + + +void qsound_update( void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length ) +{ + struct qsound_info *chip = param; + int i,j; + int rvol, lvol, count; + struct QSOUND_CHANNEL *pC=&chip->channel[0]; + stream_sample_t *datap[2]; + + datap[0] = buffer[0]; + datap[1] = buffer[1]; + memset( datap[0], 0x00, length * sizeof(*datap[0]) ); + memset( datap[1], 0x00, length * 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=length-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 LOG_WAVE + fwrite(datap[0], length*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataL); + fwrite(datap[1], length*sizeof(QSOUND_SAMPLE), 1, chip->fpRawDataR); +#endif +} + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +static void qsound_set_info(void *token, UINT32 state, sndinfo *info) +{ + switch (state) + { + /* no parameters to set */ + } +} + + +void qsound_get_info(void *token, UINT32 state, sndinfo *info) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case SNDINFO_PTR_SET_INFO: info->set_info = qsound_set_info; break; + case SNDINFO_PTR_START: info->start = qsound_start; break; + case SNDINFO_PTR_STOP: info->stop = qsound_stop; break; + case SNDINFO_PTR_RESET: /* Nothing */ break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case SNDINFO_STR_NAME: info->s = "Q-Sound"; break; + case SNDINFO_STR_CORE_FAMILY: info->s = "Capcom custom"; break; + case SNDINFO_STR_CORE_VERSION: info->s = "1.0"; break; + case SNDINFO_STR_CORE_FILE: info->s = __FILE__; break; + case SNDINFO_STR_CORE_CREDITS: info->s = "Copyright (c) 2004, The MAME Team"; break; + } +} + +/**************** end of file ****************/ |