アールテクニカ地下ガレージ

アールテクニカ株式会社の製品開発・研究開発・日々の活動です

アニソンを知る技術

店舗やクラブなどで気になる曲が流れていたらShazamなどの音楽識別アプリを使い曲名を探すことがあると思います。
アニソンの場合、曲名よりも何のアニメで使用された曲なのかが重要であることがあります。
そこで、音楽識別サービスAPIを使って曲名を取り出し、曲名を元にネット上のデータベースサイトから使われているアニメを割り出す、ということをやってみます。

音楽識別にはGracenoteのAPIを使います。
Gracenoteは音楽識別技術や音楽のメタデータを提供するサービスで、iTunesやSpotifyにも使われているそうです。

今回はGracenoteの音楽識別技術を使うためのアプリ開発用SDKであるGNSDKを使います。

Gracenoteユーザ登録とSDKダウンロード

GNSDKを使うためにはユーザ登録とアプリ登録が必要なので、下記ページからユーザ登録を行います。
https://developer.gracenote.com/user/register
ユーザ登録後ログインしたら、MyAppsページの「Add a new app」でアプリ登録を行います。
アプリを登録すると、「Client ID」「Client Tag」「License String」が表示されるので、これを保存しておきます。

次にGracenoteのサイトからGNSDKをダウンロードします。

GNSDK
https://developer.gracenote.com/gnsdk

今回はiOS版SDK、SDKのバージョンは1.3.1.5172。
他にAndroid版、Windows/Mac/Linux版もあります。

ダウンロードしたファイルを解凍すると、以下のようなディレクトリになっています。

/docs
HTMLマニュアル
/mmid
サンプルコード
/sample_music
サンプルMP3ファイル

サンプルコードを実行

mmid/のサンプルコードをビルドしてみます。

まずはGracenoteMusicId.xcodeprojをXcodeで開きます。

次にアプリ登録時に取得したLicense Stringをlicense.txtに入力して保存します。
また、同様にClient ID、Client Tagの情報を下記ファイルに入力します。
※Objective-CとSwiftのどちらでビルドするかでファイルが違います。

Objective-C
MusicIdObjectiveC/MusicidObjectiveC-Prefix.pch

#define CLIENTID @""
#define CLIENTIDTAG @""

Swift
MusicIdSwift/GnViewControllerSwift.swift

let CLIENT_ID = ""
let CLIENT_TAG = ""

そして、ビルドする言語に合わせて以下のように変更してビルドします。
Objective-C
storyboardのViewControllerをGnViewControllerに変更
Swift
BuildTargetをGracenoteMusicIdSwiftへ変更

ビルドが完了するとこのようなアプリが立ち上がります。
「ID Now」ボタンを押すと音楽識別が開始されます。
f:id:tobetchi-at:20171228184922p:plain

音楽識別コード

このサンプルアプリを改造してもいいのですが、GNSDKの情報があまりネット上に無いため、単純な機能のみのコードを書きました。
https://github.com/tobetchi/anisong_gracenote

GNSDKの使い方は付属のHTMLマニュアルに書いてあるのですが、内容が更新されてなかったり記載漏れがあったりします。
今回のコードはHTMLマニュアルを参考にしつつ、詳細はサンプルコードを元にしています。
下記に要点を記載していきます。

Xcodeプロジェクト設定

サンプルコードからGnSDKObjC.frameworkをコピーしてきてプロジェクトへ追加

Build Settings > Linking > Other Linker Flags
へ以下を追加

  • lstdc++
  • ObjC

license.txt

サンプルコード時と同様にlicense.txt を追加
Client ID、Client Tagを定数などで追加

Info.plist

Info.plistへマイクアクセス許可のため以下を追加

<key>NSMicrophoneUsageDescription</key>
<string>Microphone Access To Find Matches</string>

GNユーザオブジェクト初期化

Gracenoteへクエリを飛ばすために必要なGNユーザオブジェクトを作成

self.gnUserStore = [GnUserStore new];
self.gnUser = [[GnUser alloc] initWithUserStoreDelegate:self.gnUserStore
                                               clientId:CLIENTID
                                              clientTag:CLIENTIDTAG
                                     applicationVersion:@"1.0.0.0"
                                                  error:&error];

GNユーザのロケールを設定

