您的位置:首页 > 其它

libmad-----------------转载的几篇文章

2013-02-25 09:45 281 查看

怎么使用libmad

Category: C/C++, MP3libmad是一个开源mp3解码库,其对mp3解码算法做了很多优化,性能较好,很多播放器如mplayer、xmms等都是使用这个开源库进行解码的;如果要设计mp3播放器而又不想研究mp3解码算法的话,libmad是个不错的选择,可是问题来了:libmad配套的相关文档太少,可以说几乎没有,只有一个示例程序minimad.c,但没有一定经验的人根本不知道怎么编译这个minimad.c,就算是编译了也不知道怎么运行、怎么播放mp3;
网上讲libmad和minimad.c的文章很多,但能解释清楚的少之又少,大家都是抄来抄去,要么是不懂装懂,要么是懂了一点就自以为精通了,这样一来的结果是:在网上搜两天也弄不明白libmad究竟怎么使用。所幸手里有Altera公司的一个工程,借助对该工程的分析、minimad.c中少的可怜的注释和网上搜索的Linux音频方面的相关知识,反复思考编码,总算把libmad库用起来了,现记录一下其使用方法,在帮助别人的同时也方便自己回头查询。在开始之前,最好先把mp3文件格式和Linux音频编程方面的知识先学习一下,不然后面有的东西可能听不懂,还有就是一定要熟悉Linux系统,后面的代码都是在linux系统中用gcc编译的,在Windows下不能用的。首先看下面几个问题,这也是我一开始最迷惑的,弄明白这几个问题了,也就对libmad库的使用相当熟悉了:minimad.c怎么编译?编译后怎么运行?运行时的输入输出分别是什么,或者说运行时什么效果?
怎样播放minimad输出的数据?或者说怎么播放解码后的数据?
minimad运行时,mp3数据来源是标准输入,能不能改为从文件中读入数据?该怎么改?
minimad运行时首先要将整个mp3文件读入内存,能不能改成边解码边读入的形式,比如每次读入16K,解码完再读入16K,而又不影响播放的连贯性,这样可以节省内存开销,方便在嵌入式系统中使用;
怎样用libmad做一个简单的mp3播放器?一个一个来讲吧。minimad.c怎么编译?编译后怎么运行?运行时的输入输出分别是什么,或者说运行时什么效果?在Linux下(我前面说了,本文所有的工作都是在Linux进行)先安装libmad,说白了就是把libmad库导入C标准库,安装方法见libmad-0.15.1b中的README和INSTALL文件。安装libmad后,新建一个文件夹,将libmad-0.15.1b中的minimad.c和mad.h复制过来,用gcc编译minimad.c,编译命令为(假设要生成的可执行程序为minimad):
gcc -o minimad minimad.c -lmad
minimad程序从标准输入读入mp3文件,然后将解码后的音频数据送到标准输出,我们可以用重定向的方式从文件中读入数据并将结果写至文件,命令如下:
./minimad tmp.pcm
怎样播放minimad输出的数据?或者说怎么播放解码后的数据?假设你有Linux音频编程方面的基础的话,这个应该不成问题,如果没有也没关系,在Linux的设计理念中,一切皆是文件,音频设备也是文件,只需要打开/dev/dsp(音频设备)这个文件,然后将解码后的数据写入这个文件即可实现播放,新建pcmplay.c文件,拷入如下代码:
#include
#include
#include
#include
#include
#include

int main(int argc, char *argv[])
{
int  id, fd, i;
char buf[1024];
int  rate;      /*simple rate 44.1KHz*/
int  format;    /*quatize args*/
int  channels;  /*sound channel*/

if(argc != 2)
{
fprintf(stderr, "usage : %s \n", argv[0]);
exit(-1);
}

if((fd = open(argv[1], O_RDONLY)) < 0)
{
fprintf(stderr, "Can't open sound file!\n");
exit(-2);
}

if((id = open("/dev/dsp", O_WRONLY)) 0)
{
write(id, buf, i);
//printf("i=%d\n", i);
}

close(fd);
close(id);

exit(0);

}

编译pcmplay文件,然后就可以用生成的可执行程序播放第一步中声称的tmp.pcm文件,命令如下:
gcc -o pcmplay pcmplay.c
./minimad tmp.pcm
./pcmplay  tmp.pcm

