mirror of https://github.com/987123879113/mame
namco/namcos10.cpp: Add MP3 decoder support to the MEM(P3) board. (#11210)
* 3rdparty/minimp3: Update to latest source (afb604c06bc8beb145fecd42c0ceb5bda8795144). * sound/mp3_audio.cpp: Add helper class to decode MP3 frame data, abstracting away minimp3 from devices. * sound/lc82310.cpp: Added basic Sanyo LC82310 MP3 decoder emulation. * namco/namcos10.cpp: Fixed light gun inputs for Golgo 13: Juusei no Requiem. Systems promoted to working ------------------ Golgo 13: Juusei no Requiem (Japan, GLT1 VER.A) Tsukkomi Yousei Gips Nice Tsukkomi (NTK1 Ver.A) Seishun-Quiz Colorful High School (CHS1 Ver.A)konami_53cf96
parent
f64d568f8e
commit
fb2b5745d1
11 changed files with 834 additions and 1499 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,297 @@ |
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/**********************************************************************
|
||||
|
||||
Sanyo LC82310 MP3 decoder |
||||
|
||||
**********************************************************************/ |
||||
|
||||
#include "emu.h" |
||||
#include "lc82310.h" |
||||
#include "mp3_audio.h" |
||||
|
||||
DEFINE_DEVICE_TYPE(LC82310, lc82310_device, "lc82310", "Sanyo LC82310 MP3 Decoder") |
||||
|
||||
lc82310_device::lc82310_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) |
||||
: device_t(mconfig, LC82310, tag, owner, clock) |
||||
, device_sound_interface(mconfig, *this) |
||||
{ |
||||
} |
||||
|
||||
void lc82310_device::device_start() |
||||
{ |
||||
stream = stream_alloc(0, 2, 44100); |
||||
mp3dec = std::make_unique<mp3_audio>(reinterpret_cast<const uint8_t *>(&mp3data[0])); |
||||
|
||||
save_item(NAME(mp3data)); |
||||
save_item(NAME(samples)); |
||||
save_item(NAME(m_mp3data_count)); |
||||
save_item(NAME(m_sample_count)); |
||||
save_item(NAME(m_samples_idx)); |
||||
save_item(NAME(m_frame_channels)); |
||||
save_item(NAME(m_output_gain)); |
||||
|
||||
save_item(NAME(m_csctl)); |
||||
save_item(NAME(m_ckctl)); |
||||
save_item(NAME(m_dictl)); |
||||
save_item(NAME(m_doctl)); |
||||
save_item(NAME(m_ctl_bits)); |
||||
save_item(NAME(m_ctl_byte)); |
||||
save_item(NAME(m_ctl_out_byte)); |
||||
} |
||||
|
||||
void lc82310_device::device_reset() |
||||
{ |
||||
std::fill(std::begin(m_output_gain), std::end(m_output_gain), 0); |
||||
|
||||
m_csctl = 0; |
||||
m_ckctl = 0; |
||||
m_dictl = 0; |
||||
m_doctl = 0; |
||||
m_ctl_bits = 0; |
||||
m_ctl_byte = 0; |
||||
m_ctl_out_byte = 0; |
||||
m_ctl_state = ACCEPTING_CMD; |
||||
|
||||
reset_playback(); |
||||
} |
||||
|
||||
void lc82310_device::reset_playback() |
||||
{ |
||||
std::fill(mp3data.begin(), mp3data.end(), 0); |
||||
std::fill(samples.begin(), samples.end(), 0); |
||||
|
||||
mp3dec->clear(); |
||||
m_mp3data_count = 0; |
||||
m_sample_count = 0; |
||||
m_samples_idx = 0; |
||||
m_frame_channels = 2; |
||||
} |
||||
|
||||
WRITE_LINE_MEMBER(lc82310_device::zcsctl_w) |
||||
{ |
||||
m_csctl = state; |
||||
} |
||||
|
||||
WRITE_LINE_MEMBER(lc82310_device::ckctl_w) |
||||
{ |
||||
if (m_csctl == 0 && m_ckctl == 0 && state == 1) |
||||
{ |
||||
m_ctl_byte |= m_dictl << m_ctl_bits; |
||||
m_ctl_bits++; |
||||
|
||||
if (m_ctl_bits > 7) |
||||
{ |
||||
if (m_ctl_state == ACCEPTING_CMD) |
||||
{ |
||||
// Expected to be able to read the return value while sending the second byte
|
||||
// Everything in the 0x80 range of commands seems to respond with a value
|
||||
// 0x80, 0x81, 0x82 return separate 8-bit values
|
||||
// 0x83 returns an 8-bit error status, bits 0 and 1 are used to signal errors, any non-0 bit is considered an error
|
||||
// 0x84 and 0x85 are used together to form a 16-bit value
|
||||
m_ctl_out_byte = 0; |
||||
|
||||
m_ctl_cmd = m_ctl_byte; |
||||
m_ctl_state = ACCEPTING_PARAM; |
||||
} |
||||
else if (m_ctl_state == ACCEPTING_PARAM) |
||||
{ |
||||
handle_command(m_ctl_cmd, m_ctl_byte); |
||||
m_ctl_state = ACCEPTING_CMD; |
||||
} |
||||
|
||||
m_ctl_byte = 0; |
||||
m_ctl_bits = 0; |
||||
} |
||||
|
||||
m_doctl = m_ctl_out_byte & 1; |
||||
m_ctl_out_byte >>= 1; |
||||
} |
||||
|
||||
m_ckctl = state; |
||||
} |
||||
|
||||
WRITE_LINE_MEMBER(lc82310_device::dictl_w) |
||||
{ |
||||
m_dictl = state; |
||||
} |
||||
|
||||
READ_LINE_MEMBER(lc82310_device::doctl_r) |
||||
{ |
||||
return m_doctl; |
||||
} |
||||
|
||||
READ_LINE_MEMBER(lc82310_device::demand_r) |
||||
{ |
||||
return m_mp3data_count < mp3data.size(); |
||||
} |
||||
|
||||
void lc82310_device::dimpg_w(uint8_t data) |
||||
{ |
||||
if (m_mp3data_count >= mp3data.size()) |
||||
{ |
||||
// Drop a byte if the buffer is full and it's still trying to send data
|
||||
std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin()); |
||||
m_mp3data_count--; |
||||
} |
||||
|
||||
mp3data[m_mp3data_count++] = data; |
||||
} |
||||
|
||||
void lc82310_device::handle_command(uint8_t cmd, uint8_t param) |
||||
{ |
||||
if (cmd == CMD_UNK13_VOL || cmd == CMD_UNK15_VOL) |
||||
{ |
||||
// These are calculated based on the configurable values in-game vs what is sent to the MP3 decoder
|
||||
constexpr float gain_table[] = { |
||||
1.0, // 0
|
||||
30.0 / 31.0, // 1
|
||||
29.0 / 31.0, // 2
|
||||
28.0 / 31.0, // 3
|
||||
27.0 / 31.0, // 4
|
||||
26.0 / 31.0, // 5
|
||||
25.0 / 31.0, // 6
|
||||
24.0 / 31.0, // 7
|
||||
23.0 / 31.0, // 8
|
||||
22.0 / 31.0, // 9
|
||||
21.0 / 31.0, // 10
|
||||
20.0 / 31.0, // 11
|
||||
19.0 / 31.0, // 12
|
||||
18.0 / 31.0, // 13
|
||||
17.0 / 31.0, // 14
|
||||
16.0 / 31.0, // 15
|
||||
15.0 / 31.0, // 16
|
||||
14.0 / 31.0, // 17
|
||||
13.0 / 31.0, // 18
|
||||
12.0 / 31.0, // 19
|
||||
11.0 / 31.0, // 20
|
||||
10.0 / 31.0, // 21
|
||||
9.0 / 31.0, // 22
|
||||
8.5 / 31.0, // 23
|
||||
8.0 / 31.0, // 24
|
||||
7.0 / 31.0, // 25
|
||||
6.0 / 31.0, // 25
|
||||
5.5 / 31.0, // 27
|
||||
5.0 / 31.0, // 28
|
||||
4.5 / 31.0, // 29
|
||||
4.0 / 31.0, // 30
|
||||
3.5 / 31.0, // 31
|
||||
3.0 / 31.0, // 32
|
||||
2.75 / 31.0, // 33
|
||||
2.5 / 31.0, // 34
|
||||
2.25 / 31.0, // 35
|
||||
2.0 / 31.0, // 36
|
||||
(1.0 + (1.0 / 6) * 5) / 31.0, // 37
|
||||
(1.0 + (1.0 / 6) * 4) / 31.0, // 38
|
||||
(1.0 + (1.0 / 6) * 3) / 31.0, // 39
|
||||
(1.0 + (1.0 / 6) * 2) / 31.0, // 40
|
||||
(1.0 + (1.0 / 6) * 1) / 31.0, // 41
|
||||
1.0 / 31.0, // 42
|
||||
((1.0 / 34) * 33) / 31.0, // 43
|
||||
((1.0 / 34) * 32) / 31.0, // 44
|
||||
((1.0 / 34) * 31) / 31.0, // 45
|
||||
((1.0 / 34) * 30) / 31.0, // 46
|
||||
((1.0 / 34) * 29) / 31.0, // 47
|
||||
((1.0 / 34) * 28) / 31.0, // 48
|
||||
((1.0 / 34) * 27) / 31.0, // 49
|
||||
((1.0 / 34) * 26) / 31.0, // 50
|
||||
((1.0 / 34) * 25) / 31.0, // 51
|
||||
((1.0 / 34) * 24) / 31.0, // 52
|
||||
((1.0 / 34) * 23) / 31.0, // 53
|
||||
((1.0 / 34) * 22) / 31.0, // 54
|
||||
((1.0 / 34) * 21) / 31.0, // 55
|
||||
((1.0 / 34) * 20) / 31.0, // 56
|
||||
((1.0 / 34) * 19) / 31.0, // 57
|
||||
((1.0 / 34) * 18) / 31.0, // 58
|
||||
((1.0 / 34) * 17) / 31.0, // 59
|
||||
((1.0 / 34) * 16) / 31.0, // 60
|
||||
((1.0 / 34) * 15) / 31.0, // 61
|
||||
((1.0 / 34) * 14) / 31.0, // 62
|
||||
((1.0 / 34) * 13) / 31.0, // 63
|
||||
((1.0 / 34) * 12) / 31.0, // 64
|
||||
((1.0 / 34) * 11) / 31.0, // 65
|
||||
((1.0 / 34) * 10) / 31.0, // 66
|
||||
((1.0 / 34) * 9) / 31.0, // 67
|
||||
((1.0 / 34) * 8) / 31.0, // 68
|
||||
((1.0 / 34) * 7) / 31.0, // 69
|
||||
((1.0 / 34) * 6) / 31.0, // 70
|
||||
((1.0 / 34) * 5) / 31.0, // 71
|
||||
((1.0 / 34) * 4) / 31.0, // 72
|
||||
((1.0 / 34) * 3) / 31.0, // 73
|
||||
((1.0 / 34) * 2) / 31.0, // 74
|
||||
((1.0 / 34) * 1) / 31.0, // 75
|
||||
0.0, // 76
|
||||
}; |
||||
|
||||
int speaker_idx = cmd == CMD_UNK15_VOL ? 1 : 0; // guessed, both are set at the same time in current use cases
|
||||
m_output_gain[speaker_idx] = gain_table[std::min<uint8_t>(param, 0x4c)]; |
||||
|
||||
set_output_gain(speaker_idx, m_output_gain[speaker_idx]); |
||||
} |
||||
} |
||||
|
||||
void lc82310_device::fill_buffer() |
||||
{ |
||||
int pos = 0, frame_sample_rate = 0; |
||||
bool decoded_frame = mp3dec->decode_buffer(pos, m_mp3data_count, &samples[0], m_sample_count, frame_sample_rate, m_frame_channels); |
||||
m_samples_idx = 0; |
||||
|
||||
if (!decoded_frame || m_sample_count <= 0) |
||||
{ |
||||
// Frame decode failed
|
||||
if (m_mp3data_count >= mp3data.size()) |
||||
{ |
||||
std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin()); |
||||
m_mp3data_count--; |
||||
} |
||||
|
||||
return; |
||||
} |
||||
|
||||
std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin()); |
||||
m_mp3data_count -= pos; |
||||
|
||||
stream->set_sample_rate(frame_sample_rate); |
||||
} |
||||
|
||||
void lc82310_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount) |
||||
{ |
||||
int s1 = std::min(scount - pos, m_sample_count); |
||||
int words_per_sample = std::min(m_frame_channels, 2); |
||||
|
||||
for (int i = 0; i < s1; i++) |
||||
{ |
||||
outputs[0].put_int(pos, samples[m_samples_idx * words_per_sample], 32768); |
||||
outputs[1].put_int(pos, samples[m_samples_idx * words_per_sample + (words_per_sample >> 1)], 32768); |
||||
|
||||
m_samples_idx++; |
||||
pos++; |
||||
|
||||
if (m_samples_idx >= m_sample_count) |
||||
{ |
||||
m_sample_count = 0; |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void lc82310_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) |
||||
{ |
||||
int csamples = outputs[0].samples(); |
||||
int pos = 0; |
||||
|
||||
while (pos < csamples) |
||||
{ |
||||
if (m_sample_count == 0) |
||||
fill_buffer(); |
||||
|
||||
if (m_sample_count <= 0) |
||||
{ |
||||
outputs[0].fill(0, pos); |
||||
outputs[1].fill(0, pos); |
||||
return; |
||||
} |
||||
|
||||
append_buffer(outputs, pos, csamples); |
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/**********************************************************************
|
||||
|
||||
Sanyo LC82310 MP3 decoder |
||||
|
||||
**********************************************************************/ |
||||
|
||||
#ifndef MAME_SOUND_LC82310_H |
||||
#define MAME_SOUND_LC82310_H |
||||
|
||||
#pragma once |
||||
|
||||
#include "mp3_audio.h" |
||||
|
||||
class lc82310_device : public device_t, |
||||
public device_sound_interface |
||||
{ |
||||
public: |
||||
lc82310_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); |
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(zcsctl_w); |
||||
DECLARE_WRITE_LINE_MEMBER(ckctl_w); |
||||
DECLARE_WRITE_LINE_MEMBER(dictl_w); |
||||
DECLARE_READ_LINE_MEMBER(doctl_r); |
||||
DECLARE_READ_LINE_MEMBER(demand_r); |
||||
|
||||
void dimpg_w(uint8_t data); |
||||
|
||||
void reset_playback(); |
||||
|
||||
protected: |
||||
virtual void device_start() override; |
||||
virtual void device_reset() override; |
||||
|
||||
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override; |
||||
|
||||
private: |
||||
enum : uint8_t |
||||
{ |
||||
ACCEPTING_CMD, |
||||
ACCEPTING_PARAM, |
||||
}; |
||||
|
||||
enum : uint8_t |
||||
{ |
||||
CMD_UNK10 = 0x10, |
||||
CMD_UNK11 = 0x11, |
||||
CMD_UNK12 = 0x12, // Set to 0 when writing data and 1 when not writing data
|
||||
CMD_UNK13_VOL = 0x13, |
||||
CMD_UNK15_VOL = 0x15, |
||||
CMD_UNK17 = 0x17, |
||||
CMD_UNK18 = 0x18, |
||||
CMD_SET_CONFIGURATION = 0x22, // has PLLOFF and SLEEP bits
|
||||
CMD_UNK80 = 0x80, |
||||
CMD_UNK81 = 0x81, |
||||
CMD_UNK82 = 0x82, |
||||
CMD_GET_ERROR_STATUS = 0x83, |
||||
CMD_UNK84 = 0x84, |
||||
CMD_UNK85 = 0x85, |
||||
}; |
||||
|
||||
void handle_command(uint8_t cmd, uint8_t param); |
||||
|
||||
void fill_buffer(); |
||||
void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount); |
||||
|
||||
sound_stream *stream; |
||||
std::unique_ptr<mp3_audio> mp3dec; |
||||
|
||||
std::array<uint8_t, 0x1000> mp3data; |
||||
std::array<short, 1152*2> samples; |
||||
|
||||
uint32_t m_mp3data_count; |
||||
int32_t m_sample_count, m_samples_idx; |
||||
int32_t m_frame_channels; |
||||
float m_output_gain[2]; |
||||
|
||||
uint8_t m_csctl; |
||||
uint8_t m_ckctl; |
||||
uint8_t m_dictl; |
||||
uint8_t m_doctl; |
||||
uint8_t m_ctl_state; |
||||
uint8_t m_ctl_cmd; |
||||
uint8_t m_ctl_bits; |
||||
uint8_t m_ctl_byte; |
||||
uint8_t m_ctl_out_byte; |
||||
}; |
||||
|
||||
DECLARE_DEVICE_TYPE(LC82310, lc82310_device) |
||||
|
||||
#endif // MAME_SOUND_LC82310_H
|
@ -0,0 +1,79 @@ |
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/***************************************************************************
|
||||
|
||||
MP3 audio decoder |
||||
|
||||
***************************************************************************/ |
||||
|
||||
#include "emu.h" |
||||
#include "mp3_audio.h" |
||||
|
||||
#define MINIMP3_IMPLEMENTATION |
||||
#define MAX_FRAME_SYNC_MATCHES 2 |
||||
#include "minimp3/minimp3.h" |
||||
|
||||
// To avoid modifying minimp3.h, forward declare mp3dec_local_t in mp3_audio.h and then make it an mp3dec_t using inheritance
|
||||
struct mp3_audio::mp3dec_local_t : public mp3dec_t |
||||
{ |
||||
}; |
||||
|
||||
mp3_audio::mp3_audio(const void *_base) |
||||
: base((const uint8_t *)_base) |
||||
{ |
||||
dec = std::make_unique<mp3dec_local_t>(); |
||||
clear(); |
||||
} |
||||
|
||||
mp3_audio::~mp3_audio() |
||||
{ |
||||
} |
||||
|
||||
void mp3_audio::clear() |
||||
{ |
||||
mp3dec_init(dec.get()); |
||||
m_found_stream = false; |
||||
} |
||||
|
||||
bool mp3_audio::decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels) |
||||
{ |
||||
mp3dec_frame_info_t info = {}; |
||||
|
||||
if (!m_found_stream) |
||||
{ |
||||
// Guarantee a specified number of frames are buffered before starting decoding to ensure it's not full of garbage that looks like a valid frame
|
||||
int free_format_bytes = 0; |
||||
int frame_bytes = 0; |
||||
int frame_offset = mp3d_find_frame(base, limit, &free_format_bytes, &frame_bytes); |
||||
|
||||
if (frame_bytes && frame_offset + frame_bytes <= limit) |
||||
{ |
||||
int i = 0, nmatch = 0; |
||||
|
||||
for (i = frame_offset, nmatch = 0; nmatch < MAX_FRAME_SYNC_MATCHES; nmatch++) |
||||
{ |
||||
i += hdr_frame_bytes(base + i, frame_bytes) + hdr_padding(base + i); |
||||
if (i + HDR_SIZE > limit || !hdr_compare(base + frame_offset, base + i)) |
||||
break; |
||||
} |
||||
|
||||
m_found_stream = nmatch >= MAX_FRAME_SYNC_MATCHES; |
||||
} |
||||
|
||||
if (!m_found_stream) |
||||
{ |
||||
output_samples = 0; |
||||
sample_rate = 0; |
||||
channels = 0; |
||||
pos = 0; |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
output_samples = mp3dec_decode_frame(dec.get(), base, limit, output, &info); |
||||
sample_rate = info.hz; |
||||
channels = info.channels; |
||||
pos = info.frame_bytes; |
||||
|
||||
return pos > 0 && output_samples > 0; |
||||
} |
@ -0,0 +1,35 @@ |
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/***************************************************************************
|
||||
|
||||
MP3 audio decoder |
||||
|
||||
***************************************************************************/ |
||||
|
||||
#ifndef MAME_SOUND_MP3_AUDIO_H |
||||
#define MAME_SOUND_MP3_AUDIO_H |
||||
|
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
|
||||
class mp3_audio |
||||
{ |
||||
public: |
||||
mp3_audio(const void *base); |
||||
~mp3_audio(); |
||||
|
||||
bool decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels); |
||||
|
||||
void clear(); |
||||
|
||||
private: |
||||
struct mp3dec_local_t; |
||||
|
||||
const uint8_t *base; |
||||
|
||||
std::unique_ptr<mp3dec_local_t> dec; |
||||
bool m_found_stream; |
||||
}; |
||||
|
||||
#endif |
Loading…
Reference in new issue