Programming」カテゴリーアーカイブ

CsharpRepl

mono にはこんなのがあるらしい。
ようするに C# 言語のインタラクティブシェルだ。

CsharpRepl – Mono

Ubuntu 10.10 には最初から入っていたので試してみる。
公式の解説が思いっきり LINQ だけど普通に foreach とかはどうだ。

using System; は初期状態から行われている。
for 文は一行で終わらせるか中括弧を語尾に付けないとエラーになる。
日本語は入力時に化けるけど UNICODE として処理しているようだ。
class なんかは作れない。
終了は quit; となっているが普通に Ctrl+D でいい。

Ctrl+D に慣れすぎて Windows の cmd.exe で EXIT を打つのが面倒…
というか Windows でも cmd.exe のほうがある程度の処理は簡単な体に…
それはどうでもよくて。

インタラクティブシェルだと嫌でも Python と比較してしまう。
静的言語を無理やり使うのだから Python レベルを求めるのは無謀か。

template and generic

Fedora 15 待ちで Linux ネタが作れないのでしばらく .NET で。

.NET Tips ページもかなり整理が終わった。
UTF-8 INI 読み書き関連と STL/CLR はほとんど書き直し。
.NET Tips – L’Isola di Niente

C++/CLI を久々に使ってみて見つけた。

#include "stdafx.h"

using namespace System;

/* こっちだと演算子が使えないとエラーになる
generic <typename T>
T calc(T a, T b)
{
	return a + b;
}*/

template <class T>
T calc(T a, T b)
{
	return a + b;
}

int wmain(int argc, wchar_t* argv[])
{
	Console::WriteLine(calc(3, 7));
	Console::WriteLine(calc<double>(3.1, 7.3));
	Console::WriteLine(calc<String^>("私って", "ほんとバカ"));
	//=>10
	//=>10.4
	//=>私ってほんとバカ
	return 0;
}

template は generic へのマクロだと思っていたけど全然違う。
template キーワードなら C++ の STL と完全に同様なんだね。
使うかどうかは置いておいて。

せっかく C# の INI 読み書きクラスを作り直したんだから何かに使いたい。
SeeMe が現行 Opera で使えないのは知っているんだけどヤル気が出ない。
ヤルとしたらまったく違うインターフェイスに変更したいし、何か思いついたら。

PyGtk 関係は GTK3 が普通に使える GNOME3 Fedora に乗り換えが終わってから。
今整理したってあっというまに時代遅れになる、進化が早すぎるよ…

追記

今頃気がついたけどコレってもしかして C++ のテンプレート機能を呼んでいるだけ?
標準 C++ と何も違いが無いのだとしたら STL/CLR の存在価値って何なんだ。
知れば知るほど解らないコトが増えるのは標準 C++ と同じかよ…

Dynamic Menu in GtkUIManager

PyGtk でメニュー項目の動的な追加と削除。
コレををやらないとマルチトラック音声の切り替え機能が実装できない。

GtkUIManager を使うのが一番簡単、てか私はこの方法しか知らない。
Gedit プラグインでメニューを追加するのと同じ方法でいいはず。

Gedit プラグインの作り方 – L’Isola di Niente

切り替え自体はラジオメニューに、Totem もそうなっているし。
ということは GtkActionGroup には以前書いたコレを指定すればいいかな。

GtkRadioAction でラヂオメニューとツールバーを同期させる

しらない間に自身で方法だけは書いていた。
この2つをまとめれば動的なメニュー項目の追加削除ができそうなのでやってみる。

class AudioMenu(object):
    __slots__ = ["uimanager", "ui_id", "action_group"]
    def __init__(self, uimanager):
        self.uimanager = uimanager
        self.ui_id = -1

    def add_menu(self, actions, xml, callback, value):
        self.action_group = gtk.ActionGroup("AudioMenuActions")
        self.action_group.add_radio_actions(actions, value, callback)
        self.uimanager.insert_action_group(self.action_group, -1)
        self.ui_id = self.uimanager.add_ui_from_string(xml)

    def remove_menu(self):
        if self.ui_id != -1:
            self.uimanager.remove_ui(self.ui_id)
            self.uimanager.remove_action_group(self.action_group)
            self.uimanager.ensure_update()
            self.ui_id = -1
            del self.action_group

一ヶ所でしか使わないけど class にしたほうがメンテナンスが楽なので。

