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播放器就写好了。
程序已亲自验证,请放心阅览。如有错误,欢迎批评指正。
享受阳光,享受生活。愿与大家共同进步。