月別アーカイブ: 2010年5月

Visual Studio 2010

Visual Studio 2010 はこんな優待パッケージがあるのね。

今日現在 33,390 円にて予約受付中。
たしかにアップグレード版との比較なら超お買い得なんだが、正直微妙な値段設定だ。

Standard Edition ライセンス保持者対象だなんてありえない、挫折者九割越えが目に見えているのに。
VC6 の時一度挫折した人間は思う、Delphi 5 がこの世に出なかったら今の私は確実に只のオッサン。
開発に興味を持つ若い人の意識が当時と全然変わっていないなんて Blog 検索をすれば解る。
大げさに書くと統合開発環境を手に入れれば魔法のようにアプリが作れると思い込むとかね。

.NET Framework のメジャー更新毎に六万円を支払うことに疑問を持った人達を引き止めたいのかも。
それだったら一万円代が限界かと…

とはいえコッチより全然安い。
一旦 Std 版を手に入れ安上がりに Pro 版を仕入れたいなら面倒だけどこんなルートがある。
VS2008 Pro を持っている私には関係なかったりするんですけど。
少なくとも公開するアプリを作りたいなら正規のライセンスを持っていないと後が怖い。

さて私はどうしよう?
minipoli のメンテを続けるだけなら VS2008 のままでイケるわけで。
C# で SeeMe 以外を作りたくなったら、それまで待つと売り切れの可能性。
しかしその頃には別の販促バージョンが出るような、これは迷う。

IronPython つか DLR の初期化がもう少し早ければ笑い飛ばせるんだが…
と、Ubuntu Linux の Gedit で書いている現実では説得力の欠片もありゃしねぇ。

Human sort

Python の List で sort() しても Nautilus のファイル名順にならない。
Windows XP の時のように数値優先な自然順ソートをしなければならない。

