summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/msm5205.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/msm5205.c')
-rw-r--r--src/emu/sound/msm5205.c330
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;
+ }
+}
+