libmad简介
官网:https://www.underbit.com/products/mad/
MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24
-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。
移植涉及到的库:
zlib-1.2.3.tar.gz
libid3tag-0.15.1b.tar.gz
libmad-0.15.1b.tar.gz
---------------------------------------------------------------------------------------------------------------
madplay介绍:
madplay基于libmad的基础上做了一个播放器,该播放器除了目前不支持网络播放以为,其余功能都支持。如快进播放,seek播放,暂停,恢复等
最后移植了一个基于libmad的应用madplay,可以直接用它来播放mp3.
madplay-0.15.2b.tar.gz
介绍完了,当然移植不是一番风顺的,折腾了一天。中间遇到不少问题,还好,最后都一一解决了。
网上提供的代码。交叉编译,由于环境不一样,会出现各种问题。
这里记录一下过程。
首先是交叉编译zlib-1.2.3.tar.gz
用交叉编译工具编译zlib,并且把库生成到交叉编译环境的库目录下
./configure --prefix=/home/ban/madplay/source
修改Makefile.
CC=arm-linux-gnueabihf-gcc
AR=arm-linux-gnueabihf-ar rc
RANLIB=arm-linux-gnueabihf-ranlib
make
make install
安装完成后,在/home/ban/madplay/source/ 中将生产lib跟include2个文件夹。
这步一般不会有啥问题,但是,默认成功的是静态库啊, 虽然配置上是说默认配置生成动态库,但是确实没有。
实际是有的,指定--shared即可。
或者仔细查makefile,把相关的编译语句找出来,我手动调用gcc - shared -fPIC - $(OBJS)生成了.so
这里还需要注意的是,指定好自己的--prifix,因为后续的编译,好多是要依赖这个的。
接下来编译libid3tag-0.15.1b.tar.gz
这时候要注意了,如果上一步编译不过,或者没有指定--prifix, 这里就麻烦了。
由于我需要的是动态库,发现configer后,竟没带-FPIC参数,还要去改makefile才行。
./configure --host=arm-linux-gnueabihf --disable-debugging --prefix=/home/ban/madplay/source CPPFLAGS=-I/home/ban/madplay/source/include LDFLAGS=-L/home/ban/madplay/source/lib
make
make install
编译libmad
./configure --host=arm-linux-gnueabihf --disable-debugging --prefix=/home/ban/madplay/source CPPFLAGS=-I/home/ban/madplay/source/include LDFLAGS=-L/home/ban/madplay/source/lib
make
make install
出现错误:
cc1: error: unrecognized command line option “-fforce-mem”
原因是高版本的gcc,已经将-fforce-mem去除了,解决方法:
sed -i '/-fforce-mem/d' configure
再执行:
./configure --host=arm-linux-gnueabihf --prefix=/usr/local/libmad_arm --enable-shared --enable-static --enable-fpm=arm --
with-gnu-ld=arm-linux-gnueabihf-ld --build=arm
出现错误:
/tmp/ccf2FxyW.s:1299: Error: selected processor does not support Thumb mode `rsc r0,r0,#0'
/tmp/ccf2FxyW.s:1435: Error: selected processor does not support Thumb mode `rsc r8,r8,#0'
/tmp/ccf2FxyW.s:1857: Error: selected processor does not support Thumb mode `rsc r0,r0,#0'
/tmp/ccf2FxyW.s:1996: Error: selected processor does not support Thumb mode `rsc r0,r0,#0
百度一下发现这是libmad的一个bug.
解决方法是:
vim fixed.h
将
# define MAD_F_MLN(hi, lo) \
asm ("rsbs %0, %2, #0\n\t" \
"rsc %1, %3, #0" \
: "=r" (lo), "=r" (hi) \
: "0" (lo), "1" (hi) \
: "cc")
改为
#ifdef __thumb__
/* In Thumb-2, the RSB-immediate instruction is only allowed with a zero
operand. If needed this code can also support Thumb-1
(simply append "s" to the end of the second two instructions). */
# define MAD_F_MLN(hi, lo) \
asm ("rsbs %0, %0, #0\n\t" \
" sbc %1, %1, %1\n\t" \
"sub %1, %1, %2" \
: "+&r" (lo), "=&r" (hi) \
: "r" (hi) \
: "cc")
#else /* ! __thumb__ */
# define MAD_F_MLN(hi, lo) \
asm ("rsbs %0, %2, #0\n\t" \
"rsc %1, %3, #0" \
: "=r" (lo), "=r" (hi) \
: "=&r" (lo), "=r" (hi) \
: "0" (lo), "1" (hi) \
: "cc")
#endif /* __thumb__ */
再make,编译通过了!
编译madplay
./configure --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc --disable-debugging --with-alsa CPPFLAGS=-I/home/ban/madplay/source/include LDFLAGS=-L/home/ban/madplay/source/lib
make
make install
完成以后把生成的可执行文件madplay下载到开发板中
执行./madplay filename.mp3
这个需要注意的是,如果不指定--with-alsa,即便编译成功了,放到板子上也是跑不起的,提示找不到dev/dsp,这个让我折腾了好久,竟发现,配置上没启用alsa啊,
但板子上带的是alsa架构的linux音频驱动。
总体上操作是就这么几步,但是,你会发现,如果照这个步骤来,仍是有错。
具体细节。,根据编译提示的错误,基本都能定为到。比如,找不到上几步编译出的库,就去改makefile吧,添加进去路径
或者仍拷贝到 --prifix指定的目录中。
如果编译遇到以下错误:
只需更新alsa api,libmad里的api太旧了,新的alsa-lib不支持了
解决方案参考:https://gitlab.savoirfairelinux.com/netdsa/buildroot/commit/97b392b98468859ab2366995b6bee4140b38e388
最后再说一点儿,编译网上这种开源库,最好设置下交叉工具链的环境变量为全局的,且用root权限。否则,坑真的好多。
附截图:
如果不用这个现成的播放器madplay,只测试下libmad是否成功,
可以编译测试下 libmad提供的一个简单demo,这个demo 不是播放mp3的,而是把mp3解码成 pcm文件 。
测试如下:
./testmad.out <demo1.mp3 >out1.pcm
显示出了信息,且在当前路径下产生了out1.pcm文件。
mad功能:
madpay功能很强求,可以实现多个节目循环播放,快进拖动播放,暂停,恢复,支持wav等格式输出等。
目前madplay还不支持网络播放.不过mp123支持网络播放。
madplay使用方法: 传入打印既可以使用短传入(-n ),也可以使用长传入(--abc),二者等效,下面只讲短传入
-v 获取播放时间
-q 不存在任何打印,但现实警告
-Q 不存在任何打印
--downsample 只采用一半数据
-i 忽略CRC校验错误
-o PATH/xx.wav 可以用来转码,将mp3转为wav
也可以是其他格式,见说明;.raw 表示是元素pcm,.hex等
-a 开启衰减音量 ,增加音量,衰减系统为-175~+18 (当然通过键盘+ —也可以调整音量)
-A 同-a
-1 -2 -m -S 分别指左声道,右声道,双声道,立体声
-s 用于seek播放 如:0:1:20:11 ,seek到1小时,2分钟,11秒时开始播放
-t 用于播放时间现在 0:1:20:11 ,播放到1小时,2分钟,11秒时就停止
-z 用于随机播放列表
-r, --repeat[=MAX] 循环播放无限次或Max次
--tty-control enable keyboard controls 默认是使能热键
--no-tty-control disable keyboard controls
热键使用
下一首歌曲; f,或ctrl+n 或者>
上一首歌曲 b ,或ctrl+p 或者<
退出:q, Q,获取ctrl+c
获取播放状态信息: i ?
调节音量: - + _ =
暂停恢复:p
停止:s
Usage: ./madplay [OPTIONS] FILE [...]
Decode and play MPEG audio FILE(s).
Verbosity:
-v, --verbose show status while decoding
-q, --quiet be quiet but show warnings
-Q, --very-quiet be quiet and do not show warnings
--display-time=MODE use default verbose time display MODE
(remaining, current, overall)
Decoding:
--downsample reduce sample rate 2:1
-i, --ignore-crc ignore CRC errors
--ancillary-output=PATH write ancillary data to PATH
Audio output:
-o, --output=[TYPE:]PATH write output to PATH with format TYPE (below)
-b, --bit-depth=DEPTH request DEPTH bits per sample
-R, --sample-rate=HERTZ request HERTZ samples per second
-d, --no-dither do not dither output PCM samples
--fade-in[=DURATION] fade-in songs over DURATION (default 0:05)
-a, --attenuate=DECIBELS attenuate signal by DECIBELS (-)
-a, --amplify=DECIBELS amplify signal by DECIBELS (+)
-A, --adjust-volume=DECIBELS override per-file volume adjustments
-G, --replay-gain[=PROFILE] enable Replay Gain volume adjustments using
PROFILE (radio, audiophile)
Channel selection:
-1, --left output first (left) channel only
-2, --right output second (right) channel only
-m, --mono mix left and right channels for monaural output
-S, --stereo force stereo output
Playback:
-s, --start=TIME skip to begin at TIME (HH:MM:SS.DDD)
-t, --time=DURATION play only for DURATION (HH:MM:SS.DDD)
-z, --shuffle randomize file list
-r, --repeat[=MAX] play files MAX times, or indefinitely
--tty-control enable keyboard controls
--no-tty-control disable keyboard controls
Miscellaneous:
-T, --show-tags-only show ID3/encoder tags only (do not decode)
-V, --version display version number and exit
--license show copyright/license message and exit
-h, --help display this help and exit
Supported output formats:
cdda CD audio, 16-bit big-endian 44100 Hz stereo PCM (*.cdr, *.cda)
aiff Audio IFF, [16-bit] PCM (*.aif, *.aiff)
wave Microsoft RIFF/WAVE, [16-bit] PCM (*.wav)
snd Sun/NeXT audio, 8-bit ISDN mu-law (*.au, *.snd)
raw binary [16-bit] host-endian linear PCM
hex ASCII hexadecimal [24-bit] linear PCM
null no output (decode only)
————————————————
9522 frames decoded (0:04:08.7), +1.7 dB peak amplitude, 4202 clipped samples
/*
* 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;
}