2016/01/30


昔から Linux でも Windows でも mpd を使ってきてる。Windows 10 にしてから環境を作ってなかったので重い腰をあげた。

まずはサーバ、これはオフィシャルが配布している Windows バイナリのまま使った。ただし Windows にログインした後毎回起動するのもうざったいし、コンソールが見えたままになるのは嫌なので nssm を使ってNTサービス化する。nssm には GUI の設定画面があるので

nssm install mpd

を実行して mpd へのパスを設定する。また設定ファイル mpd.conf は %LOCALAPPDATA%\mpd\mpd.conf に置く。mp3 は id3v2 なので filesystem_charset だけ cp932 にする。

mpd1
mpd2

ここで気を付けないといけないのは、nssm でNTサービス化した場合、Administrator で実行されこの mpd.conf のパスが教えられないので nssm の設定画面で .\mattn の様にログインユーザを設定する。

mpd3

次にクライアント側。コマンドプロンプトで生きる人なのでそのまま mpc を使う。

master/mpc.git -

wiki | mailing list/newsgroup | bug tracker | request an account Thanks to Alexander Wirt for provid...

http://git.musicpd.org/cgit/master/mpc.git/

ただし mpc のバイナリは iconv で端末のエンコーディングに変換を行ってくれないので、パッチを当てる。

diff --git a/configure.ac b/configure.ac
index 9da26be..df1adaf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -91,18 +91,11 @@ AC_ARG_ENABLE(iconv,
 
 if test x$enable_iconv = xyes; then
    AC_CHECK_FUNC(iconv,
-       [ICONV_CFLAGS="" ICONV_LIBS=""],
-       [AC_CHECK_LIB(intl, iconv,
-           [ICONV_CFLAGS="" ICONV_LIBS="-lintl"],
-           [enable_iconv=no])])
+       [ICONV_CFLAGS="" ICONV_LIBS="-liconv"])
 fi
 
 if test x$enable_iconv = xyes; then
    AM_LANGINFO_CODESET
-   if test x$am_cv_langinfo_codeset != xyes; then
-       enable_iconv=no
-       AC_MSG_WARN(nl_langinfo and CODESET not available - disabling iconv)
-   fi
 fi
 
 if test x$enable_iconv = xyes; then
diff --git a/src/charset.c b/src/charset.c
index 2e34e47..a50096d 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -26,7 +26,11 @@
 #include <string.h>
 
 #include <locale.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
 #include <langinfo.h>
+#endif
 #include <iconv.h>
 
 static bool charset_enable_input;
@@ -181,9 +185,17 @@ charset_init(bool enable_input, bool enable_output)
    if (original_locale == NULL)
        return;
 
+#ifdef _WIN32
+   if (ignore_invalid) {
+        char buf[16];
+        sprintf(buf, "cp%d", GetACP());
+       locale_charset = strdup(buf);
+    }
+#else
    const char *charset = nl_langinfo(CODESET);
    if (charset != NULL)
        locale_charset = strdup(charset);
+#endif
 
    setlocale(LC_CTYPE,original_locale);
 
diff --git a/src/charset.h b/src/charset.h
index 2eca94b..67a027c 100644
--- a/src/charset.h
+++ b/src/charset.h
@@ -25,7 +25,7 @@
 #include "Compiler.h"
 
 #include <stdbool.h>
