summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/mame/audio/micro3d.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/mame/audio/micro3d.c')
-rw-r--r--trunk/src/mame/audio/micro3d.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/trunk/src/mame/audio/micro3d.c b/trunk/src/mame/audio/micro3d.c
new file mode 100644
index 00000000000..8d63b2d07e2
--- /dev/null
+++ b/trunk/src/mame/audio/micro3d.c
@@ -0,0 +1,424 @@
+/***************************************************************************
+
+ Microprose sound hardware
+
+ Bit of a rush job - needs to be implemented properly eventually
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sound/upd7759.h"
+#include "includes/micro3d.h"
+
+
+#define MM5837_CLOCK 100000
+
+
+/*************************************
+ *
+ * Type definitions
+ *
+ *************************************/
+
+typedef struct _biquad_
+{
+ double a0, a1, a2; /* Numerator coefficients */
+ double b0, b1, b2; /* Denominator coefficients */
+} biquad;
+
+typedef struct _filter_
+{
+ float *history;
+ float *coef;
+ double fs;
+ biquad ProtoCoef[2];
+} lp_filter;
+
+typedef struct _filter_state filter_state;
+struct _filter_state
+{
+ double capval;
+ double exponent;
+};
+
+typedef struct _noise_state
+{
+ union
+ {
+ struct
+ {
+ UINT8 vcf;
+ UINT8 vcq;
+ UINT8 vca;
+ UINT8 pan;
+ };
+ UINT8 dac[4];
+ };
+
+ float gain;
+ UINT32 noise_shift;
+ UINT8 noise_state;
+ UINT8 noise_subcount;
+
+ filter_state noise_filters[4];
+ lp_filter filter;
+ sound_stream *stream;
+} noise_state;
+
+
+/*************************************
+ *
+ * Pink noise filtering
+ *
+ *************************************/
+
+/* Borrowed from segasnd.c */
+INLINE void configure_filter(filter_state *state, double r, double c)
+{
+ state->capval = 0;
+ state->exponent = 1.0 - exp(-1.0 / (r * c * 2000000/8));
+}
+
+INLINE double step_rc_filter(filter_state *state, double input)
+{
+ state->capval += (input - state->capval) * state->exponent;
+ return state->capval;
+}
+
+INLINE double step_cr_filter(filter_state *state, double input)
+{
+ double result = (input - state->capval);
+ state->capval += (input - state->capval) * state->exponent;
+ return result;
+}
+
+
+/*************************************
+ *
+ * SSM2047 simulation
+ *
+ *************************************/
+
+static void filter_init(running_machine &machine, lp_filter *iir, double fs)
+{
+ /* Section 1 */
+ iir->ProtoCoef[0].a0 = 1.0;
+ iir->ProtoCoef[0].a1 = 0;
+ iir->ProtoCoef[0].a2 = 0;
+ iir->ProtoCoef[0].b0 = 1.0;
+ iir->ProtoCoef[0].b1 = 0.765367;
+ iir->ProtoCoef[0].b2 = 1.0;
+
+ /* Section 2 */
+ iir->ProtoCoef[1].a0 = 1.0;
+ iir->ProtoCoef[1].a1 = 0;
+ iir->ProtoCoef[1].a2 = 0;
+ iir->ProtoCoef[1].b0 = 1.0;
+ iir->ProtoCoef[1].b1 = 1.847759;
+ iir->ProtoCoef[1].b2 = 1.0;
+
+ iir->coef = (float *)auto_alloc_array_clear(machine, float, 4 * 2 + 1);
+ iir->fs = fs;
+ iir->history = (float *)auto_alloc_array_clear(machine, float, 2 * 2);
+}
+
+static void prewarp(double *a0, double *a1, double *a2,double fc, double fs)
+{
+ double wp, pi;
+
+ pi = 4.0 * atan(1.0);
+ wp = 2.0 * fs * tan(pi * fc / fs);
+
+ *a2 = *a2 / (wp * wp);
+ *a1 = *a1 / wp;
+}
+
+static void bilinear(double a0, double a1, double a2,
+ double b0, double b1, double b2,
+ double *k, double fs, float *coef)
+{
+ double ad, bd;
+
+ ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
+ bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;
+
+ *k *= ad/bd;
+
+ *coef++ = (2. * b0 - 8. * b2 * fs * fs) / bd;
+ *coef++ = (4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd;
+
+ *coef++ = (2. * a0 - 8. * a2 * fs * fs) / ad;
+ *coef = (4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad;
+}
+
+static void recompute_filter(lp_filter *iir, double k, double q, double fc)
+{
+ int nInd;
+ double a0, a1, a2, b0, b1, b2;
+
+ float *coef = iir->coef + 1;
+
+ for (nInd = 0; nInd < 2; nInd++)
+ {
+ a0 = iir->ProtoCoef[nInd].a0;
+ a1 = iir->ProtoCoef[nInd].a1;
+ a2 = iir->ProtoCoef[nInd].a2;
+
+ b0 = iir->ProtoCoef[nInd].b0;
+ b1 = iir->ProtoCoef[nInd].b1 / q;
+ b2 = iir->ProtoCoef[nInd].b2;
+
+ prewarp(&a0, &a1, &a2, fc, iir->fs);
+ prewarp(&b0, &b1, &b2, fc, iir->fs);
+ bilinear(a0, a1, a2, b0, b1, b2, &k, iir->fs, coef);
+
+ coef += 4;
+ }
+
+ iir->coef[0] = k;
+}
+
+void micro3d_noise_sh_w(running_machine &machine, UINT8 data)
+{
+ micro3d_state *state = machine.driver_data<micro3d_state>();
+
+ if (~data & 8)
+ {
+ device_t *device = machine.device(data & 4 ? "noise_2" : "noise_1");
+ noise_state *nstate = (noise_state *)downcast<legacy_device_base *>(device)->token();
+
+ if (state->m_dac_data != nstate->dac[data & 3])
+ {
+ double q;
+ double fc;
+
+ nstate->stream->update();
+
+ nstate->dac[data & 3] = state->m_dac_data;
+
+ if (nstate->vca == 255)
+ nstate->gain = 0;
+ else
+ nstate->gain = exp(-(float)(nstate->vca) / 25.0) * 10.0;
+
+ q = 0.75/255 * (255 - nstate->vcq) + 0.1;
+ fc = 4500.0/255 * (255 - nstate->vcf) + 100;
+
+ recompute_filter(&nstate->filter, nstate->gain, q, fc);
+ }
+ }
+}
+
+INLINE noise_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == MICRO3D);
+
+ return (noise_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+static STREAM_UPDATE( micro3d_stream_update )
+{
+ noise_state *state = (noise_state *)param;
+ lp_filter *iir = &state->filter;
+ float pan_l, pan_r;
+
+ stream_sample_t *fl = &outputs[0][0];
+ stream_sample_t *fr = &outputs[1][0];
+
+ /* Clear the buffers */
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples * sizeof(*outputs[1]));
+
+ if (state->gain == 0)
+ return;
+
+ pan_l = (float)(255 - state->pan) / 255.0;
+ pan_r = (float)(state->pan) / 255.0;
+
+ while (samples--)
+ {
+ unsigned int i;
+ float *hist1_ptr,*hist2_ptr,*coef_ptr;
+ float output,new_hist,history1,history2;
+ float input, white;
+ int step;
+
+ /* Update the noise source */
+ for (step = 2000000 / (2000000/8); step >= state->noise_subcount; step -= state->noise_subcount)
+ {
+ state->noise_shift = (state->noise_shift << 1) | (((state->noise_shift >> 13) ^ (state->noise_shift >> 16)) & 1);
+ state->noise_state = (state->noise_shift >> 16) & 1;
+ state->noise_subcount = 2000000 / MM5837_CLOCK;
+ }
+ state->noise_subcount -= step;
+ input = (float)state->noise_state - 0.5;
+ white = input;
+
+ /* Pink noise filtering */
+ state->noise_filters[0].capval = 0.99765 * state->noise_filters[0].capval + input * 0.0990460;
+ state->noise_filters[1].capval = 0.96300 * state->noise_filters[1].capval + input * 0.2965164;
+ state->noise_filters[2].capval = 0.57000 * state->noise_filters[2].capval + input * 1.0526913;
+ input = state->noise_filters[0].capval + state->noise_filters[1].capval + state->noise_filters[2].capval + input * 0.1848;
+
+ input += white;
+ input *= 200.0f;
+
+ coef_ptr = iir->coef;
+
+ hist1_ptr = iir->history;
+ hist2_ptr = hist1_ptr + 1;
+
+ /* 1st number of coefficients array is overall input scale factor, * or filter gain */
+ output = input * (*coef_ptr++);
+
+ for (i = 0 ; i < 2; i++)
+ {
+ history1 = *hist1_ptr;
+ history2 = *hist2_ptr;
+
+ output = output - history1 * (*coef_ptr++);
+ new_hist = output - history2 * (*coef_ptr++);
+
+ output = new_hist + history1 * (*coef_ptr++);
+ output = output + history2 * (*coef_ptr++);
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+ }
+ output *= 3.5;
+
+ /* Clip */
+ if (output > 32767)
+ output = 32767;
+ else if (output < -32768)
+ output = -32768;
+
+ *fl++ = output * pan_l;
+ *fr++ = output * pan_r;
+ }
+}
+
+
+/*************************************
+ *
+ * Initialisation
+ *
+ *************************************/
+
+static DEVICE_START( micro3d_sound )
+{
+ running_machine &machine = device->machine();
+ noise_state *state = get_safe_token(device);
+
+ /* Allocate the stream */
+ state->stream = device->machine().sound().stream_alloc(*device, 0, 2, machine.sample_rate(), state, micro3d_stream_update);
+ filter_init(machine, &state->filter, machine.sample_rate());
+
+ configure_filter(&state->noise_filters[0], 2.7e3 + 2.7e3, 1.0e-6);
+ configure_filter(&state->noise_filters[1], 2.7e3 + 1e3, 0.30e-6);
+ configure_filter(&state->noise_filters[2], 2.7e3 + 270, 0.15e-6);
+ configure_filter(&state->noise_filters[3], 2.7e3 + 0, 0.082e-6);
+// configure_filter(&state->noise_filters[4], 33e3, 0.1e-6);
+}
+
+static DEVICE_RESET( micro3d_sound )
+{
+ noise_state *state = get_safe_token(device);
+ state->noise_shift = 0x15555;
+ state->dac[0] = 255;
+ state->dac[1] = 255;
+ state->dac[2] = 255;
+ state->dac[3] = 255;
+}
+
+DEVICE_GET_INFO( micro3d_sound )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(noise_state); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(micro3d_sound); break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(micro3d_sound); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Microprose Custom"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ }
+}
+
+
+/***************************************************************************
+
+ 8031 port mappings:
+
+ Port 1 Port 2
+ ======= ======
+ 0: S/H sel A (O) 0:
+ 1: S/H sel B (O) 1:
+ 2: S/H sel C (O) 2: uPD bank select (O)
+ 3: S/H en (O) 3: /uPD busy (I)
+ 4: DS1267 data (O) 4: /uPD reset (O)
+ 5: DS1267 clock (O) 5: Watchdog reset (O)
+ 6: /DS1267 reset (O) 6:
+ 7: Test SW (I) 7:
+
+***************************************************************************/
+
+
+WRITE8_MEMBER(micro3d_state::micro3d_snd_dac_a)
+{
+ m_dac_data = data;
+}
+
+WRITE8_MEMBER(micro3d_state::micro3d_snd_dac_b)
+{
+ /* TODO: This controls upd7759 volume */
+}
+
+WRITE8_MEMBER(micro3d_state::micro3d_sound_io_w)
+{
+
+ m_sound_port_latch[offset] = data;
+
+ switch (offset)
+ {
+ case 0x01:
+ {
+ micro3d_noise_sh_w(machine(), data);
+ break;
+ }
+ case 0x03:
+ {
+ device_t *upd = machine().device("upd7759");
+ upd7759_set_bank_base(upd, (data & 0x4) ? 0x20000 : 0);
+ upd7759_reset_w(upd, (data & 0x10) ? 0 : 1);
+ break;
+ }
+ }
+}
+
+READ8_MEMBER(micro3d_state::micro3d_sound_io_r)
+{
+
+ switch (offset)
+ {
+ case 0x01: return (m_sound_port_latch[offset] & 0x7f) | ioport("SOUND_SW")->read();
+ case 0x03: return (m_sound_port_latch[offset] & 0xf7) | (upd7759_busy_r(machine().device("upd7759")) ? 0x08 : 0);
+ default: return 0;
+ }
+}
+
+WRITE8_DEVICE_HANDLER( micro3d_upd7759_w )
+{
+ upd7759_port_w(device, 0, data);
+ upd7759_start_w(device, 0);
+ upd7759_start_w(device, 1);
+}
+
+
+DEFINE_LEGACY_SOUND_DEVICE(MICRO3D, micro3d_sound);