OSD Lyrics 正式支持 MOC

我真是闲得蛋疼了,在后天就要考试的情况下还有工夫来做MOC支持。

之前所说,MOC目前我还没有找到IPC的方法来获取信息,只能用命令行来获取。

而fork+管道+mocp本身的开销实在不小,在0.1秒间隔的频率下对CPU的占用比其他的播放器要高不少。不过话虽如此,还是能用的。

另外,在aqtccgh同学的帮助下,终于定位了会导致之前可以搜索却不能下载的原因所在。现在应该没有那么多失败的情况了吧。

关于网络不好时崩溃的问题,是因为DNS解析超时加上多线程而造成的一个bug,要留到放假再解决了,在此提供一个临时解决方案:

 

su
echo 118.228.148.185 mp3.sogou.com >> /etc/hosts
echo 125.39.78.33 www.qianqian.com >> /etc/hosts

 

于是下一个目标:Quod Libet

 

osd-lyrics Comments(1) 2010年1月10日 00:51

解决MOC的乱码问题

MOC(MOCP)是一个不错的命令行下播放器,但是不少人会遇到歌词的tag乱码问题。在网上给出了一大堆删MP3的tag的不靠谱的方案。在此特意记录下正确的方法,使用此方法可以保留MP3的tag并能正确显示。

MOC正常显示中文MP3的tag有两个条件:第一,tag是用UTF-8编码的;第二,不存在ID3 v1 tag(就是旧版的MP3 Tag信息)

首先需要python-mutagen工具,在debian/ubuntu下可以用下面命令来安装:

sudo apt-get install python-mutagen

如果你的MP3文件在其他播放器下也显示乱码,那么需要首先转成UTF-8

mid3v2 -e GBK 你的MP3文件

如果要批量转换,可以用如下命令

find [音乐目录] -iname "*.mp3" -exec mid3v2 -e GBK {} \;

然后删除ID3 v1 tag

find [音乐目录] -iname '*.mp3' -exec mid3iconv --remove-v1 {} \;

Now, enjoy you MOC

linux Comments(4) 2010年1月09日 22:42

一个类型提升引发的bug

今天OSD Lyrics收到了一个issue,说是在歌手不存在的情况下xmms2搜索歌词成功,下载歌词失败。之前在Exaile下也有类似的bug,不过这个bug的问题不同(应该说Exaile下也还是会有这个问题)。

OSD Lyrics对歌词文件名的查找是基于模板的,模板中有一些占位符,例如%t代表歌名,%p代表歌手。有一个函数ol_path_get_lrc_pathname,负责给定路径模板和文件名模板以及歌曲信息,输出根据模板转换后的完整文件路径。成功时返回输出的路径长度,失败则返回-1。

但是在程序日志中发现,当歌手为空时,文件名模板“%p-%t”应该是失败的,然而却依然被当作成功来使用。这样一来,匹配的结果就成了只有目录,缺少文件名的路径,把歌词下载的目标地址设成本机的一个目录,那肯定是会失败的嘛。

把ol_path_get_lrc_pathname相关的函数里外查了一遍,没有任何问题,出错时也的确返回了-1,于是目光落到调用这个函数的地方:

if ((ol_path_get_lrc_pathname (
     path_list[i], name_list[j], info, 
     file_name, MAX_PATH_LEN)) >= 0)
{
  /* do something */
}

发现把>=0改成!=-1就正常了。

为什么会这样?原因就在于ol_path_get_lrc_pathname的返回类型是size_t,而size_t在我的系统里的定义是unsigned int。任意一个unsigned类型都不会小于0。

那么为什么改成!=-1后,反而又可以了呢?任意一个unsigned类似应该都不会等于-1呀。

这就涉及到C语言的类型提升问题了。在C语言中,那个操作数的类型不同时,会把操作数都转化成两个操作数中能表示的范围最大的类型。就表示的类型来说,shot<=int<=long<float<double,这个是没有任何问题的。问题在于,当出现了unsigned后,怎样计算。具体来说,两个操作数是unsigned int 和 int时,如何转化?

