继续纠结mplayer

2010-03-26 11:34

前面已经写了我在纠结用mplayer来播放电脑里的音频文件. 当时做的设置能够让我用键盘上方的多媒体键来控制mplayer的播放. 今天继续纠结如何往这个系统里添加重复播放同一首歌和上一首歌的功能.

要播放上一首歌, 关键就是要取消之前mplayer播放时使用的-shuffle功能, 而是使用自己生成的播放列表. 为此, 我写了一个简单的python脚本来控制播放列表的生成. 这个脚本有两个功能: 1. 用tree拿到音乐库目录下的文件列表并写入到一个名为complete的文件; 2. 读取complete文件, 得到一个列表, 用random模块里的shuffle函数来打乱这个列表, 以此生成一个新列表, 并将其写入到一个名为shuffled的文件. mplayer每次播放时会先用上面脚本的第二个功能来生成一个乱序列表(这样每次乱序列表都是不一样的), 然后进行播放. 这个python脚本的关键代码放在下面 了:

#!/usr/bin/env python                                                                                                                
#coding=utf-8                                                                                                                        
import sys
from random import shuffle
from subprocess import Popen, PIPE


def generate_playlist():
"""                 
    In order to get a playlist, we run the following command:
    tree -fin /mnt/archive/我的音乐 | grep -i ".*.m[p4][3a]$"
    which get file list from MEDIA_DIR, and grep files with extension m4a/mp3.
    We then split the output of the file into a list.                         
    """
# Get tree result                                                         
tree_command = 'tree -fin ' + MEDIA_DIR
ptree = Popen(tree_command.split(), stdout=PIPE)
grep_command = 'grep -i ^.*.m[p4][3a]$'
pgrep = Popen(grep_command.split(), stdin=ptree.stdout, stdout=PIPE)
output = pgrep.communicate()[0]
filelist = []
for file in output.split("n"):
if len(file) > 5:
filelist.append(file + "n")
return filelist


if __name__ == "__main__":
if len(sys.argv) == 1:
# If this program is called with no parameters, we would generate a
# full playlist, this is usually done by running a cron job.       
filelist = generate_playlist()
# Write this list to file                                          
save_playlist(filelist)
elif sys.argv[1] == "shuffle":
# we shall make a new file named "shuffled", we do this by load the
# complete playlist into memory and shuffle it and then write the  
# result to file.                                                  
filelist = read_playlist()
shuffle(filelist)
save_playlist(filelist, save_to=PLAYLIST_SHUFFLED)

OK, 现在我们用新的命令行参数来播放这个列表:

mplayer -quiet -slave -playlist $PLAYLIST -input file=$PIPE > $NOW_PLAYING 2>/dev/null &

这个命令行和之前的版本的最大区别在于去掉了-shuffle选项, 这样我们能够正确地使用next/prev键来找歌了. 另外, 这个命令和之前的另一个区别在于我们将mplayer的输出写到了一个文件, 这样我们在后面能够方便地拿到正在播放的音乐的信息. 我们这样做的原因是我们能够在后面选择性地重复播放某一首歌. 这一实现在逻辑上也是非常简单的: 在mplayer的播放过程中通过那个管道给mplayer发送命令"get_file_name", 这样我们能够在输出的文件中拿到正在播放的歌曲的文件名. 接下来我们通过文件名从完成列表(complete文件)中找到这首歌的全路径, 接下来我们只需要停止当前的mplayer进程, 并起一个新的mplayer进程重复播放这首歌就可以了:

echo "stop" > $PIPE && mplayer -quiet -slave -loop 0 "$fullname" -input file=$PIPE > $NOW_PLAYING 2>/dev/null &

这个功能完成得不错, 命令行下已经能够保证运行无误了. 但现在的问题是, 偶的系统不能接受到某两个多媒体键的信号. 连xev都拿不到键被press和release的信号. 昨晚整到这儿就郁郁地睡了. 今天早上起来, 记起系统启动地时候会报某些键不能对应的消息, 在dmesg里把这些信息找出来:

atkbd.c: Unknown key pressed (translated set 2, code 0xb4 on isa0060/serio0).
atkbd.c: Use 'setkeycodes e034 ' to make it known.
atkbd.c: Unknown key released (translated set 2, code 0xb4 on isa0060/serio0).
atkbd.c: Use 'setkeycodes e034 ' to make it known.
atkbd.c: Unknown key pressed (translated set 2, code 0xc2 on isa0060/serio0).
atkbd.c: Use 'setkeycodes e042 ' to make it known.
atkbd.c: Unknown key released (translated set 2, code 0xc2 on isa0060/serio0).
atkbd.c: Use 'setkeycodes e042 ' to make it known.

好吧, 看来果然天无绝人之路, 找了找教程, 用setkeycodes命令给这两个键安排一个keycode就可以了:

setkeycodes e034 193
setkeycodes e042 194

比较囧的事情是, 在这样设置后, 用xev已经能够捕捉到这个键的按下与松开, 但是给出的信号值却是错的, 一个是201, 一个是202. 不过反正这些值都没被用到(用xmodmap -pke看这两个值是空的), 这就样吧.

仔细看了看dmesg, 上面的出错是在网卡起来之后, 那么我们只需要在系统启动后, 网卡起来之前执行这两个命令就可以了. 加到rc.M里面去之后重启, 用xev测试没问题. 不过为了让X能够认出这个键, 我们还需要用下xmodmap. 在主目录下面写了一个.Xmodmap文件, 内容是:

keycode 202 = XF86AudioRewind NoSymbol XF86AudioRewind
keycode 201 = XF86Phone NoSymbol XF86Phone

然后KMenuEdit就能认出这个键了, 显示的名字是Rewind和Phone. 不过我尝试了一个小时都无法通过这两个键都启动程序. 即, xev能够正确识别这两个键的按下, 但是这个键的按下无法触发事件, 虽然我已经定义了这个事件. 无奈, 退而求其次, 用键盘上的一个组合键搞定了.