2009年5月28日星期四

AMR Plugin For Winamp & foobar2000

好吧,我快要叛变了。又写了个foobar2000的AMR插件,下载链接等在文末。

KMplayer就可以放AMR,但启动太慢了。foobar2000早就有相关插件,问题是歌词外挂支持不良。惦记了很久Winamp的AMR插件一直没出现,今天终于自己写了一个。

(从*UIX系统来的不算,命令行工具不算,Winamp是我现在还常用的辈分最老的程序了,总觉得有那么点英雄迟暮的味道。)

AMR由rfc3267定义,网上最容易找到的实现是3GPP发布的3GPP TS 26.104(AMR-NB)和3GPP TS 26.204(AMR-WB)。

由于写这个插件就是为了播放手机录制的音频文件,而手机录制的音频文件全部都是AMR-NB IF1格式,所以这个插件目前只实现了AMR-NB IF1的解码功能。ETSI和IF都没有文件头,应该不是设计给独立文件用的。至于AMR-WB,以后遇到了再说吧。

AMR-NB IF1格式很简单,头部为6个字节的标识信息"#!AMR\n",余下部分为若干个frame。每个frame第一字节的前4位决定了frame的字节长度(包括首字节),如1表示13,2表示14,3表示16,有个表可以查。

AMR-NB的目标码率只有一种,8kHz、13bit、单声道。而且每frame表示的数据量也是一定的,即160次采样的数据,20ms长。

因为AMR-NB格式没有任何的索引信息,所以不扫描整个文件就无法得知文件时长,这也是KMplayer播放AMR-NB时为什么不能拖动的原因。

Winamp - in_amr_v0.1_20090902.zip

Google Drive
OneDrive

foobar - foo_input_amr_v0.4.0_20220901.zip

Google Drive
OneDrive

2022-9-1 oAMpF v0.4.0
Update with SDK v20220810
Tested with fb2k v2.0b3
Add x64 support
2017-9-18
Box的下载链接挂掉了,换成了Google Drive,也不知道能用多久…
2012-8-13 oAMpF v0.3.1
增加单声道AMR-WB支持,改了几个bug。
2010-9-24 oAMpF v0.2.1
忘记具体改了什么,好像是一点bug的说。这一版的foobar插件已经比较完善了,起码我自己用没遇到什么问题。
2010-4-15 oAMRpF v0.2
基本功能跟Winamp版一样,多了示波功能
AMR-NB IF1最后一个frame的实际长度有可能小于应当值,不知道合不合规范,懒得查了
2009-5-28 oAMRpW v0.1
支持AMR-NB IF1格式
可拖放,可显示文件时长
播放损坏的文件时可能crash
不支持Winamp示波器及相关插件

2009年5月26日星期二

Build Ruby With ICC

前些日子试图用WinDDK编译Ruby失败,不过却一直惦记着。这两天突然想起,天底下除了VC和GCC,还有一个叫Intel C++ Compiler的东西,于是就试了试。

我用的ICC是v10.1.021版,虽然v11早就有了,可看起来新版本不再支持VC6了的样子,所以只好用旧版。

正常运行configure.bat,打开生成的Makefile,在最后一句include前面加上如下三行:

CC = icl
CPP = icl
LDFLAGS = -incremental:no -debug -opt:ref -opt:icf /nodefaultlib:libmmd.lib

最后一句的nodefaultlib是为了去掉对libmmd.dll的依赖,虽然据说会快一点,不过能少一个库也是好的。

编译过程没出什么问题,除了编译速度很慢很慢以外。ZLib和PDCurses用ICC重新编译过,IConv和OpenSSL则犯懒了,用的是以前VC6编译的版本,链接也都OK。

nmake test时出错,跟了一下发现是Win32API的bug。CFunc的默认调用约定是cdcel,而绝大部分Windows APIs应该用stdcall。可奇怪的是,VC6编译出来的代码,用cdcel方式调用也可以。

跑到Ruby官网上逛了一圈,发现该问题已修正,只是要下个版本才会发布。自己编译1.9.1-p120的话,还是得在编译前手工改代码。

暂时没遇到其他的问题,copy了一个用VC6编译的sqlite3_api.so,能用,ICC和VC的兼容性还蛮不错的。

benchmark没很严格地跑,随便写了一点小东西比较,比VC6编译的版本快很多,但却比MinGW(gcc 3.4)编译的略慢些,不知道算不算正常现象。我的老机器跑ICC太累,OpenMP和SSE2之类的也都用不上,先这样吧……

2009年5月21日星期四

Ruby中的负数整除问题

Ruby中两个整数相除,其结果为整数,例如1/2为0,7/3为2。这跟C是一样的,所以我一直以为Ruby的除法运算跟C是一回事。前些日子移植一个C程序时结果出错,这才发现自己又想当然了。在Ruby中,-7/3不等于-2,而是-3。

翻书,《The Ruby Programming Language》有提到这个问题。书中解释,Ruby作整除运算时对结果进行向下(负无穷大)取整,而C则是向零取整。相应的,Ruby中-7%3的结果就是2,而非-1,想来这样的结果是为了满足(a/b)*b+(a%b)=a这一等式。

C当然也可以进行向下取整,函数名为floor。近似地,向上(正无穷大)取整的函数名则叫ceil。Ruby中也有同名的两个取整方法,若想在Ruby中进行向零取整,需要调用truncate方法,而想获得跟C一样的求余结果则要用remainder方法。

仔细想想,总觉得Ruby这样的处理方式有点不妥。

像Ruby这样的动态语言,由于不存在类型声名,运算结果类型不确定就很容易出错,而且这种错误很难发现。既然都自诩为高级语言了,还不如像Javascript那样直接返回实数,这样更明确,也更贴近于数学。

退一步说,即使要取整,似乎也应该用truncate而非floor。前者是直接丢弃小数部分,应该比较快,而且跟广为流行的C保持一致。

很想知道为什么Ruby、Python、Tcl都采用(过)floor,查了查却不得其解,算了。

2012-06-28 Update

经某位家住清华园的友人指点,原来是为了所谓数学上的完备性。

当a和b都是正数时,a整除b,得数q,余数r,则满足b*q + r == a,且0 <= r < b。

当a是负数,b是正数时,向下取整可以保证上述表达式仍然成立。

至于为什么要使modulo返回正数,请参考Wikipedia

似乎不是个很有趣的原因…