播放时可能会变调,这是因为上面这段代码中将音频设备采样率固定设置为44.1k,而mp3文件不一定是这个采样率,解决方法后面会讲。minimad运行时,mp3数据来源是标准输入,能不能改为从文件中读入数据?该怎么改?当然可以改,而且改起来相当的简单,如果不知道怎么改只能说明自己没仔细看minimad.c,你可能不知道struct stat是什么,也不清楚mmap()函数有什么用,但这些都可以在网上查到的,查了之后稍加分析就会发现原来就是把一片数据放入一块内存并得到它的长度而已,那改成文件读入的方式也很容易,用fopen打开文件,计算一下文件的长度,然后用fread把数据全部读出来即可,这里就不贴代码了。minimad运行时首先要将整个mp3文件读入内存,能不能改成边解码边读入的形式,比如每次读入16K,解码完再读入16K,而又不影响播放的连贯性,这样可以节省内存开销,方便在嵌入式系统中使用;修改input()函数,在调用libmad中的mad_decoder_run()实现播放时,首先检查待解码缓存区中有没有数据,有则解码,没有则调用input()函数一次以填充数据(填充多少可以自己指定),然后开始解码,解码后的数据交给output()函数处理,解码过程中,一旦待解码缓存区中的解码数据不够则再次调用input()函数……在这里还要提一下struct buffer这个结构体,这个结构体是在input、output和decoder之间传送数据的载体,可以自行定义,比如我的数据来源是文件,待解码数据缓存区大小为4K,要传递的私有数据包括文件指针、当前的位置、数据缓冲区、缓冲区的实际大小、文件的总大小等,则我这里定义如下:
struct buffer {
FILE  *fp;                    /*file pointer*/
unsigned int  flen;           /*file length*/
unsigned int  fpos;           /*current position*/
unsigned char fbuf[BUFSIZE];  /*buffer*/
unsigned int  fbsize;         /*indeed size of buffer*/
};
typedef struct buffer mp3_file;

修改input()函数为如下形式,则每次调用填充BUFSIZE字节的数据:
static
enum mad_flow input(void *data,
struct mad_stream *stream)
{
mp3_file *mp3fp;
int      ret_code;
int      unproc_data_size;    /*the unprocessed data's size*/
int      copy_size;

mp3fp = (mp3_file *)data;
if(mp3fp->fpos flen)
{
unproc_data_size = stream->bufend - stream->next_frame;
memcpy(mp3fp->fbuf, mp3fp->fbuf+mp3fp->fbsize-unproc_data_size, unproc_data_size);
copy_size = BUFSIZE - unproc_data_size;
if(mp3fp->fpos + copy_size > mp3fp->flen)
{
copy_size = mp3fp->flen - mp3fp->fpos;
}
fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
mp3fp->fbsize = unproc_data_size + copy_size;
mp3fp->fpos  += copy_size;

/*Hand off the buffer to the mp3 input stream*/
mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
ret_code = MAD_FLOW_CONTINUE;
}
else
{
ret_code = MAD_FLOW_STOP;
}

return ret_code;

}