如果是unsigned short int和short int就好办了,直接转成int就行。然而很可惜,在32位机子里,int = long,把unsigned int和int转成long不能解决任何问题。

怎么办呢?很简单,两个都转成unsigned int。

在这个例子中,两个规则是一样的,因为返回的-1变成了size_t后,变成了最大的unsigned long,所以-1也跟着被转成了unsigned long,变成了0xFFFFFFFF,两个刚好又相等了:)

作为一个例子,请看下面一段代码:

#include <stdio.h>

int main()
{
        unsigned long a = 0;
        long b = -1;
        printf ("%s\n", a > b ? "Greater" : "Less");
        return 0;
}

会输出什么呢?gcc 4.4.1给出的答案是Less,因为-1被转型了:(

 

 

 

程序设计 Comments(0) 2010年1月08日 02:05

XMMS2支持终于到位

各位使用XMMS2的同学不用再寻问会不会支持了。

这两天搞定了XMMS2支持,同时搞定的还有关于Exaile的一个bug。如果使用Exaile有问题的话可以试着更新到SVN最新版。

不使用XMMS2的可以在configure时用--disable-xmms2屏蔽。

需要依赖xmms-client库(还好不需要xmms-client-glib),这个库在Ubuntu下的包名叫libxmmsclient-dev,Gentoo官方软件仓库没有xmms2,在gentoo-taiwan的overlay里有,安装xmms2后自动有这个包。另外这里也介绍了Gentoo下的另外两个xmms2 overlay,不过我没有测试。希望其他系统的同学能告诉我这个库对应的包名。

计划等到完成MOC支持再发布新版本,现在只能下载源代码或用SVN版本来编译。

=======================碎碎念的分割线=======================

之前在查阅MPRIS的资料的时候,发现它就是在XMMS2的wiki上的,于是一直以为,XMMS2是支持MPRIS的。然而各方面的证据无情地打破了我的乐观猜想。可以说,如果XMMS2支持MPRIS的话,它将是和Audacious、Amarok 2同一批被支持的播放器。

之后对于XMMS2这种C/S架构的播放器没有什么概念,第一次尝试使用可耻地失败,导致之后一直不愿去碰它。后来MPD也是因为不了解而没有去尝试。

直到之后看到一篇介绍MPD的文章,才对它有兴趣起来。之前挣扎了很久的依赖项问题也通过编译参数来解决了。libmpd很好用,半天就完成了对MPD的支持。

完成之后又想到了XMMS2,同样作为C/S架构的XMMS2的文档应该也差不到哪去。在官网上转了一圈之后收集到了不少资料,让我兴奋的是xmms-client的例子比libmpd还丰富。于是不禁在twitter上夸了夸XMMS2的文档化。

然而做起来了才发现xmms-client的设计和文档化实在是不如人意。xmms-client的设计不像xmms-client一样完全屏蔽了网络的操作概念,每个函数的返回值都是一个类似GValue的通用指针。说实话我对用C实现通用类型一直没什么好感,虽然对这个设计来说是必要的。且不论xmms-client这种设计的优劣,毕竟通用性高的结果必然会造成方便性降低,取决于设计者的权衡。但是让我不满的是,xmms-client的文档里,没有一个函数说明它返回的真正类型是什么,让人无法使用。好吧我可以猜测xmmsc_playback_playtime返回的是一个int,但是我如何可以猜到xmmsc_medialib_get_info返回的东西是什么?就算例子里有如何从里面得到title和artist,我又凭什么会猜测到音轨号是叫tracknr而不是track-number?xmms-client这样的文档简直是在对别人说“去看我们的例子,如果没有,那就去看代码吧”。好在有google codesearch,让我可以从前人的成果里找到这些函数的用法。另一个问题就是xmms-client居然没有一个可以简单判断连接有没有失效的方法,差点逼着我用上xmms-client-glib,而且这家伙还一点文档都没有。

抱怨了那么多,说点好的方面,XMMS2终于是又一个支持毫秒级的播放器了,内牛满面啊。我有个梦想,终有一天,能让我的elapse emulator彻底消失……

另外说说Exaile,出错的是一个十分……呃,怎么说呢……囧的地方。在获取歌曲信息的时候,如果其中某一项没有值,会返回一个错误而不是NULL或者空字符串。这里有点违反直觉,我之前是在出错时直接就判断为出错了(播放器可能关掉了嘛),结果导致Exaile在播放无歌手信息的文件时OSD Lyrics会重新检测播放器而不是搜索歌词。这个应该算是我测试不足导致的吧。

osd-lyrics Comments(6) 2010年1月07日 00:09

不能免俗的09总结

网上放眼过去一片总结的,我也来一个吧

Google实习

从去年12月起,我在Google进行了三个月的实习,一直到今年三月初。

实习是在IME team里,也就是谷歌拼音的项目小组。其实谷歌拼音在内部有个很好听的名字,叫goopy,只可惜没有对外使用。我觉得这个名字比Google Pinyin IME好听多了。另外当时还见到了正在开发中的Android版谷歌拼音,内部代号叫moopy,在内部会议上还听到很多有意思的东西。

实习的过程很愉快,虽然感觉自己作的贡献并不多。很喜欢Google里的氛围,以及内部的餐厅。

实习时和另一个实习生以及一个工程师做了一个20%项目,只可惜这个项目估计永远都不会见到天日了。

OSD Lyrics

在Google实习的过程中学会了一些基本的GTK+编程,回来后和sarlmolapple一合计,决定自己做一个项目来作为学习之用,于是就有了OSD Lyrics。

当时决定做OSD Lyrics是由几个方面因素共同决定的。第一,sarlmolapple找不到合适的OSD歌词软件;第二,看到lrcdis的模式,觉得很不错,能一举解决Linux上的各种播放器的需求;第三,在TualatriX的blog上确认了技术的可行性。

这是我第一次看到自己的软件从无到有一点一点的搭建起来的乐趣。软件中途经历几次模块架构修改,现在虽然还有些架构上的问题,但是假以时日一定能解决的。

一路上遇到许多帮助,这里就不点名感谢了。

话说我还是想用python重写一遍。

阅读

惭愧啊惭愧,今年虽然买了不少书,但是看完的只有TAOUP和APUE的前18章。

这两本书给我的收获不小。TAOUP估计要每年看一遍,才能对它所说的东西有切身的体会。APUE让我打下了Unix编程的基础,可惜还没有什么实践的机会。

2010年的目标,是入门Linux内核和驱动编程。

发现我只有在旅途中时,才能安心下来看书。

ACM

今年是第三年玩ACM,也是最后一年。依然是在三队,可惜没机会出国。哈尔滨赛区的杯具让我们赶上了,从国内第一个赛区直接变成了最后一个赛区。

在宁波是乐极生悲,乐的是我们奇迹般地拿了亚军,当然这是在清华两支旅游队不请计排名的情况下。悲的是我弄丢了钱包,银行卡和身份证都没了,户口由于升研究生到现在都还没办好,一直黑户&没钱中……

哈尔滨收官之战,中规中矩地拿了个第五,也有金牌,算是个不错的结尾。

三年ACM,第一年2银,第二年1金一银,第三年2金,也算是一帆风顺了吧。我是不是应该写篇总结什么的呢?

Android/G1

8月底的时候手机坏了,于是告别陪伴我四年,修过无数次的手机,决心换个新的。

当时已经对G1口水已久,但是2K+的价钱实在是不敢奢侈。然而就在我经过一家卖G1的柜台,拿出来把玩了五分钟之后,还是冲动了。这是我最奢侈的计划外消费了,不过好在物有所值,至今对我的G1是相当满意的。

喜欢Android就是喜欢它的自由。于是把Android的SDK文档看了个大概,也拿Anfetion来练了练手,真的很好玩,不愧是geek们喜欢的手机(虽然我并不认为我是个geek)。

2010年展望

我是一个不善于执行计划的人,所以计划什么的就算了吧。21世纪的第二的10年的第一年(真绕口),好好做自己应该做的事,读想读的书,继续充实自己,这样就够了吧。�喜欢

未分类 Comments(3) 2009年12月31日 20:36

寻求 OSD Lyrics 的 Ubuntu PPA 源维护

update: TualatriX同学已经接手了PPA源的维护工作,在此表示感谢

 OSD Lyrics 功能更新了不少了,但是PPA源里的版本一直停留在10月的,原因我之前已经说了,因为我连不上launchpad的PPA ftp服务器,准确来说是在数据连接这一层失败,提示拒绝连接。

我不确定问题的原因在哪里,在另一所学校的sarlmolapple也无法连接,不知道是教育网把PPA墙了还是整个Chintranet把它给墙了。总之,我现在无法往PPA源上上传。

试过用GAppProxy来翻墙,然而很遗憾它不支持FTP代理。不要问我为什么没有买VPN之类的专业翻墙服务,每个人都有自己的难处。

因此,如果有人有launchpad账号,有维护PPA经验或者愿意学习,希望拉手OSD Lyrics的PPA源维护,请与我联系,邮箱是见侧边栏。不胜感激。

osd-lyrics Comments(0) 2009年12月29日 09:04

OSD Lyrics开始支持MPD了

不断有人问能不能让OSD Lyrics支持MPD和MOC,之前计划是在寒假的时候进行支持。不过在前几天英语复习时稍微了解了一下MPD,对它的架构很感兴趣,正好昨天考完英语手痒痒了,于是今天就把它给干掉了。

为了支持MPD,使用了libmpd,因此又引入了一个依赖项。那些不使用MPD又有洁癖的同学,可以在configure时加上--disable-mpd选项去除对MPD的支持。

时间有限,还有下面几个问题没解决,待下一个版本处理:

  • 无法配置MPD的地址和端口,只能使用localhost和默认端口
  • 无法得到文件的URI

===========================无聊的分隔线==============================

MPD果然和它的名字Media Player Daemon一样,是一个后台服务。它只负责播放音乐,其他的东西,什么界面啊,操作啊,管理啊,你们就自己写个客户端来搞定吧。MPD采用这样一种架构是对Unix“只做一件事,把它做好”的精神的贯彻。听说开发团队坚决把修改ID3tag之类的功能扔到一边,认为这并不是MPD的任务。

既然是个后台程序,那么必须要有强大的通信能力与前台进行交互。它在后台通过socket、TCP/IP来和客户端来通信,不得不说这是一种非常灵活的方式,理论上来说可以完全不依赖任何库就能与它交互——没有哪个操作系统是不支持socket、TCP/IP的。因为是用socket这种无语义的通信方式,MPD要自己定义一套协议,这套协议必须足够灵活、强大和易用。

 

虽然说可以直接用socket来和MPD进行交互,但是这样要自己解析它的协议,代码成本就高了。好在这么个强大的播放器一定有针对各种语言的通信库。对于C/C++来说就莫过于libmpd了。原本一直在犹豫要不要使用它,因为不想在OSD Lyrics里又添加一个新的依赖,最终的解决方法是加入了编译选项来屏蔽MPD支持,这样不使用MPD的人就不必安装这个库了。当然最好的解决方案是把对播放器的支持插件化,想用哪个播放器就装哪个插件,也许以后会采用这种架构吧,在这之前还有很多工作要做。

另外不得不说libmpd的接口定义得相当不错,使用起来很自然,而且和我自己给播放器支持所定义的接口大同小异。因此MPD的支持代码应该是最少的,如果不算上audacious等使用我自己的MPRIS库的那些播放器的话。

还要再抱怨一下,MPD提供的时间精度依然只到秒,关于这个我已经抱怨过好多次了。我就奇怪了,为什么这些播放器就不愿提供个毫秒级别的精度呢?大概是没考虑到会有OSD Lyrics这样一种应用吧。好在已经抽象出了模拟毫秒精度的方法。

===========================另一个分隔线===============================

为什么是MPD,而不是MOC?这完全取决于它们对开发者的友好程度。MPD本身就是要靠客户端来支持的,因此它先天就是对开发者友好的,有着良好定义的接口,以及详细的开发文档。而MOC,我不知道它是否有IPC接口,但是至少我无法在它的网站上找到对我有用的信息。在论坛上看到似乎它的MPRIS接口正在开发中,如果是这样,那么对它的支持就是易如反掌了——MPRIS支持早就已经到位了。

不止是一个播放器存在着MOC这样的问题。之前支持的好几个非MPRIS播放器没有一个能找到它们的DBUS协议文档的,最后逼得我去看代码。诚然,良好的代码是最好的文档,但是既然已经做好了DBUS接口,准备好了让第三方开发者使用,那么为什么不把它写出来,把为开发者们定义的接口告诉开发者呢?Songbird的MPRIS插件的DBUS名称我可以猜出来,Rhythmbox、Exaile和Banshee我可以从代码里看出来,但是每一次都浪费了很多无谓的时间来定位这些接口是在哪里定义和实现的。

反观MPD,整个查找文档的过程非常快乐。libmpd的文档还带有一个极具代表性的例子(当然这不是MPD的功劳)。实现起来所需要的时间远远小于我的预期。

说了那么多,其实我的OSD Lyrics也有几个模块可以分离出来作为一个库了,代码也需要有更多的文档注释。

MOC也是一定会支持的。不过如果被迫使用管道的话,我担心效率会怎样……

 

osd-lyrics Comments(1) 2009年12月28日 03:57

Android 中的一些线程操作

昨天看了看Android的线程文档,记录一些东西。

Android用的是J2SE,因此在其中的线程就是Java的线程。但是Android有自己的一套框架,因此线程的使用有一些新的东西。

活动与服务

在Android中,程序可以分成好几个组件,其中最重要的两个就是活动(Activity)和服务(Service)。活动是用户的GUI,而服务则运行于后台。比如说,一个IM,活动就是聊天的界面,而服务则用于网络通讯。

如果仅仅是这样的话,那么服务不过是一个没有界面的活动而已。但是实际上并非如此。为了节约资源,当一个活动不可见的时候,它是不会执行任何代码的,这时候就要靠服务了。例如在播放音乐的时候,就必须要用到服务,不然一切换到别的软件音乐就停了。

因此服务就给程序提供了后台运行的可能。更进一步的,服务是可以远程调用的,不只这个程序可以调用它,其他程序也可以调用它(前提是有相应的接口和权限)。

但是有一点要注意的是,同一个进程中的服务和活动是在同一线程中的。换句话说,后台和GUI是会相互阻塞的。这个和我的直觉有点出入,因为既然是后台服务嘛,怎么会阻塞到前台界面呢,但是事实就是如此。因此要在服务中执行长时间的操作(如网络应用)时,还是要自己创建线程来操作。

LooperHandlerThread

Android在Java的线程上又加了一层,使得线程拥有了消息队列(Message Queue)的支持。提供此支持的就是Looper。一个Looper负责执行一个消息循环,当消息队列里有消息时,处理消息,否则保持休眠。通常的GUI框架都会有消息循环与分发的概念,Android通过Looper将这个概念引入到了Java的线程中。通过它,只需要很少的代码就能为Java的线程加入这个功能:

  class LooperThread extends Thread {
      public Handler mHandler;
      
      public void run() {
          Looper.prepare();
          
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // 在此处理新消息
              }
          };
          
          Looper.loop();
      }
  }

