/* * 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]; }