Skip to content

再生レンダラー ​

PlaybackRenderer は、デコード済みの映画音声をリスナーが実際に聴く音に変換します。受け付けるのは mono・stereo・5.1・7.1 の PCM だけで、ビットストリームのデコードもコンテナの解釈も映像も扱いません。出力先はヘッドホン(バイノーラル化、ヘッドトラッキング、部屋のモデル)か、stereo・5.1・7.1 のスピーカー(アップミックス、スピーカー補正、低音管理)です。ラウドネス整合、ナイトモードのダイナミクスカーブ、台詞レベルの静的ゲインはどの出力先でも適用されます。

自前のデコーダーが PCM を出力した後で使ってください。たとえば WebCodecs のパイプライン、<video> 要素の MediaElementAudioSourceNode、ディスクから読み込んだデコード済みファイルです。このページが前提とする入出力のチャンネル規約は チャンネル形式 を参照してください。

「チャンネルレイアウト」「dB」「レイテンシ」に馴染みがなければ、先に オーディオ基礎 を読んでください。本ページは用語を前提に、レンダラーの使い方に絞って説明します。

以下で使う用語

  • LFE: 5.1 / 7.1 の低域効果チャンネル。サブウーファー向けの低音専用チャンネルです。
  • 低音管理(bass management): 各チャンネルをクロスオーバー周波数で分け、小型スピーカーに深い低音を送らず、低域をサブウーファー(または large のスピーカー)へ回す処理です。
  • クロスオーバー: 信号を低域と高域に分ける一対のフィルターです。レンダラーは Linkwitz-Riley 4次(LR4)のペアを使い、足し戻すと平坦になります。詳しくはクロスオーバーを参照してください。
  • パンニングインデックス: 周波数ビンごとに、音が左(-1)と右(+1)のどのあたりにあるかを推定した値です。upmix.center_width は、ビンがセンタースピーカーへ送られるために 0 へどこまで近い必要があるかを表します。
  • チャンネル間コヒーレンス: ビンごとの左右信号の似かたです。コヒーレンスが高い成分は直接音、低い成分はアンビエンスで、アップミックスはこれを無相関化(互いに似ないように)してからサラウンドへ送ります。
  • ナイトモード: ダイナミクスカーブ(ダイナミックレンジ圧縮)で、小音量でも大きな場面を抑え、小さな場面を聞き取りやすくします。ダイナミクスを参照してください。

段のパイプライン ​

処理は 12 個の名前付きの段を通ります。最初の 6 段は入力側のチャンネル構成だけに依存し、出力先によらず一度だけ動きます。残りの段は、スピーカー側の枝(2 段)とヘッドホン側の枝(3 段)に分かれ、共通の出力リミッターに合流します。

再生レンダラーの段構成
入力側に依存スピーカーのみヘッドホンのみスピーカー出力ヘッドホン出力並べ替え台詞レベルアップミックスラウドネスナイトモードレイアウト変換スピーカー補正低音管理バイノーラル化 (HRTF)部屋・初期反射部屋・後期残響出力リミッター
現在の設定が使わない段は飛ばされ、diagnostics().inactive_stages にその段自身のレイテンシ内訳とともに名前が載ります。

台詞レベルは 5.1 / 7.1 入力の離散センターチャンネルにだけ作用し、mono / stereo 入力では何もしません。アップミックスは stereo 入力かつ upmix.enabled のときだけ動き、チャンネル間の位相相関から直接音とアンビエンスを分離して、アンビエンス成分を捨てずにサラウンドへ送ります(mono 和にパン則を掛けるのではありません)。レイアウト変換は、入力側のチャンネル構成を出力先のチャンネル数に合わせて狭めたり広げたりします。変換規則は チャンネル形式 を参照してください。ある設定でどの段が動くか(スピーカー出力では binaural・room_early・room_late が飛ばされ、mono 入力では dialogue_level・upmix が飛ばされる、など)は常に diagnostics() から読み取れ、設定から推測する必要はありません。

5.1 · DOWNMIXIDLE
5.1 のダウンミックス:フルスケールを超える折りたたみ

