具体的移植过程主要有如下几点:
(1)mad.h的嵌入汇编问题
打开mad.h会发现这是一个很大的头文件,一般我们在c++中编写头文件的时候
都是采用总的一个文件a.h,在a.h中include了若干个头文件。这样可以希望在
编译的时候,把cpp编译成.o或者.obj文件,然后链接。
(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文件
一起链接是可以的,但是太麻烦了。
能是它要使用汇编优化的原因吧,有的.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的机器上实际测试的结果来看,效率是有所降低,但是
不是致命的,是可以接受并通过一些其它方面的优化措施加以补救的。
# 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文件,里面包含了很多个头文件的定义,
需要耐心的加入上面的那些定义。
然后把所有的.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;
这个错误是非常让人郁闷的,修改起来也需要非常大的耐心才行,我们来看下面的定义:
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;
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;
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 ;
呵呵,这样做才能把原先说是数据类型没有定义的部分都变成已经定义的部分。
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)
就可以了。
例如在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,这样也会出现类型转换的错误,只要细心一些,灵活运用强制类型转换应该就可以解决这个问题。
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文件。
例如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
的源代码公布出来,希望能够对后来者有所帮助,可以省下时间做更加有意义的事情。
的源代码公布出来,希望能够对后来者有所帮助,可以省下时间做更加有意义的事情。