win mobile 5播放mp3音乐的方法(1)--libmad库的移植篇

北京理工大学  20981  陈罡
太好啦,又到周末了。时间过得真快啊!心疼ing。。。
win mobile 5上面播放mp3似乎已经有了可以依赖于win mobile提供的
multi-media播放器的调用方法。这种显而易见的方法,我认为缺少一些
主动性,只能微软怎么做,咱就怎么做。如果想做出个性化的软件或者
播放器,或者实现在线边下载边播放的方案就很困难了。对于从moto的
linux移植到win mobile的5mbox而言,我更熟悉传统的一些的处理mp3文件
的方案。
基本的思路是:先把mp3文件读取到缓冲区,然后解码,解成pcm码流,然后
下一步是把pcm码流送给系统的播放接口开始播放。
mp3文件读取自然没有什么问题,不需要多说。如果有心提高播放效率的话,
可以考虑加入一个线程后台读取,把mp3的二进制数据直接读入一个二进制
队列里面,然后解码器再从队列中读取块数据进行解码。经过我在多普达p800
真机上测试,这一步由于是二进制文件的顺序读取,如果不是网络应用
(例如:边下载边播放的那种),是可以忽略不计的,对mp3的解码没有
什么大的影响。
总体来说,解码的速度,应该是整个程序的瓶颈(实际上也确实如此)。
于是乎目光转向了libmad这个开源的mp3解码器。很多linux下面优秀的mp3
播放器都是基于这个解码器做得。
我用来移植的libmad的版本是0.14.2的这个版本(在version.h文件里面有定义)
# define MAD_VERSION_MAJOR 0
# define MAD_VERSION_MINOR 14
# define MAD_VERSION_PATCH 2
# define MAD_VERSION_EXTRA " (beta)"
但是在config.h里面写的是:
#define VERSION "0.14.2b"
不管了,大概是这个样子的。
具体的移植过程主要有如下几点:
(1)mad.h的嵌入汇编问题
打开mad.h会发现这是一个很大的头文件,一般我们在c++中编写头文件的时候
都是采用总的一个文件a.h,在a.h中include了若干个头文件。这样可以希望在
编译的时候,把cpp编译成.o或者.obj文件,然后链接。
这里的mad.h看上去就是把各个头文件的内容都直接拷贝到这个文件中,我想可
能是它要使用汇编优化的原因吧,有的.o不是由对应的.c编译出来的,而是通
过.S编译出来的,可以有选择的链接,当然也不一定对,不管它了。
这里面需要注意的是fix.h中的关于FPM_xxxx的定义,它涉及到一系列的嵌入式
汇编语句。我查看了一下,多数都是linux平台下面的gcc格式的嵌入式汇编格式
我尝试过用cygwin或者mingw编译汇编的文件,然后放到目录下跟win32的obj文件
一起链接是可以的,但是太麻烦了。
所以在这里,我还是希望能够用vs2005的编译器直接编译,这样就需要在:
# if defined(FPM_FLOAT)
前面加入#define FPM_DEFAULT,使得代码看上去是这样的:
#define FPM_DEFAULT
# if defined(FPM_FLOAT)
....
在mad.h和fix.h两个文件中都要加。这样宏就会自动选择default的形式来编译了
采用c语言的普通移位运算来做一些操作。这相比于汇编代码自然效率低了不少。
经过我在win mobile 5的机器上实际测试的结果来看,效率是有所降低,但是
不是致命的,是可以接受并通过一些其它方面的优化措施加以补救的。
(2)把所有的.c文件都改为.cpp文件,这是例牌了。
然后把所有的.h文件#ifndef xxxx, #define xxxx后面都加入:
#ifdef __cplusplus
extern "C" {
#endif
xxxxxxxxxxxx
xxxxxxxxxxxx
....代码
xxxxxxxxxxxx
xxxxxxxxxxxx
#ifdef __cplusplus
}
#endif
这里面特别需要说明的是mad.h文件,里面包含了很多个头文件的定义,
需要耐心的加入上面的那些定义。
(3)类型定义方面的错误
这个错误是非常让人郁闷的,修改起来也需要非常大的耐心才行,我们来看下面的定义:
struct A {
    struct B {
        struct C {
            ....
        } xxx ;
        ....
   } xxxx ;
} ;
在c语言中,没有所谓名字空间的概念,上述定义是可以直接引用的:
struct A xxx ;
struct B xxx ;
struct C xxx ;
这样的声明在c语言中是完全合法的,但是到了c++中,就不再合法,如果要定义的话必须用如下
方法:
struct A xxxx ;
struct A::B xxx ;
struct A::B::C xxx ;
采用这样的方法才行,理解这一点,对于成功移植layer3.cpp尤为重要,这个文件中是有一个嵌套
的结构体定义(可能还有别的,我只是用这个来举个例子):
struct sideinfo {
  unsigned int main_data_begin;
  unsigned int private_bits;
  unsigned char scfsi[2];
  struct granule {
    struct channel {
      /* from side info */
      unsigned short part2_3_length;
      unsigned short big_values;
      unsigned short global_gain;
      unsigned short scalefac_compress;
      unsigned char flags;
      unsigned char block_type;
      unsigned char table_select[3];
      unsigned char subblock_gain[3];
      unsigned char region0_count;
      unsigned char region1_count;
      /* from main_data */
      unsigned char scalefac[39]; /* scalefac_l and/or scalefac_s */
    } ch[2];
  } gr[2];
};
这里面,对于这个sideinfo里面包含的结构体的定义,就需要像我上面提到的那样嵌套的定义才可以。
struct sideinfo xxxx ;
struct sideinfo::granule xxxx ;
struct sideinfo::granule::channel xxxx ;
呵呵,这样做才能把原先说是数据类型没有定义的部分都变成已经定义的部分。
(4)常规的类型转换问题
例如在bit.cpp中定义的:
unsigned long mad_bit_read(struct mad_bitptr *bitptr, unsigned int len)
作者为了方便,直接定义为unsigned long类型的,而在具体使用中经过c++编译器编译以后,就会爆出
无法进行类型转换的warning或者error,需要手工强制类型转换为(unsigned char)mad_bit_read(xxxx)
就可以了。
这样的情况还会发生在enum类型的数据赋值上面,在c语言中,我们通常认为enum就是int数值,所以
libmad的作者也是如此考虑的(在c++语法中,这一点是不同的),很多enum类型返回值都让作者弄成是
int,这样也会出现类型转换的错误,只要细心一些,灵活运用强制类型转换应该就可以解决这个问题。
(5)一些设置文件的修改
例如config.h,可以把如下linux相关部分定义去掉
//#define HAVE_ERRNO_H 1
//#define HAVE_FCNTL_H 1
//#define HAVE_SYS_TYPES_H 1
多试几次,就可以完成libmad的移植工作,经过在多普达p800上的测试,可以流畅地播放mp3文件。
上面这些是手工移植的大体总结,下面我把移植好的,可以在win mobile 5.x上直接运行的libmad
的源代码公布出来,希望能够对后来者有所帮助,可以省下时间做更加有意义的事情。
文件: libmad.rar
大小: 155KB
下载: 下载

你可能感兴趣的:(linux,汇编,struct,mobile,音乐,编译器)