diff options
Diffstat (limited to 'src/devices/sound/va_eg.cpp')
-rw-r--r-- | src/devices/sound/va_eg.cpp | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/devices/sound/va_eg.cpp b/src/devices/sound/va_eg.cpp new file mode 100644 index 00000000000..341d7c71ac8 --- /dev/null +++ b/src/devices/sound/va_eg.cpp @@ -0,0 +1,134 @@ +// license:BSD-3-Clause +// copyright-holders:m1macrophage + +#include "emu.h" +#include "va_eg.h" +#include "machine/rescap.h" + +// The envelope is considered completed after this many time constants. +static constexpr const float TIME_CONSTANTS_TO_END = 10; + +DEFINE_DEVICE_TYPE(VA_RC_EG, va_rc_eg_device, "va_rc_eg", "RC-based Envelope Generator") + +va_rc_eg_device::va_rc_eg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, VA_RC_EG, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_stream(nullptr) + // Initialize to a valid state. + , m_r(RES_M(1)) + , m_c(CAP_U(1)) + , m_rc_inv(1.0F / (m_r * m_c)) + , m_v_start(0) + , m_v_end(0) +{ +} + +va_rc_eg_device &va_rc_eg_device::set_r(float r) +{ + assert(r > 0); + if (r == m_r) + return *this; + if (m_stream != nullptr) + m_stream->update(); + + snapshot(); // Snapshots voltage using the old `r` value. + m_r = r; + m_rc_inv = 1.0F / (m_r * m_c); + return *this; +} + +va_rc_eg_device &va_rc_eg_device::set_c(float c) +{ + assert(c > 0); + if (c == m_c) + return *this; + if (m_stream != nullptr) + m_stream->update(); + + snapshot(); // Snapshots voltage using the old `c` value. + m_c = c; + m_rc_inv = 1.0F / (m_r * m_c); + return *this; +} + +va_rc_eg_device &va_rc_eg_device::set_target_v(float v) +{ + if (v == m_v_end) + return *this; + if (m_stream != nullptr) + m_stream->update(); + + snapshot(); + m_v_end = v; + return *this; +} + +va_rc_eg_device &va_rc_eg_device::set_instant_v(float v) +{ + if (m_stream != nullptr) + m_stream->update(); + + m_v_start = v; + m_v_end = v; + m_t_start = has_running_machine() ? machine().time() : attotime::zero; + m_t_end_approx = m_t_start; + return *this; +} + +float va_rc_eg_device::get_v(const attotime &t) const +{ + assert(t >= m_t_start); + const float delta_t = float((t - m_t_start).as_double()); + return m_v_start + (m_v_end - m_v_start) * (1.0F - expf(-delta_t * m_rc_inv)); +} + +float va_rc_eg_device::get_v() const +{ + return get_v(machine().time()); +} + +void va_rc_eg_device::device_start() +{ + m_stream = stream_alloc(0, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE); + save_item(NAME(m_r)); + save_item(NAME(m_c)); + save_item(NAME(m_rc_inv)); + save_item(NAME(m_v_start)); + save_item(NAME(m_v_end)); + save_item(NAME(m_t_start)); + save_item(NAME(m_t_end_approx)); +} + +void va_rc_eg_device::sound_stream_update(sound_stream &stream) +{ + assert(stream.input_count() == 0 && stream.output_count() == 1); + attotime t = stream.start_time(); + + if (t >= m_t_end_approx) + { + // Avoid expensive get_v() calls if the envelope stage has completed. + stream.fill(0, m_v_end); + return; + } + + const int n = stream.samples(); + const attotime dt = stream.sample_period(); + for (int i = 0; i < n; ++i, t += dt) + stream.put(0, i, get_v(t)); +} + +void va_rc_eg_device::snapshot() +{ + if (has_running_machine()) + { + const attotime now = machine().time(); + m_v_start = get_v(now); + m_t_start = now; + } + else + { + m_v_start = 0; + m_t_start = attotime::zero; + } + m_t_end_approx = m_t_start + attotime::from_double(TIME_CONSTANTS_TO_END * m_r * m_c); +} |