基于alsa和libmad嵌入式mp3播放器

可移植ok6410 的mp3播放器,基于alsa和libmad库,内含编译好的库和播放器以及源代码,解压后可直接运行,和以移植好的mplayer官方播放器(可快进 暂停 时间轴 音量控制等)

源码及移植库文件下载地址:
https://download.csdn.net/download/ywueoei/10898346

####################################################################

1 设计功能概述
基于ALSA通过Libmad解码对MP3格式音乐进行播放。
2 设计方案
2.1 设计说明
由于 Linux 具有开放源码,软件资源丰富,性能高效、稳定,支持多种体系结构,大小、功能都可定制,价格低廉等优点,本MP3 播放器系统基于嵌入式Linux操作系统来实现。
而对于 MP3 播放器一些底层功能的实现,可以基于一些开源音频解码库,只要开发者遵循开发协议即可,这样可以加快开发速度和减少开发成本。在本 MP3 播放器的设计和实现的过程中,主要采用 libmad 解码库。libmad是一个开源的高精度MPEG音频解码库,它提供 24-bit 的PCM输出。用 libmad 解码库对 MP3 文件进行解码播放的音质比较好,而且使用libmad提供的API,可以实现 MP3 数据解码和顺序播放
开发环境:Linux内核版本:Linux Ubuntu 3.1.0,开发环境Ubuntu16.4交叉编译器arm-linux-gcc,开发语言为C语言,开发工具:vim,在arm-linux平台上的实现:将代码使用arm-linux-gcc工具在PC端进行静态编译,编译完成之后,将生成的可执行文件放在主机目录下,使用主机的xshell连接arm-linux开发板,通过nfs工具将可执行文件上传至开发板。
2.2 设计过程
1.ALAS和Libmad两个库的移植
1)ALSA移植
① 下载必要的文件
下载页面:http://dl.ambiweb.de/mirrors/ftp.alsa-project.org/lib/
alsa-lib-1.0.24.1.tar.bz2
alsa-utils-1.0.24.2.tar.bz2
在/forlink/nfsroot/alsa_arm目录下存放它们
② 编译安装alsa-lib
解压alsa-lib,然后进入解压后的目录
在终端下输入配置命令:
./configure --host=arm-linux --target=arm-linux --prefix=/usr/local/arm/4.5.1/arm-none-linux-gnueabi/lib/arm-alsa --enable-static --enable-shared --disable-python --with-configdir=/usr/share --with-plugindir=/usr/lib/alsa_lib
配置完成后编译:
make
编译完成后安装;
make install
③ 编译安装alsa-utils
解压alsa-utils,然后进入解压后的目录
在终端下输入配置命令:
PKG_CONFIG_PATH="/usr/local/arm/4.5.1/arm-none-linux-gnueabi/lib/arm-alsa/lib/pkgconfig"
LDFLAGS="-L/usr/local/arm/4.5.1/arm-none-linux-gnueabi/lib -lpthread -lc -lgcc"
CFLAGS="-I/usr/local/arm/4.5.1/arm-none-linux-gnueabi/include"
./configure
–host=arm-linux
–target=arm-linux
–prefix=/forlink/nfsroot/alsa_arm/alsa_u
–disable-alsamixer
–disable-xmlto
–with-alsa-prefix=/usr/local/arm/4.5.1/arm-none-linux-gnueabi/lib/arm-alsa/lib/
–with-alsa-inc-prefix=/usr/local/arm/4.5.1/arm-none-linux-gnueabi/lib/arm-alsa/include/
配置完成后编译:
make
编译完成后安装:
make install
2)Libmad移植
① 下载 libmad-0.15.1b.tar.gz,解压,运行configure,生成makefile,修改 makefile。
./configure CC=arm-linux-gcc --host=arm-linux --disable-shared --disable-debugging --prefix=/usr/local/libmad/mad
gedit Makefile
Ctrl+F找到并删除-fforce-mem -> Save(删掉-fforce-mem)
② 编译&安装。
make -j4 && make install -j4
//-j4是4线程的意思,可以不加直接make。看电脑处理器决定。
3)将编译alsa-lib,alsa-utils所产生的4个目录复制到/usr/ks/
四个目录:
/usr/local/arm/4.5.1/arm-none-linux-gnueabi/lib/arm-alsa
/usr/share/alsa
/usr/lib/alsa_lib
/forlink/nfsroot/alsa_arm/alsa_u
目标板中挂载共享目录/forlinx/nfsroot/到/mnt/nfs目录
在超级终端下输入命令:mount -o nolock 192.168.137.2:/forlinx/nfsroot /mnt/nfs
将目标板挂载目录下的文件复制到正确的位置,在超级终端下进入/mnt/nfs目录。
首先移植alsa_u中的文件:
cd /tmp/alsa_u
cp -r bin/* /bin
然后移植arm-alsa中的文件
cd /tmp/arm-alsa
cp -r lib/* /lib
然后移植alsa中的文件
cd /tmp
cp -r alsa/* /usr/share
到此alsa移植完成,alsa_lib中的文件不移植也可以
测试:输入aplay ring.wav,可以听到铃声
2.编译代码。
·编写MP3播放程序Mp3player.c
·交叉编译:arm-linux-gcc -o Mp3player Mp3player.c -lmad -lasound
编译生成Mp3player便是可以在开发板上直接运行的文件,把文件夹和一首 歌放到U盘,板子插上U盘,监测U盘盘符并挂载。
把文件拷到板子上,运行,完美播放音乐。
cp /udisk/Mp3player /bin -rf
cp /udisk/pfzl.mp3 /bin
cd /bin/
运行:./Mp3player pfzl.mp3 50 (音量)

int main(int argc, char *argv[])
{
  struct stat stat; //定义相关文件状态信息结构体
  void *fdm;

  if (argc != 3)
    {
    printf("Usage: minimad + mp3 file name + volume\n");
    return 1;
    }
  int fd;
  fd=open(argv[1],O_RDWR);
  if(fd<0)
  {
    perror("open file failed:");
    return 1;
  } 
 
  if (fstat(fd, &stat) == -1 ||stat.st_size == 0)  //获取文件状态
  {
    printf("fstat failed:\n");
    return 2;
  }
  
  fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);  //共享存储文件映射,可读
  if (fdm == MAP_FAILED)
    return 3;

  
  if(set_pcm()!=0) //设置pcm 参数
    {
        printf("set_pcm fialed:\n");
        return 1; 
    }
    set_volume(atoi(argv[2]));     //设置混音器
    decode(fdm, stat.st_size);     //解码

    if (munmap(fdm, stat.st_size) == -1)
      return 4;

    snd_pcm_drain(handle);  //把所有挂起没有传输完的声音样本传输完全,最后关闭该音频流,释放之前动态分配的缓冲区,退出。
    snd_pcm_close(handle);  //关闭声卡

  return 0;
}
Alsa设置
int set_pcm()
{
    int rc; 
    int dir=0;
    int rate = 44100;; /* 采样频率 44.1KHz*/
    //int format = SND_PCM_FORMAT_S16_LE; /* 量化位数 16 */
    int channels = 2; /* 声道数 2 */
    
    rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); //打开默认的PCM
    if(rc<0)
    {
      perror("\nopen PCM device failed:");
      exit(1);
    }
    snd_pcm_hw_params_alloca(&params); //分配params结构体

    rc=snd_pcm_hw_params_any(handle, params);//初始化params
    if(rc<0)
    {
      perror("\nsnd_pcm_hw_params_any:");
      exit(1);
    }
    rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //初始化访问权限
    if(rc<0)
    {
      perror("\nsed_pcm_hw_set_access:");
      exit(1);
    }
    rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); //设置16位采样精度 
    if(rc<0)
    {
      perror("snd_pcm_hw_params_set_format failed:");
      exit(1);
    } 
    rc=snd_pcm_hw_params_set_channels(handle, params, channels); //设置声道,1表示单声>道,2表示立体声
    if(rc<0)
    {
      perror("\nsnd_pcm_hw_params_set_channels:");
      exit(1);
    }
    rc=snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir); //设置>频率
    if(rc<0)
    {
      perror("\nsnd_pcm_hw_params_set_rate_near:");
      exit(1);
    }   
	rc = snd_pcm_hw_params(handle, params); //安装从配置空间中选择的一个PCM硬件配置,然后snd_pcm_prepare。
    if(rc<0)
    {
      perror("\nsnd_pcm_hw_params: ");
      exit(1);
    } 
    return 0; 
}