其中重载Handler的handleMessage()方法来处理消息。

Android提供了HandlerThread类,这个类本身就是一个支持消息循环的线程类。

注意要启动一个线程还是需要手动调用start()的。

Handler

Handler是用于操作线程的消息队列的类。可以用空参数创建它,这样它就自动绑定到创建它的线程的消息队列上。也可以为它提供一个Looper,这样它就会绑定到Looper所在的线程上。Handler为不同线程之间的通信提供了方便。要和另外一个线程通信,只要得到它的Handler就行了。

对于上面的示例代码,mHandler就是绑定到新创建线程的Handler(因为run()是在新的线程中运行的)。对于一个HandlerThread,可以用getLooper()来得到它的Looper,但是要注意必须在这之前用start()启动了线程,不然是Looper是空的(文档上没说明这点,囧了我好一阵子)。

Handler的最大好处在于,它不只可以发送消息,还可以用post()来发送一个Runnable。这样一来,就可以在指定进程里面执行Runnablerun()方法中的代码了,也就是跨线程方法调用:)

另外,通过将Handler绑定到自己所在的线程,然后用post()方法,可以使一段Runnable里的代码不是立刻执行,而是在线程的消息队列轮到post过去的Runnable时才执行。不过我不明白这有什么用,还请高人指点。

