Ehome:智能家居之多媒体系统mp3音频播放

来源:互联网 发布:英译汉软件下载 编辑:程序博客网 时间:2024/04/30 18:15
7. MP3功能的实现
7.1 基本概念
     PCM: 脉冲编码调制
          声音是模拟量
          计算机能处理的是数字量,涉及模拟量和数字量的相互转换
          录音时是模拟量转数字量
          播放时是数字量转模拟量

     采样频率
          每秒钟抽取声波幅度样本的次数
          当采样频率应该在40kHz左右,保证声音不失真
     量化位数
          每次采样,模拟音频信号的振幅用多少个bit来表示
          一般使用16bit
     声道数:
          有单声道和双声道之分,双声道又称为立体声  
     MP3:
          音频数据的压缩算法
          有损压缩(还原后是有失真的)
          压缩比可以接近10:1-12:1
          使压缩后的文件在回放时能够达到比较接近原音源的声音效果

7.2 MP3文件的解码工作
      // mp3.rar/MP3_format_parse.doc
      其存储格式:
          TAGV2
         {  。。。 }
          FRAME /* 一帧数据 */
         {
             帧头
             {。。。。 }
             数据实体
         }
          FRAME
          ...
          TAGV1
      {  。。。 }

      mp3文件的解码工作,可以使用开源的库完成:// mp3.rar/libmad-0.15.1b.tar.gz
      用于解码的libmad库的使用步骤:
         1)$:' cd project/
         2)$:' mkdir mp3
             $:' cd mp3
         3)$:' cp /mnt/hgfs/jy/project/env/mp3/libmad-0.15.1b.tar.gz ./
         4)$:' tar xvf libmad-0.15.1b.tar.gz
         5)$:' vi README
               // 一般开源代码查阅和使用入口,先读readme,README重要信息如下:
               // mad.h ---> API
               // minimad.c ---> 例子程序:vi minimad.c
               // INSTALL ---> configure 生成 Makefile和config.h:vi INSTALL
               // $: vi INSTALL

---> 查看详细编译步骤 1.2.3.4.5.....
             $:' ./configure --help
                 // 查看可以加什么参数来配置Makefile
             $:' ./configure CC=arm-cortex_a9-linux-gnueabi-gcc --host=arm-linux --prefix=/home/tarena/project/mp3/install
                 // 用来生成合适的Makefile
                --host: 运行目标平台
                --prefix: [前缀]安装路径 make install

         6)$:' make clean
                 // 开源代码注意清理不必要的已编译文件,方便编译最新的配置后的内容。
            $:' make && make install
                 // /home/tarena/project/mp3/install下就有两个文件夹include和lib
         7)$:' vi minimad.c
                 // 编译到arm开发板,移植库过去(/home/mp3/lib,profile添加库路径)
            libmad API使用的演示demo:
           从标准输入获取要解码的数据 ↓
           进行解码 ↓
       解码后的数据打印到标准输出设备  ↓
   计划将解码之后的数据输出到声卡设备 ↓


// Linux_sound_guide.doc
// ALSA Audio API 使用指南(中英版) - 幻雪神界 - 博客频道 - CSDN.NET.html


7.3 声卡的驱动程序
        linux下的声卡驱动分两种:
             OSS: Open Sound System
                  最早出现的
                  非开源程序
                  /dev/dsp
                  /dev/mixer
             ALSA: 开源    
 $:' make menuconfig
            Device Drivers  ---> 
                 <*> Sound card support  ---> 
                     <*>   Advanced Linux Sound Architecture  --->

                              // linux内核自带的声卡驱动程序配置选项,默认勾选。
      #:' ls /dev/snd/
         controlC0
         controlC1
         pcmC0D0c
         pcmC0D0p
         pcmC1D0p
         timer
 // 音频/声卡相关设备 ↓

7.4 alsa库的使用
     如果是alsa架构,提供了一套用户空间访问以上设备文件的库函数
     // alsa-lib-1.1.1.tar.bz2
     alsa库封装了对/dev/snd下设备文件的操作
     编译步骤:
         1)$:' cd mp3/
         2)$:' cp /mnt/hgfs/project/env/mp3/alsa-lib-1.1.1.tar.bz2 ./
            $:' tar xf alsa-lib-1.1.1.tar.bz2
            $:' cd alsa-lib-1.1.1/
              // 开源库如果没有README就从INSTALL文件中查看 $: vi INSTALL
         3)$:' ./configure --help
             // 查看可添加参数
             $:' ./configure CC=arm-cortex_a9-linux-gnueabi-gcc --host=arm-linux --prefix=/home/tarena/project/mp3/install  --with-configdir=/opt/alsa --with-plugindir=/opt/alsa_lib
   // CC:编译器。--host:运行目标平台。--prefix:安装路径。
   // --with-configdir:配置安装目录 /opt/alsa
   // --with-plugindir:插件安装目录 /opt/alsa_lib

      4) $:' make clean && make && make install
         
      以上库函数的使用可以参考:
      alsa使用官网:
         http://www.alsa-project.org/alsa-doc/alsa-lib/index.html
         http://www.alsa-project.org/main/index.php/Download
      或者参考:
        // ALSA Audio API 使用指南(中英版) - 幻雪神界 - 博客频道 - CSDN.NET.html

    两套API,一套解码,一套访问声卡设备。
    以上两个库libmad alsa库的使用实例 // mp3_player.c

    实现 #:' ./player xxx.mp3
    可以播放xxx.mp3音乐
    mp3_player.c和minimad.c相比修改以下内容:
       1) 原来从标准输入读取解码数据
            现在从xxx.mp3文件中读取解码数据
       2) 设置声卡的工作属性 44.1KHz 16bit 双声道
          set_pcm() //参考文档中“一个最简单的回放程序”        {          /*打开声卡设备*/          rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);          //硬件设置为交错模式          snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);            //设置16位采样精度          rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM _FORMAT_S16_LE);            //设置声道,1表示单声>道,2表示立体声          rc=snd_pcm_hw_params_set_channels(handle, params, channels);          //设置频率               snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);          }
       3) 修改了output
          将解码的音频数据输出到声卡设备
    注意:
     2)3)参考 //ALSA Audio API 使用指南(中英版)-幻雪神界 - 博客频道 - CSDN.NET.html
          
          /*libmad解码的音频帧数据写入声卡设备*/
          snd_pcm_writei(handle, OutputPtr, n);
      
