linux下基于Libmad库的MP3音乐播放器编写
libmad是一个开源mp3解码库,其对mp3解码算法做了很多优化,性能较好,很多播放器如mplayer、xmms等都是使用这个开源库进行解码的;如果要设计mp3播放器而又不想研 究mp3解码算法的话,libmad是个不错的选择。关于该库的使用,叙述如下。
一:安装Libmad:
1、在网上下载:Libmad库的使用.pdf文档和libmad-0.15.lb.tar.gz压缩包( http://down.51cto.com/data/1087041(免费下载))
2、解压:tar -zxvf libmad-0.15.lb.tar.gz
以下过程在Readme和INSTALL文件中列了出来,应学会自己看选项进行操作:
3、cd libmad-0.15.lb
4、./configure
5、make
6、make check
7、make install
(若最后有错误信息,说明你用的gcc版本太高,该版本的gcc有"-fforce-mem"参数,打开根目录下的Makefile去掉里面的"-fforce-mem"就OK了。)
结果:产生一个 .libs 目录
--------------------------------------------------------------------------------------------------------------――――――――――――――――
然后按照Libmad库的使用.pdf文档中的提示继续往下进行。
二:查看示例代码 minimad.c:
minimad.c
/* * libmad - MPEG audio decoder library * Copyright (C) 2000-2004 Underbit Technologies, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: minimad.c,v 1.4 2004/01/23 09:41:32 rob Exp $ */ # include <stdio.h> # include <unistd.h> # include <sys/stat.h> # include <sys/mman.h> # include "mad.h" /* * 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(unsigned char const *, unsigned long); int main(int argc, char *argv[]) { struct stat stat; void *fdm; if (argc != 1) return 1; if (fstat(STDIN_FILENO, &stat) == -1 || stat.st_size == 0) return 2; fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, STDIN_FILENO, 0); if (fdm == MAP_FAILED) return 3; decode(fdm, stat.st_size); if (munmap(fdm, stat.st_size) == -1) return 4; return 0; } /* * 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 { unsigned char const *start; unsigned long length; }; /* * 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) { struct buffer *buffer = data; if (!buffer->length) return MAD_FLOW_STOP; mad_stream_buffer(stream, buffer->start, buffer->length); buffer->length = 0; return MAD_FLOW_CONTINUE; } /* * 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]; while (nsamples--) { signed int sample; /* output sample(s) in 16-bit signed little-endian PCM */ sample = scale(*left_ch++); putchar((sample >> 0) & 0xff); putchar((sample >> 8) & 0xff); if (nchannels == 2) { sample = scale(*right_ch++); putchar((sample >> 0) & 0xff); putchar((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) { struct buffer *buffer = data; fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n", stream->error, mad_stream_errorstr(stream), stream->this_frame - buffer->start); /* 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(unsigned char const *start, unsigned long length) { struct buffer buffer; struct mad_decoder decoder; int result; /* initialize our private message structure */ buffer.start = start; buffer.length = length; /* configure input, output, and error functions */ mad_decoder_init(&decoder, &buffer, 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; }
编译: gcc -o minimad minimad.c �Clmad
运行: ./minimad <test.mp3 >test.pcm
以上是将:1、标准输入重定向到MP3文件
2、标准输出重定向到解码以后的 pcm 文件
下面将pcm数据写入音频设备(即pcmplay.c程序):
( A.设备文件/dev/dsp
B.ioctl设置音频属性: (记得加<sys/soundcard.h>头文件)
a.采样格式
b.采样频率
c.声道
C.将pcm文件写入音频设备)
文档中pcmplay.c程序中void writefully(int fd,void *buf,int size);函数未给出,下面已补全。
pcmplay.c代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/ioctl.h> #include <string.h> #include <sys/soundcard.h> void writefully(int fd,void *buf,int size) { int n = write(fd,buf,size); if(n < 0) { fprintf(stderr,"writefully error!",strerror(errno)); exit(-1); } } int main(int argc, char *argv[]) { int handle, fd; char buf[1024]; 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 ((handle = open("/dev/dsp", O_WRONLY))<0) { fprintf(stderr, "Can't open system file /dev/dsp!\n"); exit(-2); } #if 1 //设置声道 int channels = 2; int result = ioctl(handle, SNDCTL_DSP_CHANNELS, &channels); if ( result == -1 ) { perror("ioctl channel number"); return -1; } //设置采样格式:AFMT_S16_LE int format = AFMT_S16_LE; result = ioctl(handle, SNDCTL_DSP_SETFMT, &format); if ( result == -1 ) { perror("ioctl sample format"); return -1; } //设置采样频率44.1 //int rate = 22050; int rate = 44100; result = ioctl(handle, SNDCTL_DSP_SPEED, &rate); if ( result == -1 ) { perror("ioctl sample format"); return -1; } #endif int n; while((n=read(fd,buf,sizeof(buf)))) { writefully(handle,buf,n); } close(fd); close(handle); exit(0); }
编译: gcc -o pcmplay pcmplay.c
运行: ./pcmplay test.pcm
如此即可先将.mp3文件整个解压到.pcm文件中,再通过将.pcm文件写入音频设备进行.mp3音乐播放。
下面简易实现.mp3音乐文件的编解码边播放程序的编写。
------------------------------------------------------------------------------------------------―――――――――――――――――――――
三:编解码边播放,用Libmad设计一个简单的MP3播放器:
“Libmad库的使用.pdf”文档中MP3player.c程序有些许缺失或错误,现已改正,程序如下:
MP3player.c
#include "mad.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <sys/soundcard.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 rate = 44100; // int rate = 96000; int format = AFMT_S16_LE; int channels = 2; int value; soundfd = open("/dev/dsp", O_WRONLY); ioctl(soundfd,SNDCTL_DSP_SPEED,&rate); ioctl(soundfd, SNDCTL_DSP_SETFMT, &format); ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels); /* value = 16; ioctl(soundfd,SNDCTL_DSP_SAMPLESIZE,&value); value = 0; ioctl(soundfd,SNDCTL_DSP_STEREO,&value); */ } /* * 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; 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; } /* * 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 <= mp3fp->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_FRACBITS - 16); if(sample >= MAD_F_ONE) sample = MAD_F_ONE - 1; else if(sample < -MAD_F_ONE) sample = -MAD_F_ONE; 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; 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; }
编译:gcc -o mp3player MP3player.c -lmad
运行:./mp3player xxx.mp3
至此,一个简易MP3播放器就写好了。
程序已亲自验证,请放心阅览。如有错误,欢迎批评指正。
享受阳光,享受生活。愿与大家共同进步。
--刀刀