diff options
-rw-r--r-- | 3rdparty/minimp3/minimp3.h | 206 | ||||
-rw-r--r-- | 3rdparty/minimp3/minimp3_ex.h | 1224 | ||||
-rw-r--r-- | src/devices/sound/mas3507d.cpp | 301 | ||||
-rw-r--r-- | src/devices/sound/mas3507d.h | 78 | ||||
-rw-r--r-- | src/emu/devfind.h | 4 | ||||
-rw-r--r-- | src/mame/machine/k573dio.cpp | 127 | ||||
-rw-r--r-- | src/mame/machine/k573dio.h | 15 | ||||
-rw-r--r-- | src/mame/machine/k573fpga.cpp | 186 | ||||
-rw-r--r-- | src/mame/machine/k573fpga.h | 68 |
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 |