mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 04:00:42 +00:00
296 lines
13 KiB
C
296 lines
13 KiB
C
|
/*
|
||
|
* 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];
|
||
|
}
|
||
|
|