我がアプリ Y901x のリストはデフォルトのソートをしているだけ。
気にしていたけど無名なのをいいことに今まで知らん顔していた(ぉい!

Windows XP には shlwapi に StrCmpLogicalW という API 関数があったんだが…
Linux なら glib や gio に関数がありそうなんだけどな…

PyGObject Reference Manual

見つからないし。
Nautilus のソースも落としたけど何が何やらサッパリワカンネ。
自力で自然順ソート関数を作っている人のコードを参考にしてみよう。

Ned Batchelder: Human sorting

コレやったらとりあえずうまくいった。

Python sorts “u11-Phrase 1000.wav” before “u11-Phrase 101.wav”; how can I overcome this? – Stack Overflow

コッチは isdigit を使っている、その手もあるか。
というか整数の正規表現ってこのどっちでもいいんだなとか変な発見。

1
2
3
return [ tryint(c) for c in re.split('([0-9]+)', s) ]
or
return [ tryint(c) for c in re.split(r'(\d+)', s) ]

しかしコレで完全に Nautilus と同じになるのだろうか?
とりあえず自分でもう少し試してみる。

それと playbin2 化したらよく落ちるようになったなぁ…
切り替え事の gtk.main_iteration を消したらかなり良くなったけどこれでいいのかな…

GtkInfoBar

Part III. GTK+ Widgets and Objects

何か Linux とか GTK+ とかで新しいプログラミングのネタは無いものか…
なんて考えながら上記ををポッケーと眺めていた。

いきなり見つかったぞ、GtkInfoBar って何?

調べてみると例えば Gedit に動画ファイルをドロップしたエラーで出るあの少し大きなバー。
やれば解るので細かいことは書かないけど、ソレをわざわざ GTK+ 部品の一つにしたようだ。

GTK+ – Wikipedia, the free encyclopedia

GTK+ 2.18 で追加されたようだけど PyGtk から使えるのかな?
自前環境にて PyGtk から使えるか調べてみる。

Mandriva 2010.0 の GTK+ 2.20.0 と PyGtk 2.16 では使えない。
Ubuntu 10.04 の GTK+ 2.21.1 と PyGtk 2.17 では使える。

PyGTK 2.0 Reference Manual

こっちには今現在はマニュアルは無い、てかまだ version 2.15.2 のままのようで。
ドキュメントが遅れるのは Linux ではしかたがない、商売でやっている MSDN とは違うのだから。

しかしこの程度のインフォメーション表示バーなんて自作しても別に難しくない。
でも GTK+ 自体は GUI 部品の見た目統一を目指しているはずだし(違ったかな?
テーマ切り替え時の追従なんかも期待できる、Opera がまったく追従しないのが気になって…
とにかくせっかくベンダーが用意してくれたものだから使ってみよう。

GtkDialog の vbox アトリビュートのようなものは存在しない。
これ自体が GtkHBox サブクラスであらかじめボタン配置用スペースを予約しているみたい。
とりあえずボタンを押すと InfoBar が出てレスポンスでボタン文字を変更する例を。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
import gtk
 
class InfoBarTest(gtk.Window):
    """
        GtkInfoBar Test
        PyGtk 2.17 or later.
    """
    def __init__(self):
        gtk.Window.__init__(self)
        vbox = gtk.VBox()
        vbox.show()
        self.button = gtk.Button("Click!")
        self.button.show()
        # GtkInfoBar
        self.infobar = gtk.InfoBar()
        self.infobar.pack_start(gtk.Label("Hi!\nButton Click"))
        self.infobar.add_button("_Cancel", gtk.RESPONSE_CANCEL)
        self.infobar.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
        # Signal
        self.infobar.connect("response", self.on_infobar_response)
        self.button.connect("button-press-event", self.on_click)
        self.connect("delete-event", gtk.main_quit)
        # append
        vbox.pack_start(self.infobar, False)
        vbox.pack_start(self.button)
        self.add(vbox)
        self.resize(300, 300)
        self.show()
         
    def on_click(self, widget, event=None):
        self.infobar.show_all()
 
    def on_infobar_response(self, widget, response_id):
        if response_id == gtk.RESPONSE_ACCEPT:
            self.button.set_label("OK!")
        else:
            self.button.set_label("Cancel...")
        self.infobar.hide()
 
if __name__ == "__main__":
    w = InfoBarTest()
    gtk.main()

バインディングは他の GTK+ 部品と同様みたい、これなら得にマニュアルは不要か。
後はチマチマ弄くってみればもっと見た目とかもよくなると思う。

うん、StatusBar や TitleBar では面積が足りない場合なんかに有効だ。
上手く使えば Debug にも利用できそう、自作すればいいとはいわない。

というか自作すればいいというのなら GtkMessageDialog なんかもいらないわけで。
「ナンデモアリ」を徹底的にやった結果として複雑怪奇になった WPF は見習ってほしいよ。

GStreamer Change Volume

GStreamer Player のボリュームを直線的に変更したい。
GtkHScale の値を直で渡すと曲線になって使いにくい、DirectShow はもっと酷かったけど。

dBデシベルの話し 音の大きさ

音量のデシベルは非常にややこしい。
GStreamer マニュアルの GstStreamVolumeFormat 以下にも 20 * log10 (val) とある。

gststreamvolume

実は私の Cinema という Windows 用 DirectShow プレイヤーは手抜きをしていて…
DirectShow ボリュームは最大 0、無音 -10,000 をデシベル単位でということなのですが…

1
2
3
4
5
6
7
void CDirectA::SetVolume(int nVolume)
{
    double d = pow((double)(nVolume * 2), 2.0);
    m_nVolume =  static_cast<int>(-d);
    if (pBasicAudio)
        pBasicAudio->put_Volume(m_nVolume);
}</int>

というベキ乗を利用してなんとなく似たようなカーブにしている。
ぶっちゃけ WMP とは全然カーブが違うんだが面倒だということで(ぉい!

GStreamer ボリュームは最大 100.0、無音 0.0 なのでコレを Y901x で真似るには

1
2
3
def on_volume_value_changed(self, widget, event=None):
    val = widget.get_value()**2 / 10000.0
    self.player.set_property("volume", val)

とやってみたけど完全に違う、GStreamer では同じ手は使えないようだ。
キチンと調べないといけないみたい、Totem はどうやっているかコードを漁る。

bacon-video-widget-gst-0.10.c の bacon_video_widget_set_volume 関数で
GST_STREAM_VOLUME_FORMAT_CUBIC を指定している、この型と相互変換が必要か。

gststreamvolume を pygst から利用するには c ヘッダが gst/interfaces 以下にあるので

1
2
import gst
dir(gst.interfaces)

みたいにやれば関数が一覧されるので探ってみる。
stream_volume_set_volume は流石に使えないようだけど convert はできるようだ。
CLAMP 関数も必要かな C言語でCLAMP(a,b,c) | OKWave
一行なのでラムダ式を使えと言われそうだが CLAMP という関数名も覚書に使いたい。

1
2
3
4
5
6
7
8
9
10
11
def set_volume(self, value):
    v = gst.interfaces.stream_volume_convert_volume(
            gst.interfaces.STREAM_VOLUME_FORMAT_CUBIC,
            gst.interfaces.STREAM_VOLUME_FORMAT_LINEAR,
            value)
    def clamp(a,b,c): return min(max(a,b),c)
    v = clamp(v, 0.0, 1.0)
    self.player.set_property("volume", v)
 
def on_volume_value_changed(self, widget, event=None):
    self.set_volume(widget.get_value() / 100.0)

おっし、これで Totem とまったく同じ音量カーブになった。
CUBIC を LINEAR に変換すればいいのね、実はいまいちよく解っていなかったり(ぉい!

もう少し Debug して Y901x の Playbin2 化 version 0.3 はなんとかなりそう。

playbin2

Totem は

gst_stream_volume_set_volume

でボリューム調節を行っているということだけ理解した。
その関数ってもしかして playbin の volume Property とリンクしているんじゃないの?

米国の Google でコードを探し試しまくったけどさっぱり解らない…
3. Pipeline
自前でパイプラインを構築するか、しかし情報少なすぎ!

いや、そういえば playbin は古いので新しい PulseAudio に対応しているはずがない。
だから playbin2 に移行しろということなのか…

playbin2

playbin

Y901x の GstPipeline を playbin2 に書き換えて試す。

1
self.player = gst.element_factory_make("playbin2", "player")

エラーが出る場所をとにかくコメントアウトしてボリュームを試す。

あーあ、マジでそんだけだった、しかも mute Property にて簡単にミュート。
しかし DirectShow 同様に非線形カーブなので GtkHScale 値をそのまま使えないみたい。
ミュートも「サウンドの設定」の「アプリケーション」タブと見事連動。

これではもう playbin2 に移行するしか選択肢が無いようだ。

1
2
# Get GstStreamInfo List
info_arr = self.player.get_property("stream-info-value-array")

の所で例外になる、そりゃ playbin2 にそんなプロパティは無い。
本当はもの凄い遠回りをしたけど一応こう書き換えたら上手く動いた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def gst_on_message(self, bus, message):
    t = message.type
    if t == gst.MESSAGE_EOS:
        self.clear()
        # Repeat
        if self.settingwin["repeat"] > 1:
            self.on_next(self)
        elif self.settingwin["repeat"] == 1:
            self.set_play_position(0)
            self.player.set_state(gst.STATE_PLAYING)
    elif t == gst.MESSAGE_ERROR:
        self.clear()
        err, debug = message.parse_error()
        self.messagebox(str(err) + "\n" + str(debug)) 
        self.toolbox.change_img(0)
    elif t == gst.MESSAGE_STATE_CHANGED:
        if gst.STATE_PLAYING == message.parse_state_changed()[1]:
            self.toolbox.change_img(0)
            # This is First Play
            if not self.is_playing:
                try:
                    self.p_duration = self.player.query_duration(gst.FORMAT_TIME)[0]
                except:
                    self.p_duration = gst.CLOCK_TIME_NONE
                if self.p_duration != gst.CLOCK_TIME_NONE:
                    self.toolbox.seekadj.upper = self.p_duration
                    self.is_playing = True
                    # toolbox Buttons Enabled
                    self.toolbox.set_enable_ctrl(True)
                    # Timer on
                    self.timer_id = gobject.timeout_add(200, self.on_timer)
                    # File Names Listup
                    self.listup_change()
                    #
                    # playbin2
                    #
                    vsink = self.player.get_property("video-sink")
                    # vsink == None @ Music File
                    if vsink:
                        for pad in vsink.pads():
                            # print pad.get_caps()[0].get_name() # is Check
                            # W Buffer Off
                            self.video_window.unset_flags(gtk.DOUBLE_BUFFERED)
                            # caps[0] @ GstStructure
                            caps = pad.get_negotiated_caps()
                            self.video["width"] = caps[0]["width"]
                            self.video["height"] = caps[0]["height"]
                            #
                            flt = float(self.video["width"]) / float(self.video["height"])
                            self.aframe.set(xalign=0.5, yalign=0.5, ratio=flt, obey_child=False)
                            self.change_video_size()
                    """ under Old PlayBin src
                    # Get GstStreamInfo List
                    info_arr = self.player.get_property("stream-info-value-array")
                    if len(info_arr) == 1:
                        # Music
                        pass
                    else:
                        # W Buffer Off
                        self.video_window.unset_flags(gtk.DOUBLE_BUFFERED)
                    for info in info_arr:
                        # Get Video Size
                        pad = info.get_property("object")
                        #print pad
                        caps = pad.get_negotiated_caps()
                        if caps != None and "video" in caps.to_string():
                            # caps[0] @ GstStructure
                            self.video["width"] = caps[0]["width"]
                            self.video["height"] = caps[0]["height"]
                            #
                            flt = float(self.video["width"]) / float(self.video["height"])
                            self.aframe.set(xalign=0.5, yalign=0.5, ratio=flt, obey_child=False)
                            self.change_video_size()"""
        else:
            self.toolbox.change_img(1)

GstStreamInfo のリストからの情報で設定するより短くなってもーたw
video-sink なんてテキトーだったのにビンゴだったのは自分で驚いたマジで。

これで playbin2 化はなんとかなりそうだ、後はボリューム値の線形化だ。
って以前のように動かせるだけで見た目は何も進化していないのが少し悲しい…