X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=platform%2Fcommon%2Fmp3.c;h=9b347e8940a8e719fc6a84ef8fa02bb62ff47b01;hb=fc11dd059b5c7369a51ddcca532505b588bd0030;hp=1a240eb75f61923f8c831163a76757f5677175cf;hpb=90f0dedf83b5eac80fc6b7aeed36c3429ce752f4;p=picodrive.git diff --git a/platform/common/mp3.c b/platform/common/mp3.c index 1a240eb..9b347e8 100644 --- a/platform/common/mp3.c +++ b/platform/common/mp3.c @@ -1,12 +1,26 @@ /* * PicoDrive - * (C) notaz, 2010 + * (C) notaz, 2010,2013 * * This work is licensed under the terms of MAME license. * See COPYING file in the top-level directory. */ +#include +#include + +#include +#include #include "mp3.h" +static FILE *mp3_current_file; +static int mp3_file_len, mp3_file_pos; +static int cdda_out_pos; +static int decoder_active; + +unsigned short mpeg1_l3_bitrates[16] = { + 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 +}; + int mp3_find_sync_word(const unsigned char *buf, int size) { const unsigned char *p, *pe; @@ -34,3 +48,152 @@ int mp3_find_sync_word(const unsigned char *buf, int size) return -1; } +static int try_get_bitrate(unsigned char *buf, int buf_size) +{ + int offs1, offs = 0; + int ret; + + while (1) + { + offs1 = mp3_find_sync_word(buf + offs, buf_size - offs); + if (offs1 < 0) + return -2; + offs += offs1; + if (buf_size - offs < 4) + return -3; + + // printf("trying header %08x\n", *(int *)(buf + offs)); + + ret = mpeg1_l3_bitrates[buf[offs + 2] >> 4]; + if (ret > 0) + return ret; + } + + return -2; +} + +int mp3_get_bitrate(void *f_, int len) +{ + unsigned char buf[2048]; + FILE *f = f_; + int retval = -1; + int ret; + + memset(buf, 0, sizeof(buf)); + + fseek(f, 0, SEEK_SET); + ret = fread(buf, 1, sizeof(buf), f); + if (ret != sizeof(buf)) + goto out; + + ret = try_get_bitrate(buf, sizeof(buf)); + if (ret <= 0) { + // try to read somewhere around the middle + fseek(f, len / 2, SEEK_SET); + fread(buf, 1, sizeof(buf), f); + ret = try_get_bitrate(buf, sizeof(buf)); + } + if (ret > 0) + retval = ret; + + //printf("bitrate: %i\n", retval); + +out: + fseek(f, 0, SEEK_SET); + return retval; +} + +void mp3_start_play(void *f_, int pos) +{ + unsigned char buf[2048]; + FILE *f = f_; + int ret; + + mp3_file_len = mp3_file_pos = 0; + mp3_current_file = NULL; + cdda_out_pos = 0; + decoder_active = 0; + + if (!(PicoOpt & POPT_EN_MCD_CDDA) || f == NULL) // cdda disabled or no file? + return; + + ret = mp3dec_start(); + if (ret != 0) + return; + + decoder_active = 1; + + mp3_current_file = f; + fseek(f, 0, SEEK_END); + mp3_file_len = ftell(f); + + // search for first sync word, skipping stuff like ID3 tags + while (mp3_file_pos < 128*1024) { + int offs, bytes; + + fseek(f, mp3_file_pos, SEEK_SET); + bytes = fread(buf, 1, sizeof(buf), f); + if (bytes < 4) + break; + offs = mp3_find_sync_word(buf, bytes); + if (offs >= 0) { + mp3_file_pos += offs; + break; + } + mp3_file_pos += bytes - 3; + } + + // seek.. + if (pos) { + unsigned long long pos64 = mp3_file_len - mp3_file_pos; + pos64 *= pos; + mp3_file_pos += pos64 >> 10; + } + + mp3dec_decode(mp3_current_file, &mp3_file_pos, mp3_file_len); +} + +void mp3_update(int *buffer, int length, int stereo) +{ + int length_mp3, shr = 0; + void (*mix_samples)(int *dest_buf, short *mp3_buf, int count) = mix_16h_to_32; + + if (mp3_current_file == NULL || mp3_file_pos >= mp3_file_len) + return; /* no file / EOF */ + + if (!decoder_active) + return; + + length_mp3 = length; + if (PsndRate <= 11025 + 100) { + mix_samples = mix_16h_to_32_s2; + length_mp3 <<= 2; shr = 2; + } + else if (PsndRate <= 22050 + 100) { + mix_samples = mix_16h_to_32_s1; + length_mp3 <<= 1; shr = 1; + } + + if (1152 - cdda_out_pos >= length_mp3) { + mix_samples(buffer, cdda_out_buffer + cdda_out_pos * 2, + length * 2); + + cdda_out_pos += length_mp3; + } else { + int ret, left = 1152 - cdda_out_pos; + + mix_samples(buffer, cdda_out_buffer + cdda_out_pos * 2, + (left >> shr) * 2); + + ret = mp3dec_decode(mp3_current_file, &mp3_file_pos, + mp3_file_len); + if (ret == 0) { + cdda_out_pos = length_mp3 - left; + mix_samples(buffer + (left >> shr) * 2, + cdda_out_buffer, + (cdda_out_pos >> shr) * 2); + } else + cdda_out_pos = 0; + } +} +