Handler还可以指定消息/Runnable被处理的时间,这就可以把它当成定时器了(不过精确度不会太高吧)。

runOnUiThread()

当一个线程有结果了,要通知GUI更新呢?可以用ContextgetMainLooper()来得到主线程,然后用Handler操作。但是Activity类提供了一个简单的方法:runOnUiThread()。于是我们可以在线程中调用要更新的Activity的某个更新方法,在那个方法里用runOnUiThread(),将要运行的代码封装在一个Runnable里喂给它就行了。

以上就是目前所知道的Android里关于线程的一些方面,估计还有我不知道的东西,以后再说咯:P

android Comments(0) 2009年12月09日 21:11

OSD Lyrics 近况

好久没更新blog了,不过OSD Lyrics的改进一直没停下来。

自上一篇blog之后,OSD Lyrics又有了以下改进:

  • 多线程下载,解决下载时无响应的问题(不过目前的实现有个bug,虽然一般不会触发)
  • 有多个歌词匹配时,弹出歌词选择界面让用户自行选择
  • 重新下载已有的歌词
  • 下载歌词时用OSD显示下载信息
  • 可以定义歌词存放的位置和歌词文件名,音乐文件所在目录,以音乐文件名来命名
  • 其他的一些改进和bugfix

接下来要做的有:

  • 命令行选项支持
  • 手动关联歌曲与歌词

