見出し画像

MusicBrainzから音楽CDのカバーアート画像を取得する

Gnudbから音楽CD情報を取得する方法について書きましたが、楽曲データに加えて音楽CDのカバーアート(アートワーク)の画像も取得できるとうれしいです。しかし、残念ながらGnudbではカバーアートの管理はしていないです。

音楽CDのカバーアートを取得する方法はいくつかあるようですが、今回はMusicBrainzから取得する方法を試してみました。

MusicBrainzにアクセするための開発環境はかなり整備されていて、Developer Resourcesのページにまとめられています。
今回使ったのは、libdiscidlibmusicbrainzlibcoverartの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
角川映画スペシャルの結果
(16dc0178-8c08-4649-8242-d248e4a2eb99のカバーアート画像)

手持ちのCDを何枚か試した感じだと、MusicBrainzには楽曲情報が登録されているのに、ディスクIDとの関連付けがなされていないものも多いため、CDの情報(ディスクID)からカバーアートの画像を取得できないことが結構あります。タイトル名やアーティスト名などからも検索する必要がありそうです。

手持ちの音楽CDのディスクIDのMusicBrainzへの登録方法を下記に追記しました。


いいなと思ったら応援しよう!