From 3e4d77b9374d525865f6900420412722359cf906 Mon Sep 17 00:00:00 2001 From: hap Date: Thu, 9 Jun 2022 00:22:43 +0200 Subject: [PATCH] audio/cclimber: use a timer+dac instead of MAME samples, add support for looping --- src/mame/audio/cclimber.cpp | 117 +++++++++++++++++----------------- src/mame/audio/cclimber.h | 47 ++++++++------ src/mame/drivers/cclimber.cpp | 4 +- 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/src/mame/audio/cclimber.cpp b/src/mame/audio/cclimber.cpp index 60e488afe1606..13ad9e38d18a0 100644 --- a/src/mame/audio/cclimber.cpp +++ b/src/mame/audio/cclimber.cpp @@ -1,20 +1,20 @@ // license:BSD-3-Clause -// copyright-holders:Nicola Salmoria +// copyright-holders:Nicola Salmoria, hap +/*************************************************************************** + +Crazy Climber sound hardware + +It has an AY-3-8910. +And 8KB ROM for voice samples, done with TTL chips. + +***************************************************************************/ + #include "emu.h" #include "audio/cclimber.h" #include "sound/ay8910.h" -// macro to convert 4-bit unsigned samples to 16-bit signed samples -#define SAMPLE_CONV4(a) (0x1111*((a&0x0f))-0x8000) - -SAMPLES_START_CB_MEMBER( cclimber_audio_device::sh_start ) -{ - m_sample_buf = std::make_unique(2 * m_samples_region.bytes()); - save_pointer(NAME(m_sample_buf), 2 * m_samples_region.bytes()); -} - //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** @@ -26,17 +26,14 @@ DEFINE_DEVICE_TYPE(CCLIMBER_AUDIO, cclimber_audio_device, "cclimber_audio", "Cra // cclimber_audio_device: Constructor //------------------------------------------------- -cclimber_audio_device::cclimber_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : +cclimber_audio_device::cclimber_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, CCLIMBER_AUDIO, tag, owner, clock), - m_sample_buf(nullptr), - m_sample_num(0), - m_sample_freq(0), - m_sample_volume(0), - m_sample_clockdiv(2), - m_samples(*this, "samples"), - m_samples_region(*this, "samples") -{ -} + m_dac(*this, "dac"), + m_volume(*this, "volume"), + m_rom(*this, "samples"), + m_sample_clockdiv(2) +{ } + //------------------------------------------------- // device_start - device-specific startup @@ -44,11 +41,24 @@ cclimber_audio_device::cclimber_audio_device(const machine_config &mconfig, cons void cclimber_audio_device::device_start() { - save_item(NAME(m_sample_num)); - save_item(NAME(m_sample_freq)); - save_item(NAME(m_sample_volume)); + m_address = 0; + m_start_address = 0; + m_loop_address = 0; + m_sample_rate = 0; + m_sample_trigger = 0; + + m_sample_timer = timer_alloc(FUNC(cclimber_audio_device::sample_tick), this); + m_sample_timer->adjust(attotime::zero); + + // register for savestates + save_item(NAME(m_address)); + save_item(NAME(m_start_address)); + save_item(NAME(m_loop_address)); + save_item(NAME(m_sample_rate)); + save_item(NAME(m_sample_trigger)); } + //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- @@ -56,64 +66,57 @@ void cclimber_audio_device::device_start() void cclimber_audio_device::device_add_mconfig(machine_config &config) { ay8910_device &aysnd(AY8910(config, "aysnd", DERIVED_CLOCK(1, 1))); - aysnd.port_a_write_callback().set(FUNC(cclimber_audio_device::sample_select_w)); + aysnd.port_a_write_callback().set(FUNC(cclimber_audio_device::start_address_w)); + aysnd.port_b_write_callback().set(FUNC(cclimber_audio_device::loop_address_w)); aysnd.add_route(ALL_OUTPUTS, ":speaker", 0.5); - SAMPLES(config, m_samples); - m_samples->set_channels(1); - m_samples->set_samples_start_callback(FUNC(cclimber_audio_device::sh_start)); - m_samples->add_route(ALL_OUTPUTS, ":speaker", 0.5); + DAC_4BIT_R2R(config, m_dac).add_route(ALL_OUTPUTS, "volume", 0.5); + + FILTER_VOLUME(config, m_volume).add_route(ALL_OUTPUTS, ":speaker", 1.0); } -void cclimber_audio_device::sample_select_w(uint8_t data) -{ - m_sample_num = data; -} +//------------------------------------------------- +// handlers +//------------------------------------------------- -void cclimber_audio_device::sample_rate_w(uint8_t data) +void cclimber_audio_device::sample_rate_w(u8 data) { - // calculate the sampling frequency - m_sample_freq = clock() / m_sample_clockdiv / (256 - data); + m_sample_rate = data; } -void cclimber_audio_device::sample_volume_w(uint8_t data) +void cclimber_audio_device::sample_volume_w(u8 data) { - m_sample_volume = data & 0x1f; // range 0-31 + m_volume->flt_volume_set_volume(double(data & 0x1f) / 31.0); // range 0-31 } void cclimber_audio_device::sample_trigger(int state) { - if (state == 0) - return; + // start playing on rising edge + if (state && !m_sample_trigger) + m_address = m_start_address * 64; - play_sample(32 * m_sample_num, m_sample_freq, m_sample_volume); + m_sample_trigger = state; } -void cclimber_audio_device::sample_trigger_w(uint8_t data) +void cclimber_audio_device::sample_trigger_w(u8 data) { sample_trigger(data != 0); } - -void cclimber_audio_device::play_sample(int start,int freq,int volume) +TIMER_CALLBACK_MEMBER(cclimber_audio_device::sample_tick) { - int romlen = m_samples_region.bytes(); - - // decode the ROM samples - int len = 0; - while (start + len < romlen && m_samples_region[start+len] != 0x70) - { - int sample; + u16 mask = ((m_rom.bytes() << 1) - 1) & 0x3fff; + u8 data = m_rom[m_address >> 1 & mask]; - sample = (m_samples_region[start + len] & 0xf0) >> 4; - m_sample_buf[2*len] = SAMPLE_CONV4(sample) * volume / 31; + // sample end marker, continue from loop point + if (data == 0x70) + m_address = m_loop_address * 64; - sample = m_samples_region[start + len] & 0x0f; - m_sample_buf[2*len + 1] = SAMPLE_CONV4(sample) * volume / 31; + m_dac->write((m_address & 1) ? (data & 0xf) : (data >> 4)); - len++; - } + if (m_sample_trigger) + m_address++; - m_samples->start_raw(0, m_sample_buf.get(), 2 * len, freq); + m_sample_timer->adjust(attotime::from_ticks(256 - m_sample_rate, clock() / m_sample_clockdiv)); } diff --git a/src/mame/audio/cclimber.h b/src/mame/audio/cclimber.h index 8d5bc8131bfea..1f6d56703cb14 100644 --- a/src/mame/audio/cclimber.h +++ b/src/mame/audio/cclimber.h @@ -1,18 +1,18 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Nicola Salmoria, hap /*************************************************************************** - cclimber.h - - Functions to emulate the cclimber audio boards + Crazy Climber sound hardware ***************************************************************************/ + #ifndef MAME_AUDIO_CCLIMBER_H #define MAME_AUDIO_CCLIMBER_H #pragma once -#include "sound/samples.h" +#include "sound/dac.h" +#include "sound/flt_vol.h" DECLARE_DEVICE_TYPE(CCLIMBER_AUDIO, cclimber_audio_device) @@ -22,34 +22,39 @@ class cclimber_audio_device : public device_t { public: // construction/destruction - cclimber_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + cclimber_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - auto &set_sample_clockdiv(uint8_t div) { m_sample_clockdiv = div; return *this; } // determines base sound pitch (default 2) + auto &set_sample_clockdiv(u8 div) { m_sample_clockdiv = div; return *this; } // determines base sound pitch (default 2) void sample_trigger(int state); - void sample_trigger_w(uint8_t data); - void sample_rate_w(uint8_t data); - void sample_volume_w(uint8_t data); + void sample_trigger_w(u8 data); + void sample_rate_w(u8 data); + void sample_volume_w(u8 data); protected: // device level overrides virtual void device_start() override; + virtual void device_reset() override { sample_volume_w(0); } virtual void device_add_mconfig(machine_config &config) override; - void play_sample(int start, int freq, int volume); - private: - std::unique_ptr m_sample_buf; // buffer to decode samples at run time - uint8_t m_sample_num; - uint32_t m_sample_freq; - uint8_t m_sample_volume; - uint8_t m_sample_clockdiv; - required_device m_samples; - required_region_ptr m_samples_region; + required_device m_dac; + required_device m_volume; + required_region_ptr m_rom; + + void start_address_w(u8 data) { m_start_address = data; } + void loop_address_w(u8 data) { m_loop_address = data; } + + u8 m_sample_clockdiv; - void sample_select_w(uint8_t data); + emu_timer *m_sample_timer; + TIMER_CALLBACK_MEMBER(sample_tick); - SAMPLES_START_CB_MEMBER( sh_start ); + u16 m_address; + u8 m_start_address; + u8 m_loop_address; + u8 m_sample_rate; + int m_sample_trigger; }; diff --git a/src/mame/drivers/cclimber.cpp b/src/mame/drivers/cclimber.cpp index 4e1114beb8fad..3db0673c6150c 100644 --- a/src/mame/drivers/cclimber.cpp +++ b/src/mame/drivers/cclimber.cpp @@ -154,7 +154,6 @@ I/O C ;AY-3-8910 Data Read Reg. lives - $6155 TODO: - - some of the samples should loop (truck, helicopter, motorcycle) - COINB DSW is missing - few issues in cocktail mode @@ -477,6 +476,7 @@ void cclimber_state::cclimber_portmap(address_map &map) map.global_mask(0xff); map(0x08, 0x09).w("cclimber_audio:aysnd", FUNC(ay8910_device::address_data_w)); map(0x0c, 0x0c).r("cclimber_audio:aysnd", FUNC(ay8910_device::data_r)); + map(0x0d, 0x0d).nopw(); } void cclimber_state::rpatrol_portmap(address_map &map) @@ -2808,4 +2808,4 @@ GAME( 1983, guzzlers, guzzler, guzzler, guzzler, cclimber_state, empty_i GAME( 1983, yamato, 0, yamato, yamato, cclimber_state, init_yamato, ROT90, "Sega", "Yamato (US)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE ) GAME( 1983, yamato2, yamato, yamato, yamato, cclimber_state, init_yamato, ROT90, "Sega", "Yamato (World?)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE ) -GAME( 1983, toprollr, 0, toprollr, toprollr, cclimber_state, init_toprollr, ROT90, "Jaleco", "Top Roller", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) +GAME( 1983, toprollr, 0, toprollr, toprollr, cclimber_state, init_toprollr, ROT90, "Jaleco", "Top Roller", MACHINE_SUPPORTS_SAVE )