另外还有一个好消息和一个坏消息

好消息是,liangsuilong同学为OSD Lyrics提供了rpm源,现在用fedora的同学也可以直接从源里更新OSD Lyrics了:)

坏消息是,我从11月开始就连不上launch PPA的FTP了,也就是说,ubuntu的源无法更新了:(。暂时不知道是不是墙的问题,现在也没什么时间弄。

要期末了,估计进度又要慢下来了

 

 

osd-lyrics Comments(9) 2009年12月04日 22:25

hack了anfetion

AnFetion是Android上(唯一的?)飞信软件,支持了飞信上的基本功能,至少可以用来节省短信费:-)

但是这个软件有些不太完善的地方。之前申请加入开发,不过现在还没得到批准:(。

于是自己动手从最新的代码里fork了一个出来,自己hack了一个tsfetion

弄了三天,简单学习了一下android的开发,发现自己的java等于没学过。不过好在还是有一些成果滴:

  • 完善了消息通知机制,把有时从通知栏启动后,通知栏的通知不会消失,以及聊天对话框不会显示新消息的bug基本铲除了(应该是根除了吧,我想)
  • 还是消息通知机制,加入了对多个好友消息的支持,类似gtalk的效果
  • 加入了设置功能,可以设置新消息时的声音、振动和闪烁
  • 聊天消息采用列表方式显示,背景色区分自己与对方(还是抄gtalk的效果)
  • 修正了添加好友崩溃的bug

还有些是想做还没做的:

  • 好友切换(依旧是抄gtalk)
  • 解决时不时崩溃的bug(还不知道是什么原因造成的)
  • 聊天消息列表还无法用轨迹球滚动

改了不少地方,不知道会不会引入新的bug。

希望这些改动能被官方接受:P

android Comments(3) 2009年12月04日 22:06