ワンタンスープ、おいしいです。Gehirn News ライターの KOBA789 です。

今回は Emacs のお話です。Emacs といえば世界の100割くらいの人が使ってることで有名な環境です。

さて、私は業務中、よくヘッドホンで音楽を聞いています。作業は SSH でつないだリモートの Emacs 上で行っていますが、音楽を再生しているのはローカルの Mac で動く iTunes です。

iTerm and iTunes

さて私はこのややこしい環境で「Emacs から iTunes を制御したい」と思いました。環境であるところの Emacs ですから、当然 iTunes の操作程度はできなければいけないはずです。

実装

シェルから iTunes を操作できれば勝利確定だろうということは想像に難くないかと思います。で、その方法ですが、あるんですね、これが。

Mac には Apple Script というものがありプロセス間通信で云々。osascript というコマンドがあるのでこれにスクリプトを投げつければシェルから実行することができるのです。

さて、問題は Apple Script の文法なのですが、これが「恐ろしく文脈依存で謎」でありまして、「だいたいそれっぽい英語を書くと動く」のであります(本当はそれなりにちゃんとした文法を持っているらしい)。場合によっては brainf*ck よりも難解かと思われますし、読むのもさることながら書くのは非常につらいです(演算子の結合順序がよくわからん)。そしてその文法だけでなく、専用エディターであるところの Apple Script.app の挙動もまた謎です(勝手に括弧が挿入されたり、括弧の位置が変わったりします)。

ということで苦労して書いたのがこちら。結局心が折れて最低限の機能しか実装していません。`itunes.applescript` とか名前をつけて保存しましょう

on run argv
  tell application "iTunes"
    set cmd to first item of argv
    if cmd = "next" then
      next track
    else if cmd = "prev" then
      previous track
    else if cmd = "forward" then
      fast forward
    else if cmd = "back" then
      back track
    else if cmd = "rewind" then
      rewind
    else if cmd = "play" then
      play
    else if cmd = "toggle" then
      playpause
    else if cmd = "pause" then
      pause
    else if cmd = "stop" then
      stop
    else if cmd = "name" then
      return name of current track
    else if cmd = "artist" then
      return artist of current track
    else if cmd = "album" then
      return album of current track
    else if cmd = "count" then
      return played count of current track
    else if cmd = "playlist" then
      return name of current playlist
    else if cmd = "volup" then
      set sound volume to sound volume + 5
    else if cmd = "voldown" then
      set sound volume to sound volume - 5
    end if
  end tell
end run

これを osascript コマンドに食わせるとめでたく iTunes を制御できる、というわけです。ですからあとは nc で待ち受けて、飛んできたコマンドを xargs か何かでこれに投げればいいですね。(ポート番号はお好きに決めてください)

nc -k -l 5555 | xargs -n 1 osascript itunes.applescript

当然 Emacs は TCP/IP をしゃべれますから、設定ファイルにこんな感じの汚いコードを書きます(elisp な方、良い書き方教えてください)。

(defun itunes-call (command)
  (let ((proc (open-network-stream "itunes" nil "MAC_HOST" 5555))) ; edit here
    (process-send-string proc command)
    (delete-process proc)))

(define-key global-map (kbd "C-c C-c")
  (lambda () (interactive)
    (itunes-call "toggle\n")))

(define-key global-map (kbd "C-c C-f")
  (lambda () (interactive)
    (itunes-call "next\n")))

(define-key global-map (kbd "C-c C-b")
  (lambda () (interactive)
    (itunes-call "back\n")))

(define-key global-map (kbd "C-c C-p")
  (lambda () (interactive)
    (itunes-call "volup\n")))

(define-key global-map (kbd "C-c C-n")
  (lambda () (interactive)
    (itunes-call "voldown\n")))

以上でサクッと望みを叶えることができました。もう Apple Script 書きたくないです。

まとめ

nc + oscscript の組み合わせでいろいろリモートからコマンドを叩けるようにしているアプリはほかにもあるのですが、なんといっても Apple Script を書くのがつらすぎます。つらいです。非常に。

次回は Emacs や OMake から Google Chrome を操作して JavaScript によるフロントエンド開発を加速させるネタを書く予定です。予定ですが、いつになるかはわかりません。あしからず。

P.S.

ルーター自作のシリーズ、完全に停止していますが続編に期待している方がいらっしゃいましたらはてブにてお願いいたします。