void set_volume(long volume)
{
  snd_mixer_t *mixerFd;
  snd_mixer_elem_t *elem;
  long minVolume = 0,maxVolume = 100;
  int result;
  // 打开混音器
   if ((result = snd_mixer_open( &mixerFd, 0)) < 0)
   {
        printf("snd_mixer_open error!\n");
        mixerFd = NULL;
   }
  //将HCTL连接到打开的混音器
   if ((result = snd_mixer_attach( mixerFd, "default")) < 0)
   {
        printf("snd_mixer_attach error!\n");
        snd_mixer_close(mixerFd);
        mixerFd = NULL;
   }
  // 注册混音器
   if ((result = snd_mixer_selem_register( mixerFd, NULL, NULL)) < 0)
  {
        printf("snd_mixer_selem_register error!\n");
        snd_mixer_close(mixerFd);
        mixerFd = NULL;
  }
  // 加载混音器
  if ((result = snd_mixer_load( mixerFd)) < 0)
  {
        printf("snd_mixer_load error!\n");
        snd_mixer_close(mixerFd);
        mixerFd = NULL;
  }
   // 遍历混音器元素
    for(elem=snd_mixer_first_elem(mixerFd); elem; elem=snd_mixer_elem_next(elem))
    {
        if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE &&
             snd_mixer_selem_is_active(elem)) // 找到可以用的, 激活的elem
        {
            snd_mixer_selem_get_playback_volume_range(elem, &minVolume, &maxVolume);
            snd_mixer_selem_set_playback_volume_all(elem, volume);
        }
    }