6 つのチャンネルが標準順(L、R、C、LFE、Ls、Rs)でそれぞれの音を鳴らし、最後に同相のサイン波を一斉に鳴らします。downmix はセンターとサラウンドを −3 dB(×0.707)で加え、LFE を捨て、正規化はしません。そのためフルスケールでは、同相のサイン波がステレオでもモノラルでも 2.414(+7.66 dBFS)に達します。チャンネルをオフにすると、どのチャンネルが 0 dBFS を超えさせているかがわかります。

出力先
チャンネルごとのピーク
0 dBFS

設定 ​

設定は schemas/playback-renderer-config.schema.json に従う 1 つの JSON 文書で、コンストラクタと setConfig/set_config の両方に渡します。各キーは、生成時に固定されるprepareキーか、次に処理するブロックから反映されるrealtimeキーのどちらかです。setConfig は完全な文書を受け取り、現在値と異なる prepare キーは拒否されます。省略したキーは以下の既定値になります。

prepare キー ​

キー型・範囲既定値備考
input.layout"auto" | "mono" | "stereo" | "5.1" | "7.1""auto"auto は各ブロック自身のチャンネル数(1・2・6・8)に追従します。入力レイアウトの自動切り替え を参照
input.channel_map役割名の配列、または nullnull固定の input.layout が必須。null は canonical 順を意味します
target.kind"headphones" | "speakers""headphones"
target.layout"stereo" | "5.1" | "7.1"—speakers では必須、headphones では禁止
target.speakers.<role>.distance_m数値 [0.1, 30]、または nullnull出力レイアウトの LFE 以外の役割のみ。null はそのスピーカーの距離補正を無効化します
target.speakers.<role>.size"large" | "small""large""small" は、低音管理が有効なときクロスオーバー周波数でハイパスされます
target.bass_management.enabled真偽値false
target.bass_management.crossover_hz数値 [40, 200]80Linkwitz-Riley 4次のクロスオーバー
target.bass_management.subwoofer真偽値truefalse は低域和と LFE チャンネルを large の L/R ペアへ折り込みます
room.preset"none" | "living_room" | "home_theater" | "screening_room""living_room"ヘッドホンのみ。部屋プリセット を参照
クロスオーバー付近での低域と高域の和
帯域の和+4+3+2+10−11/81/41/21248周波数(カットオフ比)dB+3.0 dB+1.7 dB0 dBカットオフ
  • linkwitzRiley lr4
  • firLinearPhase
  • butterworth lr4
  • bessel lr4
  • butterworth lr2
低音管理は、各信号をクロスオーバー周波数で Linkwitz-Riley 4次のペアに分けます。このペアは足し戻すと平坦になります。small のスピーカーには高域だけが残り、低域はサブウーファーへ回ります。図には比較のため、ほかのクロスオーバーモードも描かれています。

realtime キー ​

キー型・範囲既定値備考
target.speakers.<role>.trim_db数値 [-20, 20]0
target.bass_management.lfe_gain_db数値 [-10, 15]10サブウーファーへ供給する際に LFE チャンネルへ掛けるゲイン。+10 dB は LFE の慣例的な再生ゲインです
lfe_mix_db数値 [-60, 10]0出力に LFE チャンネルが無い、または低音管理にサブウーファーが無いときに L/R へ折り込む LFE レベル。主スピーカーはサブウーファーよりヘッドルームが小さいため、既定は 10 dB ではなく 0 dB です
upmix.enabled真偽値truestereo 入力のみに作用。切り替えても報告されるレイテンシは変わりません
upmix.center_width数値 [0.05, 1]0.2パンニングインデックス上のセンター抽出窓の幅
upmix.front_ambience数値 [0, 1]0.5アンビエンス成分のうちサラウンドへ送らず前方ペアに残す電力比
upmix.lfe_from_upmix真偽値falseローパス後の前方和から LFE 信号を生成します
dialogue_level_db数値 [-12, 12]05.1 / 7.1 入力の離散センターチャンネルへの静的ゲイン。mono / stereo では何もしません
loudness.program_lufs数値 [-70, 0]、または nullnull測定済みの番組ラウドネス。null は整合ゲインを適用しません
loudness.target_lufs数値 [-40, -5]-24
night_mode.amount数値 [0, 1]00 でダイナミクスカーブを完全に無効化します
room.mix_db数値 [-30, 6]-6初期反射と後期残響を合わせたレベル
room.enabled真偽値true
head_tracking.enabled真偽値truefalse は頭部姿勢をゼロとして扱います
output_limiter.enabled真偽値true切り替えても報告されるレイテンシは変わりません
output_limiter.ceiling_db数値 [-12, 0]-1