-
+#if 0
 #ifdef HAVE_ICONV
 
 /**
@@ -74,5 +74,6 @@ charset_from_utf8(const char *from)
 }
 
 #endif
+#endif
 
 #endif

これで grep 等で絞り込む時は utf-8 だけど端末に表示される時は cp932 になる(ちょっとやっつけ)。ビルドには libmpdclient が必要。

mpc listall | grep "Taylor Swift" | mpc add

して再生していたら娘がテイラースウィフトを気に入った様だ。

Be My Taylor Swift Be My Taylor Swift

Oddball Productions / ¥ 100 (2016-01-12)
 
発送可能時間:通常1~2営業日以内に発送


2016/01/29


Vim に入った channel 機能を使ってテキスト編集中も流れる twitter タイムラインの表示を考えてみた。

functions:append(line)
  let wn = bufwinnr("tweets")
  if wn == -1
    return 0
  endif
  exe wn 'wincmd w'
  try
    let o = jsondecode(a:line)
    let ts = type(o) == 3 ? o : [o]
    for t in ts
      1put!=t.user.screen_name . ': ' . t.text
    endfor
  finally
    wincmd p
  endtry
endfunction

if bufwinnr("tweets") == -1
  10new tweets
  setlocal buftype=nofile bufhidden=hide noswapfile
  wincmd p
endif

function! TweetsCallback(handle, msg)
  for line in split(a:msg"\n")
    call s:append(line)
  endfor
  redraw
endfunction

if exists('s:handle')
  call ch_close(s:handle)
endif
let s:handle = ch_open("127.0.0.1:7777""raw""TweetsCallback")
こんな Vim script を用意しておき、別のターミナルで
$ twty -S -json | nc -l -p 7777

twty-json オプション付きでストリーミングさせる。

そして上記 Vim script を :so % で実行すると、テキスト編集中もどんどん流れる twitter タイムラインが出来上がる。

※タイムラインの内容は公開出来ないので興味のある方はご自分でお試し下さい。



13年前、僕は vim-dev (Vim の開発グループ)に Vim からソケット通信が出来る関数群のパッチを書いて送りました。

Socket functions for vim. - Yahoo! Groups
https://groups.yahoo.com/neo/groups/vimdev/conversations/topics/32576

その時は Bram (vimboss) に「Python や Perl インタフェースを使えるやないか、もしくは外部コマンドとか」と返されてしまいました。確かに言語拡張を使えば出来ますし、それに処理がブロッキングだったので実はそれほど有益では無かったかも知れません。

その後、このソケット通信のパッチは vimproc の一部に取り込まれ、シーケンシャルなソケット通信は出来る様になりました。しかし Vim には非同期インタフェースがありません。自分でイベントを投げ続けてソケットをハンドリングするしかありませんでした。しかし本日リリースされたパッチ 7.4.1191 で channel という機能により、非同期ソケット通信が出来る様になりました。

Patch 7.4.1191 - Google Groups
https://groups.google.com/forum/#!topic/vim_dev/t6RbXywJZRo

あのパッチを送った頃を知っている KoRoN さんや ynkdir さんは恐らく感慨深い思いではないかと思います。

channel は以下の様に使います。

function! Callback(handle, msg)
  echo a:msg
endfunction
let handle = ch_open("127.0.0.1:5000""raw")
call ch_sendraw(handle, "GET / HTTP/1.0\r\n\r\n""Callback")

ローカルにウェブサーバを立てておき、このコードを実行すると以下の様に表示されます。

channel

raw だけでなく json も扱えます。先日入った jsonencode() を使う事も出来ます。接続関数 ch_open の第二引数に raw を指定すると文字列がそのまま送受信されます。json を指定すると json エンコードされた文字列が送信されます。送信関数は2つあり、ch_sendraw() は生の文字列を、ch_sendexpr() はハンドルと文字列をペアにした配列を送信します。サーバ側はこのハンドルに対して応答を返すと、複数のハンドルを持った vim のどちらに応答するか、といった事が可能になります。

今日はこの ch_sendexpr を使って時刻通知機能を作ってみました。まずサーバを書きます。

package main

import (
    "encoding/json"
    "log"
    "net"
    "time"
)

func main() {
    l, err := net.Listen("tcp"":8888")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := l.Accept()
        if err != nil {
            continue
        }
        go func(conn net.Conn) {
            defer conn.Close()
            var b [500]byte
            // リクエストを受け取る
            // フォーマットは JSON の配列
            // [handle, msg]
            n, err := conn.Read(b[:])
            if err != nil {
                log.Println(err)
                return
            }
            var v [2]interface{}
            err = json.Unmarshal(b[:n], &v)
            if err != nil {
                log.Println(err)
                return
            }
            // v の1個目には handle が入っているのでそのまま使う
            for {
                // v の2個目を更新して送信する
                v[1] = time.Now().String()
                err = json.NewEncoder(conn).Encode(v)
                if err != nil {
                    break
                }
                time.Sleep(1 * time.Second)
            }
        }(conn)
    }
}

サーバは Accept すると1行リクエストを受け取ってハンドル番号を得ます。その後、そのハンドル番号を指定して現在時刻を送り続けます。Vim script 側は以下の様に書きます。

function! Callback(handle, msg)
  echo "ただ今:" . a:msg
endfunction
let handle = ch_open("127.0.0.1:8888""json""Callback")
echo ch_sendexpr(handle"GET""Callback")

これを実行すると

time server

おぉぉぉぉぉぉ!if_python だとスレッドを使ってバックグラウンドでソケット受信する必要がありましたが、これだと何も意識しなくて良いし、マルチスレッドが起因して落ちる事もない!これで勝つる!!!

今日の出来事は、個人的には数年ぶりの感動でした。これからどんどん非同期通信を使った Vim plugin が現れてくると思うのでとても楽しみです。