raze-gles/polymer/jfaud/mpadec/mp3dec.c
terminx 7b0104e9a1 JFAud
git-svn-id: https://svn.eduke32.com/eduke32@452 1a8010ca-5511-0410-912e-c29ae57300e0
2007-01-12 22:42:19 +00:00

295 lines
13 KiB
C
Executable file

/*
* mpadec - MPEG audio decoder
* Copyright (C) 2002-2004 Dmitriy Startsev (dstartsev@rambler.ru)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* $Id: mp3dec.c,v 1.5 2004/08/03 05:22:22 metal_man Exp $ */
#include "mp3dec_internal.h"
mp3dec_t MPADECAPI mp3dec_init(void)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)malloc(sizeof(struct mp3dec_t));
if (!mp3) return NULL;
memset(mp3, 0, sizeof(struct mp3dec_t));
mp3->size = sizeof(struct mp3dec_t);
mp3->fd = -1;
mp3->mpadec = mpadec_init();
if (!mp3->mpadec) {
free(mp3);
return NULL;
}
return mp3;
}
int MPADECAPI mp3dec_init_file(mp3dec_t mp3dec, int fd, int64_t length, int nogap)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
int64_t tmp; int r;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
if (fd < 0) {
mp3dec_reset(mp3);
return MP3DEC_RETCODE_INVALID_PARAMETERS;
}
if (mp3->flags & MP3DEC_FLAG_INITIALIZED) close(mp3->fd);
mp3->fd = fd;
mp3->flags = MP3DEC_FLAG_SEEKABLE;
mp3->stream_offset = mp3->stream_size = mp3->stream_position = 0;
mp3->in_buffer_offset = mp3->in_buffer_used = 0;
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
tmp = lseek(fd, 0, SEEK_CUR);
if (tmp >= 0) mp3->stream_offset = tmp; else mp3->flags &= ~MP3DEC_FLAG_SEEKABLE;
if (mp3->flags & MP3DEC_FLAG_SEEKABLE) {
tmp = lseek(fd, 0, SEEK_END);
if (tmp >= 0) {
mp3->stream_size = tmp;
lseek(fd, mp3->stream_offset, SEEK_SET);
} else mp3->flags &= ~MP3DEC_FLAG_SEEKABLE;
}
if (mp3->stream_size > mp3->stream_offset) {
mp3->stream_size -= mp3->stream_offset;
if (length && (length < mp3->stream_size)) mp3->stream_size = length;
} else mp3->stream_size = length;
r = read(fd, mp3->in_buffer, 4);
if (r < 4) {
mp3dec_reset(mp3);
return ((r < 0) ? MP3DEC_RETCODE_INVALID_PARAMETERS : MP3DEC_RETCODE_NOT_MPEG_STREAM);
} else mp3->in_buffer_used = r;
if (mp3->flags & MP3DEC_FLAG_SEEKABLE) tmp = lseek(fd, mp3->stream_offset, SEEK_SET); else tmp = -1;
if (tmp < 0) {
int32_t n = sizeof(mp3->in_buffer) - mp3->in_buffer_used;
mp3->flags &= ~MP3DEC_FLAG_SEEKABLE;
if (mp3->stream_size && (n > (mp3->stream_size - mp3->in_buffer_used))) n = (int32_t)(mp3->stream_size - mp3->in_buffer_used);
n = read(fd, mp3->in_buffer + mp3->in_buffer_used, n);
if (n < 0) n = 0;
mp3->in_buffer_used += n;
mp3->stream_position = mp3->in_buffer_used;
} else {
int32_t n = sizeof(mp3->in_buffer);
if (mp3->stream_size && (n > mp3->stream_size)) n = (int32_t)mp3->stream_size;
n = read(fd, mp3->in_buffer, n);
if (n < 0) n = 0;
mp3->stream_position = mp3->in_buffer_used = n;
}
if (mp3->in_buffer_used < 4) {
mp3dec_reset(mp3);
return MP3DEC_RETCODE_NOT_MPEG_STREAM;
}
if (nogap) {
mpadec_decode(mp3->mpadec, mp3->in_buffer, mp3->in_buffer_used, mp3->out_buffer, sizeof(mp3->out_buffer), &mp3->in_buffer_offset, &mp3->out_buffer_used);
mp3->in_buffer_used -= mp3->in_buffer_offset;
if (!mp3->out_buffer_used) {
mpadec_reset(mp3->mpadec);
mp3->in_buffer_used += mp3->in_buffer_offset;
mp3->in_buffer_offset = 0;
}
} else mpadec_reset(mp3->mpadec);
if (!mp3->out_buffer_used) {
r = mpadec_decode(mp3->mpadec, mp3->in_buffer, mp3->in_buffer_used, NULL, 0, &mp3->in_buffer_offset, NULL);
mp3->in_buffer_used -= mp3->in_buffer_offset;
if (r != MPADEC_RETCODE_OK) {
mp3dec_reset(mp3);
return MP3DEC_RETCODE_NOT_MPEG_STREAM;
}
}
if ((mpadec_get_info(mp3->mpadec, &mp3->mpainfo, MPADEC_INFO_STREAM) != MPADEC_RETCODE_OK) ||
(mpadec_get_info(mp3->mpadec, &mp3->taginfo, MPADEC_INFO_TAG) != MPADEC_RETCODE_OK)) {
mp3dec_reset(mp3);
return MP3DEC_RETCODE_NOT_MPEG_STREAM;
}
if (mp3->taginfo.flags & 2) if (!mp3->stream_size || (mp3->stream_size > mp3->taginfo.bytes)) mp3->stream_size = mp3->taginfo.bytes;
if (mp3->taginfo.flags & 1) {
mp3->mpainfo.frames = mp3->taginfo.frames;
if (mp3->mpainfo.frames && mp3->mpainfo.frame_samples) {
mp3->mpainfo.bitrate = (int32_t)((FLOAT)(((FLOAT)mp3->stream_size*(FLOAT)mp3->mpainfo.frequency + 0.5)/((FLOAT)125.0*mp3->mpainfo.frame_samples*mp3->mpainfo.frames)));
}
} else if (mp3->mpainfo.bitrate && mp3->mpainfo.frame_samples) {
mp3->mpainfo.frames = (int32_t)((FLOAT)(((FLOAT)mp3->stream_size*(FLOAT)mp3->mpainfo.frequency + 0.5)/((FLOAT)125.0*mp3->mpainfo.frame_samples*mp3->mpainfo.bitrate)));
}
mp3->mpainfo.duration = (mp3->mpainfo.frames*mp3->mpainfo.frame_samples + (mp3->mpainfo.frequency >> 1))/mp3->mpainfo.frequency;
mp3->flags |= MP3DEC_FLAG_INITIALIZED;
return MP3DEC_RETCODE_OK;
}
int MPADECAPI mp3dec_uninit(mp3dec_t mp3dec)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
if (mp3->flags & MP3DEC_FLAG_INITIALIZED) close(mp3->fd);
mp3->fd = -1;
mp3->flags = 0;
mpadec_uninit(mp3->mpadec);
mp3->size = 0;
return MP3DEC_RETCODE_OK;
}
int MPADECAPI mp3dec_reset(mp3dec_t mp3dec)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
if (mp3->flags & MP3DEC_FLAG_INITIALIZED) close(mp3->fd);
mp3->fd = -1;
mp3->flags = 0;
mpadec_reset(mp3->mpadec);
mp3->stream_offset = mp3->stream_size = mp3->stream_position = 0;
mp3->in_buffer_offset = mp3->in_buffer_used = 0;
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
memset(&mp3->mpainfo, 0, sizeof(mp3->mpainfo));
memset(&mp3->taginfo, 0, sizeof(mp3->taginfo));
return MP3DEC_RETCODE_OK;
}
int MPADECAPI mp3dec_configure(mp3dec_t mp3dec, mpadec_config_t *cfg)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
if (mpadec_configure(mp3->mpadec, cfg) != MPADEC_RETCODE_OK) return MP3DEC_RETCODE_INVALID_PARAMETERS;
return MP3DEC_RETCODE_OK;
}
int MPADECAPI mp3dec_get_info(mp3dec_t mp3dec, void *info, int info_type)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
if (!info) return MP3DEC_RETCODE_INVALID_PARAMETERS;
if (mp3->flags & MP3DEC_FLAG_INITIALIZED) {
switch (info_type) {
case MPADEC_INFO_STREAM: memcpy(info, &mp3->mpainfo, sizeof(mp3->mpainfo)); break;
case MPADEC_INFO_TAG: memcpy(info, &mp3->taginfo, sizeof(mp3->taginfo)); break;
case MPADEC_INFO_CONFIG:
default: if (mpadec_get_info(mp3->mpadec, info, info_type) != MPADEC_RETCODE_OK) return MP3DEC_RETCODE_INVALID_PARAMETERS;
}
} else return MP3DEC_RETCODE_BAD_STATE;
return MP3DEC_RETCODE_OK;
}
int MPADECAPI mp3dec_decode(mp3dec_t mp3dec, uint8_t *buf, uint32_t bufsize, uint32_t *used)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
uint32_t n, src_used, dst_used; int r;
if (used) *used = 0;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
if (!(mp3->flags & MP3DEC_FLAG_INITIALIZED)) return MP3DEC_RETCODE_BAD_STATE;
if (!buf) return MP3DEC_RETCODE_INVALID_PARAMETERS;
while (bufsize) {
if (mp3->out_buffer_used) {
n = (bufsize < mp3->out_buffer_used) ? bufsize : mp3->out_buffer_used;
memcpy(buf, mp3->out_buffer + mp3->out_buffer_offset, n);
mp3->out_buffer_offset += n;
mp3->out_buffer_used -= n;
buf += n;
bufsize -= n;
if (used) *used += n;
}
if (!bufsize) break;
if (mp3->in_buffer_used > 4) {
r = mpadec_decode(mp3->mpadec, mp3->in_buffer + mp3->in_buffer_offset, mp3->in_buffer_used, buf, bufsize, &src_used, &dst_used);
mp3->in_buffer_offset += src_used;
mp3->in_buffer_used -= src_used;
buf += dst_used;
bufsize -= dst_used;
if (used) *used += dst_used;
if (!bufsize) break;
if (r == MPADEC_RETCODE_BUFFER_TOO_SMALL) {
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
mpadec_decode(mp3->mpadec, mp3->in_buffer + mp3->in_buffer_offset, mp3->in_buffer_used, mp3->out_buffer, sizeof(mp3->out_buffer), &src_used, &mp3->out_buffer_used);
mp3->in_buffer_offset += src_used;
mp3->in_buffer_used -= src_used;
continue;
}
}
if (mp3->in_buffer_used && mp3->in_buffer_offset) memmove(mp3->in_buffer, mp3->in_buffer + mp3->in_buffer_offset, mp3->in_buffer_used);
mp3->in_buffer_offset = 0;
n = sizeof(mp3->in_buffer) - mp3->in_buffer_used;
if (mp3->stream_size && (n > (mp3->stream_size - mp3->stream_position))) n = (int32_t)(mp3->stream_size - mp3->stream_position);
if (n) r = read(mp3->fd, mp3->in_buffer + mp3->in_buffer_used, n); else r = 0;
if (r < 0) r = 0;
mp3->in_buffer_used += r;
mp3->stream_position += r;
if (mp3->stream_position > mp3->stream_size) mp3->stream_position = mp3->stream_size;
if (!r) break;
}
return MP3DEC_RETCODE_OK;
}
int MPADECAPI mp3dec_seek(mp3dec_t mp3dec, int64_t pos, int units)
{
register struct mp3dec_t *mp3 = (struct mp3dec_t *)mp3dec;
int64_t newpos;
if (!mp3 || (mp3->size != sizeof(struct mp3dec_t)) || !mp3->mpadec) return MP3DEC_RETCODE_INVALID_HANDLE;
if (!(mp3->flags & MP3DEC_FLAG_INITIALIZED)) return MP3DEC_RETCODE_BAD_STATE;
if (!(mp3->flags & MP3DEC_FLAG_SEEKABLE)) return MP3DEC_RETCODE_SEEK_FAILED;
if (units == MP3DEC_SEEK_BYTES) {
newpos = (pos < mp3->stream_size) ? pos : mp3->stream_size;
newpos = lseek(mp3->fd, mp3->stream_offset + newpos, SEEK_SET);
if (newpos < 0) return MP3DEC_RETCODE_SEEK_FAILED;
mp3->stream_position = newpos - mp3->stream_offset;
mp3->in_buffer_offset = mp3->in_buffer_used = 0;
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
} else if (units == MP3DEC_SEEK_SAMPLES) {
FLOAT fsize = (FLOAT)(125.0*mp3->mpainfo.bitrate*mp3->mpainfo.decoded_frame_samples)/(FLOAT)mp3->mpainfo.decoded_frequency;
newpos = (int64_t)((FLOAT)pos*fsize/(FLOAT)mp3->mpainfo.decoded_frame_samples);
if (newpos > mp3->stream_size) newpos = mp3->stream_size;
pos = (pos%mp3->mpainfo.decoded_frame_samples)*mp3->mpainfo.decoded_sample_size;
newpos = lseek(mp3->fd, mp3->stream_offset + newpos, SEEK_SET);
if (newpos < 0) return MP3DEC_RETCODE_SEEK_FAILED;
mp3->stream_position = newpos - mp3->stream_offset;
mp3->in_buffer_offset = mp3->in_buffer_used = 0;
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
{
uint8_t temp[8*1152];
mp3dec_decode(mp3, temp, (uint32_t)pos, NULL);
}
} else if (units == MP3DEC_SEEK_SECONDS) {
if (pos > mp3->mpainfo.duration) pos = mp3->mpainfo.duration;
if (mp3->taginfo.flags & 4) {
int32_t n = (int32_t)((100*pos + (mp3->mpainfo.duration >> 1))/mp3->mpainfo.duration);
if (n > 99) newpos = mp3->stream_size;
else newpos = (mp3->taginfo.toc[n]*mp3->stream_size)/255;
} else newpos = (pos*mp3->stream_size + (mp3->mpainfo.duration >> 1))/mp3->mpainfo.duration;
if (newpos > mp3->stream_size) newpos = mp3->stream_size;
newpos = lseek(mp3->fd, mp3->stream_offset + newpos, SEEK_SET);
if (newpos < 0) return MP3DEC_RETCODE_SEEK_FAILED;
mp3->stream_position = newpos - mp3->stream_offset;
mp3->in_buffer_offset = mp3->in_buffer_used = 0;
mp3->out_buffer_offset = mp3->out_buffer_used = 0;
} else return MP3DEC_RETCODE_INVALID_PARAMETERS;
return MP3DEC_RETCODE_OK;
}
char * MPADECAPI mp3dec_error(int code)
{
static char *mp3_errors[] = { "No error",
"Invalid handle",
"Bad decoder state",
"Invalid parameters",
"Not an MPEG audio stream",
"Seek failed",
"Unknown error" };
if (code > MP3DEC_RETCODE_UNKNOWN) code = MP3DEC_RETCODE_UNKNOWN;
return mp3_errors[code];
}