番組ラウドネスは事前に測定する

loudness.program_lufs はレンダラーが再生中に測定する値ではなく、呼び出し側が渡す値です。リアルタイムのラウドネス追従は、シーンの切り替わりでポンピングを起こすためです。PlaybackLoudnessMeter(後述)は番組全体の積分ラウドネスを、オフラインで、あるいは再生前に一度だけ測定するので、ゲインは再生中ずっと固定されます。

レイテンシ ​

latencySamples() / latency_samples() は出力先・サンプルレート・スピーカーの距離補正だけで決まり、realtime キーにも、現在有効な入力レイアウトにも依存しません。

出力先44.1 kHz48 kHz
stereo スピーカー265 サンプル(6.0 ms)288 サンプル(6.0 ms)
5.1 / 7.1 スピーカー、またはヘッドホン1289 サンプル(29.2 ms)1312 サンプル(27.3 ms)

アップミックス段を持ち得る出力先は、upmix.enabled の現在値に関わらずこの広い方の値になります。必要になり得る解析窓を常に確保しているためで、ライブセッション中にアップミックスを on/off してもレイテンシは動きません。スピーカー出力の距離補正は、この値の上に、サンプル単位に丸めた独自の遅延を加えます。

入力レイアウトの自動切り替え ​

既定の input.layout: "auto" では、レンダラーは処理する各ブロックのチャンネル数(1・2・6・8)に追従し、ブロック境界で入力レイアウトを切り替えます。音の欠落や重複は起きず、レイテンシも変わりません。内部では、切り替え前のレイアウトの 6 段(入力側依存の段)が、自身の状態が減衰しきるまで出力へ流れ続ける一方、新しいレイアウトの段は最初から動き始めます。両者は加算されるので、継ぎ目で音が失われることも重複することもありません。input_channels() / inputChannels() は現在有効なレイアウトのチャンネル数を返し(最初の呼び出し前は 2)、固定(非 auto)レイアウトで対応しないチャンネル数を渡すと、状態を進めずに拒否されます。

diagnostics() は active_input_layout、layout_switches(累計回数)、truncated_drains を返します。truncated_drains は、前のレイアウトの出力が流れ切る前にレイアウトが再度切り替わったときに増え、その際は中断された出力を 2 ms かけてフェードアウトさせてから打ち切ります。limiter_gain_reduction_db も返します。これは直前に処理したブロックで、メインリミッターと、LFE プレーンを持つスピーカー出力では LFE リミッターを比べたときの、最も深い(最も負の)ゲインリダクションです。0 はリダクションなし、負の dB 値は減衰を示します。

音声と映像の同期 ​

レンダラーは映像を扱いません。音声と映像を揃えるためにホストへ渡せるのは、固定で報告可能なレイテンシ、音の欠落や重複の無い入力レイアウト切り替え、シーク用の reset() であって、それ自体の同期機構ではありません。

  • レンダラー自身のレイテンシ: latencySamples() / sampleRate。
  • ホスト側の出力レイテンシがこれに加わります。 ブラウザでは AudioContext.baseLatency + outputLatency(outputLatency が無いブラウザではその項を 0 として扱う)。出力デバイスが変わると値も変わるため、devicechange / sinkchange のたびに読み直してください。ネイティブでは、デバイスが報告するレイテンシを使います。
  • レンダラー自身のレイテンシが変わるのは、レンダラーを作り直したときだけです。 ホスト側の出力レイテンシは出力デバイスやバックエンドで変わるため、デバイス変更の通知を受けたら読み直してください。realtime キーの変更や入力レイアウトの切り替えではレンダラーのレイテンシは変わりません。
  • シークの直後には reset() を呼んでください。 process* を呼ぶスレッドから呼ぶか(またはストリームを止めてから)、パイプラインに残っていた分を破棄します。worklet では、メディア要素の seeking イベントで { type: 'reset' } を送ってください。port のハンドラーはレンダリングの合間に走るので、process() と競合しません。
  • 2 つの組み方。 <video> 要素に再生を任せ、音声だけを MediaElementAudioSourceNode 経由でレンダラーに通す構成では、提示されるフレームの時刻が分からないため、表現できるのは相対的な遅れだけです。音声は要素を直接鳴らした場合よりレンダラー自身のレイテンシ(プラス最大 1 render quantum)だけ遅れて出てきますが、映像側を遅らせて合わせる手段はありません。自前の再生時計を持つ WebCodecs ベースのパイプラインであれば、各映像フレームの提示をレンダラーのレイテンシとホスト側の出力レイテンシの合計だけ遅らせることで、実際に同期を取れます。
  • 参考までに、ITU-R BT.1359 は視聴者が音声の遅れに気付き始めるしきい値をおよそ 125 ms(検知限界。許容限界は約 185 ms)としています。レンダラー自身のレイテンシ(27〜29 ms)はその一部でしかなく、ホスト側の出力レイテンシ(通常 10〜50 ms、無線出力ではさらに大きくなります)の方が支配的になるのが普通です。
  • ブラウザ内蔵のデコーダーは、libsonare 自身がデコードしない方式でも多チャンネル PCM を返せます。PCM になった時点で、他のデコード済みチャンネル構成と同じ経路でレンダラーに入ります。元のストリームが持っていた高さやオブジェクトの情報は、その時点ですでに失われています。