注意:在上面的代码中涉及到了断桢问题,即一桢跨了两个BUFSIZE,这时候应该将缓冲区中的剩余数据先移至缓冲区头部,然后再从文件中读出数据填充缓冲区。怎样用libmad设计一个简单的mp3播放器?修改output()函数。我在上面说过了,解码后的数据通过output()函数进行处理,在minimad.c中output()函数直接将解码后的数据送到标准输出,其实只要将这里修改为送到音频设备就可以实现播放了。还有一点需要说明的是:mp3文件的采样率不是固定不变的,解码后的数据中包括采样率,在播放过程中,一旦采样率发生变化,要重新设置一下音频设备。新建一个mp3player.c文件,然后将下面的代码复制进去,编译生成mp3player,这就是一个简单的mp3播放器了,可以用./mp3player 1.mp3命令来播放1.mp3文件。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mad.h"#define BUFSIZE 8192/*
* This is a private message structure. A generic pointer to this structure
* is passed to each of the callback functions. Put here any data you need
* to access from within the callbacks.
*/
struct buffer { FILE *fp; /*file pointer*/ unsigned int flen; /*file length*/ unsigned int fpos; /*current position*/ unsigned char fbuf[BUFSIZE]; /*buffer*/ unsigned int fbsize; /*indeed size of buffer*/ }; typedef struct buffer mp3_file;int soundfd; /*soundcard file*/
unsigned int prerate = 0; /*the pre simple rate*/int writedsp(int c)
{
return write(soundfd, (char *)&c, 1);
}void set_dsp()
{
int format = AFMT_S16_LE;
int channels = 2;soundfd = open("/dev/dsp", O_WRONLY);
ioctl(soundfd, SNDCTL_DSP_SETFMT, &format);
ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels);
}/*
* This is perhaps the simplest example use of the MAD high-level API.
* Standard input is mapped into memory via mmap(), then the high-level API
* is invoked with three callbacks: input, output, and error. The output
* callback converts MAD's high-resolution PCM samples to 16 bits, then
* writes them to standard output in little-endian, stereo-interleaved
* format.
*/static int decode(mp3_file *mp3fp);int main(int argc, char *argv[])
{
long flen, fsta, fend;
int dlen;
mp3_file *mp3fp;if (argc != 2)
return 1;mp3fp = (mp3_file *)malloc(sizeof(mp3_file));
if((mp3fp->fp = fopen(argv[1], "r")) == NULL)
{
printf("can't open source file.\n");
return 2;
}
fsta = ftell(mp3fp->fp);
fseek(mp3fp->fp, 0, SEEK_END);
fend = ftell(mp3fp->fp);
flen = fend - fsta;
if(flen fp, 0, SEEK_SET);
fread(mp3fp->fbuf, 1, BUFSIZE, mp3fp->fp);
mp3fp->fbsize = BUFSIZE;
mp3fp->fpos = BUFSIZE;
mp3fp->flen = flen;set_dsp();decode(mp3fp);close(soundfd);
fclose(mp3fp->fp);return 0;
}/*
* This is the input callback. The purpose of this callback is to (re)fill
* the stream buffer which is to be decoded. In this example, an entire file
* has been mapped into memory, so we just call mad_stream_buffer() with the
* address and length of the mapping. When this callback is called a second
* time, we are finished decoding.
*/static enum mad_flow input(void *data, struct mad_stream *stream) { mp3_file *mp3fp; int ret_code; int unproc_data_size; /*the unprocessed data's size*/ int copy_size; mp3fp = (mp3_file *)data; if(mp3fp->fpos flen) { unproc_data_size = stream->bufend - stream->next_frame; memcpy(mp3fp->fbuf, mp3fp->fbuf+mp3fp->fbsize-unproc_data_size, unproc_data_size); copy_size = BUFSIZE - unproc_data_size; if(mp3fp->fpos + copy_size > mp3fp->flen) { copy_size = mp3fp->flen - mp3fp->fpos; } fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp); mp3fp->fbsize = unproc_data_size + copy_size; mp3fp->fpos += copy_size; /*Hand off the buffer to the mp3 input stream*/ mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize); ret_code = MAD_FLOW_CONTINUE; } else { ret_code = MAD_FLOW_STOP; } return ret_code; }/*
* The following utility routine performs simple rounding, clipping, and
* scaling of MAD's high-resolution samples down to 16 bits. It does not
* perform any dithering or noise shaping, which would be recommended to
* obtain any exceptional audio quality. It is therefore not recommended to
* use this routine if high-quality output is desired.
*/static inline
signed int scale(mad_fixed_t sample)
{
/* round */
sample += (1L <= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample > (MAD_F_FRACBITS + 1 - 16);
}/*
* This is the output callback function. It is called after each frame of
* MPEG audio data has been completely decoded. The purpose of this callback
* is to output (or play) the decoded PCM audio.
*/static
enum mad_flow output(void *data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
unsigned int nchannels, nsamples;
unsigned int rate;
mad_fixed_t const *left_ch, *right_ch;/* pcm->samplerate contains the sampling frequency */rate= pcm->samplerate;
nchannels = pcm->channels;
nsamples = pcm->length;
left_ch = pcm->samples[0];
right_ch = pcm->samples[1];/* update the sample rate of dsp*/
if(rate != prerate)
{
ioctl(soundfd, SNDCTL_DSP_SPEED, &rate);
prerate = rate;
}while (nsamples--) {
signed int sample;/* output sample(s) in 16-bit signed little-endian PCM */sample = scale(*left_ch++);
writedsp((sample >> 0) & 0xff);
writedsp((sample >> 8) & 0xff);if (nchannels == 2) {
sample = scale(*right_ch++);
writedsp((sample >> 0) & 0xff);
writedsp((sample >> 8) & 0xff);
}
}return MAD_FLOW_CONTINUE;
}/*
* This is the error callback function. It is called whenever a decoding
* error occurs. The error is indicated by stream->error; the list of
* possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
* header file.
*/static enum mad_flow error(void *data,
struct mad_stream *stream,
struct mad_frame *frame)
{
mp3_file *mp3fp = data;fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
stream->error, mad_stream_errorstr(stream),
stream->this_frame - mp3fp->fbuf);/* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */return MAD_FLOW_CONTINUE;
}/*
* This is the function called by main() above to perform all the decoding.
* It instantiates a decoder object and configures it with the input,
* output, and error callback functions above. A single call to
* mad_decoder_run() continues until a callback function returns
* MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
* signal an error).
*/static int decode(mp3_file *mp3fp)
{
struct mad_decoder decoder;
int result;/* configure input, output, and error functions */
mad_decoder_init(&decoder, mp3fp,
input, 0 /* header */, 0 /* filter */, output,
error, 0 /* message */);/* start decoding */
result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);/* release the decoder */
mad_decoder_finish(&decoder);return result;
}



