diff --git a/CMakeLists.txt b/CMakeLists.txt index 72e135d2..7673086e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,6 +256,7 @@ extern/opl/opl3.c src/engine/platform/sound/sn76496.cpp src/engine/platform/sound/ay8910.cpp src/engine/platform/sound/saa1099.cpp +src/engine/platform/sound/namco.cpp src/engine/platform/sound/gb/apu.c src/engine/platform/sound/gb/timing.c src/engine/platform/sound/pce_psg.cpp diff --git a/papers/format.md b/papers/format.md index 3ee91ce8..a2c66da7 100644 --- a/papers/format.md +++ b/papers/format.md @@ -223,6 +223,8 @@ size | description | - 0xb7: PC-98 extended - 19 channels | - 0xb8: YMZ280B - 8 channels | - 0xb9: Namco WSG - 3 channels + | - 0xba: Namco 15xx - 8 channels + | - 0xbb: Namco CUS30 - 8 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfd: Dummy System - 8 channels @@ -407,6 +409,7 @@ size | description | - 28: MultiPCM | - 29: SNES | - 30: Sound Unit + | - 31: Namco WSG 1 | reserved STR | instrument name --- | **FM instrument data** diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 18cefd01..6166780f 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -58,6 +58,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_MULTIPCM=28, DIV_INS_SNES=29, DIV_INS_SU=30, + DIV_INS_NAMCO=31, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index 867b5d21..2c11b6e3 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -581,7 +581,7 @@ YM2203 English datasheet: http://www.appleii-box.de/APPLE2/JonasCard/YM2203%20da YM2203 Japanese datasheet contents, translated: http://www.larwe.com/technical/chip_ym2203.html */ -// additional modifications by tildearrow and Eulous for furnace (particularly AY8930 emulation) +// additional modifications by tildearrow, Eulous, cam900 and Grauw for furnace (particularly AY8930 emulation) #include "ay8910.h" #include diff --git a/src/engine/platform/sound/namco.cpp b/src/engine/platform/sound/namco.cpp new file mode 100644 index 00000000..61c68eb0 --- /dev/null +++ b/src/engine/platform/sound/namco.cpp @@ -0,0 +1,804 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria,Aaron Giles +/*************************************************************************** + + NAMCO sound driver. + + This driver handles the four known types of NAMCO wavetable sounds: + + - 3-voice mono (PROM-based design: Pac-Man, Pengo, Dig Dug, etc) + - 8-voice quadrophonic (Pole Position 1, Pole Position 2) + - 8-voice mono (custom 15XX: Mappy, Dig Dug 2, etc) + - 8-voice stereo (System 1) + + The 15XX custom does not have a DAC of its own; instead, it streams + the 4-bit PROM data directly into the 99XX custom DAC. Most pre-99XX + (and pre-15XX) Namco games use a LS273 latch (cleared when sound is + disabled), a 4.7K/2.2K/1K/470 resistor-weighted DAC, and a 4066 and + second group of resistors (10K/22K/47K/100K) for volume control. + Pole Position does more complicated sound mixing: a 4051 multiplexes + wavetable sound with four signals derived from the 52XX and 54XX, the + selected signal is distributed to four volume control sections, and + finally the engine noise is mixed into all four channels. The later + CUS30 also uses the 99XX DAC, or two 99XX in the optional 16-channel + stereo configuration, but it uses no PROM and delivers its own samples. + + The CUS30 has been decapped and verified to be a ULA. + +***************************************************************************/ + +// additional modifications by tildearrow for furnace + +#include "namco.h" +#include + +/* quality parameter: internal sample rate is 192 KHz, output is 48 KHz */ +#define INTERNAL_RATE 192000 + +/* 16 bits: sample bits of the stream buffer */ +/* 4 bits: volume */ +/* 4 bits: prom sample bits */ +#define MIXLEVEL (1 << (16 - 4 - 4)) + +/* stream output level */ +#define OUTPUT_LEVEL(n) ((n) * MIXLEVEL / m_voices) + +/* a position of waveform sample */ +#define WAVEFORM_POSITION(n) (((n) >> m_f_fracbits) & 0x1f) + +namco_audio_device::namco_audio_device(uint32_t clock) + : m_wave_ptr(NULL) + , m_last_channel(nullptr) + , m_wavedata(nullptr) + , m_wave_size(0) + , m_sound_enable(false) + , m_namco_clock(0) + , m_sample_rate(0) + , m_f_fracbits(0) + , m_voices(0) + , m_stereo(false) +{ +} + +namco_device::namco_device(uint32_t clock) + : namco_audio_device(clock) +{ +} + +namco_15xx_device::namco_15xx_device(uint32_t clock) + :namco_audio_device(clock) +{ +} + +namco_cus30_device::namco_cus30_device(uint32_t clock) + : namco_audio_device(clock) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void namco_audio_device::device_start() +{ + /* extract globals from the interface */ + m_last_channel = m_channel_list + m_voices; + + /* build the waveform table */ + build_decoded_waveform(m_wave_ptr); + + /* start with sound enabled, many games don't have a sound enable register */ + m_sound_enable = true; + + /* reset all the voices */ + for (sound_channel *voice = m_channel_list; voice < m_last_channel; voice++) + { + voice->frequency = 0; + voice->volume[0] = voice->volume[1] = 0; + voice->waveform_select = 0; + voice->counter = 0; + voice->noise_sw = 0; + voice->noise_state = 0; + voice->noise_seed = 1; + voice->noise_counter = 0; + voice->noise_hold = 0; + } +} + + +void namco_device::device_start() +{ + namco_audio_device::device_start(); + +} + + +void namco_15xx_device::device_start() +{ + namco_audio_device::device_start(); +} + + +void namco_audio_device::device_clock_changed() +{ + int clock_multiple; + + /* adjust internal clock */ + m_namco_clock = clock(); + for (clock_multiple = 0; m_namco_clock < INTERNAL_RATE; clock_multiple++) + m_namco_clock *= 2; + + m_f_fracbits = clock_multiple + 15; + + /* adjust output clock */ + m_sample_rate = m_namco_clock; + + //logerror("Namco: freq fractional bits = %d: internal freq = %d, output freq = %d\n", m_f_fracbits, m_namco_clock, m_sample_rate); +} + + +/* update the decoded waveform data */ +void namco_audio_device::update_namco_waveform(int offset, uint8_t data) +{ + if (m_wave_size == 1) + { + int16_t wdata; + int v; + + /* use full byte, first 4 high bits, then low 4 bits */ + for (v = 0; v < (int)MAX_VOLUME; v++) + { + wdata = ((data >> 4) & 0x0f) - 8; + m_waveform[v][offset * 2] = OUTPUT_LEVEL(wdata * v); + wdata = (data & 0x0f) - 8; + m_waveform[v][offset * 2 + 1] = OUTPUT_LEVEL(wdata * v); + } + } + else + { + int v; + + /* use only low 4 bits */ + for (v = 0; v < (int)MAX_VOLUME; v++) + m_waveform[v][offset] = OUTPUT_LEVEL(((data & 0x0f) - 8) * v); + } +} + + +/* build the decoded waveform table */ +void namco_audio_device::build_decoded_waveform(uint8_t *rgnbase) +{ + if (rgnbase != nullptr) + m_wavedata = rgnbase; + else + { + m_wavedata = m_waveram_alloc; + } + + for (int offset = 0; offset < 256; offset++) + update_namco_waveform(offset, m_wavedata[offset]); +} + + +/* generate sound by oversampling */ +uint32_t namco_audio_device::namco_update_one(short* buffer, int size, const int16_t *wave, uint32_t counter, uint32_t freq) +{ + for (int sampindex = 0; sampindex < size; sampindex++) + { + buffer[sampindex]=wave[WAVEFORM_POSITION(counter)]; + counter += freq; + } + + return counter; +} + + +void namco_audio_device::sound_enable_w(int state) +{ + m_sound_enable = state; +} + + +/********************************************************************************/ + +/* pacman register map + 0x05: ch 0 waveform select + 0x0a: ch 1 waveform select + 0x0f: ch 2 waveform select + + 0x10: ch 0 the first voice has extra frequency bits + 0x11-0x14: ch 0 frequency + 0x15: ch 0 volume + + 0x16-0x19: ch 1 frequency + 0x1a: ch 1 volume + + 0x1b-0x1e: ch 2 frequency + 0x1f: ch 2 volume +*/ + +void namco_device::pacman_sound_w(int offset, uint8_t data) +{ + sound_channel *voice; + int ch; + + data &= 0x0f; + if (m_soundregs[offset] == data) + return; + + /* set the register */ + m_soundregs[offset] = data; + + if (offset < 0x10) + ch = (offset - 5) / 5; + else if (offset == 0x10) + ch = 0; + else + ch = (offset - 0x11) / 5; + + if (ch >= m_voices) + return; + + /* recompute the voice parameters */ + voice = m_channel_list + ch; + switch (offset - ch * 5) + { + case 0x05: + voice->waveform_select = data & 7; + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + /* the frequency has 20 bits */ + /* the first voice has extra frequency bits */ + voice->frequency = (ch == 0) ? m_soundregs[0x10] : 0; + voice->frequency += (m_soundregs[ch * 5 + 0x11] << 4); + voice->frequency += (m_soundregs[ch * 5 + 0x12] << 8); + voice->frequency += (m_soundregs[ch * 5 + 0x13] << 12); + voice->frequency += (m_soundregs[ch * 5 + 0x14] << 16); /* always 0 */ + break; + + case 0x15: + voice->volume[0] = data; + break; + } +} + +void namco_cus30_device::pacman_sound_w(int offset, uint8_t data) +{ + sound_channel *voice; + int ch; + + uint8_t *soundregs = &m_wavedata[0x100]; + + data &= 0x0f; + if (soundregs[offset] == data) + return; + + /* set the register */ + soundregs[offset] = data; + + if (offset < 0x10) + ch = (offset - 5) / 5; + else if (offset == 0x10) + ch = 0; + else + ch = (offset - 0x11) / 5; + + if (ch >= m_voices) + return; + + /* recompute the voice parameters */ + voice = m_channel_list + ch; + switch (offset - ch * 5) + { + case 0x05: + voice->waveform_select = data & 7; + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + /* the frequency has 20 bits */ + /* the first voice has extra frequency bits */ + voice->frequency = (ch == 0) ? soundregs[0x10] : 0; + voice->frequency += (soundregs[ch * 5 + 0x11] << 4); + voice->frequency += (soundregs[ch * 5 + 0x12] << 8); + voice->frequency += (soundregs[ch * 5 + 0x13] << 12); + voice->frequency += (soundregs[ch * 5 + 0x14] << 16); /* always 0 */ + break; + + case 0x15: + voice->volume[0] = data; + break; + } +} + +/********************************************************************************/ + +/* polepos register map +Note: even if there are 8 voices, the game doesn't use the first 2 because +it select the 54XX/52XX outputs on those channels + + 0x00-0x01 ch 0 frequency + 0x02 ch 0 xxxx---- GAIN 2 volume + 0x03 ch 0 xxxx---- GAIN 3 volume + ----xxxx GAIN 4 volume + + 0x04-0x07 ch 1 + + . + . + . + + 0x1c-0x1f ch 7 + + 0x23 ch 0 xxxx---- GAIN 1 volume + -----xxx waveform select + ----x-xx channel output select + 0-7 (all the same, shared with waveform select) = wave + 8 = CHANL1 (54XX pins 17-20) + 9 = CHANL2 (54XX pins 8-11) + A = CHANL3 (54XX pins 4-7) + B = CHANL4 (52XX) + 0x27 ch 1 + 0x2b ch 2 + 0x2f ch 3 + 0x33 ch 4 + 0x37 ch 5 + 0x3b ch 6 + 0x3f ch 7 +*/ + +uint8_t namco_device::polepos_sound_r(int offset) +{ + return m_soundregs[offset]; +} + +void namco_device::polepos_sound_w(int offset, uint8_t data) +{ + sound_channel *voice; + int ch; + + if (m_soundregs[offset] == data) + return; + + /* set the register */ + m_soundregs[offset] = data; + + ch = (offset & 0x1f) / 4; + + /* recompute the voice parameters */ + voice = m_channel_list + ch; + switch (offset & 0x23) + { + case 0x00: + case 0x01: + /* the frequency has 16 bits */ + voice->frequency = m_soundregs[ch * 4 + 0x00]; + voice->frequency += m_soundregs[ch * 4 + 0x01] << 8; + break; + + case 0x23: + voice->waveform_select = data & 7; + [[fallthrough]]; + case 0x02: + case 0x03: + voice->volume[0] = voice->volume[1] = 0; + // front speakers ? + voice->volume[0] += m_soundregs[ch * 4 + 0x03] >> 4; + voice->volume[1] += m_soundregs[ch * 4 + 0x03] & 0x0f; + // rear speakers ? + voice->volume[0] += m_soundregs[ch * 4 + 0x23] >> 4; + voice->volume[1] += m_soundregs[ch * 4 + 0x02] >> 4; + + voice->volume[0] /= 2; + voice->volume[1] /= 2; + + /* if 54XX or 52XX selected, silence this voice */ + if (m_soundregs[ch * 4 + 0x23] & 8) + voice->volume[0] = voice->volume[1] = 0; + break; + } +} + + +/********************************************************************************/ + +/* 15XX register map + 0x03 ch 0 volume + 0x04-0x05 ch 0 frequency + 0x06 ch 0 waveform select & frequency + + 0x0b ch 1 volume + 0x0c-0x0d ch 1 frequency + 0x0e ch 1 waveform select & frequency + + . + . + . + + 0x3b ch 7 volume + 0x3c-0x3d ch 7 frequency + 0x3e ch 7 waveform select & frequency + +Grobda also stuffs values into register offset 0x02 with a frequency of zero +to make 15XX channels act like a 4-bit DAC instead of waveform voices. This +has been emulated by allowing writes to set the upper counter bits directly. +Possibly offsets 0x00 and 0x01 can be used to set the fractional bits. +*/ + +template constexpr T make_bitmask(U n) +{ + return T((n < (int)(8 * sizeof(T)) ? (std::make_unsigned_t(1) << n) : std::make_unsigned_t(0)) - 1); +} + +void namco_15xx_device::namco_15xx_w(int offset, uint8_t data) +{ + sound_channel *voice; + int ch; + + if (m_soundregs[offset] == data) + return; + + /* set the register */ + m_soundregs[offset] = data; + + ch = offset / 8; + if (ch >= m_voices) + return; + + /* recompute the voice parameters */ + voice = m_channel_list + ch; + switch (offset - ch * 8) + { + case 0x02: + voice->counter &= make_bitmask(m_f_fracbits); + voice->counter |= uint32_t(data & 0x1f) << m_f_fracbits; + break; + + case 0x03: + voice->volume[0] = data & 0x0f; + break; + + case 0x06: + voice->waveform_select = (data >> 4) & 7; + [[fallthrough]]; + case 0x04: + case 0x05: + /* the frequency has 20 bits */ + voice->frequency = m_soundregs[ch * 8 + 0x04]; + voice->frequency += m_soundregs[ch * 8 + 0x05] << 8; + voice->frequency += (m_soundregs[ch * 8 + 0x06] & 15) << 16; /* high bits are from here */ + break; + } +} + + +/********************************************************************************/ + +/* namcos1 register map + 0x00 ch 0 left volume + 0x01 ch 0 waveform select & frequency + 0x02-0x03 ch 0 frequency + 0x04 ch 0 right volume AND + 0x04 ch 1 noise sw + + 0x08 ch 1 left volume + 0x09 ch 1 waveform select & frequency + 0x0a-0x0b ch 1 frequency + 0x0c ch 1 right volume AND + 0x0c ch 2 noise sw + + . + . + . + + 0x38 ch 7 left volume + 0x39 ch 7 waveform select & frequency + 0x3a-0x3b ch 7 frequency + 0x3c ch 7 right volume AND + 0x3c ch 0 noise sw +*/ + +void namco_cus30_device::namcos1_sound_w(int offset, uint8_t data) +{ + sound_channel *voice; + int ch; + int nssw; + + + /* verify the offset */ + if (offset > 63) + { + //logerror("NAMCOS1 sound: Attempting to write past the 64 registers segment\n"); + return; + } + + uint8_t *soundregs = &m_wavedata[0x100]; + + if (soundregs[offset] == data) + return; + + /* set the register */ + soundregs[offset] = data; + + ch = offset / 8; + if (ch >= m_voices) + return; + + /* recompute the voice parameters */ + voice = m_channel_list + ch; + switch (offset - ch * 8) + { + case 0x00: + voice->volume[0] = data & 0x0f; + break; + + case 0x01: + voice->waveform_select = (data >> 4) & 15; + [[fallthrough]]; + case 0x02: + case 0x03: + /* the frequency has 20 bits */ + voice->frequency = (soundregs[ch * 8 + 0x01] & 15) << 16; /* high bits are from here */ + voice->frequency += soundregs[ch * 8 + 0x02] << 8; + voice->frequency += soundregs[ch * 8 + 0x03]; + break; + + case 0x04: + voice->volume[1] = data & 0x0f; + + nssw = ((data & 0x80) >> 7); + if (++voice == m_last_channel) + voice = m_channel_list; + voice->noise_sw = nssw; + break; + } +} + +void namco_cus30_device::namcos1_cus30_w(int offset, uint8_t data) +{ + if (offset < 0x100) + { + if (m_wavedata[offset] != data) + { + + m_wavedata[offset] = data; + + /* update the decoded waveform table */ + update_namco_waveform(offset, data); + } + } + else if (offset < 0x140) + namcos1_sound_w(offset - 0x100,data); + else + m_wavedata[offset] = data; +} + +uint8_t namco_cus30_device::namcos1_cus30_r(int offset) +{ + return m_wavedata[offset]; +} + +uint8_t namco_15xx_device::sharedram_r(int offset) +{ + return m_soundregs[offset]; +} + +void namco_15xx_device::sharedram_w(int offset, uint8_t data) +{ + if (offset < 0x40) + namco_15xx_w(offset, data); + else + { + m_soundregs[offset] = data; + } +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void namco_audio_device::sound_stream_update(short** outputs, int len) +{ + if (m_stereo) + { + /* zap the contents of the buffers */ + memset(outputs[0],0,len*sizeof(short)); + memset(outputs[1],0,len*sizeof(short)); + + /* if no sound, we're done */ + if (!m_sound_enable) + return; + + /* loop over each voice and add its contribution */ + for (sound_channel *voice = m_channel_list; voice < m_last_channel; voice++) + { + short* lmix = outputs[0]; + short* rmix = outputs[1]; + int lv = voice->volume[0]; + int rv = voice->volume[1]; + + if (voice->noise_sw) + { + int f = voice->frequency & 0xff; + + /* only update if we have non-zero volume */ + if (lv || rv) + { + int hold_time = 1 << (m_f_fracbits - 16); + int hold = voice->noise_hold; + uint32_t delta = f << 4; + uint32_t c = voice->noise_counter; + int16_t l_noise_data = OUTPUT_LEVEL(0x07 * (lv >> 1)); + int16_t r_noise_data = OUTPUT_LEVEL(0x07 * (rv >> 1)); + int i; + + /* add our contribution */ + for (i = 0; i < len; i++) + { + int cnt; + + if (voice->noise_state) + { + lmix[i]=l_noise_data; + rmix[i]=r_noise_data; + } + else + { + lmix[i]=-l_noise_data; + rmix[i]=-r_noise_data; + } + + if (hold) + { + hold--; + continue; + } + + hold = hold_time; + + c += delta; + cnt = (c >> 12); + c &= (1 << 12) - 1; + for( ;cnt > 0; cnt--) + { + if ((voice->noise_seed + 1) & 2) voice->noise_state ^= 1; + if (voice->noise_seed & 1) voice->noise_seed ^= 0x28000; + voice->noise_seed >>= 1; + } + } + + /* update the counter and hold time for this voice */ + voice->noise_counter = c; + voice->noise_hold = hold; + } + } + else + { + /* save the counter for this voice */ + uint32_t c = voice->counter; + + /* only update if we have non-zero left volume */ + if (lv) + { + const int16_t *lw = &m_waveform[lv][voice->waveform_select * 32]; + + /* generate sound into the buffer */ + c = namco_update_one(lmix, len, lw, voice->counter, voice->frequency); + } + + /* only update if we have non-zero right volume */ + if (rv) + { + const int16_t *rw = &m_waveform[rv][voice->waveform_select * 32]; + + /* generate sound into the buffer */ + c = namco_update_one(rmix, len, rw, voice->counter, voice->frequency); + } + + /* update the counter for this voice */ + voice->counter = c; + } + } + } + else + { + sound_channel *voice; + + short* buffer = outputs[0]; + /* zap the contents of the buffer */ + memset(buffer,0,len*sizeof(short)); + + /* if no sound, we're done */ + if (!m_sound_enable) + return; + + /* loop over each voice and add its contribution */ + for (voice = m_channel_list; voice < m_last_channel; voice++) + { + int v = voice->volume[0]; + if (voice->noise_sw) + { + int f = voice->frequency & 0xff; + + /* only update if we have non-zero volume */ + if (v) + { + int hold_time = 1 << (m_f_fracbits - 16); + int hold = voice->noise_hold; + uint32_t delta = f << 4; + uint32_t c = voice->noise_counter; + int16_t noise_data = OUTPUT_LEVEL(0x07 * (v >> 1)); + int i; + + /* add our contribution */ + for (i = 0; i < len; i++) + { + int cnt; + + if (voice->noise_state) + buffer[i]=noise_data; + else + buffer[i]=-noise_data; + + if (hold) + { + hold--; + continue; + } + + hold = hold_time; + + c += delta; + cnt = (c >> 12); + c &= (1 << 12) - 1; + for( ;cnt > 0; cnt--) + { + if ((voice->noise_seed + 1) & 2) voice->noise_state ^= 1; + if (voice->noise_seed & 1) voice->noise_seed ^= 0x28000; + voice->noise_seed >>= 1; + } + } + + /* update the counter and hold time for this voice */ + voice->noise_counter = c; + voice->noise_hold = hold; + } + } + else + { + /* only update if we have non-zero volume */ + if (v) + { + const int16_t *w = &m_waveform[v][voice->waveform_select * 32]; + + /* generate sound into buffer and update the counter for this voice */ + voice->counter = namco_update_one(buffer, len, w, voice->counter, voice->frequency); + } + } + } + } +} + +void namco_device::sound_stream_update(short** outputs, int len) +{ + namco_audio_device::sound_stream_update(outputs,len); +} + +void namco_15xx_device::sound_stream_update(short** outputs, int len) +{ + namco_audio_device::sound_stream_update(outputs,len); +} + +void namco_cus30_device::sound_stream_update(short** outputs, int len) +{ + namco_audio_device::sound_stream_update(outputs,len); +} diff --git a/src/engine/platform/sound/namco.h b/src/engine/platform/sound/namco.h new file mode 100644 index 00000000..07bf9bb6 --- /dev/null +++ b/src/engine/platform/sound/namco.h @@ -0,0 +1,130 @@ +// license:BSD-3-Clause +// copyright-holders:Nicola Salmoria,Aaron Giles +#ifndef MAME_SOUND_NAMCO_H +#define MAME_SOUND_NAMCO_H + +#include +#include + +class namco_audio_device +{ +public: + // configuration + void set_voices(int voices) { m_voices = voices; } + void set_stereo(bool stereo) { m_stereo = stereo; } + + void sound_enable_w(int state); + +protected: + static constexpr unsigned MAX_VOICES = 8; + static constexpr unsigned MAX_VOLUME = 16; + + /* this structure defines the parameters for a channel */ + struct sound_channel + { + uint32_t frequency; + uint32_t counter; + int32_t volume[2]; + int32_t noise_sw; + int32_t noise_state; + int32_t noise_seed; + uint32_t noise_counter; + int32_t noise_hold; + int32_t waveform_select; + }; + + namco_audio_device(uint32_t clock); + + // device-level overrides + void device_start(); + void device_clock_changed(); + + // internal state + + void build_decoded_waveform( uint8_t *rgnbase ); + void update_namco_waveform(int offset, uint8_t data); + uint32_t namco_update_one(short* buffer, int size, const int16_t *wave, uint32_t counter, uint32_t freq); + + /* waveform region */ + uint8_t* m_wave_ptr; + + /* data about the sound system */ + sound_channel m_channel_list[MAX_VOICES]; + sound_channel *m_last_channel; + uint8_t *m_wavedata; + + /* global sound parameters */ + int m_wave_size; + bool m_sound_enable; + int m_namco_clock; + int m_sample_rate; + int m_f_fracbits; + + int m_voices; /* number of voices */ + bool m_stereo; /* set to indicate stereo (e.g., System 1) */ + + uint8_t m_waveram_alloc[0x400]; + + /* decoded waveform table */ + int16_t m_waveform[MAX_VOLUME][512]; + + virtual void sound_stream_update(short** outputs, int len); +}; + +class namco_device : public namco_audio_device +{ +public: + namco_device(uint32_t clock); + + void pacman_sound_w(int offset, uint8_t data); + + uint8_t polepos_sound_r(int offset); + void polepos_sound_w(int offset, uint8_t data); + +protected: + // device-level overrides + virtual void device_start(); + + virtual void sound_stream_update(short** outputs, int len); + +private: + uint8_t m_soundregs[0x400]; +}; + + +class namco_15xx_device : public namco_audio_device +{ +public: + namco_15xx_device(uint32_t clock); + + void namco_15xx_w(int offset, uint8_t data); + uint8_t sharedram_r(int offset); + void sharedram_w(int offset, uint8_t data); + +protected: + // device-level overrides + virtual void device_start(); + + virtual void sound_stream_update(short** outputs, int len); + +private: + uint8_t m_soundregs[0x400]; +}; + + +class namco_cus30_device : public namco_audio_device +{ +public: + namco_cus30_device(uint32_t clock); + + void namcos1_cus30_w(int offset, uint8_t data); /* wavedata + sound registers + RAM */ + uint8_t namcos1_cus30_r(int offset); + void namcos1_sound_w(int offset, uint8_t data); + + void pacman_sound_w(int offset, uint8_t data); + +protected: + virtual void sound_stream_update(short** outputs, int len); +}; + +#endif // MAME_SOUND_NAMCO_H diff --git a/src/engine/song.h b/src/engine/song.h index 187df199..c4e1e27b 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -107,6 +107,9 @@ enum DivSystem { DIV_SYSTEM_SOUND_UNIT, DIV_SYSTEM_MSM6295, DIV_SYSTEM_MSM6258, + DIV_SYSTEM_NAMCO, + DIV_SYSTEM_NAMCO_15XX, + DIV_SYSTEM_NAMCO_CUS30, DIV_SYSTEM_DUMMY }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index d70f3a0b..b9433877 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1966,9 +1966,42 @@ void DivEngine::registerSystems() { {DIV_INS_AMIGA} ); + sysDefs[DIV_SYSTEM_NAMCO]=new DivSysDef( + "Namco WSG", NULL, 0xb9, 0, 3, false, true, 0, false, + "a wavetable sound chip used in Pac-Man, among other early Namco arcade games.", + {"Channel 1", "Channel 2", "Channel 3"}, + {"CH1", "CH2", "CH3"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO}, + {}, + waveOnlyEffectHandler + ); + + sysDefs[DIV_SYSTEM_NAMCO_15XX]=new DivSysDef( + "Namco 15XX WSG", NULL, 0xba, 0, 8, false, true, 0, false, + "successor of the original Namco WSG chip, used in later Namco arcade games.", + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO}, + {}, + waveOnlyEffectHandler + ); + + sysDefs[DIV_SYSTEM_NAMCO_CUS30]=new DivSysDef( + "Namco CUS30 WSG", NULL, 0xbb, 0, 8, false, true, 0, false, + "like Namco 15XX but with stereo sound.", + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO, DIV_INS_NAMCO}, + {}, + waveOnlyEffectHandler + ); + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, - "this is a system designed for testing purposes..", + "this is a system designed for testing purposes.", {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index cc09c96b..588d66b9 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -228,6 +228,10 @@ void FurnaceGUI::drawInsList() { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SU]); name=fmt::sprintf(ICON_FA_MICROCHIP " %.2X: %s##_INS%d",i,ins->name,i); break; + case DIV_INS_NAMCO: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NAMCO]); + name=fmt::sprintf(ICON_FA_PIE_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; default: ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); diff --git a/src/gui/gui.h b/src/gui/gui.h index babb4819..853886e6 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -149,6 +149,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_MULTIPCM, GUI_COLOR_INSTR_SNES, GUI_COLOR_INSTR_SU, + GUI_COLOR_INSTR_NAMCO, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_FM, @@ -937,6 +938,7 @@ class FurnaceGUI { int horizontalDataView; int noMultiSystem; int oldMacroVSlider; + int displayAllInsTypes; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -1026,6 +1028,7 @@ class FurnaceGUI { horizontalDataView(0), noMultiSystem(0), oldMacroVSlider(0), + displayAllInsTypes(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 1db6d413..a1656cdd 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -79,7 +79,7 @@ const int vgmVersions[6]={ 0x171 }; -const char* insTypes[DIV_INS_MAX]={ +const char* insTypes[DIV_INS_MAX+1]={ "Standard (SMS/NES)", "FM (4-operator)", "Game Boy", @@ -111,6 +111,8 @@ const char* insTypes[DIV_INS_MAX]={ "MultiPCM", "SNES", "Sound Unit", + "Namco WSG", + NULL }; const char* sampleDepths[17]={ @@ -754,6 +756,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_MULTIPCM,"",ImVec4(1.0f,0.8f,0.1f,1.0f)), D(GUI_COLOR_INSTR_SNES,"",ImVec4(0.8f,0.7f,1.0f,1.0f)), D(GUI_COLOR_INSTR_SU,"",ImVec4(0.95f,0.98f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_NAMCO,"",ImVec4(1.0f,1.0f,0.0f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)), @@ -877,6 +880,11 @@ const int availableSystems[]={ DIV_SYSTEM_MMC5, DIV_SYSTEM_SCC, DIV_SYSTEM_SCC_PLUS, + DIV_SYSTEM_MSM6258, + DIV_SYSTEM_MSM6295, + DIV_SYSTEM_NAMCO, + DIV_SYSTEM_NAMCO_15XX, + DIV_SYSTEM_NAMCO_CUS30, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 3e129ef5..663f50f8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1404,7 +1404,15 @@ void FurnaceGUI::drawInsEdit() { } */ if (ImGui::BeginCombo("##Type",insTypes[insType])) { - for (DivInstrumentType i: e->getPossibleInsTypes()) { + std::vector insTypeList; + if (settings.displayAllInsTypes) { + for (int i=0; insTypes[i]; i++) { + insTypeList.push_back((DivInstrumentType)i); + } + } else { + insTypeList=e->getPossibleInsTypes(); + } + for (DivInstrumentType i: insTypeList) { if (ImGui::Selectable(insTypes[i],insType==i)) { ins->type=i; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 13ed6847..a5e0127d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1368,6 +1368,7 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_NAMCO,"Namco WSG"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } @@ -1797,6 +1798,7 @@ void FurnaceGUI::drawSettings() { // these are the cheat codes: // "Debug" - toggles mobile UI // "Nice Amiga cover of the song!" - enables hidden systems (YMU759/SoundUnit/Dummy) + // "42 63" - enables all instrument types if (ImGui::BeginTabItem("Cheat Codes")) { ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; @@ -1824,6 +1826,10 @@ void FurnaceGUI::drawSettings() { mmlString[30]=":smile: :star_struck: :sunglasses: :ok_hand:"; settings.hiddenSystems=!settings.hiddenSystems; } + if (checker==0xe888896b && checker1==0xbde) { + mmlString[30]="enabled all instrument types"; + settings.displayAllInsTypes=!settings.displayAllInsTypes; + } mmlString[31]=""; } @@ -1944,6 +1950,7 @@ void FurnaceGUI::syncSettings() { settings.horizontalDataView=e->getConfInt("horizontalDataView",0); settings.noMultiSystem=e->getConfInt("noMultiSystem",0); settings.oldMacroVSlider=e->getConfInt("oldMacroVSlider",0); + settings.displayAllInsTypes=e->getConfInt("displayAllInsTypes",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -2018,6 +2025,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.horizontalDataView,0,1); clampSetting(settings.noMultiSystem,0,1); clampSetting(settings.oldMacroVSlider,0,1); + clampSetting(settings.displayAllInsTypes,0,1); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); if (settings.initialSys.size()<4) { @@ -2140,6 +2148,7 @@ void FurnaceGUI::commitSettings() { e->setConf("horizontalDataView",settings.horizontalDataView); e->setConf("noMultiSystem",settings.noMultiSystem); e->setConf("oldMacroVSlider",settings.oldMacroVSlider); + e->setConf("displayAllInsTypes",settings.displayAllInsTypes); // colors for (int i=0; i