ヘッドトラッキング ​

setHeadOrientation(yaw, pitch, roll) の単位は度で、右手系です。正の yaw で頭を右に、正の pitch で上を向き、正の roll で右耳を下げ、この順で適用します。スピーカー出力では無視されます。レンダラー自身にセンサーフュージョンはなく、アプリケーションが自前の IMU や device-orientation API を読み、更新のたびにこれを呼び出します。head_tracking.enabled: false は、このセッターを呼び続けたままでも姿勢をゼロとして扱います。

HRTF セット ​

ヘッドホン出力は、各仮想スピーカー方向を頭部伝達関数(HRTF)で畳み込みます。この HRTF は HrtfSet ハンドルに保持され、SHRF v1 というデータ形式で作られます。HRTF の測定データが通常公開される HDF5 ベースの SOFA 形式ではなく、最小位相インパルス応答と両耳間時間差を規則格子の方位・仰角ごとに持つ、小さく自己完結した独自形式です。

WASM 以外のすべての環境では、既定の HRTF セット(SADIE II データベースの被験者 D1、ダミーヘッドによる測定を 504 方向に間引いたもの)をファイル無しで 1 回の呼び出しから構築できます。WASM はモジュールのダウンロードサイズを抑えるため HRTF データを埋め込まず、パッケージ自身の既定アセットを取得してバイト列を HrtfSet.fromBytes に渡します。

typescript
const bytes = new Uint8Array(await (await fetch(hrtfUrl)).arrayBuffer());
const hrtf = HrtfSet.fromBytes(bytes);

ライブラリのソースツリーにある tools/playback/sofa_to_shrf.py は、SOFA 形式の HRIR 測定データを SHRF v1 ファイルに変換し、独自の HRTF セットを作れます。HrtfSet から生成したレンダラーはデータ自身のコピーを保持するので、生成直後にセットを解放しても構いません。

部屋プリセット ​

ヘッドホン出力では、直接音(HRTF で畳み込んだ音)の周りに合成された部屋を加えられます。部屋に固定した水平リングからの初期反射と、減衰していく後期残響で、どちらも直接音と同じバイノーラル経路を通るため頭の回転に追従します。room.preset は固定の直方体の部屋を選び(none で無効化)、room.mix_db は初期反射と後期残響を合わせたレベルを、room.enabled は報告されるレイテンシを変えずにライブで切り替えます。部屋プリセットはスピーカー出力では使えません。スピーカー出力はすでに実在の部屋の中で鳴っているためです。

プリセット特徴
none部屋を加えない。直接の HRTF 音のみ
living_room小さめ、中程度の吸音
home_theaterより大きく、より吸音的
screening_room最大、最も吸音的

ラウドネス整合とメーター ​

loudness.program_lufs と loudness.target_lufs は clamp(target_lufs - program_lufs, -40, +12) dB の静的ゲインを 1 つ適用します。上限と下限があるため目標へ届かない場合もありますが、その代わり、ライブの圧縮のようにシーンの切り替わりでポンピングを起こすことはありません。PlaybackLoudnessMeter(1・2・6・8 チャンネル。BS.1770 の重みはスピーカー位置で決まり、5.1 のサラウンドと 7.1 のサイド対は 1.41、7.1 のバック対とそれ以外は 1.0、LFE は除外)は、再生前に番組全体の積分ラウドネスを測定します。pushInterleaved/push_interleaved でインターリーブ済みのチャンクを流し込み、番組を最後まで流し終えたら integratedLufs()/integrated_lufs() を読み、その値を loudness.program_lufs として渡してください。

