summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/va_eg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/va_eg.cpp')
-rw-r--r--src/devices/sound/va_eg.cpp134
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);
+}