Libmad音频解码库的编译和使用
一、libmad的介绍
什么是libmad?
libmad是一个开源mp3解码库,其对mp3解码算法做了很多优化,性能较好,很多播放器如mplayer、xmms等都是使用这个开源库进行解码的;如果要设计mp3播放器而又不想研究mp3解码算法的话,libmad是个不错的选择。
libmad的编译
环境
ubuntu20.04
交叉编译工具 arm-linux-gnueabihf-gcc 8.2.1
下载
下载地址 https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz
在ubuntu下可通过wget直接下载
wget https://downloads.sourceforge.net/mad/libmad-0.15.1b.tar.gz
解压、配置编译
tar -zxvf libmad-0.15.1b.tar.gz
cd libmad-0.15.1b/
mkdir libmad
//先执行下面的命令:这条命令是为了适配高版本的gcc,因为高版本的gcc已经将-fforce-mem去除了:
sed -i '/-fforce-mem/d' configure
./configure CC=arm-linux-gnueabihf-gcc --host=arm-linux --prefix=$PWD/libmad
make -j8
make install
方法二:添加脚本build.sh
#!/bin/bash
current_path=$(cd "$(dirname $0)";pwd)
rm -rf host
mkdir -p $current_path/host
install_path=$current_path/host
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
./configure --prefix=$install_path --enable-shared --host=arm-linux
make clean
make
make install
cp -rf $install_path/* ../libmad/
编译完成后将libmad复制到你的项目中,别忘了链接动态库
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./libmad/lib/
二、libmad的应用
1.minimad.c怎么编译?编译后怎么运行?运行时的输入输出分别是什么,或者说运行时什么效果?
在Linux下(我前面说了,本文所有的工作都是在Linux进行)先安装libmad,说白了就是把libmad库导入C标准库,安装方法见libmad-0.15.1b中的README和INSTALL文件。 安装libmad后,新建一个文件夹,将libmad-0.15.1b中的minimad.c和mad.h复制过来,用gcc编译minimad.c,编译命令为(假设要生成的可执行程序为minimad):
gcc -o minimad minimad.c -lmad minimad
程序从标准输入读入mp3文件,然后将解码后的音频数据送到标准输出,我们可以用重定向的方式从文件中读入数据并将结果写至文件,命令如下:
./minimad tmp.pcm
2.怎样播放minimad输出的数据?或者说怎么播放解码后的数据?
假设你有Linux音频编程方面的基础的话,这个应该不成问题,如果没有也没关系,在Linux的设计理念中,一切皆是文件,音频设备也是文件,只需要打 开/dev/dsp(音频设备)这个文件,然后将解码后的数据写入这个文件即可实现播放,新建pcmplay.c文件,拷入如下代码:
#include <mad.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <pthread.h>int main(int argc, char *argv[])
{int id, fd, i;char buf[1024];int rate; /*simple rate 44.1KHz*/int format; /*quatize args*/int channels; /*sound channel*/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((id = open("/dev/dsp", O_WRONLY)) 0){write(id, buf, i);//printf("i=%d\n", i);}close(fd);close(id);exit(0);}
编译pcmplay文件,然后就可以用生成的可执行程序播放第一步中声称的tmp.pcm文件,命令如下:
gcc -o pcmplay pcmplay.c
./minimad tmp.pcm
./pcmplay tmp.pcm
3.minimad运行时,mp3数据来源是标准输入,能不能改为从文件中读入数据?该怎么改?
当然可以改,而且改起来相当的简单,如果不知道怎么改只能说明自己没仔细看minimad.c,你可能不知道struct stat是什么,也不清楚mmap()函数有什么用,但这些都可以在网上查到的,查了之后稍加分析就会发现原来就是把一片数据放入一块内存并得到它的长度 而已,那改成文件读入的方式也很容易,用fopen打开文件,计算一下文件的长度,然后用fread把数据全部读出来即可,这里就不贴代码了。
4.minimad运行时首先要将整个mp3文件读入内存,能不能改成边解码边读入的形式,比如每次读入16K,解码完再读入16K,而又不影响播放的连贯性,这样可以节省内存开销,方便在嵌入式系统中使用;
修改input()函数,在调用libmad中的mad_decoder_run()实现播放时,首先检查待解码缓存区中有没有数据,有则 解码,没有则调用input()函数一次以填充数据(填充多少可以自己指定),然后开始解码,解码后的数据交给output()函数处理,解码过程中,一 旦待解码缓存区中的解码数据不够则再次调用input()函数…… 在这里还要提一下struct buffer这个结构体,这个结构体是在input、output和decoder之间传送数据的载体,可以自行定义,比如我的数据来源是文件,待解码数 据缓存区大小为4K,要传递的私有数据包括文件指针、当前的位置、数据缓冲区、缓冲区的实际大小、文件的总大小等,则我这里定义如下:
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;
修改input()函数为如下形式,则每次调用填充BUFSIZE字节的数据:
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 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;}
注意:在上面的代码中涉及到了断桢问题,即一桢跨了两个BUFSIZE,这时候应该将缓冲区中的剩余数据先移至缓冲区头部,然后再从文件中读出数据填充缓冲区。
5.怎样用libmad设计一个简单的mp3播放器?
修改output()函数。 我在上面说过了,解码后的数据通过output()函数进行处理,在minimad.c中output()函数直接将解码后的数据送到标准输出,其实只要将这里修改为送到音频设备就可以实现播放了。 还有一点需要说明的是:mp3文件的采样率不是固定不变的,解码后的数据中包括采样率,在播放过程中,一旦采样率发生变化,要重新设置一下音频设备。 新建一个mp3player.c文件,然后将下面的代码复制进去,编译生成mp3player,这就是一个简单的mp3播放器了,可以用./mp3player 1.mp3命令来播放1.mp3文件。
#include <mad.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <pthread.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 format = AFMT_S16_LE;int channels = 2;soundfd = open("/dev/dsp", O_WRONLY);ioctl(soundfd, SNDCTL_DSP_SETFMT, &format);ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels);
}/** 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 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 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_ONE)sample = MAD_F_ONE - 1;else if (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;
}
Libmad音频解码库的编译和使用相关推荐
- Data Matrix 二维码解码库 libdmtx 编译方法
Data Matrix 二维码解码库 libdmtx 编译方法 libdmtx 是一个开源的 Data Matrix 编解码库.项目主页在:https://github.com/dmtx 这里简单记录 ...
- EasyDarwin开源音频解码项目EasyAudioDecoder:EasyPlayer Android音频解码库(第二部分,封装解码器接口)...
上一节我们讲了如何基于ffmpeg-Android工程编译安卓上的支持音频的ffmpeg静态库:http://blog.csdn.net/xiejiashu/article/details/52524 ...
- audioread-支持多种解码 (GStreamer + Core Audio + MAD + FFmpeg) 的python音频解码库
解码支持 使用任何可用的后端解码音频文件都是支持的.该库当前支持: 通过PyGObject的Gstreamer. 通过ctypes在Mac OS X上的核心音频.(不需要PyObjC) 通过pymad ...
- 让终端支持播放mp3,移植mp3解码库libmad和madplay到嵌入式linux
libmad简介 MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3).LIBMAD 提供 ...
- stm32 mp3软件音频解码案例分析流程(一)
最近做了一个无人机的语音播放项目,查了一下网上大部分都是硬件解码,成本相对比较高,而为了节约成本, 我们采用了stm32f446 RET6作为主控+tas5720作为dac功放,实现mp3软件音频解码 ...
- Helix MP3解码库脱离汇编指令束缚,运行在任何处理器上的解决方案
1.主要动机 Helix是应用广泛的MP3音频解码库,但是由于原版代码只针对ARM处理器和X86处理器做了底层的优化并有且只有适配这两种构架的处理器的底层代码.而常用的处理器架构远远不止这两种,例如英 ...
- 移植Opus音频编解码库到FreeScale iMX6q(飞凌嵌入式的OKMX6Q-C开发板)平台
移植Opus音频编解码库到FreeScale iMX6q(飞凌嵌入式的OKMX6Q-C开发板)平台 交叉编译器 使用飞凌提供的最新版交叉编译工具链,fsl-imx-x11-glibc-x86_64-m ...
- 【Arduino + Linux】基于 Helix 解码库实现 MP3 音频播放
目录 一.MP3 文件结构 1.1.ID3V2.3 1.1.1.标签头 1.1.2.扩展标签头 1.1.3.标签帧 1.2.音频数据 1.3.ID3V1 1.4.MP3文件结构图 二.MP3 解码库 ...
- 【原】AMR音频解码插件开发总结
近段时间一直在忙碌AMR音频解码插件的开发,由于对驱动开发和音频编解码知识了解不是很多,加之自身经验欠缺,致使开发过程中碰到太多棘手的问题.经过努力学习和领导的大力帮助下终于完成了插件的开发,现将开发 ...
最新文章
- Sublime Text3 使用
- jQuery对象和DOM对象
- ssm集成mysql_idea+SSM+Mysql框架整合
- GraphQL的JavaScript实现graphql-js应用举例
- 华泰证券 python 自动交易软件_为何选用股票自动交易助手
- 电子邮件营销的优势和劣势有哪些?哪些邮箱适合电子邮件营销?
- 虚拟服务器忘记密码,Win7系统下VMware虚拟机忘记开机密码如何解决
- python预测你的小孩身高_儿童身高预测方法
- pycharm打开长代码文件CPU占用率过高问题
- linu系统初始化配置
- 2020北京国际金融安全论坛(二)
- Arduino:设置ADC参考电压
- linux 时区文件的规则,linux – 如何将POSIX时间转换为Olson时区文件...
- 5-2 切面条Java
- 计算机毕业设计springboot基于VUE电脑城摊位出租系统
- 编程语言中的反射机制
- linux关闭内存插槽,linux 统管理中的查看内存插槽数、最大容量和频率
- TP-Link wr703n 刷Openwrt始末
- Flink入门——DataSet Api编程指南
- word标尺工具应用:如何快速调整版面距离参数