[WWDC 2021] iOS 15: ShazamKitを使用してオンライン音楽認識を行い、カスタムサウンドカタログを構築し、独自のiOSアプリでカスタムサウンドを認識します。

ShazamKit（シャザム）は音声認識フレームワークです。Apple Musicのオンラインライブラリとの照合に使用できます。また、カスタムメイドされたオーディオライブラリとの照合にも使用できます。

この記事では、まずShazamKitを使って音楽を認識する方法について説明します。次に、独自のカスタム音声カタログの構築について説明し、独自の音声を認識します。

## 楽曲の認識

始める前に、音声を録音するための`AVKit`フレームワークと、音声を認識するための`ShazamKit`をインポートします。

また、`Info.plist`ファイルの`NSMicrophoneUsageDescription`には必ず値を指定してください。

### エンタイトルメントの設定

オンラインの`ShazamKit`サービスを利用してオンラインのミュージック ライブラリとマッチさせるには、`ShazamKit`のエンタイトルメントを追加する必要があります。

https://developer.apple.com/account/resources/identifiers/list に移動し、アプリをクリックします。次に、`App Services`タブをクリックしたら、`ShazamKit`オプションをオンにして、`Save`ボタンをクリックします。

### まず、ShazamKit音楽認識セッションを設定します

最初に、`SHSession`セッションオブジェクトを設定する必要があります。このオブジェクトは、認識対象のサウンドピースを受け取り、その結果を指定されたデリゲートに出力します。

```swift
private var session = SHSession()
private let audioEngine = AVAudioEngine()
```

表示が初期化されたらデリゲートを設定してください：

```swift
session.delegate = self
```

### デリゲートの設定

認識結果の準備ができると、デリゲートを介して報告されます。

```swift
public protocol SHSessionDelegate : NSObjectProtocol {
    optional func session(_ session: SHSession, didFind match: SHMatch)
    optional func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?)
}
```

`didFind`関数では、録音を終了してからマッチング結果を表示します：

```swift
func session(_ session: SHSession, didFind match: SHMatch) {
    audioEngine.stop()
    for mediaItem in match.mediaItems {
        print("Title: \(mediaItem.title); Artist: \(mediaItem.artist); Genres: \(mediaItem.genres.joined(separator: ",")); Artwork URL: \(mediaItem.artworkURL?.absoluteString); Apple Music URL: \(mediaItem.appleMusicURL)")
        DispatchQueue.main.async {
            // TODO: - Display the result on your UI interface
        }
    }
}
```

いくつかのプロパティは空の可能性があることに留意してください。

`didNotFindMatchFor`機能では、まずレコーディングを行い、その後UIにエラーメッセージを表示します。

```swift
func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) {
    print(error?.localizedDescription ?? "")
    audioEngine.stop()
    DispatchQueue.main.async {
        self.viewState = .noResult
    }
}
```

### レコーディングを開始し、オーディオ認識のためにオーディオサンプルをShazamKitに供給してください

セッションオブジェクトをとデリゲートを設定しました。レコーディングを開始し、音声を認識することが可能です。

```swift
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: .zero)

inputNode.removeTap(onBus: .zero)
inputNode.installTap(onBus: .zero, bufferSize: 1024, format: recordingFormat) { [weak self] buffer, time in
    self?.session.matchStreamingBuffer(buffer, at: time)
}

audioEngine.prepare()

do {
    try audioEngine.start()
} catch {
    print(error.localizedDescription)
}
```

これで、録音を開始し、ShazamKitに音声を認識させることができるようになります。

## カスタムサウンドライブラリの構築

上のセクションでは、ShazamKitがApple Musicライブラリで音楽を認識しています。独自のサウンドライブラリを構築することも可能です。

サウンドライブラリを構築するには、まず様々な音声のシグネチャとそのメタデータを収集する必要があります。

```swift
private lazy var generator = SHSignatureGenerator()
```

レコーディング機能では、レコーディングを開始し、それに伴って新しいシグネチャをシグネチャジェネレーターに追加してください。

```swift
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: .zero)

inputNode.removeTap(onBus: .zero)
inputNode.installTap(onBus: .zero, bufferSize: 1024, format: recordingFormat) { [weak self] buffer, time in
    try? self?.generator.append(buffer, at: time)
}

try? AVAudioSession.sharedInstance().setCategory(.record)
audioEngine.prepare()

do {
    try self.audioEngine.start()
} catch {
    DispatchQueue.main.async {
        self.state = .initial
        print(error.localizedDescription)
    }
}
```

レコーディングが終了するとシグネチャが作成されますので、アレイに保存してください（このアレイにはあなたの全てのサウンドシグネチャが保存されています）。

```swift
self.audioEngine.stop()
let signature = self.generator.signature()
self.generator = SHSignatureGenerator()
let metaData = SHMediaItem(properties: [.title: "", .artist: ""])
let newAudioEntry = Entry(metaData: metaData,
                          signature: signature)
```

ここでは、私は1つのオーディオ機器のメタデータとサウンドシグネチャを保存するために、カスタムストラクトを使用しています。

そうすると、この音声アイテムを楽曲リストに追加することができます。ユーザーはより多くの楽曲を録音してラベルを付けることができ、関連情報がそのリストに追加されます。

ユーザーが楽曲をすべて録音したら、それらの楽曲の署名とメタデータを含むShazamカタログファイル（`.shazamcatalog`の拡張子付き）を生成することができます。

```swift
let catalog = SHCustomCatalog()
do {
    try viewModel.allSignatures.forEach { entry in
        let signature = entry.signature
        try catalog.addReferenceSignature(signature, representing: [entry.metaData])
    }
    let documentURL = getDocumentsDirectory()
        .appendingPathComponent(UUID().uuidString)
        .appendingPathExtension("shazamcatalog")
    try catalog.write(to: documentURL)
} catch {
    print(error.localizedDescription)
}
```

### カスタムサウンドライブラリから音を認識

所定のShazamカスタムサウンドカタログファイルから音を認識できます：

```swift
let catalog = SHCustomCatalog()
try catalog.add(from: Bundle.main.url(forResource: "CustomSoundLibrary", withExtension: "shazamcatalog")!)
```

この記事の最初の部分で説明したものと同じ実装を使用できます。ただし、`SHSession`オブジェクトを初期化する時、そのカタログファイルを使用します。

```swift
let session = SHSession(catalog: catalog)
```

---

:relaxed: [Twitter ツイッター @MszPro](https://twitter.com/MszPro)

[私の公開されているQiita記事をカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">

:sunny: 今後ともよろしくお願い致します。