Geek-Side

<< < 1 > >>

CDDBサーバーに検索クエリーを投げる


WAVファイルからCDDBへ検索クエリーを投げるプログラムを作ってみました。
色々試しながらプログラミングするには、EmacsでClojureな環境はホントいいですね。
CDDBへクエリーを投げる方法はここを参考にさせてもらいました。

CDDBへクエリーを投げるには曲長を基に算出したdiscIdが必要になります。
さらに各曲のフレームオフセット数も必要です。フレーム数とは1秒辺り75フレームとなる値になります。
フレームオフセットとは、各曲が先頭から何フレーム目から開始されるかを表します。
これらを算出するためにjavax.sound.sampled.AudioSystemクラスを利用しました。
こんなクラスがあったんですね。
曲長ををだして、1秒辺りのフレーム数75をかけています。
 ;; オーディオファイルのフレーム数を出す
 (defn get-frame [file]
   (let [audio (javax.sound.sampled.AudioSystem/getAudioFileFormat file)]
     (* (/ (.getFrameLength audio)
           (.getFrameRate (.getFormat audio)))  75)))

まずdiscIdを算出します。
曲長を秒で算出して、桁毎に足しあわせます。
例えば185秒の曲長だったら、1+8+5=14 という具合です。
これをアルバムの全曲に対して行なって、足しあわせたあとに255で割ったあまりをだします。(A)

次に全曲長を算出します。(B)

最後に曲数を算出します。(C)

これらをビット演算します。
A << 24 | B << 8 | C

discIDを算出するClojureのコードです。HelloweenのTime Of The OathのCDをwavファイルに落としているものをCDDBへ問い合わせています。
拡張子wavのWAVファイルがトラックナンバーをファイル名として保存されている前提です。
例えば1曲目はTrack1.wavというファイル名で保存されている前提です。

 ;; 1曲目のオフセット
 (def init-offset 187)
 
 ;; .wavファイルでフィルター
 (def files (filter #(re-find #".*wav$" (.getName %)) 
                    (file-seq (clojure.java.io/file "/home/takeshi/scorpions_eye2eye/"))))
 
 ;; Track numberでソートする
 (defn sorted-files [file]
   (sort-by #(Integer. (re-find #"d+" (.getName %))) file))
 
 
 ;; フレームオフセットを計算する
 (defn get-frame-offset [seq]
   (letfn [
           (sum [seq sum-num sum-seq]
           (if (= (count seq) 1)
             (sort sum-seq)
             (let [res (+ (first seq) sum-num)]
               (sum (rest seq) res (cons res sum-seq)))))]
   (sum (cons init-offset seq) 0 '())))
 
 
 
 ;; フレーム数から10進数の桁毎に足した合計を算出する
 (defn sum-frame [frame]
   (letfn [
         (digit  [div-frame sum]
           (let [int-div-frame (bigint div-frame)]
             (if (< div-frame 1)
               (math/floor  sum)
               (digit (/ int-div-frame 10.0) (+ sum (- int-div-frame (* (math/floor (/ int-div-frame 10.0)) 10)) ) ))))]
     (digit frame 0)))
 
 ;;
 (def A   
   (mod 
    (reduce + (map #(sum-frame (/ % 75)) 
                   (get-frame-offset (map #(get-frame %) (sorted-files files))))) 0xff))
 
 ;; 曲長
 (def B
   (reduce + (map #(/ (BigDecimal. (.getFrameLength %))
          (.getFrameRate (.getFormat %))) (map #(javax.sound.sampled.AudioSystem/getAudioFileFormat %) files))))
 
 ;; 曲数
 (def C
 (count files))
 
 
 ;; discid
 (def discid 
   (Long/toHexString (bit-or (bit-shift-left (long A) 24) (bit-shift-left (long B) 8) C)))
 

フレームオフセットを+でつなぎあわせた文字列を生成します。
;; オフセット
 (def offset-string
   (clojure.string/join "+" (map long (get-frame-offset (map #(get-frame %) (sorted-files files))))))

discId、フレームオフセット、全曲長、曲数をつなぎあわせて、CDDBへの検索クエリーを生成します。
 (with-open [rdr (clojure.java.io/reader (str "http://freedb.freedb.org/~cddb/cddb.cgi?cmd=cddb+query+" (clojure.string/join "+" (list discid C offset-string (int B))) "&hello=username+hostname+clripper+R0.1&proto=6"))]
   (prn "%sn" (str "n" (line-seq rdr))))

クエリーの結果はこんなかんじです。
 "%sn" "n("200 rock d510710e Helloween / The Time Of The Oath [Japan Bonus Tracks]")"

ちゃんとした実行形式として作れてないですが、CDDBを検索するときに何をやっているかよくわかりました。
疑問点としては1曲目のオフセットが150で固定にしていますが、187のアルバムもあるようです。
一度wavファイルに落としちゃうと、算出は難しいようです。

Clojureの開発環境構築


今年はClojureを頑張ることにしてます。
以前触ったときはswank-clojureがemacsでのClojure開発では鉄板のようでしたが、時は変わって今はnreplが良いようです。
Clojure + Emacsの開発環境は流れがよく変わるので注意が必要ですね。
今のところはnreplが一番将来性が有りそうということのようです。

インストール方法はEmacs標準のパッケージインストーラーでお手軽にインストールできます。
package-list-packageでnreplがリストアップされるのでインストールします。
Emacs24からはパッケージのインストールが本当に便利になったもんです。

nreplはleingenと連動して動くので、leingenのインストールも必要です。
leingenのインストールは以前やってるので省略

インストールが終わったら、M-x nrepl-jack-in すればreplが起動します。
後は C-x C-e でガシガシ開発する感じ。

ほんと簡単になったもんだ。