zzz:http://liupingjing.oldblog.ubuntu.org.cn/?p=852基于libmad库的MP3解码简析 2012-06-21
13:51:27分类: LINUXMAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和
API 等。
网上有很多关于libmad的使用实例,在他们的基础上,我稍加总结、整理和衍生,文末给出相关参考链接,表示感谢!一、libmad库源码可以去相关网站下载,给出链接:
http://download.chinaunix.net/download.php?id=11891&ResourceID=5910
可以根据不同的平台自行编译或者移植,略述。二、相关数据结构及函数接口简介1、struct mad_decodestruct mad_decoder {enum mad_decoder_mode mode;int options;struct {long pid;int in;int out;} async;struct {struct mad_stream stream;struct mad_frame frame;struct mad_synth synth;} *sync;void *cb_data;enum mad_flow (*input_func)(void *, struct
mad_stream *);enum mad_flow (*header_func)(void *, struct
mad_header const *);enum mad_flow (*filter_func)(void *,struct mad_stream const *, struct mad_frame *);enum mad_flow (*output_func)(void *,struct mad_header const *, struct mad_pcm *);enum mad_flow (*error_func)(void *, struct
mad_stream *, struct mad_frame *);enum mad_flow (*message_func)(void *, void *, unsigned int *);};2、struct mad_streamstruct mad_stream { unsigned char const *buffer; /* input
bitstream buffer */

unsigned char const *bufend; /* end of
buffer */

unsigned long skiplen; /* bytes to skip
before next frame */int sync; /* stream
sync found */unsigned long freerate; /* free bitrate (fixed) */ unsigned char const *this_frame; /* start
of current frame */

unsigned char const *next_frame; /* start
of next frame */

struct mad_bitptr ptr; /* current processing bit pointer */struct mad_bitptr anc_ptr; /* ancillary bits pointer */unsigned int anc_bitlen; /* number
of ancillary bits */unsigned char (*main_data)[MAD_BUFFER_MDLEN];/* Layer III main_data() */unsigned int md_len; /* bytes in main_data */int options; /* decoding
options (see below) */enum mad_error error; /* error code (see
above) */};三、MP3解码流程简介MP3解码有同步方式和异步方式两种,libmad是以桢为单位对MP3进行解码的,所谓同步方式是指解码函数在解码完一帧后才返回并带回出错信息,异步方式是指解码函数在调用后立即返回,通过消息传递解码状态信息。
1、首先创建一个解码器 struct mad_decoder decoder,紧接着调用函数 mad_decoder_init(...)函数,给出这个函数的原型及定义/** NAME: decoder->init()* DESCRIPTION: initialize a decoder object with callback routines*/void mad_decoder_init(struct mad_decoder *decoder, void *data,enum mad_flow (*input_func)(void *,struct mad_stream *),enum mad_flow (*header_func)(void *,struct mad_header const *),enum mad_flow (*filter_func)(void *,struct mad_stream const *,struct mad_frame *),enum mad_flow (*output_func)(void *,struct mad_header const *,struct mad_pcm *),enum mad_flow (*error_func)(void *,struct mad_stream *,struct mad_frame *),enum mad_flow (*message_func)(void *,void *, unsigned int *)){decoder->mode = -1;decoder->options = 0;decoder->async.pid = 0;decoder->async.in = -1;decoder->async.out = -1;decoder->sync = 0;decoder->cb_data = data;decoder->input_func = input_func;decoder->header_func = header_func;decoder->filter_func = filter_func;decoder->output_func = output_func;decoder->error_func = error_func;decoder->message_func = message_func;}用户编程可以用如下方式调用,可以看到从第三个参数开始,其实都是一些列的函数指针,这里初始化的目的其实是给创建的decoder注册下面即将要自己实现的这些函数。Libmad库会在解码过程中回调这些函数:mad_decoder_init(&decoder, &buffer,input, 0 /* header */, 0 /* filter */, output,error, 0 /* message */);第一个参数,就是定义的解码器decoder;
第二个参数,是一个void型的函数指针,这里也就是给你的用户空间定义私有的数据结构体用的,下面会给出具体的例子来说明其用法;
第三个参数,input_func函数,这个是用来读取你的mp3资源的函数;
第四个参数,header_func函数,这个顾名思义是处理mp3头部信息的函数,可以根据需要取舍;
第五个参数,filter_func函数,也没有深入理解过,可以不必实现;第六个参数,output_func函数,这个是用来将解码之后的数据写入输出缓冲区或者音频设备节点的;
第七个参数,error_func函数,是用来打印返回的解码出错信息的;
第八个参数,message_func可以不必实现。2、调用mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC)函数启动解码,查看Libmad库源码可知,这个函数里面会注册一个函数指针/** NAME: decoder->run()* DESCRIPTION: run the decoder thread either synchronously or asynchronously*/int mad_decoder_run(struct mad_decoder *decoder, enum
mad_decoder_mode mode){int result;int (*run)(struct
mad_decoder *) = 0;switch (decoder->mode = mode) { case MAD_DECODER_MODE_SYNC:
run = run_sync;
break;case MAD_DECODER_MODE_ASYNC:# if defined(USE_ASYNC)run = run_async;# endifbreak;}if (run == 0)return -1;decoder->sync = malloc(sizeof(*decoder->sync));if (decoder->sync == 0)return -1; result = run(decoder);free(decoder->sync);decoder->sync = 0;return result;}而在这个run_sync(struct mad_decoder *decoder)函数中则有一个大的while循环来依次调用
decoder->input_func(decoder->cb_data, stream)获取mp3源文件,然后交由相关库函数解码。
而后会有decoder->output_func(decoder->cb_data, &frame->header, &synth->pcm)函数来输出解码后的数据。
3、最后调用mad_decoder_finish(&decoder)结束解码,释放decoder资源。
4、在input_func函数中,会调用一个很重要的函数
mad_stream_buffer(stream, buffer->start, buffer->length) ,第一个参数指向一个mad_stream变量,mad_stream结构定义在stream.h头文件里,用于记录文件的地址和当前处理的位置。第二、三个参数分别是mp3文件在内存中映像的起始地址和文件长度。mad_stream_buffer()函数将mp3文件与mad_stream结构进行关联。四、MP3解码编程实例#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/stat.h>#include <sys/mman.h>#include <fcntl.h>#include <sys/types.h>#include <sys/ioctl.h>#include <sys/soundcard.h>#include "mad.h"#define BUFSIZE 8192/** This is a private message structure. A
generic pointer to this structure* is passed to each of the callback
functions. Put here any data you need* to access from within the callbacks.*/struct buffer {FILE *fp; /*file pointer*/unsigned int flen; /*file length*/unsigned int fpos; /*current position*/unsigned char fbuf[BUFSIZE]; /*buffer*/unsigned int fbsize; /*indeed
size of buffer*/};typedef struct buffer mp3_file;int soundfd; /*soundcard file*/unsigned int prerate = 0; /*the
pre simple rate*/int writedsp(int c){return write(soundfd, (char *)&c, 1);}void set_dsp(){#if 0int format = AFMT_S16_LE;int channels = 2;int rate = 44100;soundfd = open("/dev/dsp", O_WRONLY);ioctl(soundfd, SNDCTL_DSP_SPEED,&rate);ioctl(soundfd, SNDCTL_DSP_SETFMT, &format);ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels);#elseif((soundfd = open("test.bin" , O_WRONLY | O_CREAT)) < 0){fprintf(stderr , "can't open sound device!\n");exit(-1);}#endif}/** This is perhaps the simplest example use of the MAD high-level API.* Standard input is mapped into memory via mmap(), then the
high-level API* is invoked with three callbacks: input, output, and error. The
output* callback converts MAD's high-resolution PCM samples to 16
bits, then* writes them to standard output in little-endian, stereo-interleaved* format.*/static int decode(mp3_file *mp3fp);int main(int argc, char *argv[]){long flen, fsta, fend;int dlen;mp3_file *mp3fp;if (argc != 2)return 1;mp3fp = (mp3_file *)malloc(sizeof(mp3_file));if((mp3fp->fp = fopen(argv[1], "r")) == NULL){printf("can't open source file.\n");return 2;}fsta = ftell(mp3fp->fp);fseek(mp3fp->fp, 0, SEEK_END);fend = ftell(mp3fp->fp);flen = fend - fsta;if(flen > 0)fseek(mp3fp->fp, 0, SEEK_SET);fread(mp3fp->fbuf, 1, BUFSIZE, mp3fp->fp);mp3fp->fbsize = BUFSIZE;mp3fp->fpos = BUFSIZE;mp3fp->flen = flen;set_dsp();decode(mp3fp);close(soundfd);fclose(mp3fp->fp);return 0;}static enum mad_flow input(void *data, struct mad_stream *stream){mp3_file *mp3fp;int ret_code;int unproc_data_size; /*the unprocessed
data's size*/int copy_size;mp3fp = (mp3_file *)data;if(mp3fp->fpos < mp3fp->flen) { unproc_data_size = stream->bufend - stream->next_frame;
//printf("%d,
%d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);

memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);
copy_size = BUFSIZE - unproc_data_size;
if(mp3fp->fpos + copy_size > mp3fp->flen) {
copy_size = mp3fp->flen - mp3fp->fpos;
}
fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
mp3fp->fbsize = unproc_data_size + copy_size;
mp3fp->fpos += copy_size; /*Hand off the buffer to the mp3
input stream*/

mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
ret_code = MAD_FLOW_CONTINUE;} else {ret_code = MAD_FLOW_STOP;}return ret_code;}/** The following utility routine performs simple rounding, clipping, and* scaling of MAD's high-resolution samples down to 16
bits. It does not* perform any dithering or noise shaping, which would be recommended to* obtain any exceptional audio quality. It is therefore not recommended to* use this routine if high-quality output is desired.*/static inline signed int scale(mad_fixed_t sample){/* round */sample += (1L << (MAD_F_FRACBITS - 16));/* clip */if (sample >= MAD_F_ONE)sample = MAD_F_ONE - 1;else if (sample < -MAD_F_ONE)sample = -MAD_F_ONE;/* quantize */return sample >> (MAD_F_FRACBITS + 1 - 16);}/** This is the output callback function. It is called
after each frame of* MPEG audio data has been completely decoded. The purpose of this callback* is to output (or play) the
decoded PCM audio.*///输出函数做相应的修改,目的是解决播放音乐时声音卡的问题。static enum mad_flow output(void *data, struct mad_header const *header,struct mad_pcm *pcm){unsigned int nchannels, nsamples;mad_fixed_t const *left_ch, *right_ch;// pcm->samplerate contains the
sampling frequencynchannels = pcm->channels;nsamples = pcm->length;left_ch = pcm->samples[0];right_ch = pcm->samples[1];short buf[nsamples *2];int i = 0;//printf(">>%d\n", nsamples);while (nsamples--) {signed int sample;// output sample(s) in 16-bit
signed little-endian PCMsample = scale(*left_ch++);buf[i++] = sample & 0xFFFF;if (nchannels == 2) {sample = scale(*right_ch++);buf[i++] = sample & 0xFFFF;}}//fprintf(stderr, ".");write(soundfd, &buf[0], i * 2);return MAD_FLOW_CONTINUE;}/** This is the error callback function. It is called
whenever a decoding* error occurs. The error is indicated
by stream->error; the list of* possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)* header file.*/static enum mad_flow error(void *data,struct mad_stream *stream,struct mad_frame *frame){mp3_file *mp3fp = data;fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",stream->error, mad_stream_errorstr(stream),stream->this_frame - mp3fp->fbuf);/* return MAD_FLOW_BREAK here to stop decoding (and propagate
an error) */return MAD_FLOW_CONTINUE;}/** This is the function called by main() above to perform
all the decoding.* It instantiates a decoder object and configures it with the input,* output, and error callback functions
above. A single call to* mad_decoder_run() continues until a
callback function returns* MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop
decoding and* signal an error).*/static int decode(mp3_file *mp3fp){struct mad_decoder decoder;int result;/* configure input, output, and error functions */mad_decoder_init(&decoder, mp3fp,input, 0 /* header */, 0 /* filter */, output,error, 0 /* message */);/* start decoding */result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);/* release the decoder */mad_decoder_finish(&decoder);return result;}说明:1、实例原本是基于音频OSS框架的,当然,在嵌入式领域,ALSA也是兼容OSS接口的;
2、为了在ubuntu上调试方便,并没有直接往音频接口,而是创建了一个文件,直接往文件里面写;
3、上述代码中的红色区域重点讲解一下static enum mad_flow input(void *data, struct
mad_stream *stream){mp3_file *mp3fp;int ret_code;int unproc_data_size; /*the unprocessed
data's size*/int copy_size;mp3fp = (mp3_file *)data;if(mp3fp->fpos < mp3fp->flen) {unproc_data_size = stream->bufend - stream->next_frame;//printf("%d, %d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);copy_size = BUFSIZE - unproc_data_size;if(mp3fp->fpos + copy_size > mp3fp->flen) {copy_size = mp3fp->flen - mp3fp->fpos;}fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);mp3fp->fbsize = unproc_data_size + copy_size;mp3fp->fpos += copy_size;/*Hand off the buffer to the mp3 input stream*/mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);ret_code = MAD_FLOW_CONTINUE;} else {ret_code = MAD_FLOW_STOP;}return ret_code;}我们设置的输入buff缓冲区的大小是8192字节,但是对于mp3文件来讲,不一定这8192个字节就刚好是若干个完整的帧,有可能会有若干字节是输入下一个帧的,所有要根据struct mad_stream中的两个指针,标示了缓冲区中的完整帧的起始地址:unsigned char const *this_frame; /* start
of current frame */unsigned char const *next_frame; /* start
of next frame */那么unproc_data_size = stream->bufend - stream->next_frame;得到剩余的下一个帧的数据,并且需要将其从buff数组的尾部拷贝到头部,再从mp3文件中读取一部分字节拼凑成下一个8192字节,提交给库去解码,如此周而复始。4、此代码解码出来的pcm可以加上44字节的wav头文件,则可以用播放器正常播放。五、如何从网络socket获取相应数据,边解码边播放由于我的项目是要实现一个远程播放器的功能,即手机端的mp3源文件通过wifi传输到开发板上解码播放,所以,对于输入缓冲区的控制就不像操作文件那个,可以通过file结构体精确控制好读取的数据位置了,为此,做了些许修改。可以开两个线程,一个线程用于接收socket数据,一个用于解码播放。主要是缓冲区的控制,可以如此实现:将接收buff[]大小设置为8192*10字节,然后,解码input函数里面的buff[]的大小设置为8192*11字节,也就是说,多余了8192用来缓冲多余的下一帧字节的数据(因为mp3文件的帧不会超过8192字节),那么,区别于上面的思路,我们可以固定的让socket的buff[]接收8192*10字节的数据,如果解码的buff[]里面初次解码后有剩余的数据,仍然将其复制到解码buff[]的头部,只是这时候还是将socket的buff[]的8192*10字节的数据加到解码buff[]的刚刚拷贝的数据后面,所以,这里调用mad_stream_buffer(stream,
buf, bsize)中的bsize就是8192*10+剩余的帧数据大小了。相关参考:
1、作者:cqulpj
网址: http://cqulpj.blogbus.com/logs/68406670.htmlhttp://cqulpj.blogbus.com/logs/68406669.html2、作者:李素科 网址: http://www.ibm.com/developerworks/cn/linux/l-cn-libmadmp3player/index.html3、作者:liky125 网址: http://blog.chinaunix.net/uid-26073752-id-2392553.htmlzzzz:http://blog.chinaunix.net/uid-25272011-id-3250552.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: