linux mp3播放及分析
2016-03-02 16:59
731 查看
MAD (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_decode
struct 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_stream
struct 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;
}
其中的data参数是用户需要传给回调函数的自定义数据结构。比如,解码一个文件,并且采用系统函数open函数打开,那么可以定义一个对此描述的数据结构:
[cpp]
view plain
copy
typedef struct _mp3_file
{
int *fd;//open("xx.mp3",O_RDONLY)
uint32_t flen;//mp3文件的长度
uint32_t fpos;//当前文件指针位置
uint8_t buf[BUFSIZE];
uint32_t buf_size;
} mp3_file;
数据成员buf、buf_size传递给mad_stream_buffer,用来设置流缓冲区指针。
用户编程可以用如下方式调用,可以看到从第三个参数开始,其实都是一些列的函数指针,这里初始化的目的其实是给创建的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;
# endif
break;
}
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)函数来输出解码后的数据。
在输出回调函数中,将解码数据直接写入“/dev/dsp”即可实现mp3文件的播放,也可以生成pcm文件,供支持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 0
int 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);
#else
if((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 frequency
nchannels = 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 PCM
sample = 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;
}
代码二:
(第二种解码)
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "fcntl.h"
#include "mad.h"
#include "linux/soundcard.h"
typedef unsigned int DWORD;
typedef unsigned char Byte;
typedef char *PChar;
typedef PChar *PPChar;
typedef void *Pointer;
//MAD解码必要的3个结构声明
struct mad_stream stream;
struct mad_frame frame;
struct mad_synth synth;
int sampleofs,samplecnt;
#define MAD_BLOCK_SIZE 8192
#define MAD_INBUF_SIZE MAD_BLOCK_SIZE + MAD_BUFFER_GUARD
Byte Inbuf[MAD_INBUF_SIZE];
//初始化Libmad
void Mad_Decode_Init()
{
mad_stream_init(&stream);
mad_frame_init(&frame);
mad_synth_init(&synth);
samplecnt = 0;
sampleofs = 0;
}
//释放Libmad
void Mad_Decode_Free()
{
mad_synth_finish(&synth);
mad_frame_finish(&frame);
mad_stream_finish(&stream);
}
//MP3解码和输出PCM
int Mad_Decode_Read(int hFile, Pointer buf, int len)
{
int i, num = len, ofs = 0;
Byte *in_buf;
int remaining, rlen;
DWORD nchannels, nsamples;
while(num > 0)
{
if(samplecnt == 0)
{
if((stream.buffer== NULL)||(stream.error = MAD_ERROR_BUFLEN))
{
if(stream.next_frame!= NULL)
{
remaining= stream.bufend - stream.next_frame;
memcpy(Inbuf,stream.next_frame, remaining);
in_buf= Inbuf + remaining;
rlen= MAD_BLOCK_SIZE - remaining;
}
else
{
rlen= MAD_BLOCK_SIZE;
in_buf= Inbuf;
remaining= 0;
}
rlen= read(hFile, in_buf, rlen);//读MPEG流
if(rlen<= 0)
{
returnofs;
}
//向MAD写入MP3流
mad_stream_buffer(&stream,Inbuf, rlen + remaining);
stream.error= MAD_ERROR_NONE;
}
//解码
if(mad_frame_decode(&frame,&stream) != 0)
{
if(MAD_RECOVERABLE(stream.error)||(stream.error== MAD_ERROR_BUFLEN))
continue;
returnofs;
}
mad_synth_frame(&synth,&frame);
nchannels =synth.pcm.channels;
nsamples = synth.pcm.length;
if(nchannels== 2)
{
for(i= 0; i < nsamples; i++)
{
if(synth.pcm.samples[0][i]>= MAD_F_ONE)synth.pcm.samples[0][i]
= MAD_F_ONE - 1;
if(synth.pcm.samples[0][i]< -MAD_F_ONE)synth.pcm.samples[0][i]
= -MAD_F_ONE;
synth.pcm.samples[0][i]= synth.pcm.samples[0][i]
>> (MAD_F_FRACBITS + 1 - 16 +1);
if(synth.pcm.samples[1][i]>= MAD_F_ONE)synth.pcm.samples[1][i]
= MAD_F_ONE - 1;
if(synth.pcm.samples[1][i]< -MAD_F_ONE)synth.pcm.samples[1][i]
= -MAD_F_ONE;
synth.pcm.samples[1][i]= synth.pcm.samples[1][i]
>> (MAD_F_FRACBITS + 1 - 16 +1);
}
}
else
{
for(i= 0; i < nsamples; i++)
{
if(synth.pcm.samples[0][i]>= MAD_F_ONE)synth.pcm.samples[0][i]
= MAD_F_ONE - 1;
if(synth.pcm.samples[0][i]< -MAD_F_ONE)synth.pcm.samples[0][i]
= -MAD_F_ONE;
API 等。
网上有很多关于libmad的使用实例,在他们的基础上,我稍加总结、整理和衍生,文末给出相关参考链接,表示感谢!
一、libmad库源码
可以去相关网站下载,给出链接:
http://download.chinaunix.net/download.php?id=11891&ResourceID=5910
可以根据不同的平台自行编译或者移植,略述。
二、相关数据结构及函数接口简介
1、struct mad_decode
struct 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_stream
struct 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;
}
其中的data参数是用户需要传给回调函数的自定义数据结构。比如,解码一个文件,并且采用系统函数open函数打开,那么可以定义一个对此描述的数据结构:
[cpp]
view plain
copy
typedef struct _mp3_file
{
int *fd;//open("xx.mp3",O_RDONLY)
uint32_t flen;//mp3文件的长度
uint32_t fpos;//当前文件指针位置
uint8_t buf[BUFSIZE];
uint32_t buf_size;
} mp3_file;
数据成员buf、buf_size传递给mad_stream_buffer,用来设置流缓冲区指针。
用户编程可以用如下方式调用,可以看到从第三个参数开始,其实都是一些列的函数指针,这里初始化的目的其实是给创建的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;
# endif
break;
}
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)函数来输出解码后的数据。
在输出回调函数中,将解码数据直接写入“/dev/dsp”即可实现mp3文件的播放,也可以生成pcm文件,供支持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 0
int 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);
#else
if((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 frequency
nchannels = 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 PCM
sample = 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;
}
代码二:
(第二种解码)
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "fcntl.h"
#include "mad.h"
#include "linux/soundcard.h"
typedef unsigned int DWORD;
typedef unsigned char Byte;
typedef char *PChar;
typedef PChar *PPChar;
typedef void *Pointer;
//MAD解码必要的3个结构声明
struct mad_stream stream;
struct mad_frame frame;
struct mad_synth synth;
int sampleofs,samplecnt;
#define MAD_BLOCK_SIZE 8192
#define MAD_INBUF_SIZE MAD_BLOCK_SIZE + MAD_BUFFER_GUARD
Byte Inbuf[MAD_INBUF_SIZE];
//初始化Libmad
void Mad_Decode_Init()
{
mad_stream_init(&stream);
mad_frame_init(&frame);
mad_synth_init(&synth);
samplecnt = 0;
sampleofs = 0;
}
//释放Libmad
void Mad_Decode_Free()
{
mad_synth_finish(&synth);
mad_frame_finish(&frame);
mad_stream_finish(&stream);
}
//MP3解码和输出PCM
int Mad_Decode_Read(int hFile, Pointer buf, int len)
{
int i, num = len, ofs = 0;
Byte *in_buf;
int remaining, rlen;
DWORD nchannels, nsamples;
while(num > 0)
{
if(samplecnt == 0)
{
if((stream.buffer== NULL)||(stream.error = MAD_ERROR_BUFLEN))
{
if(stream.next_frame!= NULL)
{
remaining= stream.bufend - stream.next_frame;
memcpy(Inbuf,stream.next_frame, remaining);
in_buf= Inbuf + remaining;
rlen= MAD_BLOCK_SIZE - remaining;
}
else
{
rlen= MAD_BLOCK_SIZE;
in_buf= Inbuf;
remaining= 0;
}
rlen= read(hFile, in_buf, rlen);//读MPEG流
if(rlen<= 0)
{
returnofs;
}
//向MAD写入MP3流
mad_stream_buffer(&stream,Inbuf, rlen + remaining);
stream.error= MAD_ERROR_NONE;
}
//解码
if(mad_frame_decode(&frame,&stream) != 0)
{
if(MAD_RECOVERABLE(stream.error)||(stream.error== MAD_ERROR_BUFLEN))
continue;
returnofs;
}
mad_synth_frame(&synth,&frame);
nchannels =synth.pcm.channels;
nsamples = synth.pcm.length;
if(nchannels== 2)
{
for(i= 0; i < nsamples; i++)
{
if(synth.pcm.samples[0][i]>= MAD_F_ONE)synth.pcm.samples[0][i]
= MAD_F_ONE - 1;
if(synth.pcm.samples[0][i]< -MAD_F_ONE)synth.pcm.samples[0][i]
= -MAD_F_ONE;
synth.pcm.samples[0][i]= synth.pcm.samples[0][i]
>> (MAD_F_FRACBITS + 1 - 16 +1);
if(synth.pcm.samples[1][i]>= MAD_F_ONE)synth.pcm.samples[1][i]
= MAD_F_ONE - 1;
if(synth.pcm.samples[1][i]< -MAD_F_ONE)synth.pcm.samples[1][i]
= -MAD_F_ONE;
synth.pcm.samples[1][i]= synth.pcm.samples[1][i]
>> (MAD_F_FRACBITS + 1 - 16 +1);
}
}
else
{
for(i= 0; i < nsamples; i++)
{
if(synth.pcm.samples[0][i]>= MAD_F_ONE)synth.pcm.samples[0][i]
= MAD_F_ONE - 1;
if(synth.pcm.samples[0][i]< -MAD_F_ONE)synth.pcm.samples[0][i]
= -MAD_F_ONE;
相关文章推荐
- Linux下双网卡绑定bond0
- Install YouTube-DL – A Command Line Video Download Tool for Linux
- Linux IO模型
- Linux device tree 简要笔记
- linux安装mysq总监
- C语言中linux下查看sd卡mount的位置
- Linux---Nagios无法登录Internal Server Error
- Linux之awk
- 用串口终端作为Linux系统控制台
- CentOS6.5 下 extundelete回复删除的文件
- Linux下日志按日分割的shell
- linux下vi或vim操作Found a swap file by the name的原因及解决方法
- centos下安装resin4
- Linux学习决心书
- Centos6.5安装 scipy sciki-learn 一堆报错
- linux 让root用户可以telnet
- linux 隐藏进程
- Linux Fedora13系统配置上海交大yum源
- linux存储技术与应用:配置iSCSI服务及应用示例
- 安装 CentOS 7 后必做的七件事