NSError *localeError = nil;
self.locale = [[GnLocale alloc] initWithLocaleGroup:kLocaleGroupMusic
                                           language:kLanguageJapanese
                                             region:kRegionGlobal
                                         descriptor:kDescriptorSimplified
                                               user:self.gnUser
                               statusEventsDelegate:nil
                                              error:&localeError];
[self.locale setGroupDefault:&localeError];

マイク設定

AVAudioSessionの設定

AVAudioSession *session = [AVAudioSession sharedInstance];
[session setPreferredSampleRate:44100 error:nil];
[session setInputGain:0.5 error:nil];
[session setActive:YES error:nil];

マイク設定はGnMicというヘルパークラスが用意されており、AVAudioSessionの設定をした上で、以下のようにします。

self.gnMic = [[GnMic alloc] initWithSampleRate:44100 bitsPerChannel:16 numberOfChannels:1];
self.internalQueue = dispatch_queue_create("gnsdk.TaskQueue", DISPATCH_QUEUE_CONCURRENT);

オーディオストリームの処理

マイクから入力されるオーディオストリームを処理するGnMusicIdStreamクラスを作成し、GnMusicIdStreamOptionsクラスでクエリのオプションを設定します。
resultSingleは最良の単一結果のみ返すかどうか、lookupDataは返すデータの設定です。
audioProcessStartメソッドを実行するとオーディオの検索と処理が開始されます。

self.gnMusicIDStream = [[GnMusicIdStream alloc] initWithUser:self.gnUser preset:kPresetMicrophone locale:self.locale musicIdStreamEventsDelegate:self error:&musicIDStreamError];

musicIDStreamError = nil;
GnMusicIdStreamOptions *options = [self.gnMusicIDStream options];
[options resultSingle:YES error:&musicIDStreamError];
[options lookupData:kLookupDataSonicData enable:YES error:&musicIDStreamError];
[options lookupData:kLookupDataContent enable:YES error:&musicIDStreamError];
[options preferResultCoverart:YES error:&musicIDStreamError];

musicIDStreamError = nil;
dispatch_async(self.internalQueue, ^{
  self.idNowButton.enabled = NO;

  [self.gnMusicIDStream audioProcessStart:self.gnMic error:&musicIDStreamError];

  if (musicIDStreamError) {
    dispatch_async(dispatch_get_main_queue(), ^{
      NSLog(@"Error while starting Audio Process With AudioSource - %@", [musicIDStreamError localizedDescription]);
    });
  }
});

音楽識別の開始

GnMusicIDStreamのidentifyAlbumAsyncメソッドを呼び出すと識別が開始されます。

[self.gnMusicIDStream identifyAlbumAsync:&error];

識別結果からトラック情報を取得

識別された結果は下記デリゲートに渡されます。

- (void)musicIdStreamAlbumResult:(GnResponseAlbums *)result cancellableDelegate:(id <GnCancellableDelegate>)canceller;

デリゲートで渡されたGnResponseAlbumsにはトラック情報が入っており、アーティスト名や曲タイトルを取得できます。

if ([responseAlbums isKindOfClass:[GnResponseAlbums class]])
  albums = [responseAlbums albums];
else
  albums = responseAlbums;

for (GnAlbum *album in albums) {
  GnTrackEnumerator *tracksMatched  = [album tracksMatched];
  albumArtist = [[[album artist] name] display];
  albumTitle = [[album title] display];

  for (GnTrack *track in tracksMatched) {
    trackTitle = [[track title] display];
  }
}

曲名から使用アニメを検索

取得した曲データから何のアニメで使われた曲かを検索します。
今回、アニメと曲の対応データはAnison Generation(http://anison.info)のデータベース検索を利用しています。
WKWebViewから上記サイトへ曲名で検索リクエストを投げるというやり方です。

また、Info.plistへATSの設定を追加しておきます。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoadsInWebContent</key>
    <true/>
    <key>NSExceptionDomains</key>
    <dict>
      <key>anison.info</key>
      <dict>
        <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
        <true/>
      </dict>
    </dict>
</dict>

以下は実際に動作させてる動画です。
流れている曲から使用されているアニメを検索できています。
vimeo.com

Author

とべっち (Tobetchi)

ART Teknika佐賀スタジオマネージャ、プログラマ。WebプログラマからiOSプログラマへ転身。VJとしても活動中。

スポンサーリンク