summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/okim9810.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/okim9810.cpp')
-rw-r--r--src/devices/sound/okim9810.cpp270
1 files changed, 166 insertions, 104 deletions
diff --git a/src/devices/sound/okim9810.cpp b/src/devices/sound/okim9810.cpp
index 2641e1527e2..f9d0d6e26c6 100644
--- a/src/devices/sound/okim9810.cpp
+++ b/src/devices/sound/okim9810.cpp
@@ -4,7 +4,7 @@
okim9810.h
- OKI MSM9810 ADPCM(2) sound chip.
+ OKI MSM9810 / MSM9811 ADPCM(2) sound chips.
TODO:
Serial input/output are not verified
@@ -16,6 +16,9 @@
#include "emu.h"
#include "okim9810.h"
+#define VERBOSE 0
+#include "logmacro.h"
+
//**************************************************************************
// GLOBAL VARIABLES
@@ -80,7 +83,7 @@ const uint32_t okim9810_device::s_sampling_freq_div_table[16] =
okim9810_device::okim9810_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, OKIM9810, tag, owner, clock),
device_sound_interface(mconfig, *this),
- device_rom_interface(mconfig, *this, 24),
+ device_rom_interface(mconfig, *this),
m_stream(nullptr),
m_TMP_register(0x00),
m_global_volume(0x00),
@@ -109,7 +112,7 @@ okim9810_device::okim9810_device(const machine_config &mconfig, const char *tag,
void okim9810_device::device_start()
{
// create the stream
- m_stream = machine().sound().stream_alloc(*this, 0, 2, clock());
+ m_stream = stream_alloc(0, 2, clock());
// save state stuff
save_item(NAME(m_TMP_register));
@@ -135,26 +138,32 @@ void okim9810_device::device_start()
{
okim_voice *voice = &m_voice[i];
+ // can't use STRUCT_MEMBER on nested structs
save_item(NAME(voice->m_adpcm.m_signal), i);
save_item(NAME(voice->m_adpcm.m_step), i);
save_item(NAME(voice->m_adpcm2.m_signal), i);
save_item(NAME(voice->m_adpcm2.m_step), i);
- save_item(NAME(voice->m_playbackAlgo), i);
- save_item(NAME(voice->m_looping), i);
- save_item(NAME(voice->m_startFlags), i);
- save_item(NAME(voice->m_endFlags), i);
- save_item(NAME(voice->m_base_offset), i);
- save_item(NAME(voice->m_count), i);
- save_item(NAME(voice->m_samplingFreq), i);
- save_item(NAME(voice->m_playing), i);
- save_item(NAME(voice->m_sample), i);
- save_item(NAME(voice->m_channel_volume), i);
- save_item(NAME(voice->m_pan_volume_left), i);
- save_item(NAME(voice->m_pan_volume_right), i);
- save_item(NAME(voice->m_startSample), i);
- save_item(NAME(voice->m_endSample), i);
- save_item(NAME(voice->m_interpSampleNum), i);
}
+
+ save_item(STRUCT_MEMBER(m_voice, m_playbackAlgo));
+ save_item(STRUCT_MEMBER(m_voice, m_looping));
+ save_item(STRUCT_MEMBER(m_voice, m_startFlags));
+ save_item(STRUCT_MEMBER(m_voice, m_endFlags));
+ save_item(STRUCT_MEMBER(m_voice, m_base_offset));
+ save_item(STRUCT_MEMBER(m_voice, m_count));
+ save_item(STRUCT_MEMBER(m_voice, m_samplingFreq));
+ save_item(STRUCT_MEMBER(m_voice, m_playing));
+ save_item(STRUCT_MEMBER(m_voice, m_sample));
+ save_item(STRUCT_MEMBER(m_voice, m_phrase_offset));
+ save_item(STRUCT_MEMBER(m_voice, m_phrase_count));
+ save_item(STRUCT_MEMBER(m_voice, m_phrase_wait_cnt));
+ save_item(STRUCT_MEMBER(m_voice, m_phrase_state));
+ save_item(STRUCT_MEMBER(m_voice, m_channel_volume));
+ save_item(STRUCT_MEMBER(m_voice, m_pan_volume_left));
+ save_item(STRUCT_MEMBER(m_voice, m_pan_volume_right));
+ save_item(STRUCT_MEMBER(m_voice, m_startSample));
+ save_item(STRUCT_MEMBER(m_voice, m_endSample));
+ save_item(STRUCT_MEMBER(m_voice, m_interpSampleNum));
}
@@ -195,10 +204,11 @@ void okim9810_device::device_clock_changed()
//-------------------------------------------------
-// rom_bank_updated - the rom bank has changed
+// rom_bank_pre_change - refresh the stream if the
+// ROM banking changes
//-------------------------------------------------
-void okim9810_device::rom_bank_updated()
+void okim9810_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -209,15 +219,11 @@ void okim9810_device::rom_bank_updated()
// our sound stream
//-------------------------------------------------
-void okim9810_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+void okim9810_device::sound_stream_update(sound_stream &stream)
{
- // reset the output streams
- memset(outputs[0], 0, samples * sizeof(*outputs[0]));
- memset(outputs[1], 0, samples * sizeof(*outputs[1]));
-
// iterate over voices and accumulate sample data
for (auto & elem : m_voice)
- elem.generate_audio(*this, outputs, samples, m_global_volume, m_filter_type);
+ elem.generate_audio(*this, stream, m_global_volume, m_filter_type);
}
@@ -242,7 +248,7 @@ uint8_t okim9810_device::read_status()
// read - memory interface for reading the active status
//-------------------------------------------------
-READ8_MEMBER( okim9810_device::read )
+uint8_t okim9810_device::read()
{
assert(!m_serial);
return read_status();
@@ -263,14 +269,14 @@ void okim9810_device::write_command(uint8_t data)
{
case 0x00: // START
{
- osd_printf_debug("START channel mask %02x\n", m_TMP_register);
+ LOG("START channel mask %02x\n", m_TMP_register);
uint8_t channelMask = 0x01;
for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
{
if (channelMask & m_TMP_register)
{
m_voice[i].m_playing = true;
- osd_printf_debug("\t\tPlaying channel %d: encoder type %d @ %dhz (volume = %d %d). From %08x for %d samples (looping=%d).\n",
+ LOG("\t\tPlaying channel %d: encoder type %d @ %dhz (volume = %d %d). From %08x for %d samples (looping=%d).\n",
i,
m_voice[i].m_playbackAlgo,
m_voice[i].m_samplingFreq,
@@ -285,52 +291,52 @@ void okim9810_device::write_command(uint8_t data)
}
case 0x01: // STOP
{
- osd_printf_debug("STOP channel mask %02x\n", m_TMP_register);
+ LOG("STOP channel mask %02x\n", m_TMP_register);
uint8_t channelMask = 0x01;
for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
{
if (channelMask & m_TMP_register)
{
m_voice[i].m_playing = false;
- osd_printf_debug("\tChannel %d stopping.\n", i);
+ LOG("\tChannel %d stopping.\n", i);
}
}
break;
}
case 0x02: // LOOP
{
- osd_printf_debug("LOOP channel mask %02x\n", m_TMP_register);
+ LOG("LOOP channel mask %02x\n", m_TMP_register);
uint8_t channelMask = 0x01;
for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
{
if (channelMask & m_TMP_register)
{
m_voice[i].m_looping = true;
- osd_printf_debug("\tChannel %d looping.\n", i);
+ LOG("\tChannel %d looping.\n", i);
}
else
{
m_voice[i].m_looping = false;
- osd_printf_debug("\tChannel %d done looping.\n", i);
+ LOG("\tChannel %d done looping.\n", i);
}
}
break;
}
case 0x03: // OPT (options)
{
- osd_printf_debug("OPT complex data %02x\n", m_TMP_register);
+ LOG("OPT complex data %02x\n", m_TMP_register);
m_global_volume = (m_TMP_register & 0x18) >> 3;
m_filter_type = (m_TMP_register & 0x06) >> 1;
m_output_level = (m_TMP_register & 0x01);
- osd_printf_debug("\tOPT setting main volume scale to Vdd/%d\n", m_global_volume+1);
- osd_printf_debug("\tOPT setting output filter type to %d\n", m_filter_type);
- osd_printf_debug("\tOPT setting output amp level to %d\n", m_output_level);
+ LOG("\tOPT setting main volume scale to Vdd/%d\n", m_global_volume+1);
+ LOG("\tOPT setting output filter type to %d\n", m_filter_type);
+ LOG("\tOPT setting output amp level to %d\n", m_output_level);
break;
}
case 0x04: // MUON (silence)
{
- osd_printf_warning("MUON channel %d length %02x\n", channel, m_TMP_register);
- osd_printf_warning("MSM9810: UNIMPLEMENTED COMMAND!\n");
+ logerror("MUON channel %d length %02x\n", channel, m_TMP_register);
+ logerror("MSM9810: UNIMPLEMENTED COMMAND!\n");
break;
}
@@ -350,39 +356,37 @@ void okim9810_device::write_command(uint8_t data)
endAddr |= read_byte(base + 6) << 8;
endAddr |= read_byte(base + 7) << 0;
- // Sub-table
- if (startFlags & 0x80)
+ if (BIT(startFlags, 7))
{
- offs_t subTable = startAddr;
- // TODO: New startFlags &= 0x80. Are there further subtables?
- startFlags = read_byte(subTable + 0);
- startAddr = read_byte(subTable + 1) << 16;
- startAddr |= read_byte(subTable + 2) << 8;
- startAddr |= read_byte(subTable + 3) << 0;
-
- // TODO: What does byte (subTable + 4) refer to?
- endAddr = read_byte(subTable + 5) << 16;
- endAddr |= read_byte(subTable + 6) << 8;
- endAddr |= read_byte(subTable + 7) << 0;
+ // use "Phrase Control Table" mode
+ m_voice[channel].m_phrase_offset = startAddr;
+ m_voice[channel].m_phrase_count = endAddr - startAddr + 1;
+ if (m_voice[channel].m_phrase_count == 0)
+ LOG("MSM9810: Empty phrase subtable\n");
+ m_voice[channel].m_phrase_state = SEQ_ACTIVE;
}
+ else
+ {
+ // single sample mode
+ m_voice[channel].m_phrase_state = SEQ_STOP;
+ m_voice[channel].m_sample = 0;
+ m_voice[channel].m_interpSampleNum = 0;
+ m_voice[channel].m_startFlags = startFlags;
+ m_voice[channel].m_base_offset = startAddr;
+ m_voice[channel].m_endFlags = endFlags;
+ m_voice[channel].m_count = endAddr - startAddr + 1;
- m_voice[channel].m_sample = 0;
- m_voice[channel].m_interpSampleNum = 0;
- m_voice[channel].m_startFlags = startFlags;
- m_voice[channel].m_base_offset = startAddr;
- m_voice[channel].m_endFlags = endFlags;
- m_voice[channel].m_count = (endAddr-startAddr) + 1; // Is there yet another extra byte at the end?
-
- m_voice[channel].m_playbackAlgo = (startFlags & 0x30) >> 4; // Not verified
- m_voice[channel].m_samplingFreq = startFlags & 0x0f;
- if (m_voice[channel].m_playbackAlgo == ADPCM_PLAYBACK ||
- m_voice[channel].m_playbackAlgo == ADPCM2_PLAYBACK)
- m_voice[channel].m_count *= 2;
- else if (m_voice[channel].m_playbackAlgo == NONLINEAR8_PLAYBACK)
- osd_printf_warning("MSM9810: UNIMPLEMENTED PLAYBACK METHOD %d\n", m_voice[channel].m_playbackAlgo);
-
- osd_printf_debug("FADR channel %d phrase offset %02x => ", channel, m_TMP_register);
- osd_printf_debug("startFlags(%02x) startAddr(%06x) endFlags(%02x) endAddr(%06x) bytes(%d)\n", startFlags, startAddr, endFlags, endAddr, endAddr-startAddr);
+ m_voice[channel].m_playbackAlgo = (startFlags & 0x30) >> 4; // Not verified
+ m_voice[channel].m_samplingFreq = startFlags & 0x0f;
+ if (m_voice[channel].m_playbackAlgo == ADPCM_PLAYBACK ||
+ m_voice[channel].m_playbackAlgo == ADPCM2_PLAYBACK)
+ m_voice[channel].m_count *= 2;
+ else if (m_voice[channel].m_playbackAlgo == NONLINEAR8_PLAYBACK)
+ logerror("MSM9810: UNIMPLEMENTED PLAYBACK METHOD %d\n", m_voice[channel].m_playbackAlgo);
+
+ LOG("FADR channel %d phrase offset %02x => ", channel, m_TMP_register);
+ LOG("startFlags(%02x) startAddr(%06x) endFlags(%02x) endAddr(%06x) bytes(%d)\n", startFlags, startAddr, endFlags, endAddr, endAddr - startAddr);
+ }
break;
}
@@ -394,6 +398,7 @@ void okim9810_device::write_command(uint8_t data)
offs_t endAddr = m_dadr_end_offset;
uint8_t startFlags = m_dadr_flags;
+ m_voice[channel].m_phrase_state = SEQ_STOP;
m_voice[channel].m_sample = 0;
m_voice[channel].m_interpSampleNum = 0;
m_voice[channel].m_startFlags = startFlags;
@@ -407,20 +412,20 @@ void okim9810_device::write_command(uint8_t data)
m_voice[channel].m_playbackAlgo == ADPCM2_PLAYBACK)
m_voice[channel].m_count *= 2;
else if (m_voice[channel].m_playbackAlgo == NONLINEAR8_PLAYBACK)
- osd_printf_warning("MSM9810: UNIMPLEMENTED PLAYBACK METHOD %d\n", m_voice[channel].m_playbackAlgo);
+ logerror("MSM9810: UNIMPLEMENTED PLAYBACK METHOD %d\n", m_voice[channel].m_playbackAlgo);
- osd_printf_debug("startFlags(%02x) startAddr(%06x) endAddr(%06x) bytes(%d)\n", startFlags, startAddr, endAddr, endAddr-startAddr);
+ LOG("startFlags(%02x) startAddr(%06x) endAddr(%06x) bytes(%d)\n", startFlags, startAddr, endAddr, endAddr-startAddr);
}
else
{
- osd_printf_warning("MSM9810: UNKNOWN COMMAND!\n");
+ logerror("MSM9810: UNKNOWN COMMAND!\n");
}
break;
}
case 0x07: // CVOL (channel volume)
{
- osd_printf_debug("CVOL channel %d data %02x\n", channel, m_TMP_register);
- osd_printf_debug("\tChannel %d -> volume index %d.\n", channel, m_TMP_register & 0x0f);
+ LOG("CVOL channel %d data %02x\n", channel, m_TMP_register);
+ LOG("\tChannel %d -> volume index %d.\n", channel, m_TMP_register & 0x0f);
m_voice[channel].m_channel_volume = m_TMP_register & 0x0f;
break;
@@ -429,22 +434,22 @@ void okim9810_device::write_command(uint8_t data)
{
const uint8_t leftVolIndex = (m_TMP_register & 0xf0) >> 4;
const uint8_t rightVolIndex = m_TMP_register & 0x0f;
- osd_printf_debug("PAN channel %d left index: %02x right index: %02x (%02x)\n", channel, leftVolIndex, rightVolIndex, m_TMP_register);
- osd_printf_debug("\tChannel %d left -> %d right -> %d\n", channel, leftVolIndex, rightVolIndex);
+ LOG("PAN channel %d left index: %02x right index: %02x (%02x)\n", channel, leftVolIndex, rightVolIndex, m_TMP_register);
+ LOG("\tChannel %d left -> %d right -> %d\n", channel, leftVolIndex, rightVolIndex);
m_voice[channel].m_pan_volume_left = leftVolIndex;
m_voice[channel].m_pan_volume_right = rightVolIndex;
break;
}
default:
{
- osd_printf_warning("MSM9810: UNKNOWN COMMAND!\n");
+ logerror("MSM9810: UNKNOWN COMMAND!\n");
break;
}
}
m_dadr = 0;
}
-WRITE8_MEMBER( okim9810_device::write )
+void okim9810_device::write(uint8_t data)
{
assert(!m_serial);
write_command(data);
@@ -488,12 +493,12 @@ void okim9810_device::write_tmp_register(uint8_t data)
default:
break;
}
- osd_printf_debug("DADR direct offset %02x = %02x => ", m_dadr, m_TMP_register);
+ LOG("DADR direct offset %02x = %02x => ", m_dadr, m_TMP_register);
m_dadr++;
}
}
-WRITE8_MEMBER( okim9810_device::tmp_register_w )
+void okim9810_device::tmp_register_w(uint8_t data)
{
assert(!m_serial);
write_tmp_register(data);
@@ -504,12 +509,12 @@ WRITE8_MEMBER( okim9810_device::tmp_register_w )
// Serial interface, NOT verified
//-----------------------------------------------------------
-WRITE_LINE_MEMBER( okim9810_device::serial_w )
+void okim9810_device::serial_w(int state)
{
m_serial = state;
}
-WRITE_LINE_MEMBER( okim9810_device::si_w )
+void okim9810_device::si_w(int state)
{
if (m_si != state)
{
@@ -534,43 +539,43 @@ WRITE_LINE_MEMBER( okim9810_device::si_w )
}
}
-WRITE_LINE_MEMBER( okim9810_device::sd_w )
+void okim9810_device::sd_w(int state)
{
m_sd = state;
}
-WRITE_LINE_MEMBER( okim9810_device::ud_w )
+void okim9810_device::ud_w(int state)
{
m_ud = state;
}
-WRITE_LINE_MEMBER( okim9810_device::cmd_w )
+void okim9810_device::cmd_w(int state)
{
m_cmd = state;
}
-READ_LINE_MEMBER( okim9810_device::so_r )
+int okim9810_device::so_r()
{
m_serial_read_latch = (m_serial_read_latch & ~(1<<m_serial_bits)) | (read_status() & (1<<m_serial_bits));
return (read_status() >> (7-m_serial_bits)) & 1;
}
-READ_LINE_MEMBER( okim9810_device::sr0_r )
+int okim9810_device::sr0_r()
{
return (m_serial_read_latch >> ((m_ud) ? 4 : 0)) & 1;
}
-READ_LINE_MEMBER( okim9810_device::sr1_r )
+int okim9810_device::sr1_r()
{
return (m_serial_read_latch >> ((m_ud) ? 5 : 1)) & 1;
}
-READ_LINE_MEMBER( okim9810_device::sr2_r )
+int okim9810_device::sr2_r()
{
return (m_serial_read_latch >> ((m_ud) ? 6 : 2)) & 1;
}
-READ_LINE_MEMBER( okim9810_device::sr3_r )
+int okim9810_device::sr3_r()
{
return (m_serial_read_latch >> ((m_ud) ? 7 : 3)) & 1;
}
@@ -593,6 +598,10 @@ okim9810_device::okim_voice::okim_voice()
m_samplingFreq(2),
m_playing(false),
m_sample(0),
+ m_phrase_offset(0),
+ m_phrase_count(0),
+ m_phrase_wait_cnt(0),
+ m_phrase_state(SEQ_STOP),
m_channel_volume(0x00),
m_pan_volume_left(0x00),
m_pan_volume_right(0x00),
@@ -608,8 +617,7 @@ okim9810_device::okim_voice::okim_voice()
//-------------------------------------------------
void okim9810_device::okim_voice::generate_audio(device_rom_interface &rom,
- stream_sample_t **buffers,
- int samples,
+ sound_stream &stream,
const uint8_t global_volume,
const uint8_t filter_type)
{
@@ -617,10 +625,6 @@ void okim9810_device::okim_voice::generate_audio(device_rom_interface &rom,
if (!m_playing)
return;
- // separate out left and right channels
- stream_sample_t *outL = buffers[0];
- stream_sample_t *outR = buffers[1];
-
// get left and right volumes
uint8_t volume_scale_left = volume_scale(global_volume, m_channel_volume, m_pan_volume_left);
uint8_t volume_scale_right = volume_scale(global_volume, m_channel_volume, m_pan_volume_right);
@@ -631,8 +635,61 @@ void okim9810_device::okim_voice::generate_audio(device_rom_interface &rom,
return;
// loop while we still have samples to generate
- while (samples-- != 0)
+ for (int sampindex = 0; sampindex < stream.samples(); sampindex++)
{
+ if (m_phrase_state == SEQ_PAUSE)
+ {
+ m_phrase_wait_cnt--;
+ if (m_phrase_wait_cnt > 0)
+ continue;
+ else
+ m_phrase_state = SEQ_ACTIVE;
+ }
+ if (m_phrase_state == SEQ_ACTIVE)
+ {
+ if (m_phrase_count > 0)
+ {
+ offs_t startAddr, endAddr;
+ uint8_t startFlags = rom.read_byte(m_phrase_offset++);
+ m_phrase_count--;
+ if (!BIT(startFlags, 7))
+ {
+ m_phrase_state = SEQ_PAUSE;
+ m_phrase_wait_cnt = (startFlags & 0x1f) * 16384; // not verified, doc says range from 4ms to 124ms
+ continue;
+ }
+
+ startAddr = rom.read_byte(m_phrase_offset++) << 16;
+ startAddr |= rom.read_byte(m_phrase_offset++) << 8;
+ startAddr |= rom.read_byte(m_phrase_offset++) << 0;
+ m_channel_volume = rom.read_byte(m_phrase_offset++) & 0xf; // not verified
+ endAddr = rom.read_byte(m_phrase_offset++) << 16;
+ endAddr |= rom.read_byte(m_phrase_offset++) << 8;
+ endAddr |= rom.read_byte(m_phrase_offset++) << 0;
+ m_phrase_count -= 7;
+
+ m_sample = 0;
+ m_interpSampleNum = 0;
+ m_startFlags = startFlags;
+ m_base_offset = startAddr;
+ m_count = endAddr - startAddr + 1;
+ m_playbackAlgo = (startFlags & 0x30) >> 4; // Not verified
+ m_samplingFreq = startFlags & 0x0f;
+ if (m_playbackAlgo == ADPCM_PLAYBACK || m_playbackAlgo == ADPCM2_PLAYBACK)
+ m_count *= 2;
+
+ m_phrase_state = SEQ_PLAY;
+ totalInterpSamples = s_sampling_freq_div_table[m_samplingFreq];
+ if (totalInterpSamples == 1)
+ return;
+ }
+ else
+ {
+ m_phrase_state = SEQ_STOP;
+ m_playing = false;
+ break;
+ }
+ }
// If interpSampleNum == 0, we are at the beginning of a new interp chunk, gather data
if (m_interpSampleNum == 0)
{
@@ -742,11 +799,11 @@ void okim9810_device::okim_voice::generate_audio(device_rom_interface &rom,
// output to the stereo buffers, scaling by the volume
// signal in range -2048..2047, volume in range 2..128 => signal * volume / 8 in range -32768..32767
- int32_t interpValueL = (interpValue * (int32_t)volume_scale_left) / 8;
- *outL++ += interpValueL;
+ int32_t interpValueL = (interpValue * (int32_t)volume_scale_left);
+ stream.add_int(0, sampindex, interpValueL, 32768 * 8);
- int32_t interpValueR = (interpValue * (int32_t)volume_scale_right) / 8;
- *outR++ += interpValueR;
+ int32_t interpValueR = (interpValue * (int32_t)volume_scale_right);
+ stream.add_int(1, sampindex, interpValueR, 32768 * 8);
// if the interpsample has reached its end, move on to the next sample
m_interpSampleNum++;
@@ -761,8 +818,13 @@ void okim9810_device::okim_voice::generate_audio(device_rom_interface &rom,
{
if (!m_looping)
{
- m_playing = false;
- break;
+ if (m_phrase_state == SEQ_STOP)
+ {
+ m_playing = false;
+ break;
+ }
+ else
+ m_phrase_state = SEQ_ACTIVE;
}
else
{