Skip to content

Commit

Permalink
audio/cclimber: use a timer+dac instead of MAME samples, add support …
Browse files Browse the repository at this point in the history
…for looping
  • Loading branch information
happppp committed Jun 8, 2022
1 parent 1fc3a3f commit 3e4d77b
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 80 deletions.
117 changes: 60 additions & 57 deletions 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<int16_t[]>(2 * m_samples_region.bytes());
save_pointer(NAME(m_sample_buf), 2 * m_samples_region.bytes());
}

//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
Expand All @@ -26,94 +26,97 @@ 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
//-------------------------------------------------

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
//-------------------------------------------------

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));
}
47 changes: 26 additions & 21 deletions 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)

Expand All @@ -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<int16_t[]> 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<samples_device> m_samples;
required_region_ptr<uint8_t> m_samples_region;
required_device<dac_4bit_r2r_device> m_dac;
required_device<filter_volume_device> m_volume;
required_region_ptr<u8> 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;
};


Expand Down
4 changes: 2 additions & 2 deletions src/mame/drivers/cclimber.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 )

0 comments on commit 3e4d77b

Please sign in to comment.