MusicBrainzから音楽CDのカバーアート画像を取得する
Gnudbから音楽CD情報を取得する方法について書きましたが、楽曲データに加えて音楽CDのカバーアート(アートワーク)の画像も取得できるとうれしいです。しかし、残念ながらGnudbではカバーアートの管理はしていないです。
音楽CDのカバーアートを取得する方法はいくつかあるようですが、今回はMusicBrainzから取得する方法を試してみました。
MusicBrainzにアクセするための開発環境はかなり整備されていて、Developer Resourcesのページにまとめられています。
今回使ったのは、libdiscid, libmusicbrainz, libcoverartの3つのc/c++のライブラリです。
Linuxの各ディストリビューションでもパッケージがあると思うので、インストールできると思います。ラズパイ5だと下記のような感じです。
$ sudo apt install libdiscid-dev libmusicbrainz5-dev libcoverart-dev
ソースコードからインストールしても良いでしょう。configureやcmakeを実行後makeすれば多分コンパイルできると思います。
私はパッケージでインストールしましたが、各ソースコードのexamplesのディレクトリ内には簡単なサンプルプログラムがあるので、この部分を見て使い方の参考にしました。
下記は、DVDドライブに入っているCDを読み出して、カバーアートをダウンロードして、それをウインドウ表示するまでのサンプルプログラムです。
画像のウインドウの表示にはOpenCVを使っていますが、表示の必要ない場合は、OpenCVが関係する部分を削除してしまってください。
プログラムの全体の流れとしては、CDのTOC(Table Of Contents)の情報を読み出し、その情報からMusicBrainzのディスクIDを算出、その値からRelease IDを検索して、そのRelease IDで管理登録されている楽曲情報の中のカバーアート画像をダウンロードするという感じです。
具体的には、「音楽CDのTOC」ー(libdiscid)→「 ディスクID」ー(libmusicbrainz)→「 Release ID」ー(libcoverart)→「 画像ファイル」という順で3つのライブラリを順に使って音楽CDから画像ファイルにたどり着いています。
#include <stdio.h>
// (1)
#include <discid/discid.h>
#include <musicbrainz5/Query.h>
#include <musicbrainz5/Disc.h>
#include <musicbrainz5/Release.h>
#include <coverart/CoverArt.h>
using namespace std;
using namespace MusicBrainz5;
using namespace CoverArtArchive;
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char *argv[])
{
char *device = NULL;
if(argc > 1){
device = argv[1];
}
// libdiscid from here
// (2)
DiscId *disc = discid_new();
// (3)
if(discid_read_sparse(disc, device, 0) == 0){
printf("discid_read_sprse error: %s\n", discid_get_error_msg(disc));
discid_free(disc);
return -1;
}
char disc_id[30];
char free_id[10];
// (4)
snprintf(disc_id, sizeof(disc_id), "%s", discid_get_id(disc));
snprintf(free_id, sizeof(free_id), "%s", discid_get_freedb_id(disc));
printf("mb_disc_id = %s\n", disc_id);
printf("freedb_disc_id = %s\n", free_id);
// (5)
discid_free(disc);
// libmusicbrainz5cc from here
// (6)
CQuery query("example-1.0");
CMetadata metadata;
try {
// (7)
metadata = query.Query("discid", disc_id);
} catch(...){
printf("Query(%s) is not found.\n", disc_id);
return -1;
}
// (8)
CDisc *d = metadata.Disc();
if(d == NULL){
printf("CDisc is not found.\n");
return -1;
}
// (9)
CReleaseList *list = d->ReleaseList();
if(list == NULL){
printf("CReleaseList is not found.\n");
return -1;
}
// libcoverartcc from here
// CReleaseListのアイテム数だけ繰り返す
for(int i = 0; i < list->NumItems(); ++i){
// (10)
string rel_id = list->Item(i)->ID();
printf("Release ID%d: %s\n", i, rel_id.c_str());
// (11)
CCoverArt art("example-1.0");
vector<unsigned char> img;
try {
// (12)
img = art.FetchFront(rel_id);
} catch(...) {
printf("FetchFront(%s) is not found.\n", rel_id.c_str());
continue;
}
if(img.size() > 0){
// jpegファイルに保存する
char filename[50];
snprintf(filename, sizeof(filename), "%s.jpg", rel_id.c_str());
FILE *fp = fopen(filename, "wb");
fwrite(img.data(), 1, img.size(), fp);
fclose(fp);
// OpenCVで開く
Mat src = imread(filename);
imshow(filename, src);
}
}
// 何かキーを押したらOpenCVのウインドウを閉じる
waitKey(0);
return 0;
}
以下は、プログラムの簡単な説明です。
はじめはlibdiscidを使ってディスクIDを取得します。
(1) 各ライブラリのヘッダーファイルをインクルードします。各ライブラリ内のクラス毎にヘッダーファイルがあるようなので、使うクラスに応じて必要なファイルをインクルードしてください。
(2) CDディスクからTOCを読み出すためのDiscIdを生成します。
(3) discid_read_sparse()でデバイスからCDのTOC情報を読み出します。
(4) discid_get_id()でMusicBrainzのディスクIDを取得できます。MusicBrainzのIDはトラック番号と各トラックのオフセットセクター位置をハッシュ関数(SHA1)にかけてIDを生成し、Base64エンコーディングして文字列化しているようです。
ちなみに、このライブラリではdiscid_get_freedb_id()でfreedbのディスクIDも取得できるのですが、このディスクIDではGnudbの検索ができないようです。例えば、以前にご紹介したlibcddbの使い方では、「角川映画スペシャル」のCDのディスクIDは820b3a86でしたが、このライブラリを使った場合には820b3a0cとなります。Gnudb検索サイトで検索してみると、820b3a86では楽曲データが見つかりますが、今回使ったlibdiscidライブラリで生成した820b3a0cでは見つかりませんでした。
(5) 使い終わったDiscIdはdiscid_free()で解放します。
ここからは、libmusicbrainzです。
(6) CQueryを初期化します。引数にはプログラム名とバージョンの文字列を指定するようです。今回は、"example-1.0"としておきました。
(7) Query("discid", disc_id)でクエリーを実行します。1つ目の引数で検索のEntityを指定します。ここを変えることで、アーティスト名で検索したり、タイトルで検索できたりするのだと思います。今回はディスクIDなので、"discid"とします。
Queryの結果、見つからなかった場合は例外が返るようなのでtry/catchしておきます。
(8) (9)クエリーが成功すると、CMetadataが取得できるので、それを起点に今回の目的であるRelease IDを得るために、CDisc、CReleaseListと下の階層のオブジェクトを取得します。
(10) 1つのディスクIDが複数のRelease IDに紐付けられていることも多いので、順番にRelease IDを取得していきます。
ここからは、libcoverartです。
(11) Release IDからカバーアートを取得するために、CCoverArtを生成します。CQueryと同様にプログラム名とバージョンの文字列を指定します。CQueryと同じ"example-1.0"としました。
(12) FetchFront()で表のカバーアートを取得します。裏のカバーアートを取得するFetchBack()などもあるようです。FetchFront()も、見つからなかった場合は例外が返るようなのでtry/catchしておきます。
上記のソースコードをart_simple.cppなどのファイル名に保存して、下記のようにコンパイルすれば良いでしょう。各ライブラリを使うためのpkg-configを呼び出しているため、ちょっと長いです。
% g++ -o art_simple art_simple.cpp `pkg-config --cflags --libs libcoverartcc` `pkg-config --cflags --libs libmusicbrainz5cc` `pkg-config --cflags --libs libdiscid` `pkg-config --cflags --libs opencv4`
コンパイルがうまくいったら実行すると、下記のような感じの表示がされて、ReleaseIDをファイル名にしたjpegファイルが保存されます。
$ ./art_simple
mb_disc_id = 8DarjLOMtY4x.514AjiHwKcN3LE-
freedb_disc_id = 820b3a0c
Release ID0: 16dc0178-8c08-4649-8242-d248e4a2eb99
手持ちのCDを何枚か試した感じだと、MusicBrainzには楽曲情報が登録されているのに、ディスクIDとの関連付けがなされていないものも多いため、CDの情報(ディスクID)からカバーアートの画像を取得できないことが結構あります。タイトル名やアーティスト名などからも検索する必要がありそうです。
手持ちの音楽CDのディスクIDのMusicBrainzへの登録方法を下記に追記しました。