summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/gew.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/gew.cpp')
-rw-r--r--src/devices/sound/gew.cpp641
1 files changed, 641 insertions, 0 deletions
diff --git a/src/devices/sound/gew.cpp b/src/devices/sound/gew.cpp
new file mode 100644
index 00000000000..274acbc2494
--- /dev/null
+++ b/src/devices/sound/gew.cpp
@@ -0,0 +1,641 @@
+// license:BSD-3-Clause
+// copyright-holders:Miguel Angel Horna
+
+#include "emu.h"
+#include "gew.h"
+#include "wavwrite.h"
+
+ALLOW_SAVE_TYPE(gew_pcm_device::state_t); // allow save_item on a non-fundamental type
+
+/*******************************
+ ENVELOPE SECTION
+*******************************/
+
+// Times are based on a 44100Hz timebase. It's adjusted to the actual sampling rate on startup
+
+const double gew_pcm_device::BASE_TIMES[64] = {
+ 0, 0, 0, 0,
+ 6222.95, 4978.37, 4148.66, 3556.01,
+ 3111.47, 2489.21, 2074.33, 1778.00,
+ 1555.74, 1244.63, 1037.19, 889.02,
+ 777.87, 622.31, 518.59, 444.54,
+ 388.93, 311.16, 259.32, 222.27,
+ 194.47, 155.60, 129.66, 111.16,
+ 97.23, 77.82, 64.85, 55.60,
+ 48.62, 38.91, 32.43, 27.80,
+ 24.31, 19.46, 16.24, 13.92,
+ 12.15, 9.75, 8.12, 6.98,
+ 6.08, 4.90, 4.08, 3.49,
+ 3.04, 2.49, 2.13, 1.90,
+ 1.72, 1.41, 1.18, 1.04,
+ 0.91, 0.73, 0.59, 0.50,
+ 0.45, 0.45, 0.45, 0.45
+};
+
+constexpr uint32_t gew_pcm_device::TL_SHIFT;
+constexpr uint32_t gew_pcm_device::EG_SHIFT;
+
+void gew_pcm_device::retrigger_sample(slot_t &slot)
+{
+ slot.m_offset = 0;
+ slot.m_prev_sample = 0;
+ slot.m_total_level = slot.m_dest_total_level << TL_SHIFT;
+
+ envelope_generator_calc(slot);
+ slot.m_envelope_gen.m_state = state_t::ATTACK;
+ slot.m_envelope_gen.m_volume = 0;
+
+#if MULTIPCM_LOG_SAMPLES
+ dump_sample(slot);
+#endif
+}
+
+void gew_pcm_device::update_step(slot_t &slot)
+{
+ const uint8_t oct = (slot.m_octave - 1) & 0xf;
+ uint32_t pitch = m_freq_step_table[slot.m_pitch];
+ if (oct & 0x8)
+ {
+ pitch >>= (16 - oct);
+ }
+ else
+ {
+ pitch <<= oct;
+ }
+ slot.m_step = pitch / m_rate;
+}
+
+void gew_pcm_device::envelope_generator_init(const double (&rates)[64], double attack_decay_ratio)
+{
+ for (int32_t i = 4; i < 0x40; ++i)
+ {
+ // Times are based on 44100Hz clock, adjust to real chip clock
+ m_attack_step[i] = (float)(0x400 << EG_SHIFT) / (float)(rates[i] * 44100.0 / 1000.0);
+ m_decay_release_step[i] = (float)(0x400 << EG_SHIFT) / (float)(rates[i] * attack_decay_ratio * 44100.0 / 1000.0);
+ }
+ m_attack_step[0] = m_attack_step[1] = m_attack_step[2] = m_attack_step[3] = 0;
+ m_attack_step[0x3f] = 0x400 << EG_SHIFT;
+ m_decay_release_step[0] = m_decay_release_step[1] = m_decay_release_step[2] = m_decay_release_step[3] = 0;
+}
+
+int32_t gew_pcm_device::envelope_generator_update(slot_t &slot)
+{
+ switch (slot.m_envelope_gen.m_state)
+ {
+ case state_t::ATTACK:
+ slot.m_envelope_gen.m_volume += slot.m_envelope_gen.m_attack_rate;
+ if (slot.m_envelope_gen.m_volume >= (0x3ff << EG_SHIFT))
+ {
+ slot.m_envelope_gen.m_state = state_t::DECAY1;
+ if (slot.m_envelope_gen.m_decay1_rate >= (0x400 << EG_SHIFT)) //Skip DECAY1, go directly to DECAY2
+ {
+ slot.m_envelope_gen.m_state = state_t::DECAY2;
+ }
+ slot.m_envelope_gen.m_volume = 0x3ff << EG_SHIFT;
+ }
+ break;
+ case state_t::DECAY1:
+ slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_decay1_rate;
+ if (slot.m_envelope_gen.m_volume <= 0)
+ {
+ slot.m_envelope_gen.m_volume = 0;
+ }
+ if (slot.m_envelope_gen.m_volume >> (EG_SHIFT + 6) <= slot.m_envelope_gen.m_decay_level)
+ {
+ slot.m_envelope_gen.m_state = state_t::DECAY2;
+ }
+ break;
+ case state_t::DECAY2:
+ slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_decay2_rate;
+ if (slot.m_envelope_gen.m_volume <= 0)
+ {
+ slot.m_envelope_gen.m_volume = 0;
+ }
+ break;
+ case state_t::RELEASE:
+ slot.m_envelope_gen.m_volume -= slot.m_envelope_gen.m_release_rate;
+ if (slot.m_envelope_gen.m_volume <= 0)
+ {
+ slot.m_envelope_gen.m_volume = 0;
+ slot.m_playing = false;
+ }
+ break;
+ default:
+ return 1 << TL_SHIFT;
+ }
+
+ // TODO: this is currently only implemented for GEW7, it's probably not accurate
+ if (slot.m_envelope_gen.m_reverb && slot.m_envelope_gen.m_state != state_t::ATTACK
+ && (slot.m_envelope_gen.m_volume >> EG_SHIFT) <= 0x300)
+ {
+ slot.m_envelope_gen.m_decay1_rate = m_decay_release_step[17];
+ slot.m_envelope_gen.m_decay2_rate = m_decay_release_step[17];
+ slot.m_envelope_gen.m_release_rate = m_decay_release_step[17];
+ }
+
+ return m_linear_to_exp_volume[slot.m_envelope_gen.m_volume >> EG_SHIFT];
+}
+
+uint32_t gew_pcm_device::get_rate(uint32_t *steps, int32_t rate, uint32_t val)
+{
+ if (val == 0)
+ {
+ return steps[0];
+ }
+ if (val == 0xf)
+ {
+ return steps[0x3f];
+ }
+
+ const int r = std::clamp(4 * (int)val + rate, 0, 0x3f);
+ return steps[r];
+}
+
+void gew_pcm_device::envelope_generator_calc(slot_t &slot)
+{
+ int32_t octave = slot.m_octave;
+ if (octave & 8) {
+ octave = octave - 16;
+ }
+
+ int32_t rate;
+ if (slot.m_sample.m_key_rate_scale != 0xf)
+ {
+ rate = (octave + slot.m_sample.m_key_rate_scale) * 2 + BIT(slot.m_pitch, 9);
+ }
+ else
+ {
+ rate = 0;
+ }
+
+ slot.m_envelope_gen.m_attack_rate = get_rate(m_attack_step.get(), rate, slot.m_sample.m_attack_reg);
+ slot.m_envelope_gen.m_decay1_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_decay1_reg);
+ slot.m_envelope_gen.m_decay2_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_decay2_reg);
+ slot.m_envelope_gen.m_release_rate = get_rate(m_decay_release_step.get(), rate, slot.m_sample.m_release_reg);
+ slot.m_envelope_gen.m_decay_level = 0xf - slot.m_sample.m_decay_level;
+ slot.m_envelope_gen.m_reverb = false;
+}
+
+/*****************************
+ LFO SECTION
+*****************************/
+
+constexpr uint32_t gew_pcm_device::LFO_SHIFT;
+
+const float gew_pcm_device::LFO_FREQ[8] = // In Hertz
+{
+ 0.168f,
+ 2.019f,
+ 3.196f,
+ 4.206f,
+ 5.215f,
+ 5.888f,
+ 6.224f,
+ 7.066f
+};
+
+const float gew_pcm_device::PHASE_SCALE_LIMIT[8] = // In Cents
+{
+ 0.0f,
+ 3.378f,
+ 5.065f,
+ 6.750f,
+ 10.114f,
+ 20.170f,
+ 40.180f,
+ 79.307f
+};
+
+const float gew_pcm_device::AMPLITUDE_SCALE_LIMIT[8] = // In Decibels
+{
+ 0.0f,
+ 0.4f,
+ 0.8f,
+ 1.5f,
+ 3.0f,
+ 6.0f,
+ 12.0f,
+ 24.0f
+};
+
+void gew_pcm_device::lfo_init()
+{
+ m_pitch_table = make_unique_clear<int32_t[]>(256);
+ m_amplitude_table = make_unique_clear<int32_t[]>(256);
+ for (int32_t i = 0; i < 256; ++i)
+ {
+ if (i < 64)
+ {
+ m_pitch_table[i] = i * 2 + 128;
+ }
+ else if (i < 128)
+ {
+ m_pitch_table[i] = 383 - i * 2;
+ }
+ else if (i < 192)
+ {
+ m_pitch_table[i] = 384 - i * 2;
+ }
+ else
+ {
+ m_pitch_table[i] = i * 2 - 383;
+ }
+
+ if (i < 128)
+ {
+ m_amplitude_table[i] = 255 - (i * 2);
+ }
+ else
+ {
+ m_amplitude_table[i] = (i * 2) - 256;
+ }
+ }
+
+ for (int32_t table = 0; table < 8; ++table)
+ {
+ float limit = PHASE_SCALE_LIMIT[table];
+ m_pitch_scale_tables[table] = make_unique_clear<int32_t[]>(256);
+ for (int32_t i = -128; i < 128; ++i)
+ {
+ const float value = (limit * (float)i) / 128.0f;
+ const float converted = powf(2.0f, value / 1200.0f);
+ m_pitch_scale_tables[table][i + 128] = value_to_fixed(LFO_SHIFT, converted);
+ }
+
+ limit = -AMPLITUDE_SCALE_LIMIT[table];
+ m_amplitude_scale_tables[table] = make_unique_clear<int32_t[]>(256);
+ for (int32_t i = 0; i < 256; ++i)
+ {
+ const float value = (limit * (float)i) / 256.0f;
+ const float converted = powf(10.0f, value / 20.0f);
+ m_amplitude_scale_tables[table][i] = value_to_fixed(LFO_SHIFT, converted);
+ }
+ }
+}
+
+uint32_t gew_pcm_device::value_to_fixed(const uint32_t bits, const float value)
+{
+ const float float_shift = float(1 << bits);
+ return uint32_t(float_shift * value);
+}
+
+int32_t gew_pcm_device::pitch_lfo_step(lfo_t &lfo)
+{
+ lfo.m_phase += lfo.m_phase_step;
+ int32_t p = lfo.m_table[(lfo.m_phase >> LFO_SHIFT) & 0xff];
+ p = lfo.m_scale[p];
+ return p << (TL_SHIFT - LFO_SHIFT);
+}
+
+int32_t gew_pcm_device::amplitude_lfo_step(lfo_t &lfo)
+{
+ lfo.m_phase += lfo.m_phase_step;
+ int32_t p = lfo.m_table[(lfo.m_phase >> LFO_SHIFT) & 0xff];
+ p = lfo.m_scale[p];
+ return p << (TL_SHIFT - LFO_SHIFT);
+}
+
+void gew_pcm_device::lfo_compute_step(lfo_t &lfo, uint32_t lfo_frequency, uint32_t lfo_scale, int32_t amplitude_lfo)
+{
+ float step = (float)LFO_FREQ[lfo_frequency] * 256.0f / (float)m_rate;
+ lfo.m_phase_step = uint32_t(float(1 << LFO_SHIFT) * step);
+ if (amplitude_lfo)
+ {
+ lfo.m_table = m_amplitude_table.get();
+ lfo.m_scale = m_amplitude_scale_tables[lfo_scale].get();
+ }
+ else
+ {
+ lfo.m_table = m_pitch_table.get();
+ lfo.m_scale = m_pitch_scale_tables[lfo_scale].get();
+ }
+}
+
+/* MAME access functions */
+
+gew_pcm_device::gew_pcm_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock,
+ uint32_t voices, uint32_t clock_divider) :
+ device_t(mconfig, type, tag, owner, clock),
+ device_sound_interface(mconfig, *this),
+ device_rom_interface(mconfig, *this),
+ m_stream(nullptr),
+ m_slots(nullptr),
+ m_rate(0),
+ m_voices(voices),
+ m_clock_divider(clock_divider),
+ m_attack_step(nullptr),
+ m_decay_release_step(nullptr),
+ m_freq_step_table(nullptr),
+ m_left_pan_table(nullptr),
+ m_right_pan_table(nullptr),
+ m_linear_to_exp_volume(nullptr),
+ m_total_level_steps(nullptr)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void gew_pcm_device::device_start()
+{
+ m_rate = (float)clock() / m_clock_divider;
+
+ m_stream = stream_alloc(0, 2, m_rate);
+
+ // Volume + pan table
+ m_left_pan_table = make_unique_clear<int32_t[]>(0x800);
+ m_right_pan_table = make_unique_clear<int32_t[]>(0x800);
+ for (int32_t level = 0; level < 0x80; ++level)
+ {
+ const float vol_db = (float)level * (-24.0f) / 64.0f;
+ const float total_level = powf(10.0f, vol_db / 20.0f) / 4.0f;
+
+ for (int32_t pan = 0; pan < 0x10; ++pan)
+ {
+ float pan_left, pan_right;
+ if (pan == 0x8)
+ {
+ pan_left = 0.0;
+ pan_right = 0.0;
+ }
+ else if (pan == 0x0)
+ {
+ pan_left = 1.0;
+ pan_right = 1.0;
+ }
+ else if (pan & 0x8)
+ {
+ pan_left = 1.0;
+
+ const int32_t inverted_pan = 0x10 - pan;
+ const float pan_vol_db = (float)inverted_pan * (-12.0f) / 4.0f;
+
+ pan_right = pow(10.0f, pan_vol_db / 20.0f);
+
+ if ((inverted_pan & 0x7) == 7)
+ {
+ pan_right = 0.0;
+ }
+ }
+ else
+ {
+ pan_right = 1.0;
+
+ const float pan_vol_db = (float)pan * (-12.0f) / 4.0f;
+
+ pan_left = pow(10.0f, pan_vol_db / 20.0f);
+
+ if ((pan & 0x7) == 7)
+ {
+ pan_left = 0.0;
+ }
+ }
+
+ m_left_pan_table[(pan << 7) | level] = value_to_fixed(TL_SHIFT, pan_left * total_level);
+ m_right_pan_table[(pan << 7) | level] = value_to_fixed(TL_SHIFT, pan_right * total_level);
+ }
+ }
+
+ // Pitch steps
+ m_freq_step_table = make_unique_clear<uint32_t[]>(0x400);
+ for (int32_t i = 0; i < 0x400; ++i)
+ {
+ const float fcent = m_rate * (1024.0f + (float)i) / 1024.0f;
+ m_freq_step_table[i] = value_to_fixed(TL_SHIFT, fcent);
+ }
+
+ // Envelope steps
+ m_attack_step = make_unique_clear<uint32_t[]>(0x40);
+ m_decay_release_step = make_unique_clear<uint32_t[]>(0x40);
+ envelope_generator_init(BASE_TIMES, 14.32833);
+
+ // Total level interpolation steps
+ m_total_level_steps = make_unique_clear<int32_t[]>(2);
+ m_total_level_steps[0] = -(float)(0x80 << TL_SHIFT) / (78.2f * 44100.0f / 1000.0f); // lower
+ m_total_level_steps[1] = (float)(0x80 << TL_SHIFT) / (78.2f * 2 * 44100.0f / 1000.0f); // raise
+
+ // build the linear->exponential ramps
+ m_linear_to_exp_volume = make_unique_clear<int32_t[]>(0x400);
+ for (int32_t i = 0; i < 0x400; ++i)
+ {
+ const float db = -(96.0f - (96.0f * (float)i / (float)0x400));
+ const float exp_volume = powf(10.0f, db / 20.0f);
+ m_linear_to_exp_volume[i] = value_to_fixed(TL_SHIFT, exp_volume);
+ }
+
+ // Slots
+ m_slots = std::make_unique<slot_t[]>(m_voices);
+
+ save_pointer(STRUCT_MEMBER(m_slots, m_regs), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_playing), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_offset), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_octave), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_pitch), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_step), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_reverse), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_pan), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_total_level), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_dest_total_level), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_total_level_step), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_prev_sample), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_lfo_frequency), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_vibrato), m_voices);
+ save_pointer(STRUCT_MEMBER(m_slots, m_tremolo), m_voices);
+
+ for (int32_t slot = 0; slot < m_voices; ++slot)
+ {
+ save_item(NAME(m_slots[slot].m_sample.m_start), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_loop), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_end), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_attack_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_decay1_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_decay2_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_decay_level), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_release_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_key_rate_scale), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_lfo_vibrato_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_lfo_amplitude_reg), slot);
+ save_item(NAME(m_slots[slot].m_sample.m_format), slot);
+
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_volume), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_state), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_reverb), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.step), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_attack_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_decay1_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_decay2_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_release_rate), slot);
+ save_item(NAME(m_slots[slot].m_envelope_gen.m_decay_level), slot);
+
+ save_item(NAME(m_slots[slot].m_pitch_lfo.m_phase), slot);
+ save_item(NAME(m_slots[slot].m_pitch_lfo.m_phase_step), slot);
+ save_item(NAME(m_slots[slot].m_amplitude_lfo.m_phase), slot);
+ save_item(NAME(m_slots[slot].m_amplitude_lfo.m_phase_step), slot);
+ }
+
+ lfo_init();
+}
+
+void gew_pcm_device::device_reset()
+{
+ for (int32_t slot = 0; slot < m_voices; ++slot)
+ {
+ m_slots[slot].m_playing = false;
+ }
+}
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+void gew_pcm_device::device_clock_changed()
+{
+ m_rate = (float)clock() / m_clock_divider;
+ m_stream->set_sample_rate(m_rate);
+
+ for (int32_t i = 0; i < 0x400; ++i)
+ {
+ const float fcent = m_rate * (1024.0f + (float)i) / 1024.0f;
+ m_freq_step_table[i] = value_to_fixed(TL_SHIFT, fcent);
+ }
+}
+
+//-----------------------------------------------------
+// dump_sample - dump current sample to WAV file
+//-----------------------------------------------------
+
+#if MULTIPCM_LOG_SAMPLES
+void gew_pcm_device::dump_sample(slot_t &slot)
+{
+ if (m_logged_map[slot.m_sample.m_start])
+ return;
+
+ m_logged_map[slot.m_sample.m_start] = true;
+
+ char filebuf[256];
+ snprintf(filebuf, 256, "multipcm%08x.wav", slot.m_sample.m_start);
+ util::wav_file_ptr file = util::wav_open(filebuf, m_stream->sample_rate(), 1);
+ if (file == nullptr)
+ return;
+
+ uint32_t offset = slot.m_offset;
+ bool done = false;
+ while (!done)
+ {
+ int16_t sample = (int16_t)(read_byte(slot.m_sample.m_start + (offset >> TL_SHIFT)) << 8);
+ util::wav_add_data_16(*file.get(), &sample, 1);
+
+ offset += 1 << TL_SHIFT;
+ if (offset >= (slot.m_sample.m_end << TL_SHIFT))
+ {
+ done = true;
+ }
+ }
+
+ util::wav_close(file.get());
+}
+#endif
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void gew_pcm_device::sound_stream_update(sound_stream &stream)
+{
+ for (int32_t i = 0; i < stream.samples(); ++i)
+ {
+ int32_t smpl = 0;
+ int32_t smpr = 0;
+ for (int32_t sl = 0; sl < m_voices; ++sl)
+ {
+ slot_t& slot = m_slots[sl];
+ if (slot.m_playing)
+ {
+ uint32_t vol = (slot.m_total_level >> TL_SHIFT) | (slot.m_pan << 7);
+ uint32_t spos = slot.m_offset >> TL_SHIFT;
+ uint32_t step = slot.m_step;
+ int32_t csample = 0;
+ int32_t fpart = slot.m_offset & ((1 << TL_SHIFT) - 1);
+
+ if (slot.m_reverse)
+ {
+ spos = slot.m_sample.m_end - spos - 1;
+ }
+
+ if (slot.m_sample.m_format & 4) // 12-bit linear
+ {
+ offs_t adr = slot.m_sample.m_start + (spos >> 1) * 3;
+ if (!(spos & 1))
+ { // ab.c ..
+ s16 w0 = read_byte(adr) << 8 | ((read_byte(adr + 1) & 0xf) << 4);
+ csample = w0;
+ }
+ else
+ { // ..C. AB
+ s16 w0 = (read_byte(adr + 2) << 8) | (read_byte(adr + 1) & 0xf0);
+ csample = w0;
+ }
+ }
+ else
+ {
+ csample = (int16_t)(read_byte(slot.m_sample.m_start + spos) << 8);
+ }
+
+ int32_t sample = (csample * fpart + slot.m_prev_sample * ((1 << TL_SHIFT) - fpart)) >> TL_SHIFT;
+
+ if (slot.m_vibrato) // Vibrato enabled
+ {
+ step = step * pitch_lfo_step(slot.m_pitch_lfo);
+ step >>= TL_SHIFT;
+ }
+
+ slot.m_offset += step;
+
+ if (spos ^ (slot.m_offset >> TL_SHIFT))
+ {
+ slot.m_prev_sample = csample;
+ }
+
+ if (slot.m_offset >= (slot.m_sample.m_end << TL_SHIFT))
+ {
+ slot.m_offset -= (slot.m_sample.m_end - slot.m_sample.m_loop) << TL_SHIFT;
+ // DD-9 expects the looped silence at the end of some samples to be the same whether reversed or not
+ slot.m_reverse = false;
+ }
+
+ if ((slot.m_total_level >> TL_SHIFT) != slot.m_dest_total_level)
+ {
+ slot.m_total_level += slot.m_total_level_step;
+ }
+
+ if (slot.m_tremolo) // Tremolo enabled
+ {
+ sample = sample * amplitude_lfo_step(slot.m_amplitude_lfo);
+ sample >>= TL_SHIFT;
+ }
+
+ sample = (sample * envelope_generator_update(slot)) >> 10;
+
+ smpl += (m_left_pan_table[vol] * sample) >> TL_SHIFT;
+ smpr += (m_right_pan_table[vol] * sample) >> TL_SHIFT;
+ }
+ }
+
+ stream.put_int_clamp(0, i, smpl, 32768);
+ stream.put_int_clamp(1, i, smpr, 32768);
+ }
+}
+
+
+//-------------------------------------------------
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
+//-------------------------------------------------
+
+void gew_pcm_device::rom_bank_pre_change()
+{
+ m_stream->update();
+}