From fb2b5745d1d0e76ab9cb8c8418b0a9fe95330074 Mon Sep 17 00:00:00 2001 From: 987123879113 <63495610+987123879113@users.noreply.github.com> Date: Mon, 15 May 2023 13:38:30 +0900 Subject: 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) --- 3rdparty/minimp3/minimp3.h | 24 +- 3rdparty/minimp3/minimp3_ex.h | 1394 --------------------------------- scripts/src/sound.lua | 24 + src/devices/cpu/tlcs900/tmp95c061.cpp | 6 +- src/devices/sound/lc82310.cpp | 297 +++++++ src/devices/sound/lc82310.h | 92 +++ src/devices/sound/mas3507d.cpp | 80 +- src/devices/sound/mas3507d.h | 15 +- src/devices/sound/mp3_audio.cpp | 79 ++ src/devices/sound/mp3_audio.h | 35 + src/mame/namco/namcos10.cpp | 287 ++++++- 11 files changed, 834 insertions(+), 1499 deletions(-) delete mode 100644 3rdparty/minimp3/minimp3_ex.h create mode 100644 src/devices/sound/lc82310.cpp create mode 100644 src/devices/sound/lc82310.h create mode 100644 src/devices/sound/mp3_audio.cpp create mode 100644 src/devices/sound/mp3_audio.h diff --git a/3rdparty/minimp3/minimp3.h b/3rdparty/minimp3/minimp3.h index 796cbc1f8ed..3220ae1a85b 100644 --- a/3rdparty/minimp3/minimp3.h +++ b/3rdparty/minimp3/minimp3.h @@ -881,12 +881,22 @@ static void L3_midside_stereo(float *left, int n) int i = 0; float *right = left + 576; #if HAVE_SIMD - if (have_simd()) for (; i < n - 3; i += 4) + if (have_simd()) { - f4 vl = VLD(left + i); - f4 vr = VLD(right + i); - VSTORE(left + i, VADD(vl, vr)); - VSTORE(right + i, VSUB(vl, vr)); + for (; i < n - 3; i += 4) + { + f4 vl = VLD(left + i); + f4 vr = VLD(right + i); + VSTORE(left + i, VADD(vl, vr)); + VSTORE(right + i, VSUB(vl, vr)); + } +#ifdef __GNUC__ + /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc. + * For more info see: https://github.com/lieff/minimp3/issues/88 + */ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; +#endif } #endif /* HAVE_SIMD */ for (; i < n; i++) @@ -1353,7 +1363,7 @@ static void mp3d_DCT_II(float *grbuf, int n) } else #endif /* HAVE_SIMD */ #ifdef MINIMP3_ONLY_SIMD - {} + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ #else /* MINIMP3_ONLY_SIMD */ for (; k < n; k++) { @@ -1583,7 +1593,7 @@ static void mp3d_synth(float *xl, mp3d_sample_t *dstl, int nch, float *lins) } else #endif /* HAVE_SIMD */ #ifdef MINIMP3_ONLY_SIMD - {} + {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */ #else /* MINIMP3_ONLY_SIMD */ for (i = 14; i >= 0; i--) { diff --git a/3rdparty/minimp3/minimp3_ex.h b/3rdparty/minimp3/minimp3_ex.h deleted file mode 100644 index e29dd15b2ec..00000000000 --- a/3rdparty/minimp3/minimp3_ex.h +++ /dev/null @@ -1,1394 +0,0 @@ -#ifndef MINIMP3_EXT_H -#define MINIMP3_EXT_H -/* - https://github.com/lieff/minimp3 - To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. - This software is distributed without any warranty. - See . -*/ -#include "minimp3.h" - -/* flags for mp3dec_ex_open_* functions */ -#define MP3D_SEEK_TO_BYTE 0 /* mp3dec_ex_seek seeks to byte in stream */ -#define MP3D_SEEK_TO_SAMPLE 1 /* mp3dec_ex_seek precisely seeks to sample using index (created during duration calculation scan or when mp3dec_ex_seek called) */ -#define MP3D_DO_NOT_SCAN 2 /* do not scan whole stream for duration if vbrtag not found, mp3dec_ex_t::samples will be filled only if mp3dec_ex_t::vbr_tag_found == 1 */ -#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION -#define MP3D_ALLOW_MONO_STEREO_TRANSITION 4 -#define MP3D_FLAGS_MASK 7 -#else -#define MP3D_FLAGS_MASK 3 -#endif - -/* compile-time config */ -#define MINIMP3_PREDECODE_FRAMES 2 /* frames to pre-decode and skip after seek (to fill internal structures) */ -/*#define MINIMP3_SEEK_IDX_LINEAR_SEARCH*/ /* define to use linear index search instead of binary search on seek */ -#define MINIMP3_IO_SIZE (128*1024) /* io buffer size for streaming functions, must be greater than MINIMP3_BUF_SIZE */ -#define MINIMP3_BUF_SIZE (16*1024) /* buffer which can hold minimum 10 consecutive mp3 frames (~16KB) worst case */ -/*#define MINIMP3_SCAN_LIMIT (256*1024)*/ /* how many bytes will be scanned to search first valid mp3 frame, to prevent stall on large non-mp3 files */ -#define MINIMP3_ENABLE_RING 0 /* WIP enable hardware magic ring buffer if available, to make less input buffer memmove(s) in callback IO mode */ - -/* return error codes */ -#define MP3D_E_PARAM -1 -#define MP3D_E_MEMORY -2 -#define MP3D_E_IOERROR -3 -#define MP3D_E_USER -4 /* can be used to stop processing from callbacks without indicating specific error */ -#define MP3D_E_DECODE -5 /* decode error which can't be safely skipped, such as sample rate, layer and channels change */ - -typedef struct -{ - mp3d_sample_t *buffer; - size_t samples; /* channels included, byte size = samples*sizeof(mp3d_sample_t) */ - int channels, hz, layer, avg_bitrate_kbps; -} mp3dec_file_info_t; - -typedef struct -{ - const uint8_t *buffer; - size_t size; -} mp3dec_map_info_t; - -typedef struct -{ - uint64_t sample; - uint64_t offset; -} mp3dec_frame_t; - -typedef struct -{ - mp3dec_frame_t *frames; - size_t num_frames, capacity; -} mp3dec_index_t; - -typedef size_t (*MP3D_READ_CB)(void *buf, size_t size, void *user_data); -typedef int (*MP3D_SEEK_CB)(uint64_t position, void *user_data); - -typedef struct -{ - MP3D_READ_CB read; - void *read_data; - MP3D_SEEK_CB seek; - void *seek_data; -} mp3dec_io_t; - -typedef struct -{ - mp3dec_t mp3d; - mp3dec_map_info_t file; - mp3dec_io_t *io; - mp3dec_index_t index; - uint64_t offset, samples, detected_samples, cur_sample, start_offset, end_offset; - mp3dec_frame_info_t info; - mp3d_sample_t buffer[MINIMP3_MAX_SAMPLES_PER_FRAME]; - size_t input_consumed, input_filled; - int is_file, flags, vbr_tag_found, indexes_built; - int free_format_bytes; - int buffer_samples, buffer_consumed, to_skip, start_delay; - int last_error; -} mp3dec_ex_t; - -typedef int (*MP3D_ITERATE_CB)(void *user_data, const uint8_t *frame, int frame_size, int free_format_bytes, size_t buf_size, uint64_t offset, mp3dec_frame_info_t *info); -typedef int (*MP3D_PROGRESS_CB)(void *user_data, size_t file_size, uint64_t offset, mp3dec_frame_info_t *info); - -#ifdef __cplusplus -extern "C" { -#endif - -/* detect mp3/mpa format */ -int mp3dec_detect_buf(const uint8_t *buf, size_t buf_size); -int mp3dec_detect_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size); -/* decode whole buffer block */ -int mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data); -int mp3dec_load_cb(mp3dec_t *dec, mp3dec_io_t *io, uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data); -/* iterate through frames */ -int mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data); -int mp3dec_iterate_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data); -/* streaming decoder with seeking capability */ -int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int flags); -int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int flags); -void mp3dec_ex_close(mp3dec_ex_t *dec); -int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position); -size_t mp3dec_ex_read_frame(mp3dec_ex_t *dec, mp3d_sample_t **buf, mp3dec_frame_info_t *frame_info, size_t max_samples); -size_t mp3dec_ex_read(mp3dec_ex_t *dec, mp3d_sample_t *buf, size_t samples); -#ifndef MINIMP3_NO_STDIO -/* stdio versions of file detect, load, iterate and stream */ -int mp3dec_detect(const char *file_name); -int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data); -int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data); -int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int flags); -#ifdef _WIN32 -int mp3dec_detect_w(const wchar_t *file_name); -int mp3dec_load_w(mp3dec_t *dec, const wchar_t *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data); -int mp3dec_iterate_w(const wchar_t *file_name, MP3D_ITERATE_CB callback, void *user_data); -int mp3dec_ex_open_w(mp3dec_ex_t *dec, const wchar_t *file_name, int flags); -#endif -#endif - -#ifdef __cplusplus -} -#endif -#endif /*MINIMP3_EXT_H*/ - -#ifdef MINIMP3_IMPLEMENTATION -#include - -static void mp3dec_skip_id3v1(const uint8_t *buf, size_t *pbuf_size) -{ - size_t buf_size = *pbuf_size; -#ifndef MINIMP3_NOSKIP_ID3V1 - if (buf_size >= 128 && !memcmp(buf + buf_size - 128, "TAG", 3)) - { - buf_size -= 128; - if (buf_size >= 227 && !memcmp(buf + buf_size - 227, "TAG+", 4)) - buf_size -= 227; - } -#endif -#ifndef MINIMP3_NOSKIP_APEV2 - if (buf_size > 32 && !memcmp(buf + buf_size - 32, "APETAGEX", 8)) - { - buf_size -= 32; - const uint8_t *tag = buf + buf_size + 8 + 4; - uint32_t tag_size = (uint32_t)(tag[3] << 24) | (tag[2] << 16) | (tag[1] << 8) | tag[0]; - if (buf_size >= tag_size) - buf_size -= tag_size; - } -#endif - *pbuf_size = buf_size; -} - -static size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size) -{ -#define MINIMP3_ID3_DETECT_SIZE 10 -#ifndef MINIMP3_NOSKIP_ID3V2 - if (buf_size >= MINIMP3_ID3_DETECT_SIZE && !memcmp(buf, "ID3", 3) && !((buf[5] & 15) || (buf[6] & 0x80) || (buf[7] & 0x80) || (buf[8] & 0x80) || (buf[9] & 0x80))) - { - size_t id3v2size = (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10; - if ((buf[5] & 16)) - id3v2size += 10; /* footer */ - return id3v2size; - } -#endif - return 0; -} - -static void mp3dec_skip_id3(const uint8_t **pbuf, size_t *pbuf_size) -{ - uint8_t *buf = (uint8_t *)(*pbuf); - size_t buf_size = *pbuf_size; - size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size); - if (id3v2size) - { - if (id3v2size >= buf_size) - id3v2size = buf_size; - buf += id3v2size; - buf_size -= id3v2size; - } - mp3dec_skip_id3v1(buf, &buf_size); - *pbuf = (const uint8_t *)buf; - *pbuf_size = buf_size; -} - -static int mp3dec_check_vbrtag(const uint8_t *frame, int frame_size, uint32_t *frames, int *delay, int *padding) -{ - static const char g_xing_tag[4] = { 'X', 'i', 'n', 'g' }; - static const char g_info_tag[4] = { 'I', 'n', 'f', 'o' }; -#define FRAMES_FLAG 1 -#define BYTES_FLAG 2 -#define TOC_FLAG 4 -#define VBR_SCALE_FLAG 8 - /* Side info offsets after header: - / Mono Stereo - / MPEG1 17 32 - / MPEG2 & 2.5 9 17*/ - bs_t bs[1]; - L3_gr_info_t gr_info[4]; - bs_init(bs, frame + HDR_SIZE, frame_size - HDR_SIZE); - if (HDR_IS_CRC(frame)) - get_bits(bs, 16); - if (L3_read_side_info(bs, gr_info, frame) < 0) - return 0; /* side info corrupted */ - - const uint8_t *tag = frame + HDR_SIZE + bs->pos/8; - if (memcmp(g_xing_tag, tag, 4) && memcmp(g_info_tag, tag, 4)) - return 0; - int flags = tag[7]; - if (!((flags & FRAMES_FLAG))) - return -1; - tag += 8; - *frames = (uint32_t)(tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3]; - tag += 4; - if (flags & BYTES_FLAG) - tag += 4; - if (flags & TOC_FLAG) - tag += 100; - if (flags & VBR_SCALE_FLAG) - tag += 4; - *delay = *padding = 0; - if (*tag) - { /* extension, LAME, Lavc, etc. Should be the same structure. */ - tag += 21; - if (tag - frame + 14 >= frame_size) - return 0; - *delay = ((tag[0] << 4) | (tag[1] >> 4)) + (528 + 1); - *padding = (((tag[1] & 0xF) << 8) | tag[2]) - (528 + 1); - } - return 1; -} - -int mp3dec_detect_buf(const uint8_t *buf, size_t buf_size) -{ - return mp3dec_detect_cb(0, (uint8_t *)buf, buf_size); -} - -int mp3dec_detect_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size) -{ - if (!buf || (size_t)-1 == buf_size || (io && buf_size < MINIMP3_BUF_SIZE)) - return MP3D_E_PARAM; - size_t filled = buf_size; - if (io) - { - if (io->seek(0, io->seek_data)) - return MP3D_E_IOERROR; - filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data); - if (filled > MINIMP3_ID3_DETECT_SIZE) - return MP3D_E_IOERROR; - } - if (filled < MINIMP3_ID3_DETECT_SIZE) - return MP3D_E_USER; /* too small, can't be mp3/mpa */ - if (mp3dec_skip_id3v2(buf, filled)) - return 0; /* id3v2 tag is enough evidence */ - if (io) - { - size_t readed = io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data); - if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE)) - return MP3D_E_IOERROR; - filled += readed; - if (filled < MINIMP3_BUF_SIZE) - mp3dec_skip_id3v1(buf, &filled); - } else - { - mp3dec_skip_id3v1(buf, &filled); - if (filled > MINIMP3_BUF_SIZE) - filled = MINIMP3_BUF_SIZE; - } - int free_format_bytes, frame_size; - mp3d_find_frame(buf, filled, &free_format_bytes, &frame_size); - if (frame_size) - return 0; /* MAX_FRAME_SYNC_MATCHES consecutive frames found */ - return MP3D_E_USER; -} - -int mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) -{ - return mp3dec_load_cb(dec, 0, (uint8_t *)buf, buf_size, info, progress_cb, user_data); -} - -int mp3dec_load_cb(mp3dec_t *dec, mp3dec_io_t *io, uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) -{ - if (!dec || !buf || !info || (size_t)-1 == buf_size || (io && buf_size < MINIMP3_BUF_SIZE)) - return MP3D_E_PARAM; - uint64_t detected_samples = 0; - size_t orig_buf_size = buf_size; - int to_skip = 0; - mp3dec_frame_info_t frame_info; - memset(info, 0, sizeof(*info)); - memset(&frame_info, 0, sizeof(frame_info)); - - /* skip id3 */ - size_t filled = 0, consumed = 0; - int eof = 0, ret = 0; - if (io) - { - if (io->seek(0, io->seek_data)) - return MP3D_E_IOERROR; - filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data); - if (filled > MINIMP3_ID3_DETECT_SIZE) - return MP3D_E_IOERROR; - if (MINIMP3_ID3_DETECT_SIZE != filled) - return 0; - size_t id3v2size = mp3dec_skip_id3v2(buf, filled); - if (id3v2size) - { - if (io->seek(id3v2size, io->seek_data)) - return MP3D_E_IOERROR; - filled = io->read(buf, buf_size, io->read_data); - if (filled > buf_size) - return MP3D_E_IOERROR; - } else - { - size_t readed = io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data); - if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE)) - return MP3D_E_IOERROR; - filled += readed; - } - if (filled < MINIMP3_BUF_SIZE) - mp3dec_skip_id3v1(buf, &filled); - } else - { - mp3dec_skip_id3((const uint8_t **)&buf, &buf_size); - if (!buf_size) - return 0; - } - /* try to make allocation size assumption by first frame or vbr tag */ - mp3dec_init(dec); - int samples; - do - { - uint32_t frames; - int i, delay, padding, free_format_bytes = 0, frame_size = 0; - const uint8_t *hdr; - if (io) - { - if (!eof && filled - consumed < MINIMP3_BUF_SIZE) - { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ - memmove(buf, buf + consumed, filled - consumed); - filled -= consumed; - consumed = 0; - size_t readed = io->read(buf + filled, buf_size - filled, io->read_data); - if (readed > (buf_size - filled)) - return MP3D_E_IOERROR; - if (readed != (buf_size - filled)) - eof = 1; - filled += readed; - if (eof) - mp3dec_skip_id3v1(buf, &filled); - } - i = mp3d_find_frame(buf + consumed, filled - consumed, &free_format_bytes, &frame_size); - consumed += i; - hdr = buf + consumed; - } else - { - i = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size); - buf += i; - buf_size -= i; - hdr = buf; - } - if (i && !frame_size) - continue; - if (!frame_size) - return 0; - frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2; - frame_info.hz = hdr_sample_rate_hz(hdr); - frame_info.layer = 4 - HDR_GET_LAYER(hdr); - frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr); - frame_info.frame_bytes = frame_size; - samples = hdr_frame_samples(hdr)*frame_info.channels; - if (3 != frame_info.layer) - break; - int ret = mp3dec_check_vbrtag(hdr, frame_size, &frames, &delay, &padding); - if (ret > 0) - { - padding *= frame_info.channels; - to_skip = delay*frame_info.channels; - detected_samples = samples*(uint64_t)frames; - if (detected_samples >= (uint64_t)to_skip) - detected_samples -= to_skip; - if (padding > 0 && detected_samples >= (uint64_t)padding) - detected_samples -= padding; - if (!detected_samples) - return 0; - } - if (ret) - { - if (io) - { - consumed += frame_size; - } else - { - buf += frame_size; - buf_size -= frame_size; - } - } - break; - } while(1); - size_t allocated = MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t); - if (detected_samples) - allocated += detected_samples*sizeof(mp3d_sample_t); - else - allocated += (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t); - info->buffer = (mp3d_sample_t*)malloc(allocated); - if (!info->buffer) - return MP3D_E_MEMORY; - /* save info */ - info->channels = frame_info.channels; - info->hz = frame_info.hz; - info->layer = frame_info.layer; - /* decode all frames */ - size_t avg_bitrate_kbps = 0, frames = 0; - do - { - if ((allocated - info->samples*sizeof(mp3d_sample_t)) < MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t)) - { - allocated *= 2; - mp3d_sample_t *alloc_buf = (mp3d_sample_t*)realloc(info->buffer, allocated); - if (!alloc_buf) - return MP3D_E_MEMORY; - info->buffer = alloc_buf; - } - if (io) - { - if (!eof && filled - consumed < MINIMP3_BUF_SIZE) - { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ - memmove(buf, buf + consumed, filled - consumed); - filled -= consumed; - consumed = 0; - size_t readed = io->read(buf + filled, buf_size - filled, io->read_data); - if (readed != (buf_size - filled)) - eof = 1; - filled += readed; - if (eof) - mp3dec_skip_id3v1(buf, &filled); - } - samples = mp3dec_decode_frame(dec, buf + consumed, filled - consumed, info->buffer + info->samples, &frame_info); - consumed += frame_info.frame_bytes; - } else - { - samples = mp3dec_decode_frame(dec, buf, MINIMP3_MIN(buf_size, (size_t)INT_MAX), info->buffer + info->samples, &frame_info); - buf += frame_info.frame_bytes; - buf_size -= frame_info.frame_bytes; - } - if (samples) - { - if (info->hz != frame_info.hz || info->layer != frame_info.layer) - { - ret = MP3D_E_DECODE; - break; - } - if (info->channels && info->channels != frame_info.channels) - { -#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION - info->channels = 0; /* mark file with mono-stereo transition */ -#else - ret = MP3D_E_DECODE; - break; -#endif - } - samples *= frame_info.channels; - if (to_skip) - { - size_t skip = MINIMP3_MIN(samples, to_skip); - to_skip -= skip; - samples -= skip; - memmove(info->buffer, info->buffer + skip, samples*sizeof(mp3d_sample_t)); - } - info->samples += samples; - avg_bitrate_kbps += frame_info.bitrate_kbps; - frames++; - if (progress_cb) - { - ret = progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info); - if (ret) - break; - } - } - } while (frame_info.frame_bytes); - if (detected_samples && info->samples > detected_samples) - info->samples = detected_samples; /* cut padding */ - /* reallocate to normal buffer size */ - if (allocated != info->samples*sizeof(mp3d_sample_t)) - { - mp3d_sample_t *alloc_buf = (mp3d_sample_t*)realloc(info->buffer, info->samples*sizeof(mp3d_sample_t)); - if (!alloc_buf && info->samples) - return MP3D_E_MEMORY; - info->buffer = alloc_buf; - } - if (frames) - info->avg_bitrate_kbps = avg_bitrate_kbps/frames; - return ret; -} - -int mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data) -{ - const uint8_t *orig_buf = buf; - if (!buf || (size_t)-1 == buf_size || !callback) - return MP3D_E_PARAM; - /* skip id3 */ - mp3dec_skip_id3(&buf, &buf_size); - if (!buf_size) - return 0; - mp3dec_frame_info_t frame_info; - memset(&frame_info, 0, sizeof(frame_info)); - do - { - int free_format_bytes = 0, frame_size = 0, ret; - int i = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size); - buf += i; - buf_size -= i; - if (i && !frame_size) - continue; - if (!frame_size) - break; - const uint8_t *hdr = buf; - frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2; - frame_info.hz = hdr_sample_rate_hz(hdr); - frame_info.layer = 4 - HDR_GET_LAYER(hdr); - frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr); - frame_info.frame_bytes = frame_size; - - if (callback) - { - if ((ret = callback(user_data, hdr, frame_size, free_format_bytes, buf_size, hdr - orig_buf, &frame_info))) - return ret; - } - buf += frame_size; - buf_size -= frame_size; - } while (1); - return 0; -} - -int mp3dec_iterate_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data) -{ - if (!io || !buf || (size_t)-1 == buf_size || buf_size < MINIMP3_BUF_SIZE || !callback) - return MP3D_E_PARAM; - size_t filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data), consumed = 0; - uint64_t readed = 0; - mp3dec_frame_info_t frame_info; - int eof = 0; - memset(&frame_info, 0, sizeof(frame_info)); - if (filled > MINIMP3_ID3_DETECT_SIZE) - return MP3D_E_IOERROR; - if (MINIMP3_ID3_DETECT_SIZE != filled) - return 0; - size_t id3v2size = mp3dec_skip_id3v2(buf, filled); - if (id3v2size) - { - if (io->seek(id3v2size, io->seek_data)) - return MP3D_E_IOERROR; - filled = io->read(buf, buf_size, io->read_data); - if (filled > buf_size) - return MP3D_E_IOERROR; - readed += id3v2size; - } else - { - size_t readed = io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data); - if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE)) - return MP3D_E_IOERROR; - filled += readed; - } - if (filled < MINIMP3_BUF_SIZE) - mp3dec_skip_id3v1(buf, &filled); - do - { - int free_format_bytes = 0, frame_size = 0, ret; - int i = mp3d_find_frame(buf + consumed, filled - consumed, &free_format_bytes, &frame_size); - if (i && !frame_size) - { - consumed += i; - continue; - } - if (!frame_size) - break; - const uint8_t *hdr = buf + consumed + i; - frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2; - frame_info.hz = hdr_sample_rate_hz(hdr); - frame_info.layer = 4 - HDR_GET_LAYER(hdr); - frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr); - frame_info.frame_bytes = frame_size; - - readed += i; - if (callback) - { - if ((ret = callback(user_data, hdr, frame_size, free_format_bytes, filled - consumed, readed, &frame_info))) - return ret; - } - readed += frame_size; - consumed += i + frame_size; - if (!eof && filled - consumed < MINIMP3_BUF_SIZE) - { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ - memmove(buf, buf + consumed, filled - consumed); - filled -= consumed; - consumed = 0; - size_t readed = io->read(buf + filled, buf_size - filled, io->read_data); - if (readed > (buf_size - filled)) - return MP3D_E_IOERROR; - if (readed != (buf_size - filled)) - eof = 1; - filled += readed; - if (eof) - mp3dec_skip_id3v1(buf, &filled); - } - } while (1); - return 0; -} - -static int mp3dec_load_index(void *user_data, const uint8_t *frame, int frame_size, int free_format_bytes, size_t buf_size, uint64_t offset, mp3dec_frame_info_t *info) -{ - mp3dec_frame_t *idx_frame; - mp3dec_ex_t *dec = (mp3dec_ex_t *)user_data; - if (!dec->index.frames && !dec->start_offset) - { /* detect VBR tag and try to avoid full scan */ - uint32_t frames; - int delay, padding; - dec->info = *info; - dec->start_offset = dec->offset = offset; - dec->end_offset = offset + buf_size; - dec->free_format_bytes = free_format_bytes; /* should not change */ - if (3 == dec->info.layer) - { - int ret = mp3dec_check_vbrtag(frame, frame_size, &frames, &delay, &padding); - if (ret) - dec->start_offset = dec->offset = offset + frame_size; - if (ret > 0) - { - padding *= info->channels; - dec->start_delay = dec->to_skip = delay*info->channels; - dec->samples = hdr_frame_samples(frame)*info->channels*(uint64_t)frames; - if (dec->samples >= (uint64_t)dec->start_delay) - dec->samples -= dec->start_delay; - if (padding > 0 && dec->samples >= (uint64_t)padding) - dec->samples -= padding; - dec->detected_samples = dec->samples; - dec->vbr_tag_found = 1; - return MP3D_E_USER; - } else if (ret < 0) - return 0; - } - } - if (dec->flags & MP3D_DO_NOT_SCAN) - return MP3D_E_USER; - if (dec->index.num_frames + 1 > dec->index.capacity) - { - if (!dec->index.capacity) - dec->index.capacity = 4096; - else - dec->index.capacity *= 2; - mp3dec_frame_t *alloc_buf = (mp3dec_frame_t *)realloc((void*)dec->index.frames, sizeof(mp3dec_frame_t)*dec->index.capacity); - if (!alloc_buf) - return MP3D_E_MEMORY; - dec->index.frames = alloc_buf; - } - idx_frame = &dec->index.frames[dec->index.num_frames++]; - idx_frame->offset = offset; - idx_frame->sample = dec->samples; - if (!dec->buffer_samples && dec->index.num_frames < 256) - { /* for some cutted mp3 frames, bit-reservoir not filled and decoding can't be started from first frames */ - /* try to decode up to 255 first frames till samples starts to decode */ - dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, frame, MINIMP3_MIN(buf_size, (size_t)INT_MAX), dec->buffer, info); - dec->samples += dec->buffer_samples*info->channels; - } else - dec->samples += hdr_frame_samples(frame)*info->channels; - return 0; -} - -int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int flags) -{ - if (!dec || !buf || (size_t)-1 == buf_size || (flags & (~MP3D_FLAGS_MASK))) - return MP3D_E_PARAM; - memset(dec, 0, sizeof(*dec)); - dec->file.buffer = buf; - dec->file.size = buf_size; - dec->flags = flags; - mp3dec_init(&dec->mp3d); - int ret = mp3dec_iterate_buf(dec->file.buffer, dec->file.size, mp3dec_load_index, dec); - if (ret && MP3D_E_USER != ret) - return ret; - mp3dec_init(&dec->mp3d); - dec->buffer_samples = 0; - dec->indexes_built = !(dec->vbr_tag_found || (flags & MP3D_DO_NOT_SCAN)); - dec->flags &= (~MP3D_DO_NOT_SCAN); - return 0; -} - -#ifndef MINIMP3_SEEK_IDX_LINEAR_SEARCH -static size_t mp3dec_idx_binary_search(mp3dec_index_t *idx, uint64_t position) -{ - size_t end = idx->num_frames, start = 0, index = 0; - while (start <= end) - { - size_t mid = (start + end) / 2; - if (idx->frames[mid].sample >= position) - { /* move left side. */ - if (idx->frames[mid].sample == position) - return mid; - end = mid - 1; - } else - { /* move to right side */ - index = mid; - start = mid + 1; - if (start == idx->num_frames) - break; - } - } - return index; -} -#endif - -int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position) -{ - size_t i; - if (!dec) - return MP3D_E_PARAM; - if (!(dec->flags & MP3D_SEEK_TO_SAMPLE)) - { - if (dec->io) - { - dec->offset = position; - } else - { - dec->offset = MINIMP3_MIN(position, dec->file.size); - } - dec->cur_sample = 0; - goto do_exit; - } - dec->cur_sample = position; - position += dec->start_delay; - if (0 == position) - { /* optimize seek to zero, no index needed */ -seek_zero: - dec->offset = dec->start_offset; - dec->to_skip = 0; - goto do_exit; - } - if (!dec->indexes_built) - { /* no index created yet (vbr tag used to calculate track length or MP3D_DO_NOT_SCAN open flag used) */ - dec->indexes_built = 1; - dec->samples = 0; - dec->buffer_samples = 0; - if (dec->io) - { - if (dec->io->seek(dec->start_offset, dec->io->seek_data)) - return MP3D_E_IOERROR; - int ret = mp3dec_iterate_cb(dec->io, (uint8_t *)dec->file.buffer, dec->file.size, mp3dec_load_index, dec); - if (ret && MP3D_E_USER != ret) - return ret; - } else - { - int ret = mp3dec_iterate_buf(dec->file.buffer + dec->start_offset, dec->file.size - dec->start_offset, mp3dec_load_index, dec); - if (ret && MP3D_E_USER != ret) - return ret; - } - for (i = 0; i < dec->index.num_frames; i++) - dec->index.frames[i].offset += dec->start_offset; - dec->samples = dec->detected_samples; - } - if (!dec->index.frames) - goto seek_zero; /* no frames in file - seek to zero */ -#ifdef MINIMP3_SEEK_IDX_LINEAR_SEARCH - for (i = 0; i < dec->index.num_frames; i++) - { - if (dec->index.frames[i].sample >= position) - break; - } -#else - i = mp3dec_idx_binary_search(&dec->index, position); -#endif - if (i) - { - int to_fill_bytes = 511; - int skip_frames = MINIMP3_PREDECODE_FRAMES -#ifdef MINIMP3_SEEK_IDX_LINEAR_SEARCH - + ((dec->index.frames[i].sample == position) ? 0 : 1) -#endif - ; - i -= MINIMP3_MIN(i, (size_t)skip_frames); - if (3 == dec->info.layer) - { - while (i && to_fill_bytes) - { /* make sure bit-reservoir is filled when we start decoding */ - bs_t bs[1]; - L3_gr_info_t gr_info[4]; - int frame_bytes, frame_size; - const uint8_t *hdr; - if (dec->io) - { - hdr = dec->file.buffer; - if (dec->io->seek(dec->index.frames[i - 1].offset, dec->io->seek_data)) - return MP3D_E_IOERROR; - size_t readed = dec->io->read((uint8_t *)hdr, HDR_SIZE, dec->io->read_data); - if (readed != HDR_SIZE) - return MP3D_E_IOERROR; - frame_size = hdr_frame_bytes(hdr, dec->free_format_bytes) + hdr_padding(hdr); - readed = dec->io->read((uint8_t *)hdr + HDR_SIZE, frame_size - HDR_SIZE, dec->io->read_data); - if (readed != (size_t)(frame_size - HDR_SIZE)) - return MP3D_E_IOERROR; - bs_init(bs, hdr + HDR_SIZE, frame_size - HDR_SIZE); - } else - { - hdr = dec->file.buffer + dec->index.frames[i - 1].offset; - frame_size = hdr_frame_bytes(hdr, dec->free_format_bytes) + hdr_padding(hdr); - bs_init(bs, hdr + HDR_SIZE, frame_size - HDR_SIZE); - } - if (HDR_IS_CRC(hdr)) - get_bits(bs, 16); - i--; - if (L3_read_side_info(bs, gr_info, hdr) < 0) - break; /* frame not decodable, we can start from here */ - frame_bytes = (bs->limit - bs->pos)/8; - to_fill_bytes -= MINIMP3_MIN(to_fill_bytes, frame_bytes); - } - } - } - dec->offset = dec->index.frames[i].offset; - dec->to_skip = position - dec->index.frames[i].sample; - while ((i + 1) < dec->index.num_frames && !dec->index.frames[i].sample && !dec->index.frames[i + 1].sample) - { /* skip not decodable first frames */ - const uint8_t *hdr; - if (dec->io) - { - hdr = dec->file.buffer; - if (dec->io->seek(dec->index.frames[i].offset, dec->io->seek_data)) - return MP3D_E_IOERROR; - size_t readed = dec->io->read((uint8_t *)hdr, HDR_SIZE, dec->io->read_data); - if (readed != HDR_SIZE) - return MP3D_E_IOERROR; - } else - hdr = dec->file.buffer + dec->index.frames[i].offset; - dec->to_skip += hdr_frame_samples(hdr)*dec->info.channels; - i++; - } -do_exit: - if (dec->io) - { - if (dec->io->seek(dec->offset, dec->io->seek_data)) - return MP3D_E_IOERROR; - } - dec->buffer_samples = 0; - dec->buffer_consumed = 0; - dec->input_consumed = 0; - dec->input_filled = 0; - dec->last_error = 0; - mp3dec_init(&dec->mp3d); - return 0; -} - -size_t mp3dec_ex_read_frame(mp3dec_ex_t *dec, mp3d_sample_t **buf, mp3dec_frame_info_t *frame_info, size_t max_samples) -{ - if (!dec || !buf || !frame_info) - { - if (dec) - dec->last_error = MP3D_E_PARAM; - return 0; - } - if (dec->detected_samples && dec->cur_sample >= dec->detected_samples) - return 0; /* at end of stream */ - if (dec->last_error) - return 0; /* error eof state, seek can reset it */ - *buf = NULL; - uint64_t end_offset = dec->end_offset ? dec->end_offset : dec->file.size; - int eof = 0; - while (dec->buffer_consumed == dec->buffer_samples) - { - const uint8_t *dec_buf; - if (dec->io) - { - if (!eof && (dec->input_filled - dec->input_consumed) < MINIMP3_BUF_SIZE) - { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ - memmove((uint8_t*)dec->file.buffer, (uint8_t*)dec->file.buffer + dec->input_consumed, dec->input_filled - dec->input_consumed); - dec->input_filled -= dec->input_consumed; - dec->input_consumed = 0; - size_t readed = dec->io->read((uint8_t*)dec->file.buffer + dec->input_filled, dec->file.size - dec->input_filled, dec->io->read_data); - if (readed > (dec->file.size - dec->input_filled)) - { - dec->last_error = MP3D_E_IOERROR; - readed = 0; - } - if (readed != (dec->file.size - dec->input_filled)) - eof = 1; - dec->input_filled += readed; - if (eof) - mp3dec_skip_id3v1((uint8_t*)dec->file.buffer, &dec->input_filled); - } - dec_buf = dec->file.buffer + dec->input_consumed; - if (!(dec->input_filled - dec->input_consumed)) - return 0; - dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, dec_buf, dec->input_filled - dec->input_consumed, dec->buffer, frame_info); - dec->input_consumed += frame_info->frame_bytes; - } else - { - dec_buf = dec->file.buffer + dec->offset; - uint64_t buf_size = end_offset - dec->offset; - if (!buf_size) - return 0; - dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, dec_buf, MINIMP3_MIN(buf_size, (uint64_t)INT_MAX), dec->buffer, frame_info); - } - dec->buffer_consumed = 0; - if (dec->info.hz != frame_info->hz || dec->info.layer != frame_info->layer) - { -return_e_decode: - dec->last_error = MP3D_E_DECODE; - return 0; - } - if (dec->buffer_samples) - { - dec->buffer_samples *= frame_info->channels; - if (dec->to_skip) - { - size_t skip = MINIMP3_MIN(dec->buffer_samples, dec->to_skip); - dec->buffer_consumed += skip; - dec->to_skip -= skip; - } - if ( -#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION - !(dec->flags & MP3D_ALLOW_MONO_STEREO_TRANSITION) && -#endif - dec->buffer_consumed != dec->buffer_samples && dec->info.channels != frame_info->channels) - { - goto return_e_decode; - } - } else if (dec->to_skip) - { /* In mp3 decoding not always can start decode from any frame because of bit reservoir, - count skip samples for such frames */ - int frame_samples = hdr_frame_samples(dec_buf)*frame_info->channels; - dec->to_skip -= MINIMP3_MIN(frame_samples, dec->to_skip); - } - dec->offset += frame_info->frame_bytes; - } - size_t out_samples = MINIMP3_MIN((size_t)(dec->buffer_samples - dec->buffer_consumed), max_samples); - if (dec->detected_samples) - { /* count decoded samples to properly cut padding */ - if (dec->cur_sample + out_samples >= dec->detected_samples) - out_samples = dec->detected_samples - dec->cur_sample; - } - dec->cur_sample += out_samples; - *buf = dec->buffer + dec->buffer_consumed; - dec->buffer_consumed += out_samples; - return out_samples; -} - -size_t mp3dec_ex_read(mp3dec_ex_t *dec, mp3d_sample_t *buf, size_t samples) -{ - if (!dec || !buf) - { - if (dec) - dec->last_error = MP3D_E_PARAM; - return 0; - } - mp3dec_frame_info_t frame_info; - memset(&frame_info, 0, sizeof(frame_info)); - size_t samples_requested = samples; - while (samples) - { - mp3d_sample_t *buf_frame = NULL; - size_t read_samples = mp3dec_ex_read_frame(dec, &buf_frame, &frame_info, samples); - if (!read_samples) - { - break; - } - memcpy(buf, buf_frame, read_samples * sizeof(mp3d_sample_t)); - buf += read_samples; - samples -= read_samples; - } - return samples_requested - samples; -} - -int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int flags) -{ - if (!dec || !io || (flags & (~MP3D_FLAGS_MASK))) - return MP3D_E_PARAM; - memset(dec, 0, sizeof(*dec)); -#ifdef MINIMP3_HAVE_RING - int ret; - if (ret = mp3dec_open_ring(&dec->file, MINIMP3_IO_SIZE)) - return ret; -#else - dec->file.size = MINIMP3_IO_SIZE; - dec->file.buffer = (const uint8_t*)malloc(dec->file.size); - if (!dec->file.buffer) - return MP3D_E_MEMORY; -#endif - dec->flags = flags; - dec->io = io; - mp3dec_init(&dec->mp3d); - if (io->seek(0, io->seek_data)) - return MP3D_E_IOERROR; - int ret = mp3dec_iterate_cb(io, (uint8_t *)dec->file.buffer, dec->file.size, mp3dec_load_index, dec); - if (ret && MP3D_E_USER != ret) - return ret; - if (dec->io->seek(dec->start_offset, dec->io->seek_data)) - return MP3D_E_IOERROR; - mp3dec_init(&dec->mp3d); - dec->buffer_samples = 0; - dec->indexes_built = !(dec->vbr_tag_found || (flags & MP3D_DO_NOT_SCAN)); - dec->flags &= (~MP3D_DO_NOT_SCAN); - return 0; -} - - -#ifndef MINIMP3_NO_STDIO - -#if defined(__linux__) || defined(__FreeBSD__) -#include -#include -#include -#include -#include -#include -#if !defined(_GNU_SOURCE) -#include -#include -#endif -#if !defined(MAP_POPULATE) && defined(__linux__) -#define MAP_POPULATE 0x08000 -#elif !defined(MAP_POPULATE) -#define MAP_POPULATE 0 -#endif - -static void mp3dec_close_file(mp3dec_map_info_t *map_info) -{ - if (map_info->buffer && MAP_FAILED != map_info->buffer) - munmap((void *)map_info->buffer, map_info->size); - map_info->buffer = 0; - map_info->size = 0; -} - -static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info) -{ - if (!file_name) - return MP3D_E_PARAM; - int file; - struct stat st; - memset(map_info, 0, sizeof(*map_info)); -retry_open: - file = open(file_name, O_RDONLY); - if (file < 0 && (errno == EAGAIN || errno == EINTR)) - goto retry_open; - if (file < 0 || fstat(file, &st) < 0) - { - close(file); - return MP3D_E_IOERROR; - } - - map_info->size = st.st_size; -retry_mmap: - map_info->buffer = (const uint8_t *)mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, file, 0); - if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR)) - goto retry_mmap; - close(file); - if (MAP_FAILED == map_info->buffer) - return MP3D_E_IOERROR; - return 0; -} - -#if MINIMP3_ENABLE_RING && defined(__linux__) && defined(_GNU_SOURCE) -#define MINIMP3_HAVE_RING -static void mp3dec_close_ring(mp3dec_map_info_t *map_info) -{ -#if defined(__linux__) && defined(_GNU_SOURCE) - if (map_info->buffer && MAP_FAILED != map_info->buffer) - munmap((void *)map_info->buffer, map_info->size*2); -#else - if (map_info->buffer) - { - shmdt(map_info->buffer); - shmdt(map_info->buffer + map_info->size); - } -#endif - map_info->buffer = 0; - map_info->size = 0; -} - -static int mp3dec_open_ring(mp3dec_map_info_t *map_info, size_t size) -{ - int memfd, page_size; -#if defined(__linux__) && defined(_GNU_SOURCE) - void *buffer; - int res; -#endif - memset(map_info, 0, sizeof(*map_info)); - -#ifdef _SC_PAGESIZE - page_size = sysconf(_SC_PAGESIZE); -#else - page_size = getpagesize(); -#endif - map_info->size = (size + page_size - 1)/page_size*page_size; - -#if defined(__linux__) && defined(_GNU_SOURCE) - memfd = memfd_create("mp3_ring", 0); - if (memfd < 0) - return MP3D_E_MEMORY; - -retry_ftruncate: - res = ftruncate(memfd, map_info->size); - if (res && (errno == EAGAIN || errno == EINTR)) - goto retry_ftruncate; - if (res) - goto error; - -retry_mmap: - map_info->buffer = (const uint8_t *)mmap(NULL, map_info->size*2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR)) - goto retry_mmap; - if (MAP_FAILED == map_info->buffer || !map_info->buffer) - goto error; -retry_mmap2: - buffer = mmap((void *)map_info->buffer, map_info->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, memfd, 0); - if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR)) - goto retry_mmap2; - if (MAP_FAILED == map_info->buffer || buffer != (void *)map_info->buffer) - goto error; -retry_mmap3: - buffer = mmap((void *)map_info->buffer + map_info->size, map_info->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, memfd, 0); - if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR)) - goto retry_mmap3; - if (MAP_FAILED == map_info->buffer || buffer != (void *)(map_info->buffer + map_info->size)) - goto error; - - close(memfd); - return 0; -error: - close(memfd); - mp3dec_close_ring(map_info); - return MP3D_E_MEMORY; -#else - memfd = shmget(IPC_PRIVATE, map_info->size, IPC_CREAT | 0700); - if (memfd < 0) - return MP3D_E_MEMORY; -retry_mmap: - map_info->buffer = (const uint8_t *)mmap(NULL, map_info->size*2, PROT_NONE, MAP_PRIVATE, -1, 0); - if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR)) - goto retry_mmap; - if (MAP_FAILED == map_info->buffer) - goto error; - if (map_info->buffer != shmat(memfd, map_info->buffer, 0)) - goto error; - if ((map_info->buffer + map_info->size) != shmat(memfd, map_info->buffer + map_info->size, 0)) - goto error; - if (shmctl(memfd, IPC_RMID, NULL) < 0) - return MP3D_E_MEMORY; - return 0; -error: - shmctl(memfd, IPC_RMID, NULL); - mp3dec_close_ring(map_info); - return MP3D_E_MEMORY; -#endif -} -#endif /*MINIMP3_ENABLE_RING*/ -#elif defined(_WIN32) -#include - -static void mp3dec_close_file(mp3dec_map_info_t *map_info) -{ - if (map_info->buffer) - UnmapViewOfFile(map_info->buffer); - map_info->buffer = 0; - map_info->size = 0; -} - -static int mp3dec_open_file_h(HANDLE file, mp3dec_map_info_t *map_info) -{ - memset(map_info, 0, sizeof(*map_info)); - - HANDLE mapping = NULL; - LARGE_INTEGER s; - s.LowPart = GetFileSize(file, (DWORD*)&s.HighPart); - if (s.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) - goto error; - map_info->size = s.QuadPart; - - mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL); - if (!mapping) - goto error; - map_info->buffer = (const uint8_t*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, s.QuadPart); - CloseHandle(mapping); - if (!map_info->buffer) - goto error; - - CloseHandle(file); - return 0; -error: - mp3dec_close_file(map_info); - CloseHandle(file); - return MP3D_E_IOERROR; -} - -static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info) -{ - if (!file_name) - return MP3D_E_PARAM; - HANDLE file = CreateFileA(file_name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if (INVALID_HANDLE_VALUE == file) - return MP3D_E_IOERROR; - return mp3dec_open_file_h(file, map_info); -} - -static int mp3dec_open_file_w(const wchar_t *file_name, mp3dec_map_info_t *map_info) -{ - if (!file_name) - return MP3D_E_PARAM; - HANDLE file = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if (INVALID_HANDLE_VALUE == file) - return MP3D_E_IOERROR; - return mp3dec_open_file_h(file, map_info); -} -#else -#include - -static void mp3dec_close_file(mp3dec_map_info_t *map_info) -{ - if (map_info->buffer) - free((void *)map_info->buffer); - map_info->buffer = 0; - map_info->size = 0; -} - -static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info) -{ - if (!file_name) - return MP3D_E_PARAM; - memset(map_info, 0, sizeof(*map_info)); - FILE *file = fopen(file_name, "rb"); - if (!file) - return MP3D_E_IOERROR; - int res = MP3D_E_IOERROR; - long size = -1; - if (fseek(file, 0, SEEK_END)) - goto error; - size = ftell(file); - if (size < 0) - goto error; - map_info->size = (size_t)size; - if (fseek(file, 0, SEEK_SET)) - goto error; - map_info->buffer = (uint8_t *)malloc(map_info->size); - if (!map_info->buffer) - { - res = MP3D_E_MEMORY; - goto error; - } - if (fread((void *)map_info->buffer, 1, map_info->size, file) != map_info->size) - goto error; - fclose(file); - return 0; -error: - mp3dec_close_file(map_info); - fclose(file); - return res; -} -#endif - -static int mp3dec_detect_mapinfo(mp3dec_map_info_t *map_info) -{ - int ret = mp3dec_detect_buf(map_info->buffer, map_info->size); - mp3dec_close_file(map_info); - return ret; -} - -static int mp3dec_load_mapinfo(mp3dec_t *dec, mp3dec_map_info_t *map_info, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) -{ - int ret = mp3dec_load_buf(dec, map_info->buffer, map_info->size, info, progress_cb, user_data); - mp3dec_close_file(map_info); - return ret; -} - -static int mp3dec_iterate_mapinfo(mp3dec_map_info_t *map_info, MP3D_ITERATE_CB callback, void *user_data) -{ - int ret = mp3dec_iterate_buf(map_info->buffer, map_info->size, callback, user_data); - mp3dec_close_file(map_info); - return ret; -} - -static int mp3dec_ex_open_mapinfo(mp3dec_ex_t *dec, int flags) -{ - int ret = mp3dec_ex_open_buf(dec, dec->file.buffer, dec->file.size, flags); - dec->is_file = 1; - if (ret) - mp3dec_ex_close(dec); - return ret; -} - -int mp3dec_detect(const char *file_name) -{ - int ret; - mp3dec_map_info_t map_info; - if ((ret = mp3dec_open_file(file_name, &map_info))) - return ret; - return mp3dec_detect_mapinfo(&map_info); -} - -int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) -{ - int ret; - mp3dec_map_info_t map_info; - if ((ret = mp3dec_open_file(file_name, &map_info))) - return ret; - return mp3dec_load_mapinfo(dec, &map_info, info, progress_cb, user_data); -} - -int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data) -{ - int ret; - mp3dec_map_info_t map_info; - if ((ret = mp3dec_open_file(file_name, &map_info))) - return ret; - return mp3dec_iterate_mapinfo(&map_info, callback, user_data); -} - -int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int flags) -{ - int ret; - if (!dec) - return MP3D_E_PARAM; - if ((ret = mp3dec_open_file(file_name, &dec->file))) - return ret; - return mp3dec_ex_open_mapinfo(dec, flags); -} - -void mp3dec_ex_close(mp3dec_ex_t *dec) -{ -#ifdef MINIMP3_HAVE_RING - if (dec->io) - mp3dec_close_ring(&dec->file); -#else - if (dec->io && dec->file.buffer) - free((void*)dec->file.buffer); -#endif - if (dec->is_file) - mp3dec_close_file(&dec->file); - if (dec->index.frames) - free(dec->index.frames); - memset(dec, 0, sizeof(*dec)); -} - -#ifdef _WIN32 -int mp3dec_detect_w(const wchar_t *file_name) -{ - int ret; - mp3dec_map_info_t map_info; - if ((ret = mp3dec_open_file_w(file_name, &map_info))) - return ret; - return mp3dec_detect_mapinfo(&map_info); -} - -int mp3dec_load_w(mp3dec_t *dec, const wchar_t *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) -{ - int ret; - mp3dec_map_info_t map_info; - if ((ret = mp3dec_open_file_w(file_name, &map_info))) - return ret; - return mp3dec_load_mapinfo(dec, &map_info, info, progress_cb, user_data); -} - -int mp3dec_iterate_w(const wchar_t *file_name, MP3D_ITERATE_CB callback, void *user_data) -{ - int ret; - mp3dec_map_info_t map_info; - if ((ret = mp3dec_open_file_w(file_name, &map_info))) - return ret; - return mp3dec_iterate_mapinfo(&map_info, callback, user_data); -} - -int mp3dec_ex_open_w(mp3dec_ex_t *dec, const wchar_t *file_name, int flags) -{ - int ret; - if ((ret = mp3dec_open_file_w(file_name, &dec->file))) - return ret; - return mp3dec_ex_open_mapinfo(dec, flags); -} -#endif -#else /* MINIMP3_NO_STDIO */ -void mp3dec_ex_close(mp3dec_ex_t *dec) -{ -#ifdef MINIMP3_HAVE_RING - if (dec->io) - mp3dec_close_ring(&dec->file); -#else - if (dec->io && dec->file.buffer) - free((void*)dec->file.buffer); -#endif - if (dec->index.frames) - free(dec->index.frames); - memset(dec, 0, sizeof(*dec)); -} -#endif - -#endif /*MINIMP3_IMPLEMENTATION*/ diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 09f2c24e81d..08f421cf056 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1286,6 +1286,18 @@ if (SOUNDS["MULTIPCM"]~=null) then } end +--------------------------------------------------- +-- MP3 AUDIO +--@src/devices/sound/mp3_audio.h,SOUNDS["MP3_AUDIO"] = true +--------------------------------------------------- + +if (SOUNDS["MP3_AUDIO"]~=null) then + files { + MAME_DIR .. "src/devices/sound/mp3_audio.cpp", + MAME_DIR .. "src/devices/sound/mp3_audio.h", + } +end + --------------------------------------------------- -- MPEG AUDIO --@src/devices/sound/mpeg_audio.h,SOUNDS["MPEG_AUDIO"] = true @@ -1429,6 +1441,18 @@ if (SOUNDS["LC7535"]~=null) then } end +--------------------------------------------------- +-- Sanyo LC82310 +--@src/devices/sound/lc82310.h,SOUNDS["LC82310"] = true +--------------------------------------------------- + +if (SOUNDS["LC82310"]~=null) then + files { + MAME_DIR .. "src/devices/sound/lc82310.cpp", + MAME_DIR .. "src/devices/sound/lc82310.h", + } +end + --------------------------------------------------- -- NEC uPD934G --@src/devices/sound/upd934g.h,SOUNDS["UPD934G"] = true diff --git a/src/devices/cpu/tlcs900/tmp95c061.cpp b/src/devices/cpu/tlcs900/tmp95c061.cpp index 3fd8be4fe32..7174569e800 100644 --- a/src/devices/cpu/tlcs900/tmp95c061.cpp +++ b/src/devices/cpu/tlcs900/tmp95c061.cpp @@ -583,6 +583,10 @@ void tmp95c061_device::tlcs900_handle_ad() m_int_reg[TMP95C061_INTE0AD] |= 0x80; m_check_irqs = 1; + + /* AD repeat mode */ + if ( m_ad_mode & 0x20 ) + m_ad_cycles_left = ( m_ad_mode & 0x08 ) ? 320 : 160; } } } @@ -1397,7 +1401,7 @@ void tmp95c061_device::admod_w(uint8_t data) { data &= ~0x04; data |= 0x40; - m_ad_cycles_left = ( data & 0x08 ) ? 640 : 320; + m_ad_cycles_left = ( data & 0x08 ) ? 320 : 160; } m_ad_mode = data; diff --git a/src/devices/sound/lc82310.cpp b/src/devices/sound/lc82310.cpp new file mode 100644 index 00000000000..119a12b033b --- /dev/null +++ b/src/devices/sound/lc82310.cpp @@ -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(reinterpret_cast(&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(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 &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 const &inputs, std::vector &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); + } +} diff --git a/src/devices/sound/lc82310.h b/src/devices/sound/lc82310.h new file mode 100644 index 00000000000..c4305c45542 --- /dev/null +++ b/src/devices/sound/lc82310.h @@ -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 const &inputs, std::vector &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 &outputs, int &pos, int scount); + + sound_stream *stream; + std::unique_ptr mp3dec; + + std::array mp3data; + std::array 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 diff --git a/src/devices/sound/mas3507d.cpp b/src/devices/sound/mas3507d.cpp index eb156ec63b7..ddcf0d41b94 100644 --- a/src/devices/sound/mas3507d.cpp +++ b/src/devices/sound/mas3507d.cpp @@ -13,13 +13,7 @@ #include "emu.h" #include "mas3507d.h" - -#define MINIMP3_ONLY_MP3 -#define MINIMP3_NO_STDIO -#define MINIMP3_IMPLEMENTATION -#define MAX_FRAME_SYNC_MATCHES 3 -#include "minimp3/minimp3.h" -#include "minimp3/minimp3_ex.h" +#include "mp3_audio.h" #define LOG_GENERAL (1 << 0) #define LOG_READ (1 << 1) @@ -59,6 +53,7 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, void mas3507d_device::device_start() { stream = stream_alloc(0, 2, 44100); + mp3dec = std::make_unique(reinterpret_cast(&mp3data[0])); cb_mpeg_frame_sync.resolve(); cb_demand.resolve(); @@ -78,7 +73,6 @@ void mas3507d_device::device_start() save_item(NAME(mp3data_count)); save_item(NAME(decoded_frame_count)); - save_item(NAME(decoded_samples)); save_item(NAME(sample_count)); save_item(NAME(samples_idx)); save_item(NAME(is_muted)); @@ -91,22 +85,8 @@ void mas3507d_device::device_start() save_item(NAME(i2c_io_val)); save_item(NAME(i2c_sdao_data)); save_item(NAME(playback_status)); - save_item(NAME(mp3_is_buffered)); - - // This should be removed in the future if/when native MP3 decoding is implemented in MAME - save_item(NAME(mp3_dec.mdct_overlap)); - save_item(NAME(mp3_dec.qmf_state)); - save_item(NAME(mp3_dec.reserv)); - save_item(NAME(mp3_dec.free_format_bytes)); - save_item(NAME(mp3_dec.header)); - save_item(NAME(mp3_dec.reserv_buf)); - - save_item(NAME(mp3_info.frame_bytes)); - save_item(NAME(mp3_info.frame_offset)); - save_item(NAME(mp3_info.channels)); - save_item(NAME(mp3_info.hz)); - save_item(NAME(mp3_info.layer)); - save_item(NAME(mp3_info.bitrate_kbps)); + + save_item(NAME(frame_channels)); } void mas3507d_device::device_reset() @@ -121,6 +101,8 @@ void mas3507d_device::device_reset() is_muted = false; gain_ll = gain_rr = 0; + frame_channels = 2; + stream->set_sample_rate(44100); reset_playback(); @@ -439,50 +421,43 @@ void mas3507d_device::sid_w(uint8_t byte) mp3data[mp3data_count++] = byte; - if (!mp3_is_buffered) { - // Only start the decoder when a full MP3 frame is found - int free_format_bytes = 0, frame_size = 0; - int frame_offset = mp3d_find_frame(static_cast(&mp3data[0]), mp3data_count, &free_format_bytes, &frame_size); - mp3_is_buffered = frame_size && frame_offset + frame_size < mp3data_count; - } - - cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size()); + cb_demand(mp3data_count < mp3data.size()); } void mas3507d_device::fill_buffer() { cb_mpeg_frame_sync(0); - if (!mp3_is_buffered) { - cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size()); - return; - } - - memset(&mp3_info, 0, sizeof(mp3dec_frame_info_t)); - sample_count = mp3dec_decode_frame(&mp3_dec, static_cast(&mp3data[0]), mp3data_count, static_cast(&samples[0]), &mp3_info); + int pos = 0, frame_sample_rate = 0; + bool decoded_frame = mp3dec->decode_buffer(pos, mp3data_count, &samples[0], sample_count, frame_sample_rate, frame_channels); samples_idx = 0; - if (sample_count == 0) { + if (!decoded_frame || sample_count == 0) { // Frame decode failed - reset_playback(); + if (mp3data_count >= mp3data.size()) { + std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin()); + mp3data_count--; + } + + cb_demand(mp3data_count < mp3data.size()); return; } - std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin()); - mp3data_count -= mp3_info.frame_bytes; + std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin()); + mp3data_count -= pos; - stream->set_sample_rate(mp3_info.hz); + stream->set_sample_rate(frame_sample_rate); decoded_frame_count++; cb_mpeg_frame_sync(1); - cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size()); + cb_demand(mp3data_count < mp3data.size()); } void mas3507d_device::append_buffer(std::vector &outputs, int &pos, int scount) { int s1 = scount - pos; - int bytes_per_sample = mp3_info.channels > 2 ? 2 : mp3_info.channels; // More than 2 channels is unsupported here + int bytes_per_sample = std::min(frame_channels, 2); // More than 2 channels is unsupported here if(s1 > sample_count) s1 = sample_count; @@ -492,7 +467,6 @@ void mas3507d_device::append_buffer(std::vector &outputs, int outputs[1].put_int(pos, samples[samples_idx * bytes_per_sample + (bytes_per_sample >> 1)], 32768); samples_idx++; - decoded_samples++; pos++; if(samples_idx >= sample_count) { @@ -504,20 +478,14 @@ void mas3507d_device::append_buffer(std::vector &outputs, int void mas3507d_device::reset_playback() { - if (mp3data_count != 0) - std::fill(mp3data.begin(), mp3data.end(), 0); - - if (sample_count != 0 || decoded_samples != 0) - std::fill(samples.begin(), samples.end(), 0); + std::fill(mp3data.begin(), mp3data.end(), 0); + std::fill(samples.begin(), samples.end(), 0); - mp3dec_init(&mp3_dec); + mp3dec->clear(); mp3data_count = 0; sample_count = 0; decoded_frame_count = 0; - decoded_samples = 0; samples_idx = 0; - - mp3_is_buffered = false; } void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) diff --git a/src/devices/sound/mas3507d.h b/src/devices/sound/mas3507d.h index 52c2e924109..4d8eac31539 100644 --- a/src/devices/sound/mas3507d.h +++ b/src/devices/sound/mas3507d.h @@ -5,9 +5,7 @@ #pragma once -#define MINIMP3_ONLY_MP3 -#define MINIMP3_NO_STDIO -#include "minimp3/minimp3.h" +#include "mp3_audio.h" class mas3507d_device : public device_t, public device_sound_interface { @@ -72,13 +70,10 @@ private: i2c_subdest_t i2c_subdest; i2c_command_t i2c_command; - mp3dec_t mp3_dec; - mp3dec_frame_info_t mp3_info; - sound_stream *stream; std::array mp3data; - std::array samples; + std::array samples; bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao; int i2c_bus_curbit; @@ -87,15 +82,17 @@ private: uint32_t i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val; uint32_t i2c_sdao_data; - bool mp3_is_buffered; uint32_t mp3data_count; - uint32_t decoded_frame_count, decoded_samples; + uint32_t decoded_frame_count; int32_t sample_count, samples_idx; + int32_t frame_channels; bool is_muted; float gain_ll, gain_rr; uint32_t playback_status; + + std::unique_ptr mp3dec; }; diff --git a/src/devices/sound/mp3_audio.cpp b/src/devices/sound/mp3_audio.cpp new file mode 100644 index 00000000000..d6a45413e7d --- /dev/null +++ b/src/devices/sound/mp3_audio.cpp @@ -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(); + 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; +} diff --git a/src/devices/sound/mp3_audio.h b/src/devices/sound/mp3_audio.h new file mode 100644 index 00000000000..df75ddd1969 --- /dev/null +++ b/src/devices/sound/mp3_audio.h @@ -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 + +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 dec; + bool m_found_stream; +}; + +#endif diff --git a/src/mame/namco/namcos10.cpp b/src/mame/namco/namcos10.cpp index cfcaddc64ab..7f966849b9c 100644 --- a/src/mame/namco/namcos10.cpp +++ b/src/mame/namco/namcos10.cpp @@ -660,6 +660,7 @@ User data note: #include "machine/ram.h" #include "machine/ticket.h" #include "machine/timer.h" +#include "sound/lc82310.h" #include "sound/spu.h" #include "video/psx.h" @@ -907,6 +908,7 @@ public: : namcos10_memn_state(mconfig, type, tag) , m_ram(*this, "maincpu:ram") , m_memp3_mcu(*this, "memp3_mcu") + , m_lc82310(*this, "mp3_decoder") , m_mcu_ram(*this, "mcu_ram") , m_p3_analog(*this, "P3_ANALOG%u", 1U) { } @@ -926,26 +928,55 @@ private: void namcos10_memp3_map(address_map &map); void mcu_map(address_map &map); + template uint8_t port_read(offs_t offset); + template void port_write(offs_t offset, uint8_t data); + void firmware_write_w(uint16_t data); void ram_bank_w(uint16_t data); uint16_t unk_status1_r(); - uint16_t unk_status2_r(); - void mcu_int5_w(uint16_t data); + uint16_t subcomm_busy_r(); + void subcomm_int_w(uint16_t data); uint16_t ram_r(offs_t offset); void ram_w(offs_t offset, uint16_t data); - uint16_t io_analog_r(offs_t offset); + void subcomm_ack_w(uint16_t data); + + uint16_t mp3_unk_r(); + void mp3_unk_w(uint16_t data); + + uint16_t mp3_unk2_r(); + void mp3_unk2_w(uint16_t data); + + uint16_t mp3_unk3_r(); + void mp3_unk3_w(uint16_t data); + + uint16_t decode_pld_ver_r(); + uint16_t dram_pld_ver_r(); + uint16_t nand_pld_ver_r(); + + uint16_t mp3_stream_status_r(); + void mp3_stream_status_w(uint16_t data); + + void mp3_data_w(uint16_t data); required_device m_ram; required_device m_memp3_mcu; + required_device m_lc82310; required_shared_ptr m_mcu_ram; optional_ioport_array<4> m_p3_analog; uint16_t m_mcu_ram_bank; + bool m_subcomm_busy; + + uint16_t m_mp3_unk_val, m_mp3_unk2_val, m_mp3_unk3_val; + uint8_t m_mp3_port7_data; + uint8_t m_mp3_porta_data; + bool m_mp3_stream_available; + bool m_mp3_received_byte; }; /////////////////////////////////////////////////////////////////////////////////////////////// @@ -1258,8 +1289,6 @@ void namcos10_state::namcos10_exio(machine_config &config) void namcos10_state::namcos10_map_exio_inner(address_map &map) { - // TODO: Base registers are probably similar between EXIO and MGEXIO, fill in registers and rename if possible - // TODO: Figure out what the commented out registers are actually used for map(0x06000, 0x0ffff).rw(m_exio, FUNC(namcos10_exio_device::ram_r), FUNC(namcos10_exio_device::ram_w)); map(0x10000, 0x10003).rw(m_exio, FUNC(namcos10_exio_device::ctrl_r), FUNC(namcos10_exio_device::ctrl_w)); map(0x18000, 0x18003).rw(m_exio, FUNC(namcos10_exio_device::bus_req_r), FUNC(namcos10_exio_device::bus_req_w)); @@ -1758,6 +1787,9 @@ void namcos10_memn_state::namcos10_memn_map_inner(address_map &map) map(0xf460000, 0xf460001).w(FUNC(namcos10_memn_state::nand_bank_w)); map(0xf470000, 0xf470001).rw(FUNC(namcos10_memn_state::ctrl_reg_r), FUNC(namcos10_memn_state::ctrl_reg_w)); //map(0xf480000, 0xf480001).w(); // 0xffff is written here after a nand write/erase? + + map(0xfe40000, 0xfe40003).noprw(); + map(0xfe48000, 0xfe48003).noprw(); } void namcos10_memn_state::namcos10_memn_map(address_map &map) @@ -2551,37 +2583,28 @@ void namcos10_memp3_state::ram_w(offs_t offset, uint16_t data) m_mcu_ram[m_mcu_ram_bank * 0x10000 + offset] = data; } -uint16_t namcos10_memp3_state::unk_status2_r() +uint16_t namcos10_memp3_state::subcomm_busy_r() { - // Some kind of status flag. - // Game code loops until this is non-zero before writing to data to f30000c/320000. - // Possibly related to MP3 decoder? - return 1; + // Before writing a new command this register is checked to make sure the + // previous command has already been finished processing + return !m_subcomm_busy; } -void namcos10_memp3_state::mcu_int5_w(uint16_t data) +void namcos10_memp3_state::subcomm_int_w(uint16_t data) { - // MP3 decoder commands will only be processed when INT5 is triggered. - // 0 gets written to this register (only?) after the MP3 decoder commands are - // written into the MCU's memory so I believe it's responsible for making the - // INT5 get triggered. + // MP3 decoder commands will only be processed when INT5 is triggered m_memp3_mcu->set_input_line(TLCS900_INT5, ASSERT_LINE); -} - -uint16_t namcos10_memp3_state::io_analog_r(offs_t offset) -{ - return m_p3_analog[offset].read_safe(0); + m_subcomm_busy = true; } void namcos10_memp3_state::namcos10_memp3_map_inner(address_map &map) { map(0xf300000, 0xf300001).w(FUNC(namcos10_memp3_state::firmware_write_w)); // 1f300004 unk - map(0xf300006, 0xf300007).rw(FUNC(namcos10_memp3_state::unk_status2_r), FUNC(namcos10_memp3_state::mcu_int5_w)); + map(0xf300006, 0xf300007).rw(FUNC(namcos10_memp3_state::subcomm_busy_r), FUNC(namcos10_memp3_state::subcomm_int_w)); map(0xf30000c, 0xf30000d).w(FUNC(namcos10_memp3_state::ram_bank_w)); - map(0xf30000e, 0xf30000f).r(FUNC(namcos10_memp3_state::unk_status1_r)); + map(0xf30000e, 0xf30000f).r(FUNC(namcos10_memp3_state::unk_status1_r)); // similar to 0xf300006, only used when writing firmware? map(0xf320000, 0xf33ffff).rw(FUNC(namcos10_memp3_state::ram_r), FUNC(namcos10_memp3_state::ram_w)); - map(0xf33fff0, 0xf33fff7).r(FUNC(namcos10_memp3_state::io_analog_r)); } void namcos10_memp3_state::namcos10_memp3_map(address_map &map) @@ -2595,9 +2618,143 @@ void namcos10_memp3_state::namcos10_memp3_map(address_map &map) void namcos10_memp3_state::mcu_map(address_map &map) { - map(0x000000, 0x7fffff).ram().mirror(0x800000).share(m_mcu_ram); + map(0x100000, 0x100001).rw(FUNC(namcos10_memp3_state::mp3_unk_r), FUNC(namcos10_memp3_state::mp3_unk_w)); + map(0x100002, 0x100003).portr("MEMP3_DIPSW"); + map(0x100004, 0x100005).rw(FUNC(namcos10_memp3_state::mp3_unk2_r), FUNC(namcos10_memp3_state::mp3_unk2_w)); + map(0x100008, 0x100009).w(FUNC(namcos10_memp3_state::subcomm_ack_w)); + map(0x10000e, 0x10000f).r(FUNC(namcos10_memp3_state::decode_pld_ver_r)); + map(0x100010, 0x100011).rw(FUNC(namcos10_memp3_state::mp3_unk3_r), FUNC(namcos10_memp3_state::mp3_unk3_w)); + map(0x10001e, 0x10002f).r(FUNC(namcos10_memp3_state::dram_pld_ver_r)); + map(0x10002e, 0x10002f).r(FUNC(namcos10_memp3_state::nand_pld_ver_r)); + map(0x100030, 0x100031).w(FUNC(namcos10_memp3_state::mp3_data_w)); + map(0x100032, 0x100033).rw(FUNC(namcos10_memp3_state::mp3_stream_status_r), FUNC(namcos10_memp3_state::mp3_stream_status_w)); + map(0x800000, 0xffffff).ram().share(m_mcu_ram); +} + +uint16_t namcos10_memp3_state::decode_pld_ver_r() +{ + // DECODE PLD version, only seems to be read when sub CPU is in debug mode + return 1; +} + +uint16_t namcos10_memp3_state::dram_pld_ver_r() +{ + // DRAM-C PLD version, only seems to be read when sub CPU is in debug mode + return 1; +} + +uint16_t namcos10_memp3_state::nand_pld_ver_r() +{ + // NAND-C PLD version, only seems to be read when sub CPU is in debug mode + return 1; +} + +uint16_t namcos10_memp3_state::mp3_stream_status_r() +{ + // The debug messages when debug mode are enabled checks this register + // and if it's 0 then it prints "MUTE:ON" and if it's 1 it prints "MUTE:OFF" + return m_mp3_stream_available; +} + +void namcos10_memp3_state::mp3_stream_status_w(uint16_t data) +{ + // This gets set to 0 and then 1 every time a new MP3 stream is started + bool is_available = BIT(data, 0); + + if (m_mp3_stream_available && !is_available) + m_lc82310->reset_playback(); + + m_mp3_stream_available = is_available; +} + +void namcos10_memp3_state::mp3_data_w(uint16_t data) +{ + m_lc82310->dimpg_w(data & 0xff); + m_mp3_received_byte = true; } +void namcos10_memp3_state::subcomm_ack_w(uint16_t data) +{ + m_subcomm_busy = false; + m_memp3_mcu->set_input_line(TLCS900_INT5, CLEAR_LINE); +} + +uint16_t namcos10_memp3_state::mp3_unk_r() +{ + // Seems to be related to timer 3, something to do with the "L" LEDs maybe? + // When the timer 3 value has ticked 10 times, set to 1 if value is 0 else shift value by 1 + // mp3_unk_w(mp3_unk_r() ? mp3_unk_r() << 1 : 1) + return m_mp3_unk_val; +} + +void namcos10_memp3_state::mp3_unk_w(uint16_t data) +{ + m_mp3_unk_val = data; +} + +uint16_t namcos10_memp3_state::mp3_unk2_r() +{ + // The following are how the sub CPU program handles the bits. + // It's unclear what writing to these registers actually does outside of the sub CPU. + // Only available in g13jnr and nicetsuk, maybe some kind of debug controls? + // bit 0 = Start playback + // bit 1 = Stop playback + // bit 2 = Fade out + // bit 3 = Fade in + // bit 4 = Decrease volume by 1 + // bit 5 = Increase volume by 1 + // bit 6 = Mute/Unmute (uses 0x100032) + // bit 7 = Print state from MP3 decoder + state byte from sub CPU to the sub CPU's serial output + return m_mp3_unk2_val; +} + +void namcos10_memp3_state::mp3_unk2_w(uint16_t data) +{ + m_mp3_unk2_val = data; +} + +uint16_t namcos10_memp3_state::mp3_unk3_r() +{ + // When the timer 3 value has ticked 10 times: mp3_unk3_w(3 - mp3_unk3_r()) + return m_mp3_unk3_val; +} + +void namcos10_memp3_state::mp3_unk3_w(uint16_t data) +{ + m_mp3_unk3_val = data; +} + +template +void namcos10_memp3_state::port_write(offs_t offset, uint8_t data) +{ + if (Port == 7) { + m_lc82310->zcsctl_w(BIT(data, 3)); + m_lc82310->dictl_w(BIT(data, 1)); + m_lc82310->ckctl_w(BIT(data, 2)); + m_mp3_port7_data = data; + } else if (Port == 10) { + // bit 2 is toggled off/on after MP3 byte is written + // bit 3 is set to 0 before writing data and then set to 1 after writing data + m_mp3_porta_data = data; + } +} + +template +uint8_t namcos10_memp3_state::port_read(offs_t offset) +{ + uint8_t r = 0; + + if (Port == 7) { + r = (m_mp3_port7_data & ~1) | m_lc82310->doctl_r(); + } else if (Port == 10) { // Port A + r = m_mp3_porta_data; + } else if (Port == 11) { // Port B + r = !m_mp3_received_byte && m_lc82310->demand_r(); + m_mp3_received_byte = false; + } + + return r; +} void namcos10_memp3_state::namcos10_memp3_base(machine_config &config) { @@ -2607,11 +2764,44 @@ void namcos10_memp3_state::namcos10_memp3_base(machine_config &config) m_maincpu->subdevice("dma")->install_write_handler(5, psxdma_device::write_delegate(&namcos10_memp3_state::pio_dma_read, this)); m_maincpu->set_addrmap(AS_PROGRAM, &namcos10_memp3_state::namcos10_memp3_map); - TMP95C061(config, m_memp3_mcu, XTAL(16'934'400)); + TMP95C061(config, m_memp3_mcu, XTAL(101'491'200) / 2); // Measured m_memp3_mcu->set_addrmap(AS_PROGRAM, &namcos10_memp3_state::mcu_map); - // Port 7 is used for communicating with the MP3 decoder chip - // LC82310 16.9344MHz + m_memp3_mcu->port1_read().set(FUNC(namcos10_memp3_state::port_read<1>)); + m_memp3_mcu->port5_read().set(FUNC(namcos10_memp3_state::port_read<5>)); + m_memp3_mcu->port7_read().set(FUNC(namcos10_memp3_state::port_read<7>)); + m_memp3_mcu->port8_read().set(FUNC(namcos10_memp3_state::port_read<8>)); + m_memp3_mcu->port9_read().set(FUNC(namcos10_memp3_state::port_read<9>)); + m_memp3_mcu->porta_read().set(FUNC(namcos10_memp3_state::port_read<10>)); + m_memp3_mcu->portb_read().set(FUNC(namcos10_memp3_state::port_read<11>)); + m_memp3_mcu->port1_write().set(FUNC(namcos10_memp3_state::port_write<1>)); + m_memp3_mcu->port2_write().set(FUNC(namcos10_memp3_state::port_write<2>)); + m_memp3_mcu->port5_write().set(FUNC(namcos10_memp3_state::port_write<5>)); + m_memp3_mcu->port6_write().set(FUNC(namcos10_memp3_state::port_write<6>)); + m_memp3_mcu->port7_write().set(FUNC(namcos10_memp3_state::port_write<7>)); + m_memp3_mcu->port8_write().set(FUNC(namcos10_memp3_state::port_write<8>)); + m_memp3_mcu->porta_write().set(FUNC(namcos10_memp3_state::port_write<10>)); + m_memp3_mcu->portb_write().set(FUNC(namcos10_memp3_state::port_write<11>)); + + m_memp3_mcu->an_read<0>().set([this] () { + return m_p3_analog[0].read_safe(0); + }); + + m_memp3_mcu->an_read<1>().set([this] () { + return m_p3_analog[1].read_safe(0); + }); + + m_memp3_mcu->an_read<2>().set([this] () { + return m_p3_analog[2].read_safe(0); + }); + + m_memp3_mcu->an_read<3>().set([this] () { + return m_p3_analog[3].read_safe(0); + }); + + LC82310(config, m_lc82310, XTAL(16'934'400)); + m_lc82310->add_route(0, "lspeaker", 1.0); + m_lc82310->add_route(1, "rspeaker", 1.0); } void namcos10_memp3_state::machine_start() @@ -2619,13 +2809,31 @@ void namcos10_memp3_state::machine_start() namcos10_memn_state::machine_start(); save_item(NAME(m_mcu_ram_bank)); + save_item(NAME(m_mp3_unk_val)); + save_item(NAME(m_mp3_unk2_val)); + save_item(NAME(m_mp3_unk3_val)); + save_item(NAME(m_mp3_port7_data)); + save_item(NAME(m_mp3_porta_data)); + save_item(NAME(m_mp3_stream_available)); + save_item(NAME(m_mp3_received_byte)); + save_item(NAME(m_subcomm_busy)); } void namcos10_memp3_state::machine_reset() { namcos10_memn_state::machine_reset(); + std::fill(m_mcu_ram.begin(), m_mcu_ram.end(), 0); + m_mcu_ram_bank = 0; + m_mp3_unk_val = m_mp3_unk3_val = 0; + m_mp3_unk2_val = 0xffff; + m_mp3_port7_data = 0; + m_mp3_porta_data = 0; + m_mp3_stream_available = false; + m_mp3_received_byte = false; + + m_subcomm_busy = false; m_memp3_mcu->suspend(SUSPEND_REASON_HALT, 1); } @@ -2774,6 +2982,18 @@ static INPUT_PORTS_START( namcos10 ) INPUT_PORTS_END +static INPUT_PORTS_START( memp3 ) + PORT_START("MEMP3_DIPSW") + PORT_BIT( 0xfff0, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_DIPUNKNOWN_DIPLOC( 0x0001, IP_ACTIVE_LOW, "MEMP3_SW1:1" ) + PORT_DIPUNKNOWN_DIPLOC( 0x0002, IP_ACTIVE_LOW, "MEMP3_SW1:2" ) + PORT_DIPUNKNOWN_DIPLOC( 0x0004, IP_ACTIVE_LOW, "MEMP3_SW1:3" ) + PORT_DIPNAME( 0x0008, 0x0008, "Debug Mode" ) PORT_DIPLOCATION("MEMP3_SW1:4") + PORT_DIPSETTING( 0x0008, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + +INPUT_PORTS_END + static INPUT_PORTS_START( mrdrilr2 ) PORT_INCLUDE(namcos10) @@ -2925,6 +3145,7 @@ INPUT_PORTS_END static INPUT_PORTS_START( g13jnr ) PORT_INCLUDE(namcos10) + PORT_INCLUDE(memp3) PORT_MODIFY("IN1") PORT_BIT( 0x071f0043, IP_ACTIVE_LOW, IPT_UNUSED ) @@ -2947,10 +3168,10 @@ static INPUT_PORTS_START( g13jnr ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_START("P3_ANALOG1") - PORT_BIT( 0xffff, 0x7fff, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, -1.0, 0.0, 0) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_PLAYER(1) PORT_REVERSE + PORT_BIT( 0x3ff, 0, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, -1.0, 0.0, 0) PORT_MINMAX(0x000,0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50) PORT_PLAYER(1) PORT_REVERSE PORT_START("P3_ANALOG2") - PORT_BIT( 0xffff, 0x7fff, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_PLAYER(1) + PORT_BIT( 0x3ff, 0, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0x000,0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50) PORT_PLAYER(1) INPUT_PORTS_END @@ -3022,6 +3243,7 @@ INPUT_PORTS_END static INPUT_PORTS_START( nicetsuk ) PORT_INCLUDE(namcos10) + PORT_INCLUDE(memp3) PORT_MODIFY("IN1") PORT_BIT( 0x0ff9ef40, IP_ACTIVE_LOW, IPT_UNUSED ) @@ -3041,6 +3263,7 @@ INPUT_PORTS_END static INPUT_PORTS_START( squizchs ) PORT_INCLUDE(namcos10) + PORT_INCLUDE(memp3) PORT_MODIFY("IN1") PORT_BIT( 0x0fff7070, IP_ACTIVE_LOW, IPT_UNUSED ) @@ -3675,6 +3898,6 @@ GAME( 2003, taiko5, 0, ns10_taiko5, taiko, namcos10_memn_sta GAME( 2004, taiko6, 0, ns10_taiko6, taiko, namcos10_memn_state, memn_driver_init, ROT0, "Namco", "Taiko no Tatsujin 6 (Japan, TK61 Ver.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) // MEM(P3) -GAME( 2001, g13jnr, 0, ns10_g13jnr, g13jnr, namcos10_memp3_state, memn_driver_init, ROT0, "Eighting / Raizing / Namco", "Golgo 13: Juusei no Requiem (Japan, GLT1 VER.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) -GAME( 2002, nicetsuk, 0, ns10_nicetsuk, nicetsuk, namcos10_memp3_state, memn_driver_init, ROT0, "Namco / Metro", "Tsukkomi Yousei Gips Nice Tsukkomi (NTK1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) -GAME( 2003, squizchs, 0, ns10_squizchs, squizchs, namcos10_memp3_state, memn_driver_init, ROT0, "Namco", "Seishun-Quiz Colorful High School (CHS1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND ) +GAME( 2001, g13jnr, 0, ns10_g13jnr, g13jnr, namcos10_memp3_state, memn_driver_init, ROT0, "Eighting / Raizing / Namco", "Golgo 13: Juusei no Requiem (Japan, GLT1 VER.A)", MACHINE_IMPERFECT_SOUND ) +GAME( 2002, nicetsuk, 0, ns10_nicetsuk, nicetsuk, namcos10_memp3_state, memn_driver_init, ROT0, "Namco / Metro", "Tsukkomi Yousei Gips Nice Tsukkomi (NTK1 Ver.A)", MACHINE_IMPERFECT_SOUND ) +GAME( 2003, squizchs, 0, ns10_squizchs, squizchs, namcos10_memp3_state, memn_driver_init, ROT0, "Namco", "Seishun-Quiz Colorful High School (CHS1 Ver.A)", MACHINE_IMPERFECT_SOUND ) -- cgit v1.2.3