どの言語でも同じ流れ ​

どのバインディングも同じ形です。設定文書(とヘッドホン出力用の HrtfSet)からレンダラーを生成し、ブロックを処理し、ハンドルを解放します。CLI だけはファイル全体を 1 回の呼び出しでレンダーします。

c
#include <sonare/sonare_c_playback.h>

SonareHrtfSet* hrtf = NULL;
sonare_hrtf_set_create_default(&hrtf);  // ネイティブビルドのみ。WASM には存在しない

SonarePlaybackRenderer* renderer = NULL;
SonareError err = sonare_playback_renderer_create_json(
    "{\"target\":{\"kind\":\"headphones\"}}", hrtf, 48000, 1024, &renderer);
if (err != SONARE_OK) return err;

err = sonare_playback_renderer_process_interleaved(renderer, in, in_channels, out, 2, frames);

int latency = 0;
sonare_playback_renderer_latency_samples(renderer, &latency);

sonare_playback_renderer_destroy(renderer);
sonare_hrtf_set_destroy(hrtf);
python
import libsonare as sonare

with sonare.HrtfSet.default() as hrtf, sonare.PlaybackRenderer(
    {"target": {"kind": "headphones"}}, hrtf=hrtf, sample_rate=48000
) as renderer:
    out = renderer.process_interleaved(samples, in_channels=6)
    print(renderer.latency_samples(), renderer.diagnostics())

# 同じ設定で配列全体をレンダーする:
rendered = sonare.render_playback(samples, channels=6, sample_rate=48000, config={"target": {"kind": "headphones"}})
typescript
import { HrtfSet, PlaybackRenderer } from '@libraz/libsonare-native';

using hrtf = HrtfSet.default();
using renderer = new PlaybackRenderer({
  config: { target: { kind: 'headphones' } },
  hrtf,
  sampleRate: 48000,
});

const out = renderer.processInterleaved(samples, 6);
console.log(renderer.latencySamples(), renderer.diagnostics());
typescript
import { HrtfSet, PlaybackRenderer, renderPlayback } from '@libraz/libsonare';

// bytes: パッケージの hrtf/default.shrf(後述の「HRTF セット」を参照)
using hrtf = HrtfSet.fromBytes(bytes);
using renderer = new PlaybackRenderer({
  config: { target: { kind: 'headphones' } },
  hrtf,
  sampleRate: 48000,
});
const out = renderer.processInterleaved(samples, 6);

// 同じ設定でバッファ全体をレンダー:
const rendered = renderPlayback({ samples, channels: 6, sampleRate: 48000, config: { target: { kind: 'headphones' } } });
typescript
// AudioWorklet 上でのリアルタイム利用。動画要素自身の多チャンネルデコードを、
// そのままバイノーラルヘッドホンへ渡します。ブラウザ自身のデコーダーが出す
// 5.1 入力は仕様で定義済みで libsonare の canonical 順と一致しますが、7.1 の
// チャンネル順はブラウザ間でまだ確認が取れていません(チャンネル形式を参照)。
// 確認が済むまで worklet の入力は mono/stereo/5.1 に留めてください。

// playback-worklet.ts(worklet モジュール)。プロセッサは worklet 自身の realm で
// レンダラーを作りますが、そこにはまだ WASM モジュールがありません。Emscripten
// ファクトリを公開しておけば、プロセッサが渡されたバイト列でモジュールを初期化します。
import { registerSonarePlaybackWorkletProcessor } from '@libraz/libsonare/worklet';
import createModule from './sonare.js'; // パッケージの WASM グルーに含まれる Emscripten ファクトリ

globalThis.SonareEmbindModuleFactory = createModule;
registerSonarePlaybackWorkletProcessor(); // 'sonare-playback-processor'

// main.ts
import { createSonarePlaybackNode } from '@libraz/libsonare/worklet';

