summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/es8712.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/es8712.cpp')
-rw-r--r--src/devices/sound/es8712.cpp349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/devices/sound/es8712.cpp b/src/devices/sound/es8712.cpp
new file mode 100644
index 00000000000..f6087f83dc1
--- /dev/null
+++ b/src/devices/sound/es8712.cpp
@@ -0,0 +1,349 @@
+// license:BSD-3-Clause
+// copyright-holders:Quench
+/**********************************************************************************************
+ *
+ * Streaming single channel ADPCM core for the ES8712 chip
+ * Chip is branded by Excellent Systems, probably OEM'd.
+ *
+ * Samples are currently looped, but whether they should and how, is unknown.
+ * Interface to the chip is also not 100% clear.
+ * Should there be any status signals signifying busy, end of sample - etc?
+ *
+ * Heavily borrowed from the OKI M6295 source
+ *
+ **********************************************************************************************/
+
+
+#include "emu.h"
+#include "es8712.h"
+
+#define MAX_SAMPLE_CHUNK 10000
+
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[49*16];
+
+
+// device type definition
+const device_type ES8712 = &device_creator<es8712_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// es8712_device - constructor
+//-------------------------------------------------
+
+es8712_device::es8712_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ES8712, "ES8712", tag, owner, clock, "es8712", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_playing(0),
+ m_base_offset(0),
+ m_sample(0),
+ m_count(0),
+ m_signal(0),
+ m_step(0),
+ m_start(0),
+ m_end(0),
+ m_repeat(0),
+ m_bank_offset(0),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - start emulation of an ES8712 chip
+//-------------------------------------------------
+
+void es8712_device::device_start()
+{
+ compute_tables();
+
+ m_start = 0;
+ m_end = 0;
+ m_repeat = 0;
+
+ m_bank_offset = 0;
+
+ /* generate the name and create the stream */
+ m_stream = stream_alloc(0, 1, clock());
+
+ /* initialize the rest of the structure */
+ m_signal = -2;
+
+ es8712_state_save_register();
+}
+
+
+//-------------------------------------------------
+// device_reset - stop emulation of an ES8712-compatible chip
+//-------------------------------------------------
+
+void es8712_device::device_reset()
+{
+ if (m_playing)
+ {
+ /* update the stream, then turn it off */
+ m_stream->update();
+ m_playing = 0;
+ m_repeat = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - update the sound chip so that it is in sync with CPU execution
+//-------------------------------------------------
+
+void es8712_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+
+ /* generate them into our buffer */
+ generate_adpcm(buffer, samples);
+}
+
+
+//-------------------------------------------------
+// compute_tables -- compute the difference tables
+//-------------------------------------------------
+
+void es8712_device::compute_tables()
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// generate_adpcm -- general ADPCM decoding routine
+//-------------------------------------------------
+
+void es8712_device::generate_adpcm(stream_sample_t *buffer, int samples)
+{
+ /* if this chip is active */
+ if (m_playing)
+ {
+ UINT8 *base = &m_rom[m_bank_offset + m_base_offset];
+ int sample = m_sample;
+ int signal = m_signal;
+ int count = m_count;
+ int step = m_step;
+ int val;
+
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* compute the new amplitude and update the current step */
+ val = base[sample / 2] >> (((sample & 1) << 2) ^ 4);
+ signal += diff_lookup[step * 16 + (val & 15)];
+
+ /* clamp to the maximum */
+ if (signal > 2047)
+ signal = 2047;
+ else if (signal < -2048)
+ signal = -2048;
+
+ /* adjust the step size and clamp */
+ step += index_shift[val & 7];
+ if (step > 48)
+ step = 48;
+ else if (step < 0)
+ step = 0;
+
+ /* output to the buffer */
+ *buffer++ = signal * 16;
+ samples--;
+
+ /* next! */
+ if (++sample >= count)
+ {
+ if (m_repeat)
+ {
+ sample = 0;
+ signal = -2;
+ step = 0;
+ }
+ else
+ {
+ m_playing = 0;
+ break;
+ }
+ }
+ }
+
+ /* update the parameters */
+ m_sample = sample;
+ m_signal = signal;
+ m_step = step;
+ }
+
+ /* fill the rest with silence */
+ while (samples--)
+ *buffer++ = 0;
+}
+
+
+
+//-------------------------------------------------
+// state save support for MAME
+//-------------------------------------------------
+
+void es8712_device::es8712_state_save_register()
+{
+ save_item(NAME(m_bank_offset));
+
+ save_item(NAME(m_playing));
+ save_item(NAME(m_sample));
+ save_item(NAME(m_count));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_step));
+
+ save_item(NAME(m_base_offset));
+
+ save_item(NAME(m_start));
+ save_item(NAME(m_end));
+ save_item(NAME(m_repeat));
+}
+
+
+
+
+//-------------------------------------------------
+// es8712_set_bank_base -- set the base of the bank on a given chip
+//-------------------------------------------------
+
+void es8712_device::set_bank_base(int base)
+{
+ m_stream->update();
+ m_bank_offset = base;
+}
+
+
+//-------------------------------------------------
+// es8712_set_frequency -- dynamically adjusts the frequency of a given ADPCM chip
+//-------------------------------------------------
+
+void es8712_device::set_frequency(int frequency)
+{
+ /* update the stream and set the new base */
+ m_stream->update();
+ m_stream->set_sample_rate(frequency);
+}
+
+
+//-------------------------------------------------
+// play -- Begin playing the addressed sample
+//-------------------------------------------------
+
+void es8712_device::play()
+{
+ if (m_start < m_end)
+ {
+ if (!m_playing)
+ {
+ m_playing = 1;
+ m_base_offset = m_start;
+ m_sample = 0;
+ m_count = 2 * (m_end - m_start + 1);
+ m_repeat = 0;//1;
+
+ /* also reset the ADPCM parameters */
+ m_signal = -2;
+ m_step = 0;
+ }
+ }
+ /* invalid samples go here */
+ else
+ {
+ logerror("ES871295:'%s' requested to play invalid sample range %06x-%06x\n", tag(), m_start, m_end);
+
+ if (m_playing)
+ {
+ /* update the stream */
+ m_stream->update();
+ m_playing = 0;
+ }
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ es8712_data_0_w -- generic data write functions
+ es8712_data_1_w
+
+***********************************************************************************************/
+
+/**********************************************************************************************
+ *
+ * offset Start End
+ * 0hmmll - 0HMMLL
+ * 00 ----ll
+ * 01 --mm--
+ * 02 0h----
+ * 03 ----LL
+ * 04 --MM--
+ * 05 0H----
+ * 06 Go!
+ *
+ * Offsets are written in the order -> 00, 02, 01, 03, 05, 04, 06
+ * Offset 06 is written with the same value as offset 04.
+ *
+***********************************************************************************************/
+
+WRITE8_MEMBER( es8712_device::es8712_w )
+{
+ switch (offset)
+ {
+ case 00: m_start &= 0x000fff00;
+ m_start |= ((data & 0xff) << 0); break;
+ case 01: m_start &= 0x000f00ff;
+ m_start |= ((data & 0xff) << 8); break;
+ case 02: m_start &= 0x0000ffff;
+ m_start |= ((data & 0x0f) << 16); break;
+ case 03: m_end &= 0x000fff00;
+ m_end |= ((data & 0xff) << 0); break;
+ case 04: m_end &= 0x000f00ff;
+ m_end |= ((data & 0xff) << 8); break;
+ case 05: m_end &= 0x0000ffff;
+ m_end |= ((data & 0x0f) << 16); break;
+ case 06:
+ play(); break;
+ default: break;
+ }
+ m_start &= 0xfffff; m_end &= 0xfffff;
+}