

音楽ファイルを一意に特定できるIDを自動生成生成したいと思っています。

対象は、ID3v2.3またはID3v2.4タグが付いているmp3のファイルです。

なぜIDがほしいかというと、「生成されたIDをキーにデータベースを作って、ID3タグに全く依存しない独自の楽曲管理システムを構築したい」ためです。

  * IDに期待する機能は、識別できることのみで、楽曲のタイトルなどのメタ情報は自分で管理したい IDは音楽ファイルから自動計算したい
  * IDはID3タグの更新に影響されないようにしたい（単純にファイル全体のmd5を使うことが出来ません)
  * できれば、（技術的に可能なのかわからないが）原音ファイルから不可逆圧縮をしなおしても同じものが計算されるものだと、嬉しいです

もし、質問の情報が足りなければ補足しますので、どうか有識者のかた、よろしくお願いいたします。

※質問者は、CDからリッピングされた楽曲がどのようにDBとマッチングされているかわかっていないレベルです。


CC BY-SA 3.0
2014-12-30T11:23:31.727
2833
2014-12-31T07:29:36.993
2014-12-30T11:50:13.037
3639
427
question
mp3
何を使って音楽ファイルのIDを生成すればよいか


ID3タグのデータサイズを取得した上で、残りの部分のMD5を算出すれば、ID3タグに影響されないかと思います。


CC BY-SA 3.0
2014-12-30T11:36:08.137
2834
2014-12-30T11:36:08.137
2207
2833
answer


> ID3タグに全く依存しない独自の楽曲管理システムを構築したい

[グレースノート](https://developer.gracenote.com/ja/web-
api?language=ja)のようなものを構築しようということでしょうか。だとすると、グレースノートそのものに API や SDK
がありますので、ID3 や音声ファイルからその楽曲に一意の ID (GN_ID) が取得できます。

ただし、メジャー（インディーズでない、という意味で）にリリースされたものしか登録されていません。また、無料で使えるのは非商用の場合のみです。


CC BY-SA 3.0
2014-12-30T15:07:47.257
2837
2014-12-30T15:07:47.257
79
2833
answer


MP3 の音声データフレームの MD5 を計算する方法ですが、例えば bash のシェルスクリプトでは以下の様になります。GNU coreutils
に含まれる head と tail コマンド(`-c` オプションが用意されている)を使用しています。

[code]

    mp3="hoge.mp3"
    len=0
    for offset in {1..4}
    do
     # Syncsafe Integer を計算
     len=$((len +
           $((
             $(cat "$mp3" | head -c $((6 + offset)) | tail -c 1 | od -An -td1)
               << $((7 * (4 - offset) ))
             ))
           ))
    done
    
    # 10 bytes: size of ID3v2 header, 1 byte: offset of "tail -c +n" 
    tail -c +$((len+11)) "$mp3" | md5sum
    0ea78f4e6687ac5fdbb979cc06c5c34a  -
    
    # Strip all ID3v2 tags
    mid3v2 -D "$mp3"
    
    # Calculate MD5 again
    md5sum "$mp3"
    0ea78f4e6687ac5fdbb979cc06c5c34a  hoge.mp3
    
[/code]

ID3v2 拡張ヘッダとファイル末尾にあるかもしれない ID3v1 ヘッダについては考慮していません。  
実際には MP3 や ID3 タグを扱うパッケージが用意されている言語(Python など)で実装した方が良いかと思います。


CC BY-SA 3.0
2014-12-31T02:36:53.460
2844
2014-12-31T03:03:39.120
2014-12-31T03:03:39.120
2833
answer


WAVファイルから波形データを抽出してハッシュを取るだけであれば python で、、

[code]

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import wave, hashlib
    
    wr = wave.open('tmp.wav','r')
    data = wr.readframes(wr.getnframes())
    wr.close();
    
    hash_data = hashlib.sha256(data).hexdigest()
    
[/code]

で取れます。

なので(あまりうまくいく気がしないのですが)、 ffmpeg を使って元の mp3 ファイルを、、

[code]

    $ ffmpeg -i in.mp3 -ac 1 -ar 8000 -acodec pcm_s16le tmp.wav
    
[/code]

とかして、モノラルの framerate 8000 の sampwidth 2(16bit-little)
と乱暴に劣化させてから、上記のスクリプトを通せば、多少はマシな ID もどきが作れるかもしれません。

もう少し頑張るなら、、

[code]

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import wave,struct,hashlib
    
    wr = wave.open('tmp.wav','r')
    
    # debug..
    # print "channels  : ", wr.getnchannels()
    # print "sampwidth : ", wr.getsampwidth()
    # print "framerate : ", wr.getframerate()
    # print "frame num : ", wr.getnframes()
    # print "prams : ", wr.getparams()
    # print "sec : ", float(wr.getnframes()) / wr.getframerate()
    
    tmp = []
    length = wr.getnframes()
    for i in range(0, length):
        data = wr.readframes(1)
        nn = struct.unpack("<H", data[0:2])[0]        
        # 乱暴に割ってしまう        
        nn = nn / 32
        # 乱暴に小さい波形は無視する
        if nn > 8:
            tmp.append(nn)
    wr.close();
    
    hash_data = hashlib.sha256(str(tmp)).hexdigest()
    
[/code]

くらいすると良いかもしれません。(かなり適当です。。)

乱暴をしている箇所と、`debug`で出してるデータあたりの考慮と、その他もろもろを工夫すると、幸せになれるような気が少しします。


CC BY-SA 3.0
2014-12-31T03:15:14.180
2845
2014-12-31T07:29:36.993
2014-12-31T07:29:36.993
2992
2992
2833
answer