GtkRadioActionEntry List とメニュー XML とコールバックと選択するラジオメニュー値
を引数に渡してメニューを作成する。
メニューの削除は Gedit プラグインとまったく同じで大丈夫だろう。
GtkActionGroup は同じアクションを追記してしまうので都度新規作成と削除をする。

この引数に指定するコールバックを本体クラスに。

def on_soundmenu(self, action, current):
    """
        Multi Track Audio Select
    """
    n = action.get_current_value()
    self.player.set_property("current-audio", n)
    # 下記は本体側の設定保存用
    self.settingwin.n_audio = n

これだけでラジオメニューのどこが選択されているかで勝手にトラック選択になる。

後は GtkActionGroup の List と XML 文字列を黙々と完成させればいい。
とりあえず XML の大雑把なテンプレートを用意。

audio_str = """<ui>
    <menubar name="MenuBar">
        <menu action="File">
            <menu action="sound">
            {0}
            </menu>
        </menu>
    </menubar>
    <popup name="pop_main">
        <menu action="sound">
        {0}
        </menu>
    </popup>
</ui>
"""

やはり Gedit プラグイン同様にメニューの XML を用意する。
自分で作った XML ツリーだからまったく迷わないwww

str.format() なら一つの引数で {0} に入るから間違えない。
新しいフォーマッタはこういう場合には便利だね。

asink = self.player.get_property("audio-sink")
if asink:
    # versioon 0.3.6
    # 一度メニューを空にする
    self.audiomenu.remove_menu()
    # オーディオトラック数で振り分ける
    n_audio = self.player.get_property("n-audio")
    if n_audio == 1:
        self.player.set_property("current-audio", 0)
    else:
        actions = []
        x = ""
        for i in range(n_audio):
            a = "sound{0}".format(i)
            s = "トラック #{0}".format(i)
            # GtkRadioActionEntry の List を作成
            # name, stock_id, label, accelerator, tooltip, value
            t = (a, None, s, None, s, i)
            actions.append(t)
            x += '<menuitem action="{0}"/>'.format(a)
        xml = audio_str.format(x)
        # メニューに追加
        self.audiomenu.add_menu(actions, xml, self.on_soundmenu, self.settingwin.n_audio)
        self.player.set_property("current-audio", self.settingwin.n_audio)

こんな感じで for ループでひたすら文字列を加工し作成。
GtkRadioActionEntry 作成は PyGtk の場合は単なるタプルでいいので簡単。
GtkMenuItem の XML も当然文字列なので同時に作って最後にテンプレートに流し込む。

新しいことは得に何もやっていないのでスンナリと動的メニューが完成した。
まぁ GTK+ と GStreamer に命令を送っているだけのチッポケなアプリですからね。

current-audio property

動画コンテナ(DVD や mp4 等)は音声や映像のトラックを複数格納できる。
主に字幕用途で考えられた構造だろう、と思う…

しかしこの構造を利用して昨今では様々な応用というか実験が進んでいる。
…っぽい、業界全般なんて現場のオッサンに解るはずが無いのだからしょーがない。

確実なのは特定用途限定かつ軽いことだけがウリのプレイヤーにはトドメでしかない。
たとえば私が公開しているプレイヤーアプリとかである。
こんなのってないよ…あんまりだよ…

つまり我が Y901x 0.3.5 はまったく複数トラックメディアに対応していない。
当然開発終了している Windows 用もそうだが開発は終了しているので放置。
DirectShow のコードなんて海外にゴロゴロ転がっているけどシラネ!

実は Totem で指定すれば Y901x にも反映されるのであまり気にしていなかった。
ぶっちゃけ Totem が終了時に X の設定を初期化しないというのを利用しているだけ。
だったけどいいかげんに自分自身が気になるようになってきたのでそろそろ対策しよう。

日本語版 Ubuntu てか GNOME 環境専用アプリで続けるなら別に…
って次で GNOME を排除すると明言している Ubuntu は終わった感が…
GNOME だから選んでいた人は多いと思うんですけど…
私も Fedora 15 待ちで移行は確定だけど Ubuntu 11.04 は全然話題になっていないね…

いつものように長い戯れ言はこのくらいにして。

playbin2

n-audio プロパティでオーディオトラック数。
current-audio プロパティでオーディオトラックの指定。
が可能であるようだ。

