WAVファイルからCDDBへ検索クエリーを投げるプログラムを作ってみました。
色々試しながらプログラミングするには、EmacsでClojureな環境はホントいいですね。
CDDBへクエリーを投げる方法はここを参考にさせてもらいました。
CDDBへクエリーを投げるには曲長を基に算出したdiscIdが必要になります。
さらに各曲のフレームオフセット数も必要です。フレーム数とは1秒辺り75フレームとなる値になります。
フレームオフセットとは、各曲が先頭から何フレーム目から開始されるかを表します。
これらを算出するためにjavax.sound.sampled.AudioSystemクラスを利用しました。
こんなクラスがあったんですね。
曲長ををだして、1秒辺りのフレーム数75をかけています。
1
2
3
4
5
| ;; オーディオファイルのフレーム数を出す
(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| ;; 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)))
|
フレームオフセットを+でつなぎあわせた文字列を生成します。
;; オフセット
1
2
| (def offset-string
(clojure.string/join "+" (map long (get-frame-offset (map #(get-frame %) (sorted-files files))))))
|
discId、フレームオフセット、全曲長、曲数をつなぎあわせて、CDDBへの検索クエリーを生成します。
1
2
| (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))))
|
クエリーの結果はこんなかんじです。
1
| "%sn" "n("200 rock d510710e Helloween / The Time Of The Oath [Japan Bonus Tracks]")"
|
ちゃんとした実行形式として作れてないですが、CDDBを検索するときに何をやっているかよくわかりました。
疑問点としては1曲目のオフセットが150で固定にしていますが、187のアルバムもあるようです。
一度wavファイルに落としちゃうと、算出は難しいようです。