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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
  * 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 –lmad

   运行: ./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代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#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播放器就写好了。


   程序已亲自验证,请放心阅览。如有错误,欢迎批评指正。


   享受阳光,享受生活。愿与大家共同进步。




本文转自 006玩命 51CTO博客,原文链接:http://blog.51cto.com/weiyuqingcheng/1362179,如需转载请自行联系原作者
相关文章
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
1月前
|
Linux
linux 交叉编译libpng,libjpeg库
linux 交叉编译libpng,libjpeg库
23 1
|
1月前
|
Linux 编译器 Android开发
linux交叉编译nss3,nspr库精讲
linux交叉编译nss3,nspr库精讲
37 1
|
1月前
|
Unix Linux vr&ar
【详解】静态库和动态库的认识和使用【Linux】
【详解】静态库和动态库的认识和使用【Linux】
|
1月前
|
Linux 编译器 vr&ar
linux交叉编译一些常用依赖库util-linux,zlib,sqlite3,eudev ,openssl,libpng,glibc
linux交叉编译一些常用依赖库util-linux,zlib,sqlite3,eudev ,openssl,libpng,glibc
31 1
|
3月前
|
Linux 编译器 Shell
深入理解Linux中的动态库与静态库
深入理解Linux中的动态库与静态库
|
4月前
|
Linux 编译器 vr&ar
Linux基础IO【软硬链接与动静态库】
Linux基础IO【软硬链接与动静态库】
44 1
|
7天前
|
Linux 网络安全 开发工具
【GitLab私有仓库】在Linux上用Gitlab搭建自己的私有库并配置cpolar内网穿透
【GitLab私有仓库】在Linux上用Gitlab搭建自己的私有库并配置cpolar内网穿透
|
1月前
|
Linux 编译器 网络安全
linux 交叉编译libcurl库
linux 交叉编译libcurl库
35 1
|
1月前
|
算法 安全 Linux
Linux 裁剪并交叉编译openssl库
Linux 裁剪并交叉编译openssl库
34 1