diff options
Diffstat (limited to 'src/emu/sound/msm5205.c')
-rw-r--r-- | src/emu/sound/msm5205.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src/emu/sound/msm5205.c b/src/emu/sound/msm5205.c new file mode 100644 index 00000000000..678ca78b919 --- /dev/null +++ b/src/emu/sound/msm5205.c @@ -0,0 +1,330 @@ +/* + * streaming ADPCM driver + * by Aaron Giles + * + * Library to transcode from an ADPCM source to raw PCM. + * Written by Buffoni Mirko in 08/06/97 + * References: various sources and documents. + * + * HJB 08/31/98 + * modified to use an automatically selected oversampling factor + * for the current sample rate + * + * 01/06/99 + * separate MSM5205 emulator form adpcm.c and some fix + */ + +#include <math.h> + +#include "sndintrf.h" +#include "streams.h" +#include "msm5205.h" + +/* + * + * MSM 5205 ADPCM chip: + * + * Data is streamed from a CPU by means of a clock generated on the chip. + * + * A reset signal is set high or low to determine whether playback (and interrupts) are occuring + * + */ + +struct MSM5205Voice +{ + const struct MSM5205interface *intf; + sound_stream * stream; /* number of stream system */ + INT32 index; + INT32 clock; /* clock rate */ + emu_timer *timer; /* VCLK callback timer */ + INT32 data; /* next adpcm data */ + INT32 vclk; /* vclk signal (external mode) */ + INT32 reset; /* reset pin signal */ + INT32 prescaler; /* prescaler selector S1 and S2 */ + INT32 bitwidth; /* bit width selector -3B/4B */ + INT32 signal; /* current ADPCM signal */ + INT32 step; /* current ADPCM step */ + int diff_lookup[49*16]; +}; + + +/* + * ADPCM lockup tabe + */ + +/* step size index shift table */ +static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + +/* + * Compute the difference table + */ + +static void ComputeTables (struct MSM5205Voice *voice) +{ + /* nibble to bit map */ + static const int nbl2bit[16][4] = + { + { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1}, + { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1}, + {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1}, + {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1} + }; + + int step, nib; + + /* loop over all possible steps */ + for (step = 0; step <= 48; step++) + { + /* compute the step value */ + int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step)); + + /* loop over all nibbles and compute the difference */ + for (nib = 0; nib < 16; nib++) + { + voice->diff_lookup[step*16 + nib] = nbl2bit[nib][0] * + (stepval * nbl2bit[nib][1] + + stepval/2 * nbl2bit[nib][2] + + stepval/4 * nbl2bit[nib][3] + + stepval/8); + } + } +} + +/* stream update callbacks */ +static void MSM5205_update(void *param,stream_sample_t **inputs, stream_sample_t **_buffer,int length) +{ + struct MSM5205Voice *voice = param; + stream_sample_t *buffer = _buffer[0]; + + /* if this voice is active */ + if(voice->signal) + { + short val = voice->signal * 16; + while (length) + { + *buffer++ = val; + length--; + } + } + else + memset (buffer,0,length*sizeof(*buffer)); +} + +/* timer callback at VCLK low eddge */ +static TIMER_CALLBACK_PTR( MSM5205_vclk_callback ) +{ + struct MSM5205Voice *voice = param; + int val; + int new_signal; + /* callback user handler and latch next data */ + if(voice->intf->vclk_callback) (*voice->intf->vclk_callback)(voice->index); + + /* reset check at last hieddge of VCLK */ + if(voice->reset) + { + new_signal = 0; + voice->step = 0; + } + else + { + /* update signal */ + /* !! MSM5205 has internal 12bit decoding, signal width is 0 to 8191 !! */ + val = voice->data; + new_signal = voice->signal + voice->diff_lookup[voice->step * 16 + (val & 15)]; + if (new_signal > 2047) new_signal = 2047; + else if (new_signal < -2048) new_signal = -2048; + voice->step += index_shift[val & 7]; + if (voice->step > 48) voice->step = 48; + else if (voice->step < 0) voice->step = 0; + } + /* update when signal changed */ + if( voice->signal != new_signal) + { + stream_update(voice->stream); + voice->signal = new_signal; + } +} + +/* + * Reset emulation of an MSM5205-compatible chip + */ +static void msm5205_reset(void *chip) +{ + struct MSM5205Voice *voice = chip; + + /* initialize work */ + voice->data = 0; + voice->vclk = 0; + voice->reset = 0; + voice->signal = 0; + voice->step = 0; + /* timer and bitwidth set */ + MSM5205_playmode_w(voice->index,voice->intf->select); +} + +/* + * Start emulation of an MSM5205-compatible chip + */ + +static void *msm5205_start(int sndindex, int clock, const void *config) +{ + struct MSM5205Voice *voice; + + voice = auto_malloc(sizeof(*voice)); + memset(voice, 0, sizeof(*voice)); + sndintrf_register_token(voice); + + /* save a global pointer to our interface */ + voice->intf = config; + voice->index = sndindex; + voice->clock = clock; + + /* compute the difference tables */ + ComputeTables (voice); + + /* stream system initialize */ + voice->stream = stream_create(0,1,clock,voice,MSM5205_update); + voice->timer = timer_alloc_ptr(MSM5205_vclk_callback, voice); + + /* initialize */ + msm5205_reset(voice); + + /* register for save states */ + state_save_register_item("msm5205", sndindex, voice->clock); + state_save_register_item("msm5205", sndindex, voice->data); + state_save_register_item("msm5205", sndindex, voice->vclk); + state_save_register_item("msm5205", sndindex, voice->reset); + state_save_register_item("msm5205", sndindex, voice->prescaler); + state_save_register_item("msm5205", sndindex, voice->bitwidth); + state_save_register_item("msm5205", sndindex, voice->signal); + state_save_register_item("msm5205", sndindex, voice->step); + + /* success */ + return voice; +} + +/* + * Handle an update of the vclk status of a chip (1 is reset ON, 0 is reset OFF) + * This function can use selector = MSM5205_SEX only + */ +void MSM5205_vclk_w (int num, int vclk) +{ + struct MSM5205Voice *voice = sndti_token(SOUND_MSM5205, num); + + if( voice->prescaler != 0 ) + { + logerror("error: MSM5205_vclk_w() called with chip = %d, but VCLK selected master mode\n", num); + } + else + { + if( voice->vclk != vclk) + { + voice->vclk = vclk; + if( !vclk ) MSM5205_vclk_callback(Machine, voice); + } + } +} + +/* + * Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF) + */ + +void MSM5205_reset_w (int num, int reset) +{ + struct MSM5205Voice *voice = sndti_token(SOUND_MSM5205, num); + voice->reset = reset; +} + +/* + * Handle an update of the data to the chip + */ + +void MSM5205_data_w (int num, int data) +{ + struct MSM5205Voice *voice = sndti_token(SOUND_MSM5205, num); + if( voice->bitwidth == 4) + voice->data = data & 0x0f; + else + voice->data = (data & 0x07)<<1; /* unknown */ +} + +/* + * Handle an change of the selector + */ + +void MSM5205_playmode_w(int num,int select) +{ + struct MSM5205Voice *voice = sndti_token(SOUND_MSM5205, num); + static const int prescaler_table[4] = {96,48,64,0}; + int prescaler = prescaler_table[select & 3]; + int bitwidth = (select & 4) ? 4 : 3; + + + if( voice->prescaler != prescaler ) + { + stream_update(voice->stream); + + voice->prescaler = prescaler; + /* timer set */ + if( prescaler ) + { + attotime period = attotime_mul(ATTOTIME_IN_HZ(voice->clock), prescaler); + timer_adjust_ptr(voice->timer, period, period); + } + else + timer_adjust_ptr(voice->timer, attotime_never, attotime_zero); + } + + if( voice->bitwidth != bitwidth ) + { + stream_update(voice->stream); + + voice->bitwidth = bitwidth; + } +} + + +void MSM5205_set_volume(int num,int volume) +{ + struct MSM5205Voice *voice = sndti_token(SOUND_MSM5205, num); + + stream_set_output_gain(voice->stream,0,volume / 100.0); +} + + + + +/************************************************************************** + * Generic get_info + **************************************************************************/ + +static void msm5205_set_info(void *token, UINT32 state, sndinfo *info) +{ + switch (state) + { + /* no parameters to set */ + } +} + + +void msm5205_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 = msm5205_set_info; break; + case SNDINFO_PTR_START: info->start = msm5205_start; break; + case SNDINFO_PTR_STOP: /* nothing */ break; + case SNDINFO_PTR_RESET: /* nothing */ break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case SNDINFO_STR_NAME: info->s = "MSM5205"; break; + case SNDINFO_STR_CORE_FAMILY: info->s = "ADPCM"; 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; + } +} + |