summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--3rdparty/minimp3/minimp3.h206
-rw-r--r--3rdparty/minimp3/minimp3_ex.h1224
-rw-r--r--src/devices/sound/mas3507d.cpp301
-rw-r--r--src/devices/sound/mas3507d.h78
-rw-r--r--src/emu/devfind.h4
-rw-r--r--src/mame/machine/k573dio.cpp127
-rw-r--r--src/mame/machine/k573dio.h15
-rw-r--r--src/mame/machine/k573fpga.cpp186
-rw-r--r--src/mame/machine/k573fpga.h68
9 files changed, 1782 insertions, 427 deletions
diff --git a/3rdparty/minimp3/minimp3.h b/3rdparty/minimp3/minimp3.h
index 08c03b49ce5..796cbc1f8ed 100644
--- a/3rdparty/minimp3/minimp3.h
+++ b/3rdparty/minimp3/minimp3.h
@@ -12,7 +12,7 @@
typedef struct
{
- int frame_bytes, channels, hz, layer, bitrate_kbps;
+ int frame_bytes, frame_offset, channels, hz, layer, bitrate_kbps;
} mp3dec_frame_info_t;
typedef struct
@@ -86,7 +86,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
#if !defined(MINIMP3_NO_SIMD)
-#if !defined(MINIMP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__))
+#if !defined(MINIMP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
/* x64 always have SSE2, arm64 always have neon, no need for generic code */
#define MINIMP3_ONLY_SIMD
#endif /* SIMD checks... */
@@ -136,7 +136,7 @@ static __inline__ __attribute__((always_inline)) void minimp3_cpuid(int CPUInfo[
#endif /* defined(__PIC__)*/
}
#endif /* defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) */
-static int have_simd()
+static int have_simd(void)
{
#ifdef MINIMP3_ONLY_SIMD
return 1;
@@ -161,8 +161,9 @@ end:
return g_have_simd - 1;
#endif /* MINIMP3_ONLY_SIMD */
}
-#elif defined(__ARM_NEON) || defined(__aarch64__)
+#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
#include <arm_neon.h>
+#define HAVE_SSE 0
#define HAVE_SIMD 1
#define VSTORE vst1q_f32
#define VLD vld1q_f32
@@ -180,6 +181,7 @@ static int have_simd()
return 1;
}
#else /* SIMD checks... */
+#define HAVE_SSE 0
#define HAVE_SIMD 0
#ifdef MINIMP3_ONLY_SIMD
#error MINIMP3_ONLY_SIMD used, but SSE/NEON not enabled
@@ -189,6 +191,18 @@ static int have_simd()
#define HAVE_SIMD 0
#endif /* !defined(MINIMP3_NO_SIMD) */
+#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
+#define HAVE_ARMV6 1
+static __inline__ __attribute__((always_inline)) int32_t minimp3_clip_int16_arm(int32_t a)
+{
+ int32_t x = 0;
+ __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
+ return x;
+}
+#else
+#define HAVE_ARMV6 0
+#endif
+
typedef struct
{
const uint8_t *buf;
@@ -767,41 +781,72 @@ static void L3_huffman(float *dst, bs_t *bs, const L3_gr_info_t *gr_info, const
int sfb_cnt = gr_info->region_count[ireg++];
const int16_t *codebook = tabs + tabindex[tab_num];
int linbits = g_linbits[tab_num];
- do
+ if (linbits)
{
- np = *sfb++ / 2;
- pairs_to_decode = MINIMP3_MIN(big_val_cnt, np);
- one = *scf++;
do
{
- int j, w = 5;
- int leaf = codebook[PEEK_BITS(w)];
- while (leaf < 0)
+ np = *sfb++ / 2;
+ pairs_to_decode = MINIMP3_MIN(big_val_cnt, np);
+ one = *scf++;
+ do
{
- FLUSH_BITS(w);
- w = leaf & 7;
- leaf = codebook[PEEK_BITS(w) - (leaf >> 3)];
- }
- FLUSH_BITS(leaf >> 8);
+ int j, w = 5;
+ int leaf = codebook[PEEK_BITS(w)];
+ while (leaf < 0)
+ {
+ FLUSH_BITS(w);
+ w = leaf & 7;
+ leaf = codebook[PEEK_BITS(w) - (leaf >> 3)];
+ }
+ FLUSH_BITS(leaf >> 8);
- for (j = 0; j < 2; j++, dst++, leaf >>= 4)
+ for (j = 0; j < 2; j++, dst++, leaf >>= 4)
+ {
+ int lsb = leaf & 0x0F;
+ if (lsb == 15)
+ {
+ lsb += PEEK_BITS(linbits);
+ FLUSH_BITS(linbits);
+ CHECK_BITS;
+ *dst = one*L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
+ } else
+ {
+ *dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
+ }
+ FLUSH_BITS(lsb ? 1 : 0);
+ }
+ CHECK_BITS;
+ } while (--pairs_to_decode);
+ } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
+ } else
+ {
+ do
+ {
+ np = *sfb++ / 2;
+ pairs_to_decode = MINIMP3_MIN(big_val_cnt, np);
+ one = *scf++;
+ do
{
- int lsb = leaf & 0x0F;
- if (lsb == 15 && linbits)
+ int j, w = 5;
+ int leaf = codebook[PEEK_BITS(w)];
+ while (leaf < 0)
{
- lsb += PEEK_BITS(linbits);
- FLUSH_BITS(linbits);
- CHECK_BITS;
- *dst = one*L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
- } else
+ FLUSH_BITS(w);
+ w = leaf & 7;
+ leaf = codebook[PEEK_BITS(w) - (leaf >> 3)];
+ }
+ FLUSH_BITS(leaf >> 8);
+
+ for (j = 0; j < 2; j++, dst++, leaf >>= 4)
{
+ int lsb = leaf & 0x0F;
*dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
+ FLUSH_BITS(lsb ? 1 : 0);
}
- FLUSH_BITS(lsb ? 1 : 0);
- }
- CHECK_BITS;
- } while (--pairs_to_decode);
- } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
+ CHECK_BITS;
+ } while (--pairs_to_decode);
+ } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
+ }
}
for (np = 1 - big_val_cnt;; dst += 4)
@@ -1374,10 +1419,16 @@ static void mp3d_DCT_II(float *grbuf, int n)
#ifndef MINIMP3_FLOAT_OUTPUT
static int16_t mp3d_scale_pcm(float sample)
{
+#if HAVE_ARMV6
+ int32_t s32 = (int32_t)(sample + .5f);
+ s32 -= (s32 < 0);
+ int16_t s = (int16_t)minimp3_clip_int16_arm(s32);
+#else
if (sample >= 32766.5) return (int16_t) 32767;
if (sample <= -32767.5) return (int16_t)-32768;
int16_t s = (int16_t)(sample + .5f);
s -= (s < 0); /* away from zero, to be compliant */
+#endif
return s;
}
#else /* MINIMP3_FLOAT_OUTPUT */
@@ -1641,7 +1692,7 @@ static int mp3d_find_frame(const uint8_t *mp3, int mp3_bytes, int *free_format_b
}
}
*ptr_frame_bytes = 0;
- return i;
+ return mp3_bytes;
}
void mp3dec_init(mp3dec_t *dec)
@@ -1670,7 +1721,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
i = mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
if (!frame_size || i + frame_size > mp3_bytes)
{
- info->frame_bytes = 0;
+ info->frame_bytes = i;
return 0;
}
}
@@ -1678,6 +1729,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
hdr = mp3 + i;
memcpy(dec->header, hdr, HDR_SIZE);
info->frame_bytes = i + frame_size;
+ info->frame_offset = i;
info->channels = HDR_IS_MONO(hdr) ? 1 : 2;
info->hz = hdr_sample_rate_hz(hdr);
info->layer = 4 - HDR_GET_LAYER(hdr);
@@ -1746,60 +1798,56 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
#ifdef MINIMP3_FLOAT_OUTPUT
void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples)
{
- if(num_samples > 0)
- {
- int i = 0;
+ int i = 0;
#if HAVE_SIMD
- int aligned_count = num_samples & ~7;
-
- for(;i < aligned_count;i+=8)
- {
- static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
- f4 a = VMUL(VLD(&in[i ]), g_scale);
- f4 b = VMUL(VLD(&in[i+4]), g_scale);
+ int aligned_count = num_samples & ~7;
+ for(; i < aligned_count; i += 8)
+ {
+ static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
+ f4 a = VMUL(VLD(&in[i ]), g_scale);
+ f4 b = VMUL(VLD(&in[i+4]), g_scale);
#if HAVE_SSE
- static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
- static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
- __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
- _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
- out[i ] = _mm_extract_epi16(pcm8, 0);
- out[i+1] = _mm_extract_epi16(pcm8, 1);
- out[i+2] = _mm_extract_epi16(pcm8, 2);
- out[i+3] = _mm_extract_epi16(pcm8, 3);
- out[i+4] = _mm_extract_epi16(pcm8, 4);
- out[i+5] = _mm_extract_epi16(pcm8, 5);
- out[i+6] = _mm_extract_epi16(pcm8, 6);
- out[i+7] = _mm_extract_epi16(pcm8, 7);
+ static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
+ static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
+ __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
+ _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
+ out[i ] = _mm_extract_epi16(pcm8, 0);
+ out[i+1] = _mm_extract_epi16(pcm8, 1);
+ out[i+2] = _mm_extract_epi16(pcm8, 2);
+ out[i+3] = _mm_extract_epi16(pcm8, 3);
+ out[i+4] = _mm_extract_epi16(pcm8, 4);
+ out[i+5] = _mm_extract_epi16(pcm8, 5);
+ out[i+6] = _mm_extract_epi16(pcm8, 6);
+ out[i+7] = _mm_extract_epi16(pcm8, 7);
#else /* HAVE_SSE */
- int16x4_t pcma, pcmb;
- a = VADD(a, VSET(0.5f));
- b = VADD(b, VSET(0.5f));
- pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0)))));
- pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0)))));
- vst1_lane_s16(out+i , pcma, 0);
- vst1_lane_s16(out+i+1, pcma, 1);
- vst1_lane_s16(out+i+2, pcma, 2);
- vst1_lane_s16(out+i+3, pcma, 3);
- vst1_lane_s16(out+i+4, pcmb, 0);
- vst1_lane_s16(out+i+5, pcmb, 1);
- vst1_lane_s16(out+i+6, pcmb, 2);
- vst1_lane_s16(out+i+7, pcmb, 3);
+ int16x4_t pcma, pcmb;
+ a = VADD(a, VSET(0.5f));
+ b = VADD(b, VSET(0.5f));
+ pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0)))));
+ pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0)))));
+ vst1_lane_s16(out+i , pcma, 0);
+ vst1_lane_s16(out+i+1, pcma, 1);
+ vst1_lane_s16(out+i+2, pcma, 2);
+ vst1_lane_s16(out+i+3, pcma, 3);
+ vst1_lane_s16(out+i+4, pcmb, 0);
+ vst1_lane_s16(out+i+5, pcmb, 1);
+ vst1_lane_s16(out+i+6, pcmb, 2);
+ vst1_lane_s16(out+i+7, pcmb, 3);
#endif /* HAVE_SSE */
- }
+ }
#endif /* HAVE_SIMD */
- for(; i < num_samples; i++)
+ for(; i < num_samples; i++)
+ {
+ float sample = in[i] * 32768.0f;
+ if (sample >= 32766.5)
+ out[i] = (int16_t) 32767;
+ else if (sample <= -32767.5)
+ out[i] = (int16_t)-32768;
+ else
{
- float sample = in[i] * 32768.0f;
- if (sample >= 32766.5)
- out[i] = (int16_t) 32767;
- else if (sample <= -32767.5)
- out[i] = (int16_t)-32768;
- else
- {
- int16_t s = (int16_t)(sample + .5f);
- s -= (s < 0); /* away from zero, to be compliant */
- out[i] = s;
- }
+ int16_t s = (int16_t)(sample + .5f);
+ s -= (s < 0); /* away from zero, to be compliant */
+ out[i] = s;
}
}
}
diff --git a/3rdparty/minimp3/minimp3_ex.h b/3rdparty/minimp3/minimp3_ex.h
index 9e370e2c0ed..e29dd15b2ec 100644
--- a/3rdparty/minimp3/minimp3_ex.h
+++ b/3rdparty/minimp3/minimp3_ex.h
@@ -8,15 +8,37 @@
*/
#include "minimp3.h"
-#define MP3D_SEEK_TO_BYTE 0
-#define MP3D_SEEK_TO_SAMPLE 1
-#define MP3D_SEEK_TO_SAMPLE_INDEXED 2
+/* 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(int16_t) */
- int channels, hz, layer, avg_bitrate_kbps, frame_bytes;
+ 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
@@ -27,36 +49,78 @@ typedef struct
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;
- int seek_method;
-#ifndef MINIMP3_NO_STDIO
- int is_file;
-#endif
+ 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, size_t offset, mp3dec_frame_info_t *info);
-typedef int (*MP3D_PROGRESS_CB)(void *user_data, size_t file_size, size_t offset, mp3dec_frame_info_t *info);
+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
-size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size);
+/* 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 */
-void 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);
-/* iterate through frames with optional decoding */
-void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
-/* decoder with seeking capability */
-int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method);
+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);
-void mp3dec_ex_seek(mp3dec_ex_t *dec, size_t position);
-int mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, int samples);
+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 with file pre-load */
+/* 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 seek_method);
+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
@@ -65,113 +129,387 @@ int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int seek_method);
#endif /*MINIMP3_EXT_H*/
#ifdef MINIMP3_IMPLEMENTATION
+#include <limits.h>
+
+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;
+}
-size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size)
+static size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size)
{
- if (buf_size > 10 && !strncmp((char *)buf, "ID3", 3))
+#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)))
{
- return (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) |
- ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10;
+ 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;
}
-void 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)
+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;
- mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
+ int to_skip = 0;
mp3dec_frame_info_t frame_info;
memset(info, 0, sizeof(*info));
memset(&frame_info, 0, sizeof(frame_info));
- /* skip id3v2 */
- size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);
- if (id3v2size > buf_size)
- return;
- buf += id3v2size;
- buf_size -= id3v2size;
- /* try to make allocation size assumption by first frame */
+
+ /* 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
{
- samples = mp3dec_decode_frame(dec, buf, buf_size, pcm, &frame_info);
- buf += frame_info.frame_bytes;
- buf_size -= frame_info.frame_bytes;
- if (samples) {
- info->frame_bytes += frame_info.frame_bytes + id3v2size;
+ 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;
}
- } while (frame_info.frame_bytes);
- if (!samples)
- return;
- samples *= frame_info.channels;
- size_t allocated = (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t) + MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t);
+ 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;
- info->samples = samples;
- memcpy(info->buffer, pcm, info->samples*sizeof(mp3d_sample_t));
+ return MP3D_E_MEMORY;
/* save info */
info->channels = frame_info.channels;
info->hz = frame_info.hz;
info->layer = frame_info.layer;
- size_t avg_bitrate_kbps = frame_info.bitrate_kbps;
- size_t frames = 1;
- /* decode rest frames */
- int frame_bytes;
+ /* 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;
- info->buffer = (mp3d_sample_t*)realloc(info->buffer, allocated);
+ 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;
}
- samples = mp3dec_decode_frame(dec, buf, buf_size, info->buffer + info->samples, &frame_info);
- frame_bytes = frame_info.frame_bytes;
- buf += frame_bytes;
- buf_size -= frame_bytes;
if (samples)
{
- info->frame_bytes += frame_info.frame_bytes;
-
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
-
- info->samples += samples*frame_info.channels;
+ }
+ 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)
- progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info);
+ {
+ ret = progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info);
+ if (ret)
+ break;
+ }
}
- } while (frame_bytes);
+ } 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))
- info->buffer = (mp3d_sample_t*)realloc(info->buffer, info->samples*sizeof(mp3d_sample_t));
- info->avg_bitrate_kbps = avg_bitrate_kbps/frames;
+ {
+ 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;
}
-void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
+int mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
{
- if (!callback)
- return;
+ 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));
- /* skip id3v2 */
- size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);
- if (id3v2size > buf_size)
- return;
- const uint8_t *orig_buf = buf;
- buf += id3v2size;
- buf_size -= id3v2size;
do
{
- int free_format_bytes = 0, frame_size = 0;
+ 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;
@@ -186,31 +524,484 @@ void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB cal
frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);
frame_info.frame_bytes = frame_size;
- if (callback(user_data, hdr, frame_size, hdr - orig_buf, &frame_info))
- break;
+ 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;
}
-int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method)
+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->seek_method = seek_method;
+ 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;
}
-/*void mp3dec_ex_seek(mp3dec_ex_t *dec, size_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)
{
+ 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_read(mp3dec_ex_t *dec, int16_t *buf, int 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
@@ -221,6 +1012,10 @@ int mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, int samples)
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
+#if !defined(_GNU_SOURCE)
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
#if !defined(MAP_POPULATE) && defined(__linux__)
#define MAP_POPULATE 0x08000
#elif !defined(MAP_POPULATE)
@@ -237,6 +1032,8 @@ static void mp3dec_close_file(mp3dec_map_info_t *map_info)
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));
@@ -247,7 +1044,7 @@ retry_open:
if (file < 0 || fstat(file, &st) < 0)
{
close(file);
- return -1;
+ return MP3D_E_IOERROR;
}
map_info->size = st.st_size;
@@ -257,9 +1054,105 @@ retry_mmap:
goto retry_mmap;
close(file);
if (MAP_FAILED == map_info->buffer)
- return -1;
+ 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 <windows.h>
@@ -268,26 +1161,24 @@ 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;
+ map_info->size = 0;
}
-static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
+static int mp3dec_open_file_h(HANDLE file, mp3dec_map_info_t *map_info)
{
memset(map_info, 0, sizeof(*map_info));
- HANDLE file = CreateFileA(file_name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
- if (INVALID_HANDLE_VALUE == file)
- return -1;
+ 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;
- HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
+ mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
if (!mapping)
goto error;
- map_info->buffer = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, s.QuadPart);
+ map_info->buffer = (const uint8_t*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, s.QuadPart);
CloseHandle(mapping);
if (!map_info->buffer)
goto error;
@@ -297,7 +1188,27 @@ static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
error:
mp3dec_close_file(map_info);
CloseHandle(file);
- return -1;
+ 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 <stdio.h>
@@ -312,10 +1223,13 @@ static void mp3dec_close_file(mp3dec_map_info_t *map_info)
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 -1;
+ return MP3D_E_IOERROR;
+ int res = MP3D_E_IOERROR;
long size = -1;
if (fseek(file, 0, SEEK_END))
goto error;
@@ -327,7 +1241,10 @@ static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
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);
@@ -335,19 +1252,56 @@ static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
error:
mp3dec_close_file(map_info);
fclose(file);
- return -1;
+ 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;
- mp3dec_load_buf(dec, map_info.buffer, map_info.size, info, progress_cb, user_data);
- mp3dec_close_file(&map_info);
- return 0;
+ 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)
@@ -356,35 +1310,83 @@ int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_d
mp3dec_map_info_t map_info;
if ((ret = mp3dec_open_file(file_name, &map_info)))
return ret;
- mp3dec_iterate_buf(map_info.buffer, map_info.size, callback, user_data);
- mp3dec_close_file(&map_info);
- return 0;
+ 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);
- else
- free((void *)dec->file.buffer);
+ if (dec->index.frames)
+ free(dec->index.frames);
memset(dec, 0, sizeof(*dec));
}
-int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int seek_method)
+#ifdef _WIN32
+int mp3dec_detect_w(const wchar_t *file_name)
{
int ret;
- memset(dec, 0, sizeof(*dec));
- if ((ret = mp3dec_open_file(file_name, &dec->file)))
+ mp3dec_map_info_t map_info;
+ if ((ret = mp3dec_open_file_w(file_name, &map_info)))
return ret;
- dec->seek_method = seek_method;
- dec->is_file = 1;
- mp3dec_init(&dec->mp3d);
- return 0;
+ return mp3dec_detect_mapinfo(&map_info);
}
-#else
+
+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)
{
- free((void*)dec->file.buffer);
+#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
diff --git a/src/devices/sound/mas3507d.cpp b/src/devices/sound/mas3507d.cpp
index a5b7a7917d2..c2b50088d11 100644
--- a/src/devices/sound/mas3507d.cpp
+++ b/src/devices/sound/mas3507d.cpp
@@ -14,6 +14,28 @@
#include "minimp3/minimp3.h"
#include "minimp3/minimp3_ex.h"
+#define LOG_GENERAL (1 << 0)
+#define LOG_READ (1 << 1)
+#define LOG_WRITE (1 << 2)
+#define LOG_REGISTER (1 << 3)
+#define LOG_CONFIG (1 << 4)
+#define LOG_OTHER (1 << 5)
+// #define VERBOSE (LOG_GENERAL | LOG_READ | LOG_WRITE | LOG_REGISTER | LOG_CONFIG | LOG_OTHER)
+// #define LOG_OUTPUT_STREAM std::cout
+
+#include "logmacro.h"
+
+#define LOGREAD(...) LOGMASKED(LOG_READ, __VA_ARGS__)
+#define LOGWRITE(...) LOGMASKED(LOG_WRITE, __VA_ARGS__)
+#define LOGREGISTER(...) LOGMASKED(LOG_REGISTER, __VA_ARGS__)
+#define LOGCONFIG(...) LOGMASKED(LOG_CONFIG, __VA_ARGS__)
+#define LOGOTHER(...) LOGMASKED(LOG_OTHER, __VA_ARGS__)
+
+ALLOW_SAVE_TYPE(mas3507d_device::i2c_bus_state_t)
+ALLOW_SAVE_TYPE(mas3507d_device::i2c_bus_address_t)
+ALLOW_SAVE_TYPE(mas3507d_device::i2c_subdest_t)
+ALLOW_SAVE_TYPE(mas3507d_device::i2c_command_t)
+
// device type definition
DEFINE_DEVICE_TYPE(MAS3507D, mas3507d_device, "mas3507d", "MAS 3507D MPEG decoder")
@@ -21,8 +43,9 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag,
: device_t(mconfig, MAS3507D, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, cb_sample(*this)
- , i2c_bus_state(), i2c_bus_address(), i2c_scli(false), i2c_sclo(false), i2c_sdai(false), i2c_sdao(false)
- , i2c_bus_curbit(0), i2c_bus_curval(0), i2c_subdest(), i2c_command(), i2c_bytecount(0), i2c_io_bank(0), i2c_io_adr(0), i2c_io_count(0), i2c_io_val(0)
+ , i2c_bus_state(IDLE), i2c_bus_address(UNKNOWN), i2c_subdest(UNDEFINED), i2c_command(CMD_BAD)
+ , i2c_scli(false), i2c_sclo(false), i2c_sdai(false), i2c_sdao(false)
+ , i2c_bus_curbit(0), i2c_bus_curval(0), i2c_bytecount(0), i2c_io_bank(0), i2c_io_adr(0), i2c_io_count(0), i2c_io_val(0)
{
}
@@ -31,6 +54,50 @@ void mas3507d_device::device_start()
current_rate = 44100;
stream = stream_alloc(0, 2, current_rate);
cb_sample.resolve();
+
+ save_item(NAME(mp3data));
+ save_item(NAME(samples));
+ save_item(NAME(i2c_bus_state));
+ save_item(NAME(i2c_bus_address));
+ save_item(NAME(i2c_subdest));
+ save_item(NAME(i2c_command));
+ save_item(NAME(i2c_scli));
+ save_item(NAME(i2c_sclo));
+ save_item(NAME(i2c_sdai));
+ save_item(NAME(i2c_sdao));
+ save_item(NAME(i2c_bus_curbit));
+ save_item(NAME(i2c_bus_curval));
+ save_item(NAME(mp3data_count));
+ save_item(NAME(current_rate));
+ 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));
+ save_item(NAME(gain_ll));
+ save_item(NAME(gain_rr));
+ save_item(NAME(i2c_bytecount));
+ save_item(NAME(i2c_io_bank));
+ save_item(NAME(i2c_io_adr));
+ save_item(NAME(i2c_io_count));
+ save_item(NAME(i2c_io_val));
+ save_item(NAME(i2c_sdao_data));
+ save_item(NAME(playback_status));
+
+ // 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));
}
void mas3507d_device::device_reset()
@@ -42,13 +109,10 @@ void mas3507d_device::device_reset()
i2c_bus_curbit = -1;
i2c_bus_curval = 0;
- mp3dec_init(&mp3_dec);
- memset(mp3data.data(), 0, mp3data.size());
- memset(samples.data(), 0, samples.size());
- mp3_count = 0;
- sample_count = 0;
- total_frame_count = 0;
- buffered_frame_count = 0;
+ is_muted = false;
+ gain_ll = gain_rr = 0;
+
+ reset_playback();
}
void mas3507d_device::i2c_scl_w(bool line)
@@ -61,6 +125,14 @@ void mas3507d_device::i2c_scl_w(bool line)
if(i2c_bus_state == STARTED) {
if(i2c_sdai)
i2c_bus_curval |= 1 << i2c_bus_curbit;
+
+ if(i2c_subdest == DATA_READ)
+ i2c_sdao = BIT(i2c_sdao_data, i2c_bus_curbit + (i2c_bytecount * 8));
+ else {
+ i2c_sdao_data = 0;
+ i2c_sdao = false;
+ }
+
i2c_bus_curbit --;
if(i2c_bus_curbit == -1) {
if(i2c_bus_address == UNKNOWN) {
@@ -86,7 +158,7 @@ void mas3507d_device::i2c_scl_w(bool line)
i2c_bus_state = STARTED;
i2c_bus_curbit = 7;
i2c_bus_curval = 0;
- i2c_sdao = true;
+ i2c_sdao = false;
}
}
}
@@ -131,24 +203,30 @@ int mas3507d_device::i2c_sda_r()
bool mas3507d_device::i2c_device_got_address(uint8_t address)
{
- if (address == 0x3b) {
+ if(address == CMD_DEV_READ)
i2c_subdest = DATA_READ;
- } else {
+ else
i2c_subdest = UNDEFINED;
- }
- return (address & 0xfe) == 0x3a;
+ return (address & 0xfe) == CMD_DEV_WRITE;
}
void mas3507d_device::i2c_device_got_byte(uint8_t byte)
{
switch(i2c_subdest) {
case UNDEFINED:
- if(byte == 0x68)
+ if(byte == CMD_DATA_WRITE)
i2c_subdest = DATA_WRITE;
- else if(byte == 0x69)
+ else if(byte == CMD_DATA_READ) {
i2c_subdest = DATA_READ;
- else if(byte == 0x6a)
+
+ // Default read, returns the current frame count
+ i2c_sdao_data = ((decoded_frame_count >> 8) & 0xff)
+ | ((decoded_frame_count & 0xff) << 8)
+ | (((decoded_frame_count >> 24) & 0xff) << 16)
+ | (((decoded_frame_count >> 16) & 0xff) << 24);
+ }
+ else if(byte == CMD_CONTROL_WRITE)
i2c_subdest = CONTROL;
else
i2c_subdest = BAD;
@@ -159,19 +237,19 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
break;
case BAD:
- logerror("MAS I2C: Dropping byte %02x\n", byte);
+ LOGOTHER("MAS I2C: Dropping byte %02x\n", byte);
break;
case DATA_READ:
- // Default Read
- // This should return the current MPEGFrameCount value when called
+ switch(i2c_bytecount) {
+ case 0: i2c_io_val = byte; break;
+ case 1: i2c_io_val |= byte << 8; break;
+ case 2: i2c_nak(); return;
+ }
- // TODO: Figure out how to use this data exactly (chip docs are a little unclear to me)
- i2c_io_val <<= 8;
- i2c_io_val |= byte;
- i2c_bytecount++;
+ LOGREAD("MAS I2C: DATA_READ %d %02x %08x\n", i2c_bytecount, byte, i2c_io_val);
- logerror("MAS I2C: DATA_READ %d %08x\n", i2c_bytecount, i2c_io_val);
+ i2c_bytecount++;
break;
@@ -184,7 +262,7 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
break;
case 3:
i2c_command = CMD_READ_CTRL;
- logerror("MAS I2C: READ_CTRL\n");
+ LOGWRITE("MAS I2C: READ_CTRL\n");
break;
case 9:
i2c_io_adr = (byte & 15) << 4;
@@ -196,22 +274,22 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
break;
case 0xd:
i2c_command = CMD_READ_REG;
- logerror("MAS I2C: READ_REG\n");
+ LOGWRITE("MAS I2C: READ_REG\n");
break;
case 0xe: case 0xf:
i2c_io_bank = (byte >> 4) & 1;
i2c_command = CMD_READ_MEM;
- logerror("MAS I2C: READ_MEM\n");
+ LOGWRITE("MAS I2C: READ_MEM\n");
break;
default:
i2c_command = CMD_BAD;
- logerror("MAS I2C: BAD\n");
+ LOGWRITE("MAS I2C: BAD\n");
break;
}
} else {
switch(i2c_command) {
default:
- logerror("MAS I2C: Ignoring byte %02x\n", byte);
+ LOGWRITE("MAS I2C: Ignoring byte %02x\n", byte);
break;
case CMD_WRITE_REG:
@@ -260,14 +338,14 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
break;
case CONTROL:
- logerror("MAS I2C: Control byte %02x\n", byte);
+ LOGOTHER("MAS I2C: Control byte %02x\n", byte);
break;
}
}
void mas3507d_device::i2c_device_got_stop()
{
- logerror("MAS I2C: got stop\n");
+ LOGOTHER("MAS I2C: got stop\n");
}
int gain_to_db(double val) {
@@ -275,9 +353,8 @@ int gain_to_db(double val) {
}
float gain_to_percentage(int val) {
- if (val == 0) {
+ if(val == 0)
return 0; // Special case for muting it seems
- }
double db = gain_to_db(val);
@@ -287,113 +364,129 @@ float gain_to_percentage(int val) {
void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val)
{
switch(adr | (bank ? 0x10000 : 0)) {
- case 0x0032f: logerror("MAS3507D: OutputConfig = %05x\n", val); break;
+ case 0x0032d: LOGCONFIG("MAS3507D: PLLOffset48 = %05x\n", val); break;
+ case 0x0032e: LOGCONFIG("MAS3507D: PLLOffset44 = %05x\n", val); break;
+ case 0x0032f: LOGCONFIG("MAS3507D: OutputConfig = %05x\n", val); break;
case 0x107f8:
- logerror("MAS3507D: left->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
- set_output_gain(0, gain_to_percentage(val));
+ gain_ll = gain_to_percentage(val);
+ LOGCONFIG("MAS3507D: left->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_ll);
+
+ if(!is_muted) {
+ set_output_gain(0, gain_ll);
+ }
break;
case 0x107f9:
- logerror("MAS3507D: left->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
+ LOGCONFIG("MAS3507D: left->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
break;
case 0x107fa:
- logerror("MAS3507D: right->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
+ LOGCONFIG("MAS3507D: right->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
break;
case 0x107fb:
- logerror("MAS3507D: right->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
- set_output_gain(1, gain_to_percentage(val));
+ gain_rr = gain_to_percentage(val);
+ LOGCONFIG("MAS3507D: right->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_rr);
+
+ if(!is_muted) {
+ set_output_gain(1, gain_rr);
+ }
break;
- default: logerror("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
+ default: LOGCONFIG("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
}
}
void mas3507d_device::reg_write(uint32_t adr, uint32_t val)
{
switch(adr) {
- case 0x8e: logerror("MAS3507D: DCCF = %05x\n", val); break;
- case 0xaa: logerror("MAS3507D: Mute/bypass = %05x\n", val); break;
- case 0xe6: logerror("MAS3507D: StartupConfig = %05x\n", val); break;
- case 0xe7: logerror("MAS3507D: Kprescale = %05x\n", val); break;
- case 0x6b: logerror("MAS3507D: Kbass = %05x\n", val); break;
- case 0x6f: logerror("MAS3507D: Ktreble = %05x\n", val); break;
- default: logerror("MAS3507D: reg %02x = %05x\n", adr, val); break;
+ case 0x8e: LOGCONFIG("MAS3507D: DCCF = %05x\n", val); break;
+ case 0xaa:
+ LOGCONFIG("MAS3507D: Mute/bypass = %05x\n", val);
+ set_output_gain(0, val == 1 ? 0 : gain_ll);
+ set_output_gain(1, val == 1 ? 0 : gain_rr);
+ break;
+ case 0xe6: LOGCONFIG("MAS3507D: StartupConfig = %05x\n", val); break;
+ case 0xe7: LOGCONFIG("MAS3507D: Kprescale = %05x\n", val); break;
+ case 0x6b: LOGCONFIG("MAS3507D: Kbass = %05x\n", val); break;
+ case 0x6f: LOGCONFIG("MAS3507D: Ktreble = %05x\n", val); break;
+ default: LOGCONFIG("MAS3507D: reg %02x = %05x\n", adr, val); break;
}
}
void mas3507d_device::run_program(uint32_t adr)
{
switch(adr) {
- case 0xfcb: logerror("MAS3507D: validate OutputConfig\n"); break;
- default: logerror("MAS3507D: run %04x\n", adr); break;
+ case 0xfcb: LOGCONFIG("MAS3507D: validate OutputConfig\n"); break;
+ default: LOGCONFIG("MAS3507D: run %04x\n", adr); break;
}
}
void mas3507d_device::fill_buffer()
{
- while(mp3_count + 2 < mp3data.size()) {
- u16 v = cb_sample();
- mp3data[mp3_count++] = v >> 8;
- mp3data[mp3_count++] = v;
+ while(mp3data_count + 2 < mp3data.size()) {
+ uint16_t v = cb_sample();
+ mp3data[mp3data_count++] = v >> 8;
+ mp3data[mp3data_count++] = v;
}
- int scount = mp3dec_decode_frame(&mp3_dec, static_cast<const uint8_t *>(&mp3data[0]), mp3_count, static_cast<mp3d_sample_t *>(&samples[0]), &mp3_info);
+ sample_count = mp3dec_decode_frame(&mp3_dec, static_cast<const uint8_t *>(&mp3data[0]), mp3data_count, static_cast<mp3d_sample_t *>(&samples[0]), &mp3_info);
+ samples_idx = 0;
+ playback_status = PLAYBACK_STATE_BUFFER_FULL;
- if(!scount) {
- int to_drop = mp3_info.frame_bytes;
- // At 1MHz, we can transfer around 2082 bytes/video frame. So
- // that puts a boundary on how much we're ready to drop
- if(to_drop > 2082 || !to_drop)
- to_drop = 2082;
- std::copy(mp3data.begin() + to_drop, mp3data.end(), mp3data.begin());
- mp3_count -= to_drop;
+ if(sample_count == 0)
return;
- }
std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin());
- mp3_count -= mp3_info.frame_bytes;
-
- sample_count = scount;
+ mp3data_count -= mp3_info.frame_bytes;
if(mp3_info.hz != current_rate) {
current_rate = mp3_info.hz;
stream->set_sample_rate(current_rate);
}
+
+ decoded_frame_count++;
}
void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount)
{
- buffered_frame_count = scount;
-
int s1 = scount - pos;
+ int bytes_per_sample = mp3_info.channels > 2 ? 2 : mp3_info.channels; // More than 2 channels is unsupported here
+
if(s1 > sample_count)
s1 = sample_count;
- if(mp3_info.channels == 1) {
- for(int i=0; i<s1; i++) {
- outputs[0].put_int(i+pos, samples[i], 32768);
- outputs[1].put_int(i+pos, samples[i], 32768);
- }
- } else {
- for(int i=0; i<s1; i++) {
- outputs[0].put_int(i+pos, samples[i*2], 32768);
- outputs[1].put_int(i+pos, samples[i*2+1], 32768);
- }
- }
+ playback_status = PLAYBACK_STATE_DEMAND_BUFFER;
- if(s1 == sample_count) {
- pos += s1;
- sample_count = 0;
- total_frame_count += s1;
- return;
+ for(int i = 0; i < s1; i++) {
+ outputs[0].put_int(pos, samples[samples_idx * bytes_per_sample], 32768);
+ 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) {
+ sample_count = 0;
+ return;
+ }
}
+}
- if(mp3_info.channels == 1)
- std::copy(samples.begin() + s1, samples.begin() + sample_count, samples.begin());
- else
- std::copy(samples.begin() + s1*2, samples.begin() + sample_count*2, samples.begin());
+void mas3507d_device::reset_playback()
+{
+ mp3dec_init(&mp3_dec);
+ mp3data_count = 0;
+ sample_count = 0;
+ decoded_frame_count = 0;
+ decoded_samples = 0;
+ playback_status = PLAYBACK_STATE_IDLE;
+ is_started = false;
+ samples_idx = 0;
+ std::fill(mp3data.begin(), mp3data.end(), 0);
+ std::fill(samples.begin(), samples.end(), 0);
+}
- pos += s1;
- sample_count -= s1;
- total_frame_count += s1;
+void mas3507d_device::start_playback()
+{
+ reset_playback();
+ is_started = true;
}
void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
@@ -401,25 +494,19 @@ void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read
int csamples = outputs[0].samples();
int pos = 0;
- append_buffer(outputs, pos, csamples);
- for(;;) {
- if(pos == csamples)
- return;
- fill_buffer();
- if(!sample_count) {
- // In the case of a bad frame or no frames being around, reset the state of the decoder
- mp3dec_init(&mp3_dec);
- memset(mp3data.data(), 0, mp3data.size());
- memset(samples.data(), 0, samples.size());
- mp3_count = 0;
- sample_count = 0;
- total_frame_count = 0;
- buffered_frame_count = 0;
+ while(pos < csamples) {
+ if(is_started && sample_count == 0)
+ fill_buffer();
+ if(!is_started || sample_count <= 0) {
+ playback_status = PLAYBACK_STATE_IDLE;
+ decoded_frame_count = 0;
+ decoded_samples = 0;
outputs[0].fill(0, pos);
outputs[1].fill(0, pos);
return;
}
+
append_buffer(outputs, pos, csamples);
}
}
diff --git a/src/devices/sound/mas3507d.h b/src/devices/sound/mas3507d.h
index b04e157e2f2..36f95de72db 100644
--- a/src/devices/sound/mas3507d.h
+++ b/src/devices/sound/mas3507d.h
@@ -12,6 +12,12 @@
class mas3507d_device : public device_t, public device_sound_interface
{
public:
+ enum {
+ PLAYBACK_STATE_IDLE,
+ PLAYBACK_STATE_BUFFER_FULL,
+ PLAYBACK_STATE_DEMAND_BUFFER
+ };
+
// construction/destruction
mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
@@ -22,7 +28,15 @@ public:
void i2c_scl_w(bool line);
void i2c_sda_w(bool line);
- u32 get_frame_count() const { return total_frame_count - buffered_frame_count; }
+ uint32_t get_samples() const { return decoded_samples; }
+ uint32_t get_status() const { return playback_status; }
+
+ void update_stream() { stream->update(); }
+
+ void reset_playback();
+ void start_playback();
+
+ bool is_started;
protected:
virtual void device_start() override;
@@ -30,40 +44,62 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private:
+ void i2c_nak();
+ bool i2c_device_got_address(uint8_t address);
+ void i2c_device_got_byte(uint8_t byte);
+ void i2c_device_got_stop();
+
+ void mem_write(int bank, uint32_t adr, uint32_t val);
+ void run_program(uint32_t adr);
+ void reg_write(uint32_t adr, uint32_t val);
+
+ void fill_buffer();
+ void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount);
+
devcb_read16 cb_sample;
- enum { IDLE, STARTED, NAK, ACK, ACK2 } i2c_bus_state;
- enum { UNKNOWN, VALIDATED, WRONG } i2c_bus_address;
+ enum {
+ CMD_DEV_WRITE = 0x3a,
+ CMD_DEV_READ = 0x3b,
- std::array<u8, 0xe00> mp3data;
- std::array<mp3d_sample_t, MINIMP3_MAX_SAMPLES_PER_FRAME> samples;
- bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
- int i2c_bus_curbit;
- uint8_t i2c_bus_curval;
- int mp3_count, sample_count, current_rate;
- u32 total_frame_count, buffered_frame_count;
+ CMD_DATA_WRITE = 0x68,
+ CMD_DATA_READ = 0x69,
+ CMD_CONTROL_WRITE = 0x6a
+ };
+
+ enum i2c_bus_state_t : uint8_t { IDLE = 0, STARTED, NAK, ACK, ACK2 };
+ enum i2c_bus_address_t : uint8_t { UNKNOWN = 0, VALIDATED, WRONG };
+ enum i2c_subdest_t : uint8_t { UNDEFINED = 0, CONTROL, DATA_READ, DATA_WRITE, BAD };
+ enum i2c_command_t : uint8_t { CMD_BAD = 0, CMD_RUN, CMD_READ_CTRL, CMD_WRITE_REG, CMD_WRITE_MEM, CMD_READ_REG, CMD_READ_MEM };
+
+ i2c_bus_state_t i2c_bus_state;
+ i2c_bus_address_t i2c_bus_address;
+ i2c_subdest_t i2c_subdest;
+ i2c_command_t i2c_command;
mp3dec_t mp3_dec;
mp3dec_frame_info_t mp3_info;
sound_stream *stream;
- void i2c_nak();
- bool i2c_device_got_address(uint8_t address);
- void i2c_device_got_byte(uint8_t byte);
- void i2c_device_got_stop();
+ std::array<uint8_t, 0xe00> mp3data;
+ std::array<mp3d_sample_t, MINIMP3_MAX_SAMPLES_PER_FRAME> samples;
- enum { UNDEFINED, CONTROL, DATA_READ, DATA_WRITE, BAD } i2c_subdest;
- enum { CMD_BAD, CMD_RUN, CMD_READ_CTRL, CMD_WRITE_REG, CMD_WRITE_MEM, CMD_READ_REG, CMD_READ_MEM } i2c_command;
+ bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
+ int i2c_bus_curbit;
+ uint8_t i2c_bus_curval;
int i2c_bytecount;
uint32_t i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val;
+ uint32_t i2c_sdao_data;
- void mem_write(int bank, uint32_t adr, uint32_t val);
- void run_program(uint32_t adr);
- void reg_write(uint32_t adr, uint32_t val);
+ uint32_t mp3data_count, current_rate;
+ uint32_t decoded_frame_count, decoded_samples;
+ int32_t sample_count, samples_idx;
- void fill_buffer();
- void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int samples);
+ bool is_muted;
+ float gain_ll, gain_rr;
+
+ uint32_t playback_status;
};
diff --git a/src/emu/devfind.h b/src/emu/devfind.h
index f7a5a09d3ad..c5f7a04929a 100644
--- a/src/emu/devfind.h
+++ b/src/emu/devfind.h
@@ -794,7 +794,7 @@ template <unsigned Count> using required_memory_bank_array = memory_bank_array_f
///
/// Creates a memory bank or finds an existing one instantiated via an
/// address map.
-class memory_bank_creator : finder_base
+class memory_bank_creator : public finder_base
{
public:
/// \brief Memory bank creator constructor
@@ -1249,7 +1249,7 @@ template <typename PointerType, unsigned Count> using required_shared_ptr_array
/// share. If an existing memory share is found, it is an error if it
/// doesn't match the requested width, length and endianness.
template <typename PointerType>
-class memory_share_creator : finder_base
+class memory_share_creator : public finder_base
{
public:
/// \brief Memory share creator constructor
diff --git a/src/mame/machine/k573dio.cpp b/src/mame/machine/k573dio.cpp
index 4b8eeddfb99..7e3c0ece28a 100644
--- a/src/mame/machine/k573dio.cpp
+++ b/src/mame/machine/k573dio.cpp
@@ -3,6 +3,19 @@
#include "emu.h"
#include "k573dio.h"
+#define LOG_GENERAL (1 << 0)
+#define LOG_FPGA (1 << 1)
+#define LOG_MP3 (1 << 2)
+#define LOG_UNKNOWNREG (1 << 3)
+// #define VERBOSE (LOG_GENERAL | LOG_FPGA | LOG_MP3 | LOG_UNKNOWNREG)
+// #define LOG_OUTPUT_STREAM std::cout
+
+#include "logmacro.h"
+
+#define LOGFPGA(...) LOGMASKED(LOG_FPGA, __VA_ARGS__)
+#define LOGMP3(...) LOGMASKED(LOG_MP3, __VA_ARGS__)
+#define LOGUNKNOWNREG(...) LOGMASKED(LOG_UNKNOWNREG, __VA_ARGS__)
+
/*
Digital I/O PCB
---------------
@@ -82,16 +95,17 @@ void k573dio_device::amap(address_map &map)
map(0xa4, 0xa5).w(FUNC(k573dio_device::mpeg_end_adr_high_w));
map(0xa6, 0xa7).w(FUNC(k573dio_device::mpeg_end_adr_low_w));
map(0xa8, 0xa9).rw(FUNC(k573dio_device::mpeg_key_1_r), FUNC(k573dio_device::mpeg_key_1_w));
+ map(0xaa, 0xab).r(FUNC(k573dio_device::mpeg_ctrl_r));
map(0xac, 0xad).rw(FUNC(k573dio_device::mas_i2c_r), FUNC(k573dio_device::mas_i2c_w));
- map(0xae, 0xaf).rw(FUNC(k573dio_device::mpeg_ctrl_r), FUNC(k573dio_device::mpeg_ctrl_w));
+ map(0xae, 0xaf).rw(FUNC(k573dio_device::fpga_ctrl_r), FUNC(k573dio_device::fpga_ctrl_w));
map(0xb0, 0xb1).w(FUNC(k573dio_device::ram_write_adr_high_w));
map(0xb2, 0xb3).w(FUNC(k573dio_device::ram_write_adr_low_w));
map(0xb4, 0xb5).rw(FUNC(k573dio_device::ram_r), FUNC(k573dio_device::ram_w));
map(0xb6, 0xb7).w(FUNC(k573dio_device::ram_read_adr_high_w));
map(0xb8, 0xb9).w(FUNC(k573dio_device::ram_read_adr_low_w));
- map(0xca, 0xcb).r(FUNC(k573dio_device::mp3_frame_count_high_r));
- map(0xcc, 0xcd).r(FUNC(k573dio_device::mp3_frame_count_low_r));
- map(0xce, 0xcf).r(FUNC(k573dio_device::mp3_unk_r));
+ map(0xca, 0xcb).r(FUNC(k573dio_device::mp3_counter_high_r));
+ map(0xcc, 0xcd).rw(FUNC(k573dio_device::mp3_counter_low_r), FUNC(k573dio_device::mp3_counter_low_w));
+ map(0xce, 0xcf).r(FUNC(k573dio_device::mp3_counter_diff_r));
map(0xe0, 0xe1).w(FUNC(k573dio_device::output_1_w));
map(0xe2, 0xe3).w(FUNC(k573dio_device::output_0_w));
map(0xe4, 0xe5).w(FUNC(k573dio_device::output_3_w));
@@ -108,9 +122,9 @@ void k573dio_device::amap(address_map &map)
k573dio_device::k573dio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, KONAMI_573_DIGITAL_IO_BOARD, tag, owner, clock),
+ ram(*this, "ram", 0x2000000, ENDIANNESS_LITTLE),
k573fpga(*this, "k573fpga"),
digital_id(*this, "digital_id"),
- mas3507d(*this, "mpeg"),
output_cb(*this),
is_ddrsbm_fpga(false)
{
@@ -120,10 +134,12 @@ void k573dio_device::device_start()
{
output_cb.resolve_safe();
- ram = std::make_unique<uint16_t[]>(0x2000000/2);
- save_pointer(NAME(ram), 0x2000000/2 );
+ save_item(NAME(ram_adr));
+ save_item(NAME(ram_read_adr));
+ save_item(NAME(output_data));
+ save_item(NAME(is_ddrsbm_fpga));
+ save_item(NAME(crypto_key1));
- k573fpga->set_ram(ram.get());
k573fpga->set_ddrsbm_fpga(is_ddrsbm_fpga);
}
@@ -131,8 +147,9 @@ void k573dio_device::device_reset()
{
ram_adr = 0;
ram_read_adr = 0;
+ crypto_key1 = 0;
- memset(output_data, 0, sizeof(output_data));
+ std::fill(std::begin(output_data), std::end(output_data), 0);
}
ROM_START( k573dio )
@@ -148,11 +165,11 @@ const tiny_rom_entry *k573dio_device::device_rom_region() const
void k573dio_device::device_add_mconfig(machine_config &config)
{
KONAMI_573_DIGITAL_FPGA(config, k573fpga);
+ k573fpga->set_ram(ram);
+ k573fpga->add_route(0, ":lspeaker", 1.0);
+ k573fpga->add_route(1, ":rspeaker", 1.0);
+
DS2401(config, digital_id);
- MAS3507D(config, mas3507d);
- mas3507d->sample_cb().set(k573fpga, FUNC(k573fpga_device::get_decrypted));
- mas3507d->add_route(0, ":lspeaker", 1.0);
- mas3507d->add_route(1, ":rspeaker", 1.0);
}
void k573dio_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
@@ -161,61 +178,61 @@ void k573dio_device::device_timer(emu_timer &timer, device_timer_id id, int para
uint16_t k573dio_device::a00_r()
{
- logerror("%s: a00_r (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a00_r (%s)\n", tag(), machine().describe_context());
return 0x0000;
}
uint16_t k573dio_device::a02_r()
{
- logerror("%s: a02_r (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a02_r (%s)\n", tag(), machine().describe_context());
return 0x0001;
}
uint16_t k573dio_device::a04_r()
{
- logerror("%s: a04_r (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a04_r (%s)\n", tag(), machine().describe_context());
return 0x0000;
}
uint16_t k573dio_device::a06_r()
{
- logerror("%s: a06_r (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a06_r (%s)\n", tag(), machine().describe_context());
return 0x0000;
}
uint16_t k573dio_device::a0a_r()
{
- logerror("%s: a0a_r (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a0a_r (%s)\n", tag(), machine().describe_context());
return 0x0000;
}
void k573dio_device::a10_w(uint16_t data)
{
- logerror("%s: a10_w (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a10_w: %04x (%s)\n", tag(), data, machine().describe_context());
}
uint16_t k573dio_device::ac4_r()
{
- // What is this?
+ LOGUNKNOWNREG("%s: ac4_r (%s)\n", tag(), machine().describe_context());
return 0;
}
uint16_t k573dio_device::a80_r()
{
- logerror("%s: a80_r (%s)\n", tag(), machine().describe_context());
+ LOGUNKNOWNREG("%s: a80_r (%s)\n", tag(), machine().describe_context());
return 0x1234;
}
void k573dio_device::mpeg_start_adr_high_w(uint16_t data)
{
- logerror("FPGA MPEG start address high %04x\n", data);
- k573fpga->set_mp3_cur_adr((k573fpga->get_mp3_cur_adr() & 0x0000ffff) | (data << 16)); // high
+ LOGMP3("FPGA MPEG start address high %04x\n", data);
+ k573fpga->set_mp3_start_addr((k573fpga->get_mp3_start_addr() & 0x0000ffff) | (data << 16)); // high
}
void k573dio_device::mpeg_start_adr_low_w(uint16_t data)
{
- logerror("FPGA MPEG start address low %04x\n", data);
- k573fpga->set_mp3_cur_adr((k573fpga->get_mp3_cur_adr() & 0xffff0000) | data); // low
+ LOGMP3("FPGA MPEG start address low %04x\n", data);
+ k573fpga->set_mp3_start_addr((k573fpga->get_mp3_start_addr() & 0xffff0000) | data); // low
if(is_ddrsbm_fpga)
k573fpga->set_crypto_key3(0);
@@ -223,14 +240,14 @@ void k573dio_device::mpeg_start_adr_low_w(uint16_t data)
void k573dio_device::mpeg_end_adr_high_w(uint16_t data)
{
- logerror("FPGA MPEG end address high %04x\n", data);
- k573fpga->set_mp3_end_adr((k573fpga->get_mp3_end_adr() & 0x0000ffff) | (data << 16)); // high
+ LOGMP3("FPGA MPEG end address high %04x\n", data);
+ k573fpga->set_mp3_end_addr((k573fpga->get_mp3_end_addr() & 0x0000ffff) | (data << 16)); // high
}
void k573dio_device::mpeg_end_adr_low_w(uint16_t data)
{
- logerror("FPGA MPEG end address low %04x\n", data);
- k573fpga->set_mp3_end_adr((k573fpga->get_mp3_end_adr() & 0xffff0000) | data); // low
+ LOGMP3("FPGA MPEG end address low %04x\n", data);
+ k573fpga->set_mp3_end_addr((k573fpga->get_mp3_end_addr() & 0xffff0000) | data); // low
}
uint16_t k573dio_device::mpeg_key_1_r()
@@ -241,36 +258,32 @@ uint16_t k573dio_device::mpeg_key_1_r()
void k573dio_device::mpeg_key_1_w(uint16_t data)
{
- logerror("FPGA MPEG key 1/3 %04x\n", data);
+ LOGMP3("FPGA MPEG key 1/3 %04x\n", data);
crypto_key1 = data;
k573fpga->set_crypto_key1(data);
}
uint16_t k573dio_device::mas_i2c_r()
{
- int scl = mas3507d->i2c_scl_r() << 13;
- int sda = mas3507d->i2c_sda_r() << 12;
-
- return scl | sda;
+ return k573fpga->mas_i2c_r();
}
void k573dio_device::mas_i2c_w(uint16_t data)
{
- mas3507d->i2c_scl_w(data & 0x2000);
- mas3507d->i2c_sda_w(data & 0x1000);
+ k573fpga->mas_i2c_w(data);
}
uint16_t k573dio_device::mpeg_ctrl_r()
{
- if (k573fpga->get_mpeg_ctrl() == 0x1000 && !k573fpga->is_playing()) {
- // Set the FPGA to stop mode so that data won't be sent anymore
- k573fpga->set_mpeg_ctrl(0xa000);
- }
-
return k573fpga->get_mpeg_ctrl();
}
-void k573dio_device::mpeg_ctrl_w(uint16_t data)
+uint16_t k573dio_device::fpga_ctrl_r()
+{
+ return k573fpga->get_fpga_ctrl();
+}
+
+void k573dio_device::fpga_ctrl_w(uint16_t data)
{
k573fpga->set_mpeg_ctrl(data);
}
@@ -312,14 +325,25 @@ void k573dio_device::ram_read_adr_low_w(uint16_t data)
ram_read_adr = ((ram_read_adr & 0xffff0000) | data) & 0x1ffffff;
}
-uint16_t k573dio_device::mp3_frame_count_high_r()
+uint16_t k573dio_device::mp3_counter_high_r()
{
- return (mas3507d->get_frame_count() & 0xffff0000) >> 16;
+ return (k573fpga->get_counter() & 0xffff0000) >> 16;
}
-uint16_t k573dio_device::mp3_frame_count_low_r()
+uint16_t k573dio_device::mp3_counter_low_r()
{
- return mas3507d->get_frame_count() & 0x0000ffff;
+ return k573fpga->get_counter() & 0x0000ffff;
+}
+
+void k573dio_device::mp3_counter_low_w(uint16_t data)
+{
+ LOGMP3("mp3_counter_low_w %04x\n", data);
+ k573fpga->reset_counter();
+}
+
+uint16_t k573dio_device::mp3_counter_diff_r()
+{
+ return k573fpga->get_counter_diff() & 0x0000ffff;
}
void k573dio_device::output_1_w(uint16_t data)
@@ -344,13 +368,13 @@ void k573dio_device::output_7_w(uint16_t data)
void k573dio_device::mpeg_key_2_w(uint16_t data)
{
- logerror("FPGA MPEG key 2/3 %04x\n", data);
+ LOGMP3("FPGA MPEG key 2/3 %04x\n", data);
k573fpga->set_crypto_key2(data);
}
void k573dio_device::mpeg_key_3_w(uint16_t data)
{
- logerror("FPGA MPEG key 3/3 %04x\n", data);
+ LOGMP3("FPGA MPEG key 3/3 %04x\n", data);
k573fpga->set_crypto_key3(data);
}
@@ -366,7 +390,7 @@ void k573dio_device::digital_id_w(uint16_t data)
uint16_t k573dio_device::fpga_status_r()
{
- //logerror("%s: fpga_status_r (%s)\n", tag(), machine().describe_context());
+ LOGFPGA("%s: fpga_status_r (%s)\n", tag(), machine().describe_context());
// fpga/digital board status checks
// wants & c000 = 8000 (just after program upload?)
@@ -400,11 +424,6 @@ void k573dio_device::output_2_w(uint16_t data)
output(2, data);
}
-uint16_t k573dio_device::mp3_unk_r()
-{
- return 0;
-}
-
void k573dio_device::output(int offset, uint16_t data)
{
data = (data >> 12) & 0x0f;
diff --git a/src/mame/machine/k573dio.h b/src/mame/machine/k573dio.h
index 25fb3565b8e..2c22c82e453 100644
--- a/src/mame/machine/k573dio.h
+++ b/src/mame/machine/k573dio.h
@@ -33,18 +33,21 @@ public:
void mpeg_end_adr_low_w(uint16_t data);
uint16_t mpeg_key_1_r();
void mpeg_key_1_w(uint16_t data);
+ uint16_t mpeg_ctrl_r();
uint16_t mas_i2c_r();
void mas_i2c_w(uint16_t data);
- uint16_t mpeg_ctrl_r();
- void mpeg_ctrl_w(uint16_t data);
+ uint16_t fpga_ctrl_r();
+ void fpga_ctrl_w(uint16_t data);
void ram_write_adr_high_w(uint16_t data);
void ram_write_adr_low_w(uint16_t data);
uint16_t ram_r();
void ram_w(uint16_t data);
void ram_read_adr_high_w(uint16_t data);
void ram_read_adr_low_w(uint16_t data);
- uint16_t mp3_frame_count_high_r();
- uint16_t mp3_frame_count_low_r();
+ uint16_t mp3_counter_high_r();
+ uint16_t mp3_counter_low_r();
+ void mp3_counter_low_w(uint16_t data);
+ uint16_t mp3_counter_diff_r();
void output_0_w(uint16_t data);
void output_1_w(uint16_t data);
void output_7_w(uint16_t data);
@@ -58,7 +61,6 @@ public:
void output_4_w(uint16_t data);
void output_2_w(uint16_t data);
void output_5_w(uint16_t data);
- uint16_t mp3_unk_r();
protected:
virtual void device_start() override;
@@ -68,12 +70,11 @@ protected:
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
private:
+ memory_share_creator<uint16_t> ram;
required_device<k573fpga_device> k573fpga;
required_device<ds2401_device> digital_id;
- required_device<mas3507d_device> mas3507d;
devcb_write8 output_cb;
- std::unique_ptr<uint16_t[]> ram;
uint32_t ram_adr, ram_read_adr;
uint8_t output_data[8];
diff --git a/src/mame/machine/k573fpga.cpp b/src/mame/machine/k573fpga.cpp
index 3ea14abae9b..ad8134d9002 100644
--- a/src/mame/machine/k573fpga.cpp
+++ b/src/mame/machine/k573fpga.cpp
@@ -5,50 +5,185 @@
#include "k573fpga.h"
+#define LOG_GENERAL (1 << 0)
+#define VERBOSE (LOG_GENERAL)
+// #define LOG_OUTPUT_STREAM std::cout
-k573fpga_device::k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+#include "logmacro.h"
+
+k573fpga_device::k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, KONAMI_573_DIGITAL_FPGA, tag, owner, clock),
+ ram(*this, finder_base::DUMMY_TAG),
+ mas3507d(*this, "mpeg"),
use_ddrsbm_fpga(false)
{
}
+void k573fpga_device::device_add_mconfig(machine_config &config)
+{
+ MAS3507D(config, mas3507d);
+ mas3507d->sample_cb().set(*this, FUNC(k573fpga_device::get_decrypted));
+}
+
void k573fpga_device::device_start()
{
+ save_item(NAME(crypto_key1));
+ save_item(NAME(crypto_key2));
+ save_item(NAME(crypto_key3));
+ save_item(NAME(mp3_start_addr));
+ save_item(NAME(mp3_cur_addr));
+ save_item(NAME(mp3_end_addr));
+ save_item(NAME(use_ddrsbm_fpga));
+ save_item(NAME(is_stream_active));
+ save_item(NAME(is_timer_active));
+ save_item(NAME(counter_previous));
+ save_item(NAME(counter_current));
+ save_item(NAME(last_playback_status));
}
void k573fpga_device::device_reset()
{
- mp3_cur_adr = 0;
- mp3_end_adr = 0;
+ mp3_start_addr = 0;
+ mp3_cur_addr = 0;
+ mp3_end_addr = 0;
+
crypto_key1 = 0;
crypto_key2 = 0;
crypto_key3 = 0;
+
+ is_stream_active = false;
+ is_timer_active = false;
+
+ counter_current = counter_previous = counter_offset = 0;
+
+ mas3507d->reset_playback();
+ last_playback_status = get_mpeg_ctrl();
+}
+
+void k573fpga_device::reset_counter() {
+ counter_current = counter_previous = counter_offset = 0;
+ status_update();
+}
+
+void k573fpga_device::status_update() {
+ auto cur_playback_status = get_mpeg_ctrl();
+ is_timer_active = is_streaming() || ((cur_playback_status == last_playback_status && last_playback_status > PLAYBACK_STATE_IDLE) || cur_playback_status > last_playback_status);
+ last_playback_status = cur_playback_status;
+
+ if(!is_timer_active)
+ counter_current = counter_previous = counter_offset = 0;
}
-u16 k573fpga_device::get_mpeg_ctrl()
+uint32_t k573fpga_device::get_counter() {
+ status_update();
+
+ counter_previous = counter_current;
+
+ if(is_timer_active) {
+ mas3507d->update_stream();
+ counter_current = mas3507d->get_samples() - counter_offset;
+ }
+
+ return counter_current;
+}
+
+uint32_t k573fpga_device::get_counter_diff() {
+ // Delta playback time since last counter update.
+ // I couldn't find any active usages of this register but it exists in some code paths.
+ // The functionality was tested using custom code running on real hardware.
+ // When this is called, it will return the difference between the current counter value
+ // and the last read counter value, and then reset the counter back to the previously read counter's value.
+ auto diff = counter_current - counter_previous;
+ counter_current -= diff;
+ counter_previous = counter_current;
+ get_counter();
+ return diff;
+}
+
+uint16_t k573fpga_device::mas_i2c_r()
{
- if ((mpeg_ctrl_flag & 0xe000) == 0xe000) {
- // This has been tested with real hardware, but this flag is always held 0x1000 when the audio is being played
- return 0x1000;
+ uint16_t scl = mas3507d->i2c_scl_r() << 13;
+ uint16_t sda = mas3507d->i2c_sda_r() << 12;
+ return scl | sda;
+}
+
+void k573fpga_device::mas_i2c_w(uint16_t data)
+{
+ mas3507d->i2c_scl_w(data & 0x2000);
+ mas3507d->i2c_sda_w(data & 0x1000);
+}
+
+uint16_t k573fpga_device::get_mpeg_ctrl()
+{
+ switch(mas3507d->get_status()) {
+ case mas3507d_device::PLAYBACK_STATE_IDLE:
+ return PLAYBACK_STATE_IDLE;
+
+ case mas3507d_device::PLAYBACK_STATE_BUFFER_FULL:
+ return PLAYBACK_STATE_BUFFER_FULL;
+
+ case mas3507d_device::PLAYBACK_STATE_DEMAND_BUFFER:
+ return PLAYBACK_STATE_DEMAND_BUFFER;
}
- return 0x0000;
+ return PLAYBACK_STATE_IDLE;
}
-void k573fpga_device::set_mpeg_ctrl(u16 data)
+bool k573fpga_device::is_mp3_playing()
{
- logerror("FPGA MPEG control %c%c%c | %08x %08x\n",
+ return get_mpeg_ctrl() > PLAYBACK_STATE_IDLE;
+}
+
+uint16_t k573fpga_device::get_fpga_ctrl()
+{
+ // 0x0000 Not Streaming
+ // 0x1000 Streaming
+ return is_streaming() << 12;
+}
+
+bool k573fpga_device::is_streaming()
+{
+ return is_stream_active && mp3_cur_addr < mp3_end_addr;
+}
+
+void k573fpga_device::set_mpeg_ctrl(uint16_t data)
+{
+ LOG("FPGA MPEG control %c%c%c | %04x\n",
data & 0x8000 ? '#' : '.',
- data & 0x4000 ? '#' : '.',
+ data & 0x4000 ? '#' : '.', // "Active" flag. The FPGA will never start streaming data without this bit set
data & 0x2000 ? '#' : '.',
- mp3_cur_adr, mp3_end_adr);
+ data);
- mpeg_ctrl_flag = data;
+ mas3507d->reset_playback();
+
+ if(data == 0xa000) {
+ is_stream_active = false;
+ counter_current = counter_previous = 0;
+ status_update();
+ } else if(data == 0xe000) {
+ is_stream_active = true;
+ mp3_cur_addr = mp3_start_addr;
+
+ reset_counter();
+
+ if(!mas3507d->is_started) {
+ mas3507d->start_playback();
+ mas3507d->update_stream();
+
+ // Audio should be buffered by this point.
+ // The assumption is that the number of samples actually played can be
+ // calculated by subtracting the base sample count when the song was started
+ // from the current sample count when the counter register is read.
+ // Otherwise, the sample count will always be ahead by the number of samples
+ // that were in the buffered frames.
+ counter_offset = mas3507d->get_samples();
+ }
+ }
}
-u16 k573fpga_device::decrypt_default(u16 v)
+uint16_t k573fpga_device::decrypt_default(uint16_t v)
{
- u16 m = crypto_key1 ^ crypto_key2;
+ uint16_t m = crypto_key1 ^ crypto_key2;
v = bitswap<16>(
v,
@@ -80,7 +215,7 @@ u16 k573fpga_device::decrypt_default(u16 v)
(BIT(m, 0x0) << 0);
v ^= bitswap<16>(
- (u16)crypto_key3,
+ (uint16_t)crypto_key3,
7, 0, 6, 1,
5, 2, 4, 3,
3, 4, 2, 5,
@@ -97,10 +232,10 @@ u16 k573fpga_device::decrypt_default(u16 v)
return v;
}
-u16 k573fpga_device::decrypt_ddrsbm(u16 data)
+uint16_t k573fpga_device::decrypt_ddrsbm(uint16_t data)
{
- u8 key[16] = {0};
- u16 key_state = bitswap<16>(
+ uint8_t key[16] = {0};
+ uint16_t key_state = bitswap<16>(
crypto_key1,
13, 11, 9, 7,
5, 3, 1, 15,
@@ -114,7 +249,7 @@ u16 k573fpga_device::decrypt_ddrsbm(u16 data)
key_state = ((key_state & 0x8080) >> 7) | ((key_state & 0x7f7f) << 1);
}
- u16 output_word = 0;
+ uint16_t output_word = 0;
for(int cur_bit = 0; cur_bit < 8; cur_bit++) {
int even_bit_shift = cur_bit * 2;
int odd_bit_shift = cur_bit * 2 + 1;
@@ -138,15 +273,16 @@ u16 k573fpga_device::decrypt_ddrsbm(u16 data)
return output_word;
}
-u16 k573fpga_device::get_decrypted()
+uint16_t k573fpga_device::get_decrypted()
{
- if(mp3_cur_adr >= mp3_end_adr || (mpeg_ctrl_flag & 0xe000) != 0xe000) {
+ if(!is_streaming()) {
+ is_stream_active = false;
return 0;
}
- u16 src = ram[mp3_cur_adr >> 1];
- u16 result = use_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src);
- mp3_cur_adr += 2;
+ uint16_t src = ram[mp3_cur_addr >> 1];
+ uint16_t result = use_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src);
+ mp3_cur_addr += 2;
return result;
}
diff --git a/src/mame/machine/k573fpga.h b/src/mame/machine/k573fpga.h
index bf6b6ba7671..1b89d623d41 100644
--- a/src/mame/machine/k573fpga.h
+++ b/src/mame/machine/k573fpga.h
@@ -13,46 +13,72 @@ DECLARE_DEVICE_TYPE(KONAMI_573_DIGITAL_FPGA, k573fpga_device)
class k573fpga_device : public device_t
{
public:
- k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
+ k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ template <typename... T> void add_route(T &&... args) { subdevice<mas3507d_device>("mpeg")->add_route(std::forward<T>(args)...); }
+ template <typename T> void set_ram(T &&tag) { ram.set_tag(std::forward<T>(tag)); }
void set_ddrsbm_fpga(bool flag) { use_ddrsbm_fpga = flag; }
- void set_ram(u16 *v) { ram = v; }
- u16 get_decrypted();
+ uint16_t get_decrypted();
+
+ void set_crypto_key1(uint16_t v) { crypto_key1 = v; }
+ void set_crypto_key2(uint16_t v) { crypto_key2 = v; }
+ void set_crypto_key3(uint8_t v) { crypto_key3 = v; }
+
+ uint32_t get_mp3_start_addr() { return mp3_start_addr; }
+ void set_mp3_start_addr(uint32_t v) { mp3_start_addr = v; }
- void set_crypto_key1(u16 v) { crypto_key1 = v; }
- void set_crypto_key2(u16 v) { crypto_key2 = v; }
- void set_crypto_key3(u8 v) { crypto_key3 = v; }
+ uint32_t get_mp3_end_addr() { return mp3_end_addr; }
+ void set_mp3_end_addr(uint32_t v) { mp3_end_addr = v; }
- uint32_t get_mp3_cur_adr() { return mp3_cur_adr; }
- void set_mp3_cur_adr(u32 v) { mp3_cur_adr = v; }
+ uint16_t mas_i2c_r();
+ void mas_i2c_w(uint16_t data);
- uint32_t get_mp3_end_adr() { return mp3_end_adr; }
- void set_mp3_end_adr(u32 v) { mp3_end_adr = v; }
+ uint16_t get_fpga_ctrl();
+ void set_mpeg_ctrl(uint16_t data);
- u16 i2c_read();
- void i2c_write(u16 data);
+ uint16_t get_mpeg_ctrl();
- u16 get_mpeg_ctrl();
- void set_mpeg_ctrl(u16 data);
+ uint32_t get_counter();
+ uint32_t get_counter_diff();
- bool is_playing() { return (mpeg_ctrl_flag & 0xe000) == 0xe000 && mp3_cur_adr < mp3_end_adr; }
+ void status_update();
+ void reset_counter();
protected:
virtual void device_start() override;
virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
private:
- u16 *ram;
+ uint16_t decrypt_default(uint16_t data);
+ uint16_t decrypt_ddrsbm(uint16_t data);
+
+ bool is_mp3_playing();
+ bool is_streaming();
+
+ enum {
+ PLAYBACK_STATE_UNKNOWN = 0x8000,
+ PLAYBACK_STATE_ERROR = 0xa000, // Error?
+ PLAYBACK_STATE_IDLE = 0xb000, // Not playing
+ PLAYBACK_STATE_BUFFER_FULL = 0xc000, // Playing, demand pin = 0?
+ PLAYBACK_STATE_DEMAND_BUFFER = 0xd000 // Playing, demand pin = 1?
+ };
+
+ required_shared_ptr<uint16_t> ram;
+ required_device<mas3507d_device> mas3507d;
- u16 crypto_key1, crypto_key2;
- u8 crypto_key3;
+ uint16_t crypto_key1, crypto_key2;
+ uint8_t crypto_key3;
- u32 mp3_cur_adr, mp3_end_adr, mpeg_ctrl_flag;
+ uint32_t mp3_start_addr, mp3_cur_addr, mp3_end_addr;
bool use_ddrsbm_fpga;
- u16 decrypt_default(u16 data);
- u16 decrypt_ddrsbm(u16 data);
+ bool is_stream_active, is_timer_active;
+ uint32_t counter_previous, counter_offset;
+ int32_t counter_current;
+ uint32_t last_playback_status;
};
#endif // MAME_MACHINE_K573FPGA_H