ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ShazamKitで、大規模なカスタムカタログを作成する
ShazamKitを使用して、カスタムカタログを作成したり、あらゆるApp内のオーディオソースとの完全一致をサポートする方法を解説します。新しいShazamKit CLIを活用して、オーディオシグネチャを簡単に生成したり、大規模なカタログを構築したりする方法をご覧ください。また、連続もののテレビ番組やPodcastなど、大量のオーディオコンテンツをすばやくAppに同期する方法をはじめ、ShazamKit APIやSHMediaItemsの最新情報も紹介します。これにより、時間範囲を使用するオーディオソースの重要な瞬間に、Appが正確に応答できるようになります。 ShazamKitの詳細については、WWDC21の「ShazamKitの紹介」と「ShazamKitによるカスタムオーディオエクスペリエンスの構築」をご覧ください。
リソース
関連ビデオ
WWDC23
WWDC21
-
ダウンロード
僕はNeil Foley ShazamKitチームのエンジニアです 2021年の ShazamKitの導入で Shazamの膨大な音楽カタログに オーディオをマッチングできる ようになりました また カスタムカタログ マッチングも導入されたことで デベロッパは自分のオーディオを マッチングさせて 同期体験が 提供できるようになりました さらに大規模に カスタムカタログとの連携を 合理化した重要な アップデートが行われました 今回は ShazamKitの既存の コンセプトを使っていきます 例えば 署名やカタログ メディアアイテムなどです これらに馴染みのない方は 画面の下に出ている WWDC21に行われた 2つのトークの内容を ご覧ください 簡単に言うとShazamKitは オーディオをマッチングできる 特別なフォーマットに 変換してくれます これをシグネチャと 呼んでいます メタデータを含むメディアアイテムと 組み合わせてできるのが 参照シグネチャで カスタムカタログと言う ファイルにまとめて 保管することができます 簡単な概要は以上で 今度は大規模な カスタムカタログの構築と 素晴らしいカタログにするための コツについてお話します 現在の カスタムカタログ ワークフローなら マッチングしたいコンテンツが 少ない場合は カスタムカタログでの作業は いたってシンプルです その手順は次の通りです ShazamKitが使用できる形式で オーディオを録音します
シグネチャジェネレータを使って それをシグネチャにします
それにメタデータを付加して カスタムカタログに保存します それだけで Shazam体験を 提供できるのです ですが オーディオプログラミングに 詳しくない場合は 大変に思えるかも知れませんね サンプルレートやバッファも 経験豊富なデベロッパでも 手こずることがあります 膨大な量のコンテンツを 取り扱う場合 例えばテレビ番組の10シーズン分なら どうなるでしょう?
このワークフローは かなり困難になり得ます
コンテンツの量が膨大だと 管理不能になりやすいものです このワークフローを 自分で改善するには オーディオをシグネチャに変える コードを書いて メディアアイテムを取り込んで関連付ける ために更にコードを書き コンテンツを変える度に これを繰り返すことになります オーディオのマッチングに これは膨大過ぎる作業です そしてコンテンツを ShazamKitと同期したければ 複雑な論理を使って何をいつ どうするか計算することになります このワークフローを合理化するために ShazamKitが大幅に機能強化されました まずは 簡単なデモをお見せします これは2021年にAlexが お見せした FoodMathAppで オンスクリーンレッスンと 算数クイズを同期しています 最新の ShazamKit機能で アップデートしてあり その同期状況を見るために FoodMathビデオを再生します
26秒までスキップ 2個… 3個の青りんごがあります 全部でりんごはいくつありますか? それでは皆さん… 始め! ではそこまで! 答えは何でしょうか 56秒までスキップ 今日は少しひねりますよ お店に行った時 最初は2個の赤いりんごで… それから2個の青りんごを 買いました 今度は全部でいくつの りんごをもっていたでしょうか? それでは皆さん… 始め!
ではそこまで! 問題なさそうです リッチなコンテンツが動画と同期し 僕が“始め!”と言った時 ピッタリのタイミングで メニューが表示されました またコンテンツの関連性がなくなると キューに合わせて消えていました 一体どうやったのでしょう? コードを見てみましょう シンプルなループだけです 以前使っていた デリゲートのコールバックの代わりに 非同期のシーケンスを使っています マッチしたか していないか エラーかを 表す列挙型を戻してくれます 必要なのはマッチしたものだけなので ループをそれに限定します 結果の表示を作成するために メディアアイテムを 必要なコンテンツだけに減らします
Appの中身はこれぐらいで 作成した matchResultの生み出す SwiftUIビューだけです 複雑な論理もタイミングコードもなく 完璧に同期してくれます なぜそんなに上手く同期できるのか やはり疑問ですね FoodMathsの秘密は リッチな カスタムカタログにあります ShazamKitの補完として作成した 簡易ツールでカタログを作りましたが 皆さんも同様にApp内に リッチな体験を生み出せます Shazam CLIは macOS 13の 一部として登場し コンテンツの同期を簡易化する 方法を提供します カスタムカタログの作成に関連する 反復的タスクを自動化する サポートをしてくれます 先ほどお見せした カスタムカタログを アップデートしましょう 別のデモをお見せします
このフォルダにあるのは FoodMathのビデオファイルと ターミナルも入っています CLIを使って動画を シグネチャに変換します シグネチャコマンド使用します
ビデオファイルを入力としてパスし シグネチャ出力を特定します
シグネチャができました
これをメディアアイテムと 一緒に合わせて カスタムカタログを作ります CLIはメディアアイテムの記述に コンマで分けられた シンプルなファイルを受け付けるので ここにコピーします
コンテンツの同期に必要なものが 全て記述されています
特定したタイトルはここに… そして方程式のために定義した カスタム JSONフィールドがここです ヘッダーはメディアアイテム プロパティに関連しています マッピングの詳細については カスタムカタログ作成コマンドを ヘルプのフラグ付きで 実行してください
CSVヘッダーとメディアアイテムプロパティの 関係性が記述されています では両者を合わせて カスタムカタログを作りましょう 作成コマンドを実行して…
シグネチャファイルを csvファイルに パスするとカタログを出力します
これでカタログができました 僕は最新の FoodMathエピソードに 既にアクセスできるので カタログファイルに追加します ファイルをコピーします
新エピソードのメディアアイテムが ここにあります
アップデートコマンド実行して 新しいメディア ビデオそしてカタログを 渡してアップデートします
カタログのアップデート完了です カタログの作成方法を 簡単に見てきましたが 僕のようなタイプの人は これをスクリプトしたいですね
FoodMathAppはかなり 新しいエピソードがあって その全てをこのカタログに 追加したいと思います 全部のエピソードフォルダをループし それを合わせてカスタムカタログに 入れる単純なスクリプトを書きました では実行します
できました これで1つのカタログに 全ての FoodMathエピソードと カタログ内の詳細を示すのに スクリプトの表示コマンドが使用されました 全部入っているようです FoodMathプロジェクトは既に 新しいカタログを参照しています では 作成および実行して 算数を楽しみましょうか
30秒までスキップ 全部でりんごはいくつありますか? それでは皆さん… 始め! では そこまで 答えを見てみましょう あの人 イイですね 素晴らしいエピソードだ 新しいエピソードはどうでしょう 見てみましょう
15秒までスキップ - 何年もかけて美味しいワカモレの 秘密を探ってきた僕が お気に入りのワカモレのレシピを 書いてみました 4個のアボカドが必要です 友人が遊びに来るのですが 2人だけなら半分の量の ワカモレで充分です アボカドは何個必要でしょうか? それでは皆さん… 始め!
その通り アボカド2個です 一緒にワカモレを作りましょう
味見してみましょう
うーん とても美味しい 楽しんでいただけたでしょうか それでは また次回
そうか 新しいホストとは なるほど とにかくあっという間に リッチな同期体験が作成できました Shazam CLIはコマンドの リッチなセットをサポートします 見ていきましょう
オーディオトラックを含む あらゆるメディアファイルから シグネチャが作成できます シグネチャとメディアアイテムを 合わせてカスタムカタログが 作成できます カタログのコンテンツを 表示できます シグネチャとメディアアイテム 両方の 追加・削除・エクスポートが可能です 次は CLIが FoodMathビデオから シグネチャ作成した方法です
SHSignatureGeneratorには新メソッドの signatureFromAssetがあり 全プラットフォームで利用可能です このメソッドではメディアから手動で オーディオバッファを引き抜く必要はありません オーディオトラックのついた AVAssetをパスし シグネチャに変えるだけです アセットに複数のトラックがある場合は 全てがミックスされ 必ず シグネチャ内に 全てがキャプチャされます では メディアを表すシグネチャが あったとしても どうやって正確にコンテンツを 同期したのでしょうか? Timed MediaItem APIを使いました メディアアイテムに時間を 付与することで 開始時間および終了時間の特定が 簡単になりました メディアアイテムは複数の 時間範囲を持つ場合があり シグネチャの一部分以外も ターゲットにしています コーラスをターゲットにした メディアアイテムを考えてみましょう 歌われている部分を表す 時間範囲が追加できます
時間範囲の特定が役に立つのは 開始時間と終了時間が 通知された場合だけです ShazamKitは時間の範囲と同期した マッチコールバックを送り 開始時に1つと終了時に1つです シグネチャは多くのメディアアイテムを 包括できるので このコールバックは 特定の時間の範囲内にある メディアアイテムのみを含みます どのメディアアイテムがどの順番で コールバックに戻されるのか 少しだけルールがありますので それを見てみましょう
時間範囲の外にあるものは 戻されません 時間範囲の中にあれば 戻されますが 最近のイベントが 最初に戻されます
時間範囲のないものは 常に最後に戻されますが 順序はバラバラです 時間範囲のないものは レファレンス全体に適用される 全体的な情報を保存するのに 適した場所だと言えます FoodMathの例では エピソード名の保存場所にしました 他のメディアアイテムが 見えない場合に現れます
付け加えると メディアアイテムの 全てに時間範囲があって どれもスコープ外であれば ShazamKitが常に基本的マッチ情報を付けて メディアアイテムを戻します これによって常に 重要なプロパティが手に入ります 例えば predictedCurrentMatchオフセットや frequencySkewなどです コードでも簡単です timeRangesのメディアアイテム プロパティを特定することで 時間付きのメディアアイテムが 作成されます Swift範囲の配列です timeRangesプロパティを使って リードバックすることも可能です Objective-Cのプログラマーには 新しい SHRangeのクラスが 完全互換品として使えます 作成方法は分かったので… 素晴らしいカスタムカタログを作る コツをいくつか見てみましょう メディア1つに対して数多くの 小さな シグネチャを作らないように シグネチャはそれが表すメディアへの 1対1のマッピングなので 歌であろうとビデオであろうと 1つのオーディオに対し 最初から最後までで 1つの シグネチャを作りましょう
シグネチャが長ければ ShazamKitは 音のピークをマッチする機会が増え その結果 正確性が向上するわけです また 複数の参照シグネチャが 重複する クエリ シグネチャのような 問題も避けられます
新しいTimed MediaItem APIを を使えば 個々のエリアで同期コンテンツを ターゲットにできます 1つのオーディオを複数のシグネチャに 分割する必要はありません お見せした例では メディアは1つでしたが メディアアイテムは複数ありました ですが膨大な量のコンテンツに Shazamを利用したい場合 どうすればいいのでしょうか? どう分ければいいのでしょうか? カスタムカタログのコンテンツを 分割する場合は ある種の犠牲が必要です 各メディアアセットに 個々のカタログを作るなら オーディオのどの部分が 演奏されているか知っていないと 正しいカタログを取り込めません そしてそれらを全部 1つのカタログに入れたら ダウンロードは大きくなり メモリ使用量も増えますが もっと多くのオーディオピースを マッチできるわけです 作成したカタログファイルは しっかり絞りましょう 例えば 1曲ごとや全アルバムに カタログを1つ作っても アーティストの全ディスコグラフィーに カタログ1つはやめましょう 分別というのは 実行時に何を 取り込みたいかを決める事です custom catalog add APIで それができます
自分のユースケースに合うか 試してみてください 同じ様に聞こえるオーディオ アセットが複数ある場合 例えば同じイントロで始まる テレビ番組など 各エピソードに カスタム体験を提供したり 別のトラックで標本化された 曲を提供したければ 周波数スキューを差別化要因に 使うのもいいでしょう オーディオのスキューは録音の周波数を 上げたり下げたりすることです これはオーディオの 聞こえ方に影響を与えますが わずかなスキューなら ShazamKitには分かっても 平均的な人間の耳では無理です ですから録音をして カスタムカタログを作ったら 僅かに周波数を変えて 再生すれば ShazamKitは マッチングを行い frequencySkewプロパティを通して スキュー量を報告します コードでのやり方はこちらです
人間の耳では 聞き分けられないけれど ShazamKitには聞き分けられる 程度のスキュー量は きちんと決まっています
5%未満のスキューなら安全で 複数のスキュー済みの録音も 区別がつきます 有効に活用したいなら frequencySkewRangesを使いましょう 特定のスキュー範囲にある メディアアイテムのみ 戻されてきます
その範囲はパーセントで表され オリジナルからどれだけ 変わっているかを意味します ゼロの値はスキューされて いないことを意味し .01の値は1%のスキューを意味します frequencySkewRangeプロパティを使えば メディアアイテムのプロパティにアクセスできます
Appでのやり方を 段階を踏んでお見せしましょう まずオリジナルのオーディオ録音から 参照シグネチャを作成します MediaItemを3から4%の 周波数スキューで制限します カスタムカタログ内に入れます
3から4%スキューされた オーディオを再生すると メディアアイテムが 戻されます スキュー無しやスキューの範囲を 外れたものを再生しても 戻されることはありません 以上が周波数スキューです
ShazamKitの今年のアップデートを ご覧になった今 素晴らしい同期体験を 生み出す準備ができましたね そのために重要な事をまとめます まずは メディアアセットごとに 1つの シグネチャを作成しましょう ShazamKitなら精度も上がり 作成パイプラインもシンプルです シグネチャの作成には SHSignatureGeneratorsの signatureFromAssetを使いましょう 非常に幅広いメディアを 受け入れるので 低レベルのオーディオの詳細に 時間を費やす必要はありません
新しいTimed MediaItem APIを使って 対象を絞って 同期コンテンツを ターゲットしましょう そしてカスタムカタログの作成は Shazam CLIに合理化させましょう 膨大な量のメディアと 格闘する必要を無くすので 皆さんは素晴らしい体験の 作成に集中できます ShazamKitの最新アップデートを お楽しみいただけたでしょうか 全てに役立てていただければ 非常に光栄です お話した全ての情報や ドキュメントへのリンクは 本セッションに添付されています ご清聴ありがとうございました
-
-
4:26 - Food Math Matcher
/* See LICENSE folder for this sample’s licensing information. Abstract: The model that is responsible for matching against the catalog and update the SwiftUI Views. */ import ShazamKit import AVFAudio struct MatchResult { var title: String? var equation: Equation? var episode: Episode? var answerRange: ClosedRange<Int>? var hasContent: Bool { equation != nil || title != nil || answerRange != nil } } class Matcher: NSObject, ObservableObject, SHSessionDelegate { @Published var matchResult: MatchResult? private var session: SHSession! private let audioEngine = AVAudioEngine() private var matchingTask: Task<Void, Never>? = nil func match(catalog: SHCustomCatalog) throws { session = SHSession(catalog: catalog) session.delegate = self let audioFormat = AVAudioFormat(standardFormatWithSampleRate: audioEngine.inputNode.outputFormat(forBus: 0).sampleRate, channels: 1) audioEngine.inputNode.installTap(onBus: 0, bufferSize: 2048, format: audioFormat) { [weak session] buffer, audioTime in session?.matchStreamingBuffer(buffer, at: audioTime) } try AVAudioSession.sharedInstance().setCategory(.record) AVAudioSession.sharedInstance().requestRecordPermission { [weak self] success in guard success, let self = self else { return } Task.detached { try? self.audioEngine.start() } } Task { @MainActor in for await case .match(let match) in session.results { self.matchResult = match.matchResult } } } } extension SHMatch { var matchResult: MatchResult { mediaItems.reduce(into: MatchResult()) { result, mediaItem in result.title = result.title ?? mediaItem.title result.episode = result.episode ?? mediaItem.episode result.equation = result.equation ?? mediaItem.equation result.answerRange = result.answerRange ?? mediaItem.answerRange } } }
-
13:51 - Timed Media Items
// Restrict this media item to only describe the first 5 seconds let mediaItem = SHMediaItem(properties: [ .title: "Title", .timeRanges:[0.0..<5.0] ]) let timeRanges: [Range<TimeInterval>] = mediaItem.timeRanges
-
16:02 - Combine Catalogs
let parentCatalog = SHCustomCatalog() parentCatalog.add(from: URL(fileURLWithPath: "/path/to/Episode1.shazamcatalog")) parentCatalog.add(from: URL(fileURLWithPath: "/path/to/Episode2.shazamcatalog")) parentCatalog.add(from: URL(fileURLWithPath: "/path/to/Episode3.shazamcatalog"))
-
16:58 - Frequency Skew
func within(range: Range<Float>, for matchedMediaItem: SHMatchedMediaItem) -> Bool { range.contains(matchedMediaItem.frequencySkew) }
-
17:21 - Frequency Skew Ranges
// Restrict this media item to only describe the first 5 seconds let mediaItem = SHMediaItem(properties: [ .title: “Frequency Skewed Audio”, .frequencySkewRanges:[0.01..<0.02] ]) let frequencySkewRanges: [Range<Float>] = mediaItem.frequencySkewRanges
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。