サイログ。

~雑多な記事置き場~

ATOKで選曲プラグインを公開します

ATOKダイレクト for Ruby/Perl(id:ATOKDirect)キーボード作業をやめることなく曲を再生できる「ATOKで選曲プラグイン」を作ってみました。

D

以下のリンクをクリックするとダウンロードできます。
(ドキュメントは大急ぎで用意したのでかなり不親切です・・・済みません)
http://www.twin.ne.jp/~cyross/atok_direct_senkyoku.zip

ATOKを起動して、「聴きたい曲のタイトル(の一部)@選曲」と入力してATOKダイレクトを呼び出すと、
あらかじめ設定しておいたフォルダを検索して、検索に引っかかった曲一覧を表示してくれます。
あとは、聴きたい曲を選択すれば、自動的にプレイヤー(拡張子の関連づけで指定したプログラム)が起動して曲を再生します。

また、平仮名・片仮名/大文字・小文字/全角・半角の自動判別も行いますので、片仮名の曲名でも、平仮名で検索できます。

ただし、使用には以下の条件・制限があります。
・検索対象のファイル名に、曲のタイトルを追加してください。
・再生できる曲は1曲のみです。プレイリスト形式ではありません
・再生プログラム自身の制御は出来ません(音量の変更など)

必要なソフトは、以下の通りです。

ATOK2008 for Windows
http://www.atok.com/index.html

ATOKダイレクト for Perl/Ruby
http://www.atok.com/useful/developer/api/

ActiveRuby
http://arton.hp.infoseek.co.jp/indexj.html

