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);
+ std::fill(mp3data.begin(), mp3data.end(), 0);
+ std::fill(samples.begin(), samples.end(), 0);
- if (sample_count != 0 || decoded_samples != 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 )