web版空間オーディオを利用した仮想空間での位相と距離の調整方法

## はじめに

今回の記事ではagoraの空間オーディオ(spatial audio)がweb版で正式リリースされたので、そちらを使用したサンプルを作成していきます。

以前に投稿した[こちら](https://qiita.com/v-cube/items/457746af29bcb8dba854)の記事では、各ユーザー間の位置の距離に応じてボリュームを変更するサンプルを作成しています。
今回の記事では、こちらの制作物に空間オーディオを導入し、よりパワーアップさせます。

今回空間オーディオとして実装するパラメーターは以下の2点とします。
1, 位相
→自分よりも左のpisitionにいるユーザーの声は左から、右にいるユーザーの声は右から聴こえる
2, 距離
→自他のposition間の距離が遠くなると声が小さくなる

2に関しては、前記事の制作物の段階では setVolume APIを用いて音量を調整していましたが、これを空間オーディオ要素の一種である、距離を指定するパラメーターを使用した実装に置き換えていきます。

完成物は以下になります。
[github](https://github.com/fu-jimoto/WebRTC-spatialAudio)

## 開発環境
Chrome 107.0.5304.121
その他は上記記事参照

## 実装

ここからは、index.html内の記述を改良していきます。

### 事前準備

使用するagoraRTCを最新のものに置き換えておきます。

```javascript
<script src="https://download.agora.io/sdk/release/AgoraRTC_N.js"></script>
```

また、jqueryも同リポジトリの[こちら](https://github.com/AgoraIO/API-Examples-Web/tree/main/Demo/assets)から jquery-{バージョン}.min.js を元々のjquery.js と入れ替え、scriptタグの相対パスを書き換えておきます。

また、後にjavascriptの記述を module として扱う関係上、buttonタグのonClick属性やonChange属性が正常に動かなくなります。

そこで予めこれらの属性は削除し、代わりにjqueryでJS上から発火させる形に変えておきます。

async function join()　→　$(“join”).click(async function (e)

changePosition() の方も同様です。

また、125~128行目のrtmのjoinに関しては現状の記述では警告が出る可能性があるので、以下に置き換えます。

```javascript
await client.join(appId.value,channel.value, null, userInfo.uid)
localTracks.audioTrack = await AgoraRTC.createMicrophoneAudioTrack({microphoneId:audioSource.value});
```

### spatialAudioExtension導入

agora社のドキュメントにもあるように、以下のフローでの導入を行います。

![スクリーンショット 2022-12-20 16.17.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/9538f1b2-79c5-ee70-ca47-cbb2ba89cd67.png)


今回は既に出来ているソースに付加していく形になるので、[こちら](https://github.com/AgoraIO/API-Examples-Web/tree/main/Demo/spatialAudioExtention)のagora社の提供しているサンプルから、spatialAudioの導入に必要な以下の3ファイルを拝借し、同ディレクトリに配置します。

「audio_spatializer.wasm」
「index.esm.js」
「spatial-audio-worker.js」

続いて、SpatialAudioExtensionクラスをindex.esm.jsからimportする為、javascriptをmodule化します。

```javascript
<script language="javascript" type="module">
```

その後importとインスタンスの生成を行います。

```javascript
import { SpatialAudioExtension } from "./index.esm.js";

const extension = new SpatialAudioExtension();
AgoraRTC.registerExtensions([extension]);
```

続いて、リモートユーザーのaudioTrackをspatialAudioとして再生する為の処理を記述します。
subscribe() を以下に置き換えます。

```javascript
async function subscribe(user, mediaType,volume) {
  const uid = user.uid;
  await client.subscribe(user, mediaType);
  console.log("subscribe success");

  //以下が空間オーディオに関わる記述
  const processor = extension.createProcessor();
  user.processor = processor;
  const track = user.audioTrack;
  track.pipe(processor).pipe(track.processorDestination);

  track.play();
}
```

ここからは、positionに応じて空間オーディオのパラメーターを調整する処理を記述して行きます。

冒頭で記述した通り、今回触るパラメーターは2種類です。

・位相(=azimith)
・距離(=distance)

![スクリーンショット 2022-12-20 15.51.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/1929d59d-2a88-ed06-c0af-04b87b655bb2.png)


位相は0が前から、90は自分から見て左から、180は後ろ、270は右から聞こえます。
距離は[1,50]の範囲で調整可能であり、数値が大きくなると距離が遠くなるので声が小さくなります。

ここから実際にコードを記述していきます。

### 自分が位置を変更した時

$("#position").change(function (e) 内の以下の記述で、position間の距離を用いて setVolume API のパラメーターに使用する volumeを計算しています。

```javascript
var dist = userInfo.position - res.position;
var volume = 100 - (Math.abs(dist))*20;
remoteUsers[i].user.audioTrack.setVolume(volume);
```

volumeの数値に関しては API が従来の setVolume ではなく空間オーディオ用のものを使う為、若干計算式が変わるのでここでの volume の算出の行は削除します。

ここからは、新たに空間オーディオのパラメーターを変更する為の関数 updateRemotePosition を作成します。

まず左右の振り分けに関して、dist < 0 であれば自分より相手が右にいる事になります。

オーディオの位相を指定する際には pudateSpatialAzimuth([0, 360])が使用できます。 

ですので、声が右から聴こえるようにするには processor.updateSpatialAzimuth(270); 
で実装が可能です。

距離に関しては同様に updateSpatialDistance([1, 50])で指定ができます。

これらを踏まえて、以下の記述を追記します。

```javascript
function updateRemotePosition(memberId, dist){
  if(dist < 0){
    remoteUsers[memberId].user.processor.updateSpatialAzimuth(270);
  }else if(dist == 0){
    remoteUsers[memberId].user.processor.updateSpatialAzimuth(0);
  }else{
    remoteUsers[memberId].user.processor.updateSpatialAzimuth(90);
  }

  var volume = 1 + (Math.abs(dist))*7;
  remoteUsers[memberId].user.processor.updateSpatialDistance(volume);
}
```
その後、先ほどの setVolume API で音量を調整していた箇所を、今作成した関数の呼び出しに置き換えます。

```javascript
updateRemotePosition(i, dist)
```


### 相手が位置を変更した時

空間オーディオのパラメーターを更新する関数は先ほど作成しているので、ここからあまり記述する内容はありません。

joinRtm() の終盤にある以下の記述の箇所が、音量を調整している箇所になります。

```javascript
var dist = userInfo.position - res.position;
var volume = 100 - (Math.abs(dist))*20;
remoteUsers[memberId].user.audioTrack.setVolume(volume);
```

先ほどと同様に volume の算出の行を消し、setVolume API を使用している行を以下に置き換えます。

```javascript
updateRemotePosition(memberId, dist);
```

以上で、今回の作業は終了です。

二人以上でお試しいただき、相手の声が空間オーディオのパラメーターによって変化しているのが確認できれば成功です。

## 関連リンク

- [通話アプリで仮想空間での距離取得と距離に応じた音量の調整方法](https://qiita.com/v-cube/items/457746af29bcb8dba854)
- [AgoraIO/API-Examples-Web/Demo/spatialAudioExtention](https://github.com/AgoraIO/API-Examples-Web/tree/main/Demo/spatialAudioExtention)
- [Agora documentation](https://docs.agora.io/en/interactive-live-streaming/develop/spatial-audio?platform=web)

## 最後に
[**agora.ioに関するお問い合わせはこちらから**](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=f712953155ddadcf0177)
[<img width="912" alt="Agoraの詳細はこちら" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/5865441c-9d31-c702-3698-3aeb84927f28.png">](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=f712953155ddadcf0177)