diff options
Diffstat (limited to 'src/devices/sound/imaadpcm.cpp')
-rw-r--r-- | src/devices/sound/imaadpcm.cpp | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/devices/sound/imaadpcm.cpp b/src/devices/sound/imaadpcm.cpp new file mode 100644 index 00000000000..cd6f08166a3 --- /dev/null +++ b/src/devices/sound/imaadpcm.cpp @@ -0,0 +1,133 @@ +// license:BSD-3-Clause +// copyright-holders:Andrew Gardner,Aaron Giles +/*************************************************************************** + + imaadpcm.cpp + + IMA ADPCM emulation. + +***************************************************************************/ + +#include "emu.h" +#include "imaadpcm.h" + + +//************************************************************************** +// ADPCM STATE HELPER +//************************************************************************** + +// ADPCM state and tables +bool ima_adpcm_state::s_tables_computed = false; +const int8_t ima_adpcm_state::s_index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; +int ima_adpcm_state::s_diff_lookup[89*16]; + +//------------------------------------------------- +// reset - reset the ADPCM state +//------------------------------------------------- + +void ima_adpcm_state::reset() +{ + // reset the signal/step + m_signal = m_loop_signal = 0; + m_step = m_loop_step = 0; + m_saved = false; +} + + +//------------------------------------------------- +// clock - decode single nibble and update +// ADPCM output +//------------------------------------------------- + +int16_t ima_adpcm_state::clock(uint8_t nibble) +{ + // update the signal + m_signal += s_diff_lookup[m_step * 16 + (nibble & 15)]; + + // clamp to the maximumf + if (m_signal > 32767) + m_signal = 32767; + else if (m_signal < -32768) + m_signal = -32768; + + // adjust the step size and clamp + m_step += s_index_shift[nibble & 7]; + if (m_step > 88) + m_step = 88; + else if (m_step < 0) + m_step = 0; + + // return the signal + return m_signal; +} + + +//------------------------------------------------- +// save - save current ADPCM state to buffer +//------------------------------------------------- + +void ima_adpcm_state::save() +{ + if (!m_saved) + { + m_loop_signal = m_signal; + m_loop_step = m_step; + m_saved = true; + } +} + + +//------------------------------------------------- +// restore - restore previous ADPCM state +// from buffer +//------------------------------------------------- + +void ima_adpcm_state::restore() +{ + m_signal = m_loop_signal; + m_step = m_loop_step; +} + + +//------------------------------------------------- +// compute_tables - precompute tables for faster +// sound generation +//------------------------------------------------- + +void ima_adpcm_state::compute_tables() +{ + // skip if we already did it + if (s_tables_computed) + return; + s_tables_computed = true; + + // nibble to bit map + static const int8_t 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} + }; + + // loop over all possible steps + for (int step = -8; step <= 80; step++) + { + // compute the step value + int stepval = std::min(floor(16.0 * pow(11.0 / 10.0, (double)step)), 32767.); + + // manual correction of some early values + if (step == -5 || step == -4) + stepval++; + + // loop over all nibbles and compute the difference + for (int nib = 0; nib < 16; nib++) + { + s_diff_lookup[(step + 8)*16 + nib] = nbl2bit[nib][0] * + (stepval * nbl2bit[nib][1] + + stepval/2 * nbl2bit[nib][2] + + stepval/4 * nbl2bit[nib][3] + + stepval/8); + } + } +} |