snd_mixer_close(mixerFd);

Libmad设置
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;
}
/*以下实用程序例程可对MAD的高分辨率采样进行简单的舍入,限幅和缩放,最低可达16位。*/
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;
  return sample >> (MAD_F_FRACBITS + 1 - 16);
}
  *这是输出回调函数。 在每帧MPEG音频数据被完全解码之后调用它。 此回调的目的是输出(或播放)已解码的PCM音频。
*/
static enum mad_flow output(void *data, struct mad_header const *header,struct mad_pcm *pcm)
{
  unsigned int nchannels, nsamples,n;
  mad_fixed_t const *left_ch, *right_ch;
 /*pcm-> samplerate包含采样频率*/
  nchannels = pcm->channels;
  n=nsamples = pcm->length;
  left_ch = pcm->samples[0];
  right_ch = pcm->samples[1];
  unsigned char Output[6912], *OutputPtr; 
  int fmt, wrote, speed, exact_rate, err, dir; 
   OutputPtr = Output; 
   while (nsamples--) 
   {
    signed int sample;
   /*以16位有符号的小端PCM *///输出样本
    sample = scale(*left_ch++);
    *(OutputPtr++) = sample >> 0; 
    *(OutputPtr++) = sample >> 8; 
    if (nchannels == 2) 
        { 
            sample = scale (*right_ch++); 
            *(OutputPtr++) = sample >> 0; 
            *(OutputPtr++) = sample >> 8; 
        } 
  }
    OutputPtr = Output; 
    snd_pcm_writei (handle, OutputPtr, n); 
    OutputPtr = Output; 

  return MAD_FLOW_CONTINUE;
}

  //这是错误回调函数。
Static enum mad_flow error(void *data, struct mad_stream *stream, struct mad_frame *frame)
{
  struct buffer *buffer = data;
  printf("this is mad_flow error\n");
  fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
      stream->error, mad_stream_errorstr(stream),
      stream->this_frame - buffer->start);
  / *在此返回MAD_FLOW_BREAK以停止解码(并传递错误)* /
  return MAD_FLOW_CONTINUE;
}

 //这是main()调用的函数,用于执行所有解码。
static
int decode(unsigned char const *start, unsigned long length)
{
  struct buffer buffer;
  struct mad_decoder decoder;
  int result;

 /*初始化我们的私有消息结构*/
  buffer.start = start;
  buffer.length = length;
 /*配置输入,输出和错误功能*/

  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;
}

源码及移植库文件下载地址:
https://download.csdn.net/download/ywueoei/10898346

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值