编译 部署 运行 player程序:
1) $:' cd ~/project/mp3/
     $:' cp /mnt/hgfs/jy/project/env/ehome/ehome_day07/mp3/mp3_player.c ./
     $:' arm-cortex_a9-linux-gnueabi-gcc mp3_player.c -lmad -lasound -Linstall/lib/ -I install/include/ -o player
        // 编译,注意指定的库文件路径
如果出现了以下错误:
         libmad.so : III_....
解决方法:
 cd libmad-0.15.1b
 make clean  // 主要是此步骤,清空原有版本.o文件
 make 
 make install
 arm-cortex...... mp3_player.c // 再次编译即可

 
2) $:' cp player ../rootfs/home/bin/
3) $:' cp install/lib/* ../rootfs/home/lib/ -a
      // 库文件拷贝,注意 -a 选项拷贝最安全(软连接)
4) $:' cp /opt/alsa/ ../rootfs/opt/ -a
     $:' cp /opt/alsa_lib/ ../rootfs/opt/ -a 
       // 部署配置文件和插件库文件
  注意,因为配置时指定了相应的路径:
  --with-configdir=/opt/alsa --with-plugindir=/opt/alsa_lib
  部署到开发板上时路径一定要保持一致!!!
5) $:' mkdir ../rootfs/home/songs
     $:' cp /mnt/hgfs/project/env/mp3/*.mp3 ../rootfs/home/songs/
6) $:' source /home/etc/profile
         // 刷新环境变量
     $:' /home/bin/player /home/songs/I\ Will\ Always\ Love\ You Always\ Love\ You.mp3 1>/dev/null 2>&1
         // 运行播放拷贝过去的mp3文件。1>/dev/null 2>&1 将标准输入和标准出错都定向到null设备,不显示一些不必要的出错信息,MP3正常播放即可。
   
     练习:
       1)看懂mp3_player.c
       2)编译、部署到开发板运行
       3)/home/bin/player /home/songs/xxx.mp3
            //将/home/songs目录下的所有.mp3文件播放一遍
          #:' /home/bin/player /home/songs
            // unix的目录操作,对函数进行修改。

          参考代码:// ehome.tar.gz/server/myplayer.c  myplayer_hw.c
          有目录操作
/** 代码演示 - 实现随机播放 mp3_player.c **/int main(int argc, char *argv[]){struct stat stat;void *fdm;if (argc != 2){printf("Usage: minimad + mp3 file name");return 1;}DIR *p_dir = opendir (argv[1]);if (!p_dir) {perror ("opendir");return 11;}printf ("open %s success...\n", argv[1]);srand (time (0));char buf[128];char* path[12];int i = 0, j = 0, k = 0;struct dirent *p_dire;while ( (p_dire = readdir (p_dir)) ) {if (strstr (p_dire->d_name, ".mp3")) {path[i] = (char*)malloc (sizeof (buf)); // 最佳方法使用结构体structmemset (buf, 0, sizeof (buf));strcpy (buf, argv[1]);strcat (buf, p_dire->d_name);strcpy (path[i], buf);printf ("path[%d] = %s\n", i, path[i]);i++;}}while (j < i) {k = rand() % i;printf ("start playing %s...\n", path[k]);printf ("rand k = %d\n", k);int fd = open(path[k], O_RDWR);printf ("path[%d] = %s\n", k, path[k]);if(fd<0){perror("open file failed:");return 1;}    if (fstat(fd, &stat) == -1 || stat.st_size == 0){printf("fstat failed:\n");return 2;}//printf("stat.st_size=%d\n",stat.st_size);fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);if (fdm == MAP_FAILED)return 3;if(set_pcm()!=0){printf("set_pcm fialed:\n");return 1;   }signal(SIGINT, sig_close);decode(fdm, stat.st_size);if (munmap(fdm, stat.st_size) == -1)return 4;printf ("end playing %s...\n", p_dire->d_name);free (path[j]);printf ("starting next song...\n");j++;}closedir (p_dir);snd_pcm_drain(handle);snd_pcm_close(handle);return 0;}


0 0