ref: b99e78af51ae0964fdb8f0fc2795c20e2f6c237c
dir: /src/mp3.c/
/*
* MP3 support for SoX
*
* Uses libmad for MP3 decoding
* and libmp3lame for MP3 encoding
*
* Written by Fabrizio Gennari <fabrizio.ge@tiscali.it>
*
* The decoding part is based on the decoder-tutorial program madlld
* written by Bertrand Petit <madlld@phoe.fmug.org>,
*/
#include "st_i.h"
#include <string.h>
#if defined(HAVE_LIBMAD) || defined(HAVE_LAME)
#if defined(HAVE_LIBMAD)
#include <mad.h>
#endif
#if defined(HAVE_LAME)
#include <lame/lame.h>
#endif
#ifndef MIN
#define MIN(s1,s2) ((s1)<(s2)?(s1):(s2))
#endif
#define INPUT_BUFFER_SIZE (5*ST_BUFSIZ)
/* Private data */
struct mp3priv {
#if defined(HAVE_LIBMAD)
struct mad_stream *Stream;
struct mad_frame *Frame;
struct mad_synth *Synth;
mad_timer_t *Timer;
unsigned char *InputBuffer;
st_ssize_t cursamp;
unsigned long FrameCount;
int eof;
#endif /*HAVE_LIBMAD*/
#if defined(HAVE_LAME)
lame_global_flags *gfp;
#endif /*HAVE_LAME*/
};
#if defined(HAVE_LIBMAD)
/* This function merges the functions tagtype() and id3_tag_query()
from MAD's libid3tag, so we don't have to link to it
Returns 0 if the frame is not an ID3 tag, tag length if it is */
#define ID3_TAG_FLAG_FOOTERPRESENT 0x10
int tagtype(const unsigned char *data, int length)
{
if (length >= 3 && data[0] == 'T' && data[1] == 'A' && data[2] == 'G')
return 128; /* ID3V1 */
if (length >= 10 &&
(data[0] == 'I' && data[1] == 'D' && data[2] == '3') &&
data[3] < 0xff && data[4] < 0xff &&
data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
{ /* ID3V2 */
unsigned char flags;
unsigned int size;
flags = data[5];
size = (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9];
if (flags & ID3_TAG_FLAG_FOOTERPRESENT)
size += 10;
return 10 + size;
}
return 0;
}
int st_mp3startread(ft_t ft)
{
struct mp3priv *p = (struct mp3priv *) ft->priv;
size_t ReadSize;
p->Stream=malloc(sizeof(struct mad_stream));
if (p->Stream == NULL){
st_fail_errno(ft, ST_ENOMEM, "Could not allocate memory");
return ST_EOF;
}
p->Frame=malloc(sizeof(struct mad_frame));
if (p->Frame == NULL){
st_fail_errno(ft, ST_ENOMEM, "Could not allocate memory");
free(p->Stream);
return ST_EOF;
}
p->Synth=malloc(sizeof(struct mad_synth));
if (p->Synth == NULL){
st_fail_errno(ft, ST_ENOMEM, "Could not allocate memory");
free(p->Stream);
free(p->Frame);
return ST_EOF;
}
p->Timer=malloc(sizeof(mad_timer_t));
if (p->Timer == NULL){
st_fail_errno(ft, ST_ENOMEM, "Could not allocate memory");
free(p->Stream);
free(p->Frame);
free(p->Synth);
return ST_EOF;
}
p->InputBuffer=malloc(INPUT_BUFFER_SIZE);
if (p->InputBuffer == NULL){
st_fail_errno(ft, ST_ENOMEM, "Could not allocate memory");
free(p->Stream);
free(p->Frame);
free(p->Synth);
free(p->Timer);
return ST_EOF;
}
mad_stream_init(p->Stream);
mad_frame_init(p->Frame);
mad_synth_init(p->Synth);
mad_timer_reset(p->Timer);
ft->info.encoding = ST_ENCODING_MP3;
ft->info.size = ST_SIZE_DWORD;
/* We need to decode the first frame,
* so we know the output format */
ReadSize=fread(p->InputBuffer,1,INPUT_BUFFER_SIZE,ft->fp);
if(ReadSize<=0)
{
if(ferror(ft->fp))
st_fail_errno(ft,ST_EOF,"read error on bitstream");
if(feof(ft->fp))
st_fail_errno(ft,ST_EOF,"end of input stream");
return(ST_EOF);
}
mad_stream_buffer(p->Stream,p->InputBuffer,ReadSize);
p->Stream->error = 0;
while(mad_frame_decode(p->Frame,p->Stream)) {
int tagsize;
if ((p->Stream->bufend - p->Stream->this_frame) < (INPUT_BUFFER_SIZE - ST_BUFSIZ)){
/* we assume that, if the first frame fails, the file is not an MP3 file */
st_fail_errno(ft,ST_EOF,"The file is not an MP3 file or it is corrupted");
return ST_EOF;
}
/* Walk threw the stream one byte at a time (tagsize=1)
* until we find a valid frame. Previous if() will
* abort once we got a certain distance.
*/
if ((tagsize=tagtype(p->Stream->this_frame, p->Stream->bufend - p->Stream->this_frame)) == 0)
tagsize = 1; /* Walk through the stream. */
mad_stream_skip(p->Stream, tagsize);
}
switch(p->Frame->header.mode)
{
case MAD_MODE_SINGLE_CHANNEL:
case MAD_MODE_DUAL_CHANNEL:
case MAD_MODE_JOINT_STEREO:
case MAD_MODE_STEREO:
ft->info.channels = MAD_NCHANNELS(&p->Frame->header);
break;
default:
st_fail_errno(ft,ST_EFMT,"Cannot determine number of channels");
return ST_EOF;
}
p->FrameCount=1;
ft->info.rate=p->Frame->header.samplerate;
mad_timer_add(p->Timer,p->Frame->header.duration);
mad_synth_frame(p->Synth,p->Frame);
p->cursamp = 0;
p->eof = 0;
return ST_SUCCESS;
}
/*
* Read up to len samples from p->Synth
* If needed, read some more MP3 data, decode them and synth them
* Place in buf[].
* Return number of samples read.
*/
st_ssize_t st_mp3read(ft_t ft, st_sample_t *buf, st_ssize_t len)
{
struct mp3priv *p = (struct mp3priv *) ft->priv;
st_ssize_t donow,i,done=0;
mad_fixed_t sample;
int chan;
do{
donow=MIN(len,(p->Synth->pcm.length - p->cursamp)*ft->info.channels);
i=0;
while(i<donow){
for(chan=0;chan<ft->info.channels;chan++){
sample=p->Synth->pcm.samples[chan][p->cursamp];
if (sample < -MAD_F_ONE)
sample=-MAD_F_ONE;
else if (sample >= MAD_F_ONE)
sample=MAD_F_ONE-1;
*buf++=(st_sample_t)(sample<<(32-1-MAD_F_FRACBITS));
i++;
}
p->cursamp++;
};
len-=donow;
done+=donow;
if (len==0 || p->eof) break;
/* check whether input buffer needs a refill */
if(p->Stream->error==MAD_ERROR_BUFLEN)
{
size_t ReadSize, Remaining;
/* libmad does not consume all the buffer it's given. Some
* datas, part of a truncated frame, is left unused at the
* end of the buffer. Those datas must be put back at the
* beginning of the buffer and taken in account for
* refilling the buffer. This means that the input buffer
* must be large enough to hold a complete frame at the
* highest observable bit-rate (currently 448 kb/s). XXX=XXX
* Is 2016 bytes the size of the largest frame?
* (448000*(1152/32000))/8
*/
Remaining=p->Stream->bufend - p->Stream->next_frame;
memmove(p->InputBuffer,p->Stream->next_frame,Remaining);
ReadSize=fread(p->InputBuffer+Remaining,1,INPUT_BUFFER_SIZE-Remaining,ft->fp);
if(ReadSize == 0){
p->eof=1;
memset(p->InputBuffer+Remaining,0,MAD_BUFFER_GUARD);
ReadSize=MAD_BUFFER_GUARD;
}
mad_stream_buffer(p->Stream,p->InputBuffer,ReadSize+Remaining);
p->Stream->error=0;
}
if(mad_frame_decode(p->Frame,p->Stream)){
if(MAD_RECOVERABLE(p->Stream->error))
{
int tagsize;
if ( (tagsize=tagtype(p->Stream->this_frame, p->Stream->bufend - p->Stream->this_frame)) == 0){
if (!p->eof)
st_warn("recoverable frame level error (%s).\n",
mad_stream_errorstr(p->Stream));
}
else mad_stream_skip(p->Stream,tagsize);
continue;
}
else
{
if(p->Stream->error==MAD_ERROR_BUFLEN)
continue;
else
{
st_warn("unrecoverable frame level error (%s).\n",
mad_stream_errorstr(p->Stream));
return done;
}
}
}
p->FrameCount++;
mad_timer_add(p->Timer,p->Frame->header.duration);
mad_synth_frame(p->Synth,p->Frame);
p->cursamp=0;
}while(1);
return done;
}
int st_mp3stopread(ft_t ft)
{
struct mp3priv *p=(struct mp3priv*) ft->priv;
mad_synth_finish(p->Synth);
mad_frame_finish(p->Frame);
mad_stream_finish(p->Stream);
free(p->Stream);
free(p->Frame);
free(p->Synth);
free(p->Timer);
free(p->InputBuffer);
return ST_SUCCESS;
}
#else /*HAVE_LIBMAD*/
int st_mp3startread(ft_t ft)
{
st_fail_errno(ft,ST_EOF,"SoX was compiled without MP3 decoding support");
return ST_EOF;
}
st_ssize_t st_mp3read(ft_t ft, st_sample_t *buf, st_ssize_t samp)
{
st_fail_errno(ft,ST_EOF,"SoX was compiled without MP3 decoding support");
return ST_EOF;
}
int st_mp3stopread(ft_t ft)
{
st_fail_errno(ft,ST_EOF,"SoX was compiled without MP3 decoding support");
return ST_EOF;
}
#endif /*HAVE_LIBMAD*/
#if defined (HAVE_LAME)
void null_error_func(const char* string, va_list va){
return;
}
int st_mp3startwrite(ft_t ft)
{
struct mp3priv *p = (struct mp3priv *) ft->priv;
if (ft->info.encoding != ST_ENCODING_MP3){
if(ft->info.encoding != -1)
st_report("Encoding forced to MP3");
ft->info.encoding = ST_ENCODING_MP3;
}
p->gfp = lame_init();
if (p->gfp == NULL){
st_fail_errno(ft,ST_EOF,"Initialization of LAME library failed");
return(ST_EOF);
}
if (ft->info.channels != -1){
if ( (lame_set_num_channels(p->gfp,ft->info.channels)) < 0) {
st_fail_errno(ft,ST_EOF,"Unsupported number of channels");
return(ST_EOF);
}
}
else
ft->info.channels = lame_get_num_channels(p->gfp); /* LAME default */
lame_set_in_samplerate(p->gfp,ft->info.rate);
lame_set_bWriteVbrTag(p->gfp, 0); /* disable writing VBR tag */
/* The bitrate, mode, quality and other settings are the default ones,
since SoX's command line options do not allow to set them */
if (lame_init_params(p->gfp) < 0){
st_fail_errno(ft,ST_EOF,"LAME initialization failed");
return(ST_EOF);
}
lame_set_errorf(p->gfp,null_error_func);
lame_set_debugf(p->gfp,null_error_func);
lame_set_msgf (p->gfp,null_error_func);
return(ST_SUCCESS);
}
st_ssize_t st_mp3write(ft_t ft, st_sample_t *buf, st_ssize_t samp)
{
struct mp3priv *p = (struct mp3priv *) ft->priv;
char *mp3buffer;
int mp3buffer_size;
long *buffer_l, *buffer_r;
int nsamples = samp/ft->info.channels;
int i,j;
st_ssize_t done = 0;
int written;
if ( (buffer_r=(long*)malloc(nsamples*sizeof(long))) == NULL){
st_fail_errno(ft,ST_ENOMEM,"Memory allocation failed");
goto end4;
}
if (ft->info.channels==2){ /* Why isn't there a lame_encode_buffer_long_interleaved? */
if ( (buffer_l=(long*)malloc(nsamples*sizeof(long))) == NULL){
st_fail_errno(ft,ST_ENOMEM,"Memory allocation failed");
goto end3;
}
j=0;
for (i=0;i<nsamples;i++){
buffer_l[i]=(long)buf[j++]; /* Should we paranoically check whether long is actually 32 bits? */
buffer_r[i]=(long)buf[j++];
}
}
else{
buffer_l=(long*)buf;
memset(buffer_r,0,nsamples*sizeof(long));
}
mp3buffer_size=1.25*nsamples + 7200;
if ( (mp3buffer=malloc(mp3buffer_size)) == NULL){
st_fail_errno(ft,ST_ENOMEM,"Memory allocation failed");
goto end2;
}
if ( (written = lame_encode_buffer_long2(p->gfp,
buffer_l,
buffer_r,
nsamples,
mp3buffer,
mp3buffer_size)) < 0){
st_fail_errno(ft,ST_EOF,"Encoding failed");
goto end;
}
if (fwrite(mp3buffer, 1, written, ft->fp) < written){
st_fail_errno(ft,ST_EOF,"File write failed");
goto end;
}
done = nsamples;
end:
free(mp3buffer);
end2:
if (ft->info.channels == 2)
free(buffer_l);
end3:
free(buffer_r);
end4:
return done;
}
int st_mp3stopwrite(ft_t ft)
{
struct mp3priv *p = (struct mp3priv *) ft->priv;
char mp3buffer[7200];
int written;
if ( (written=lame_encode_flush(p->gfp, mp3buffer, 7200)) <0){
st_fail_errno(ft,ST_EOF,"Encoding failed");
}
else if (fwrite(mp3buffer, 1, written, ft->fp) < written){
st_fail_errno(ft,ST_EOF,"File write failed");
}
lame_close(p->gfp);
return ST_SUCCESS;
}
#else /* HAVE_LAME */
int st_mp3startwrite(ft_t ft)
{
st_fail_errno(ft,ST_EOF,"Sorry, no MP3 encoding support");
return ST_EOF;
}
st_ssize_t st_mp3write(ft_t ft, st_sample_t *buf, st_ssize_t samp)
{
st_fail_errno(ft,ST_EOF,"Sorry, no MP3 encoding support");
return ST_EOF;
}
int st_mp3stopwrite(ft_t ft)
{
st_fail_errno(ft,ST_EOF,"Sorry, no MP3 encoding support");
return ST_EOF;
}
#endif /* HAVE_LAME */
#endif