Totem のソース bacon-video-widget-gst-0.10.c
にて parse_stream_info 関数がやっていることを参考にしただけなんだが。
C のコードを見て形を変えて Python で使うのは GPL には反していないよね。

映像やテキストのトラック関連もあるけど現状は「シラネ!」でいいだろう。
Y901x 0.3.5 の 1314 行目に以下を追記してみる。

# Audio
asink = self.player.get_property("audio-sink")
if asink:
    self.toolbox.volumebar.set_property("sensitive", True)
    v = self.toolbox.volumeadj.get_value() / 100.0
    self.set_volume(v)
    # 追加
    print self.player.get_property("n-audio")
    # デフォルトは -1 で先頭トラックはゼロ
    self.player.set_property("current-audio", 1)
else:
    self.toolbox.volumebar.set_property("sensitive", False)

なんだ、たったコレだけで 2 番目のオーディオトラックが再生できた。
playbin2 は本当に何でも簡単にやってくれる、頼るのもどうかとは解っているけど。
とにかく私程度のサンデープログラマーなオッサンにはありがたい存在である。

後は切り替えを実際に行うユーザーインターフェイスをどうするかだ。
やっぱりメニュー項目に加えるのが一番迷わない方法だと思うけど。
トラック数を調べて一度選択メニューを全部削除してそれからえっと、実装は面倒くさい…

{0:02d}

Y901x 0.3.5 公開。

Y901x はシークバーを弄くるのに gst.SEEK_FLAG_KEY_UNIT 指定で軽くしている。
けどたまに動かしたピチッとした位置で止めたい場合が多々ある。
面倒くさいので放置していたけどそろそろやろうかと。

def set_play_position(self, location):
    """
        Media Seeking
        SEEK_FLAG_KEY_UNIT -> LightWaight
    """
    if self.seekinfo.button == 1:
        seek = gst.SEEK_FLAG_KEY_UNIT
    else:
        seek = gst.SEEK_FLAG_ACCURATE
    event = gst.event_new_seek(
            1.0,
            gst.FORMAT_TIME,
            gst.SEEK_FLAG_FLUSH | seek,
            gst.SEEK_TYPE_SET,
            location,
            gst.SEEK_TYPE_NONE,
            0)
    self.player.send_event(event)

とシークバー上で押されたマウスボタンを記憶しておいて振り分けにしてみた。
GtkHScale はホイールクリックだと正確な位置に移動できるので合わせてみたんだが。
つーてもホイールクリックでシークはやりにくいので右ボタンでもオケにと。
こんなに簡単に振り分けできるならもっと早くやればよかった、我ながら。

それと一時間以上のファイルの場合は自動で
00:05/85:45 → 00:00:05/01:23:45
なんて切り替わるように表示したくなった。

こんな処理にしたけど、今見るともしかしてジャスト一時間だとアウト…
まぁ手持ちのは問題無いし次で直せばいいか。

def put_time_status(self, location):
    """
        self.player is PlayBin2
    """
    sec = location / 1000000000
    allsec = self.player.query_duration(gst.FORMAT_TIME)[0] / 1000000000
    if allsec > 3600:
        s = time.strftime("%H:%M:%S", time.gmtime(sec))
        t = time.strftime("%H:%M:%S", time.gmtime(allsec))
    else:
        s = time.strftime("%M:%S", time.gmtime(sec))
        t = time.strftime("%M:%S", time.gmtime(allsec))
    self.statusbar.label[0].set_text("{0}/{1}".format(s, t))
    """
    今までの処理
    secv = "%02d:%02d/%02d:%02d" % ((sec / 60), (sec % 60), (allsec / 60), (allsec % 60))
    self.statusbar.label[0].set_text(secv)"""

自分で計算しなくても time モジュールを使えばいいと今頃気がついた私って…

そういえば新しいフォーマッタで %02d みたくゼロ詰めするのはどうやるのだ?
どうせ V2 を使いつづけるのでほとんど気にしていなかった。

7.1. string ? Common string operations ? Python v2.7.1 documentation

# old
print "%02d:%02d" % (1, 2)
# new
print("{0:02d}:{1:02d}".format(1, 2))

スゲェ分かり辛いと思うんですけど…
せっかく C 言語とほぼ同じで覚えやすかったのに今後はこうなるんだよなぁ…