(ActiveRuby以外のRuby実行環境を使用している場合、VisualRubyを追加すればいける?
http://www.osk.3web.ne.jp/~nyasu/software/vrproject.html

Mojiモジュール
http://gimite.net/gimite/rubymess/moji.html

ATOKダイレクト for Perl/Rubyの隠れた可能性を引き出せることになればと思います。

作成したソースを載せておきます。
ソースは、かなりでかくなったので、↓リンクをクリックしてください。
FOLDER_PATH配列の内容を編集することで、検索対象にしたい音楽用フォルダを追加することが出来ます。

#! /usr/bin/ruby -Ku

# Title: ATOKで選曲させてみたモジュール
# Date: 2008/11/16
# Author: Cyross Makoto
# Abstract: ATOK2008(Windows)で曲を検索して再生する
# Contents: ATOK2008(Windows)のATOKダイレクトAPIを用いて、入力した曲名(の一部、正規表現)をもとに
#           曲を検索し、一覧を表示する。利用者は、その中の1曲を選択してその曲を聴くことが出来る
#           入力時に、「〜@選曲」と入力してATOKダイレクトを呼び出せば、選択リストが出るので、
#           聴きたい曲を選択してEnterキーを押す(取りやめたければEscキーを押す)
#           だたし、選択できる曲は1曲のみ、プレイヤー自体の制御が出来ない制限がある
# Requirement: ATOKダイレクトAPI for Perl/Ruby
#              ActiveRuby 1.8.7(VisualRuby含む)
#              「moji」モジュール
# LICENCE:Ruby Licence

$KCODE = "UTF8"

require 'dl/win32'
require 'vr/vruby'
require 'vr/vrcontrol'
require 'vr/vrlayout'
require 'vr/vrhandler'
require 'kconv'
require 'moji'

# キー押下イベントハンドラを追加したリストボックスクラス
# VisualRuby使用
class VROKSelect < VRListbox
  include VRKeyFeasible
  def vrinit
    super
    add_parentcall("char")
  end
end

# 検索した曲の選択ウィンドウモジュール
# VisualRuby使用
module SelectWindow
  include VRGridLayoutManager

  SW_SHOWNA = 8
  KY_ENTER  = 13
  KY_ESCAPE = 27
  
  def construct
    setDimension(1,10)
    self.caption = "選曲だヨ〜ん".tosjis
    
    addControl(VROKSelect, "list","", 0,0,1,10)
    @list.focus
  end

  # 選択させたい音声ファイルのパスを追加する
  # path : 追加対象のファイルパス
  def add_path(path)
    @list.addString(path)
  end

  # キーボードからキーが押されたときのイベントハンドラ
  def list_char(ansi, keydata)
    case ansi
    when KY_ENTER
      # エンターキーが押されたときは、選択したファイル名を演奏してプログラムを終了する
      play_song
      close
    when KY_ESCAPE
      # エスケープキーが押されたときは、プログラムを終了する
      close
    end
  end
  
  # 選択された曲を演奏
  # 拡張子ごとの割り当てで指定したアプリケーションが再生する
  # Win32APIモジュールを使用して、ShellExecute Win32API関数を呼び出している
  def play_song
    player = Win32API.new("shell32.dll", "ShellExecute", %W(p p p p p i), "i")
    player.call(0, "play", @list.getTextOf(@list.selectedString).tosjis, 0, 0, SW_SHOWNA)
  end
end

# ATOKダイレクトAPIモジュール
# 説明は省略
module Atok_plugin
  # この配列定数内に、検索対象のフォルダ名をリストアップする。
  # 区切りは円記号を使わず、スラッシュを用いること!
  # あと、フォルダ名はUTF-8で記述すること
  FOLDER_PATH = ["c:/sounds/"]

  @@select_key    = /(.*)[@@]選曲\Z/
  @@is_regexp_key = /\A[~〜](.+)/

  # ATOKダイレクトAPI本体
  # 説明は省略
  # 戻り値 : nil(変換結果を返さない)
  def run_process(req)
    str = req['composition_string']
  
    # 検索対象が「〜@選曲」でなければ終了
    return nil unless @@select_key.match(str)

    s = Regexp.last_match[1]
    
    # 「〜@選曲」までの文字列をもとに曲を検索
    song_list = []
    FOLDER_PATH.each{|fp|
      song_list = search_song(song_list, parse_word(s), fp)
    }
  
    # 曲が見つからなければ終了
    return nil if song_list == []
    
    # 選択用ウィンドウの生成・表示
    frm = VRLocalScreen.newform
    frm.extend SelectWindow
    frm.move 1400, 800, 400, 300
    frm.create
    song_list.each{|path| frm.add_path(path.tr('/', '\\')) }
    frm.show

    VRLocalScreen.messageloop
    
    return nil
  end

  # 文字列を解析して、出来た文字列をShift-JISに変換して、正規表現を作成する
  # 全角平仮名・片仮名 ・・・ 平仮名・片仮名ともに適応できるように変換
  # 数字 ・・・ 全角・半角ともに適応できるように変換
  # アルファベット ・・・ 全角・半角/大文字・小文字すべてに適応できるように変換
  # 記号は、正規表現の一部と認識しているため、そのまま残す
  # word : 検索対象の文字列。正規表現可能。UTF-8
  # 戻り値 : wordを変換したあとの正規表現インスタンス
  def parse_word(word)
    # 文字列が空のときは全選択
    if word.length > 0
      is_pattern = false
      is_escape = false
      word = word.split(//).inject(""){|w, c|
        # 正規表現の"[〜]" 内は評価対象外にする
        if is_pattern
          is_pattern = false if c == "]"
          next w + c
        end
        if is_escape
          is_escape = false
          next w + c
        end
        case c
          when "["
            # [は、正規表現の一部と見なす
            is_pattern = true
          when "\\"
            # \の次の文字は、正規表現の一部と見なす
            is_escape = true
          else
            # 各文字に対してパターンを作成する(大文字/小文字、平仮名/片仮名、全角/半角)
            case Moji.type(c)
              when Moji::ZEN_HIRA # 全角平仮名 -> 全角平仮名 + 全角平仮名
                c = "[" + c + Moji.hira_to_kata(c) + "]"
              when Moji::ZEN_KATA # 全角片仮名 -> 全角片仮名 + 全角平仮名
                c = "[" + c + Moji.kata_to_hira(c) + "]"
              when Moji::HAN_NUMBER # 半角数字 -> 半角数字 + 全角数字
                c = "[" + c + Moji.han_to_zen(c) + "]"
              when Moji::HAN_UPPER # 半角大文字 -> 半角大文字 + 全角大文字 + 半角小文字 + 全角小文字
                c = "[" + c + Moji.han_to_zen(c) + Moji.downcase(c) + Moji.downcase(Moji.han_to_zen(c)) + "]"
              when Moji::HAN_LOWER # 半角小文字 -> 半角小文字 + 全角小文字 + 半角大文字 + 全角大文字
                c = "[" + c + Moji.han_to_zen(c) + Moji.upcase(c) + Moji.upcase(Moji.han_to_zen(c)) + "]"
              when Moji::ZEN_NUMBER # 全角数字 -> 全角数字 + 半角数字
                c = "[" + c + Moji.zen_to_han(c) + "]"
              when Moji::ZEN_UPPER # 全角大文字 -> 全角大文字 + 半角大文字 + 全角小文字 + 半角小文字
                c = "[" + c + Moji.zen_to_han(c) + Moji.downcase(c) + Moji.downcase(Moji.zen_to_han(c)) + "]"
              when Moji::ZEN_LOWER # 全角小文字 -> 全角小文字 + 半角小文字 + 全角大文字 + 半角大文字
                c = "[" + c + Moji.zen_to_han(c) + Moji.upcase(c) + Moji.downcase(Moji.zen_to_han(c)) + "]"
            end
        end
        w += c
      }
    else
      word = ".*"
    end
    # 出来た文字列をShift-JISに変換して、Regexpクラスのインスタンスを生成する
    return Regexp.compile(word)
  end
  
  # 正規表現から、曲を検索してリストアップする
  # サブフォルダ検索にも対応
  # 検索対象のファイル名が曲名になっていることが前提
  # list : 呼び出し元が呼び出し時に所持していたリスト。このリストに曲パスを追加する
  # regexp : parse_word関数で生成した正規表現インスタンス
  # path : 検索対象となるパス名
  # 戻り値 : 検索した曲のパス名の配列
  def search_song(list, regexp, path)
    path = path + "/" unless /[\/\\]\Z/.match(path)
    Dir.glob("#{path}*"){|f|
      # 検索対象文字列はUTF-8限定のため、パスをUTF-8に変換する
      if FileTest.directory?(f)
        list = search_song(list, regexp, path)
      elsif regexp.match(f.toutf8)
        list << f
      end
    }
    return list
  end
end

プラグイン登録用のXMLファイルです。

<?xml version="1.0" encoding="UTF-8" ?>

<plugin_info>
  <name>選曲inATOK</name>
  <description>特定フォルダの曲を選曲して選択できるようにします</description>
  <copyright>2008 Cyross Makoto</copyright>
  <major_version>1</major_version>
  <minor_version>0</minor_version>
</plugin_info>