await context.audioWorklet.addModule('/playback-worklet.js');
const hrtf = await (await fetch(hrtfUrl)).arrayBuffer(); // @libraz/libsonare/hrtf/default.shrf
const wasmBinary = await (await fetch(wasmUrl)).arrayBuffer(); // sonare.wasm
const node = createSonarePlaybackNode(context, {
  config: { input: { layout: 'auto' }, target: { kind: 'headphones' } },
  hrtf,
  wasmBinary,
});
// モジュールが立ち上がるまでプロセッサは無音を返し、立ち上がると { type: 'ready' } を送ります。
context.createMediaElementSource(video).connect(node).connect(context.destination);
video.addEventListener('seeking', () => node.port.postMessage({ type: 'reset' }));
bash
# ファイル全体を 1 回の呼び出しでレンダーします。--program-lufs を省略すると
# 入力自体からラウドネスを測定します。
sonare playback movie-5.1.wav -o headphones.wav --target headphones
sonare playback movie-5.1.wav -o out-7.1.wav --target 7.1 --night 0.5
sonare playback movie-5.1.wav -o room.wav --target headphones --room home_theater

createSonarePlaybackNode の sampleRate は省略してください。AudioContext のレートが既定で、異なる値は RangeError になります(worklet は AudioWorkletGlobalScope のサンプルレートで動作します)。worklet では、入力のないブロックや未対応のチャンネル数のブロックは無音になり、未対応のものは診断の返信で unsupported_input_blocks として数えられます。worklet 内では WASM モジュールの初期化が必要です。sonare.wasm のバイト列を wasmBinary として渡し、worklet モジュールで globalThis.SonareEmbindModuleFactory を設定すれば、プロセッサが自分でモジュールを初期化します。worklet モジュールがすでに init() を呼んでいる場合、wasmBinary は不要です。既定の HRTF アセットは @libraz/libsonare/hrtf/default.shrf です。

ワンショット形式(renderPlayback、render_playback、sonare_playback_render_interleaved、sonare playback)は内部でレンダラーを生成して信号全体を流し、レンダラー自身のレイテンシを結果の先頭から取り除くので、出力は入力とサンプル単位で揃います。スピーカー出力で距離が異なる場合は、全チャンネルに共通のレイテンシだけが取り除かれるので、近いスピーカーの冒頭は残ります。

既に持っているレンダラーでバッファ全体をレンダーするには、renderer.renderOffline(samples, inChannels)(Python は render_offline、C は sonare_playback_renderer_render_offline)を呼びます。最初にレンダラーをリセットし、renderPlayback と同じ結果になり、diagnostics() はそのレンダーの内容を示します。Node は空または非有限の入力を RangeError で拒否します。setHeadOrientation は非有限の角度を RangeError、数値でない値を TypeError で拒否します。解放済みのレンダラーは InvalidState を送出します。

再生中のゲイン変更(ナイトモードをオフにする、出力リミッターを切り替える、スピーカートリム、LFE ゲイン、LFE フォールドダウンレベル)は、段差ではなく約 20 ms かけてなめらかに移行します。

非対象 ​

  • 圧縮サラウンド形式・コンテナ・映像のデコード。レンダラーが受け付けるのはデコード済みの PCM のみです。
  • 圧縮音声を無加工のまま外部の受信機へ渡すこと。
  • 高さチャンネルやオブジェクトベースの音声。チャンネルレイアウトは mono・stereo・5.1・7.1 に限られます。
  • 適応型の台詞強調(音声検出、台詞に応じたダッキング、了解度モデルなど)。台詞レベルは離散センターチャンネルへの静的ゲインのみで、そのチャンネルを持つ入力にしか作用しません。
  • マイク測定やテストトーンによる自動音場補正。部屋プリセットは固定モデルで、スピーカーの距離・トリム・クロスオーバーは呼び出し側が指定します。
  • 番組内容や周囲騒音に連動するリアルタイムのラウドネス追従。ラウドネス整合は、呼び出し側が渡す(あるいは別途事前測定した)番組ラウドネスから計算する静的ゲインです。
  • クロストークキャンセル、HRTF の個人化、任意形状・任意寸法の部屋。
  • SOFA ファイルを直接読み込むこと。まずオフラインで SHRF v1 に変換してください。
  • 頭部姿勢のセンサーフュージョン。yaw/pitch/roll はアプリケーション自身が渡します。

関連ページ ​