// license:BSD-3-Clause // copyright-holders:David Haywood /*************************************************************************** Gomoku sound driver (quick hack of the Wiping sound driver) used by wiping.c ***************************************************************************/ #include "emu.h" #include "audio/gomoku.h" constexpr int DEFGAIN = 48; // device type definition DEFINE_DEVICE_TYPE(GOMOKU_SOUND, gomoku_sound_device, "gomoku_sound", "Gomoku Narabe Renju Custom Sound") //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // gomoku_sound_device - constructor //------------------------------------------------- gomoku_sound_device::gomoku_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, GOMOKU_SOUND, tag, owner, clock) , device_sound_interface(mconfig, *this) , m_sound_rom(*this, DEVICE_SELF) , m_sound_enable(0) , m_stream(nullptr) , m_mixer_table(nullptr) , m_mixer_lookup(nullptr) , m_mixer_buffer(nullptr) , m_mixer_buffer_2(nullptr) { std::fill(std::begin(m_soundregs1), std::end(m_soundregs1), 0); std::fill(std::begin(m_soundregs2), std::end(m_soundregs2), 0); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void gomoku_sound_device::device_start() { gomoku_sound_channel *voice; int ch; /* get stream channels */ m_stream = stream_alloc(0, 1, clock()); /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */ m_mixer_buffer = std::make_unique(2 * clock()); m_mixer_buffer_2 = m_mixer_buffer.get() + clock(); /* build the mixer table */ make_mixer_table(8, DEFGAIN); /* start with sound enabled, many games don't have a sound enable register */ m_sound_enable = 1; /* reset all the voices */ for (ch = 0, voice = std::begin(m_channel_list); voice < std::end(m_channel_list); ch++, voice++) { voice->channel = ch; voice->frequency = 0; voice->counter = 0; voice->volume = 0; voice->oneshotplaying = 0; } } //------------------------------------------------- // sound_stream_update - handle a stream update in mono //------------------------------------------------- void gomoku_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { stream_sample_t *buffer = outputs[0]; gomoku_sound_channel *voice; short *mix; int i, ch; /* if no sound, we're done */ if (m_sound_enable == 0) { memset(buffer, 0, samples * sizeof(*buffer)); return; } /* zap the contents of the mixer buffer */ memset(m_mixer_buffer.get(), 0, samples * sizeof(short)); /* loop over each voice and add its contribution */ for (ch = 0, voice = std::begin(m_channel_list); voice < std::end(m_channel_list); ch++, voice++) { int f = 16 * voice->frequency; int v = voice->volume; /* only update if we have non-zero volume and frequency */ if (v && f) { int w_base; int c = voice->counter; if (ch < 3) w_base = 0x20 * (m_soundregs1[0x06 + (ch * 8)] & 0x0f); else w_base = 0x100 * (m_soundregs2[0x1d] & 0x0f); mix = m_mixer_buffer.get(); /* add our contribution */ for (i = 0; i < samples; i++) { c += f; if (ch < 3) { int offs = w_base | ((c >> 16) & 0x1f); /* use full byte, first the high 4 bits, then the low 4 bits */ if (c & 0x8000) *mix++ += ((m_sound_rom[offs] & 0x0f) - 8) * v; else *mix++ += (((m_sound_rom[offs]>>4) & 0x0f) - 8) * v; } else { int offs = (w_base + (c >> 16)) & 0x0fff; if (m_sound_rom[offs] == 0xff) { voice->oneshotplaying = 0; } if (voice->oneshotplaying) { /* use full byte, first the high 4 bits, then the low 4 bits */ if (c & 0x8000) *mix++ += ((m_sound_rom[offs] & 0x0f) - 8) * v; else *mix++ += (((m_sound_rom[offs]>>4) & 0x0f) - 8) * v; } } /* update the counter for this voice */ voice->counter = c; } } } /* mix it down */ mix = m_mixer_buffer.get(); for (i = 0; i < samples; i++) *buffer++ = m_mixer_lookup[*mix++]; } /* build a table to divide by the number of voices; gain is specified as gain*16 */ void gomoku_sound_device::make_mixer_table(int voices, int gain) { int count = voices * 128; int i; /* allocate memory */ m_mixer_table = std::make_unique(256 * voices); /* find the middle of the table */ m_mixer_lookup = m_mixer_table.get() + (128 * voices); /* fill in the table - 16 bit case */ for (i = 0; i < count; i++) { int val = i * gain * 16 / voices; if (val > 32767) val = 32767; m_mixer_lookup[ i] = val; m_mixer_lookup[-i] = -val; } } /********************************************************************************/ WRITE8_MEMBER( gomoku_sound_device::sound1_w ) { gomoku_sound_channel *voice; int base; int ch; /* update the streams */ m_stream->update(); /* set the register */ m_soundregs1[offset] = data; /* recompute all the voice parameters */ for (ch = 0, base = 0, voice = m_channel_list; voice < m_channel_list + 3; ch++, voice++, base += 8) { voice->channel = ch; voice->frequency = m_soundregs1[0x02 + base] & 0x0f; voice->frequency = voice->frequency * 16 + ((m_soundregs1[0x01 + base]) & 0x0f); voice->frequency = voice->frequency * 16 + ((m_soundregs1[0x00 + base]) & 0x0f); } } WRITE8_MEMBER( gomoku_sound_device::sound2_w ) { gomoku_sound_channel *voice; int base; int ch; /* update the streams */ m_stream->update(); /* set the register */ m_soundregs2[offset] = data; /* recompute all the voice parameters */ for (ch = 0, base = 0, voice = m_channel_list; voice < m_channel_list + 3; ch++, voice++, base += 8) { voice->channel = ch; voice->volume = m_soundregs2[0x06 + base] & 0x0f; voice->oneshotplaying = 0; } if (offset == 0x1d) { voice = &m_channel_list[3]; voice->channel = 3; // oneshot frequency is hand tune... if ((m_soundregs2[0x1d] & 0x0f) < 0x0c) voice->frequency = 3000 / 16; // ichi, ni, san, yon, go else voice->frequency = 8000 / 16; // shoot voice->volume = 8; voice->counter = 0; if (m_soundregs2[0x1d] & 0x0f) voice->oneshotplaying = 1; else voice->oneshotplaying = 0; } }