今回は自身の音声データを使うので、複数あるコールバックのうち
SetOnRecordAudioFrameCallback を設定します。
public void setUpRawAudioData() {
mAudioManager = AudioRawDataManager.GetInstance(mRtcEngine);
// Registers the audio observer.
mAudioManager.RegisterAudioRawDataObserver();
// Listens for the OnRecordAudioFrameHandler delegate.
mAudioManager.SetOnRecordAudioFrameCallback(OnRecordAudioFrameHandler);
}
RegisterAudioRawDataObserver は JoinChannel 前にコールしておく必要があります。
public void join(string channel)
{
...
setUpRawAudioData();
// join channel
mRtcEngine.JoinChannel(channel, null, 0);
...
}
コールバックを設定すると、定期的に AudioFrame が引数で渡されてきます。AudioFrame はサンプリングされた音声データを含むオブジェクトで、これを一時的にためておくキューをクラス内に用意しておきます。今回、キューに格納されたデータは描画のタイミングで 1 個ずつ順番に取り出す実装にしますが、万が一描画のタイミングが遅いなどしてデータが増えすぎてしまわないよう、キューに格納できる上限を設けておきます。
public void OnRecordAudioFrameHandler(AudioFrame audioFrame) {
if(audioFrameQueue.Count <= 3) {
audioFrameQueue.Enqueue(audioFrame);
}
}
今回、音声波形の描画は TestHome.cs に任せるため、AudioFrame オブジェクトを返すメソッドを用意します。
public AudioFrame getAudioFrame() {
return (audioFrameQueue.Count > 0) ? (AudioFrame)audioFrameQueue.Dequeue() : new AudioFrame();
}
LineRenderer を使って音声波形を描画します。先ずは、線の色・太さを設定します。
void Start()
{
...
LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
float lineWidth = 0.1f;
lineRenderer.startWidth = lineWidth;
lineRenderer.endWidth = lineWidth;
Color lineColor = new Color(0.9f, 0.9f, 0.5f);
lineRenderer.startColor = lineColor;
lineRenderer.endColor = lineColor;
lineRenderer.material = new Material(Shader.Find("Sprites/Default"));
}
次に Update() メソッドに描画ロジックを追加します。AudioFrame オブジェクトを扱うので、次の一行をファイルに追加しておきます:
using agora_gaming_rtc;
AudioFrame に含まれるサンプル数と同じサイズの Vector3 配列 (頂点リスト)を用意し、各頂点の座標を計算していきます。サンプル一つ一つは 16 bit PCM 方式で格納されているため、復号してから Y 座標を計算していきます。
void Update()
{
...
if(app != null) {
AudioFrame audioFrame = app.getAudioFrame();
LineRenderer lineRenderer = GetComponent<LineRenderer>();
lineRenderer.positionCount = audioFrame.samples;
Vector3[] points = new Vector3[lineRenderer.positionCount];
float xStretch = 8.0f;
float yAmp = 2.0f;
float yOffset = 4.0f;
for(int i = 0, idx = 0; i < lineRenderer.positionCount; i++, idx+=2) {
// AudioFrame type is PCM 16bit little endian.
short audioLevel = (short) ( (audioFrame.buffer[idx+1] << 8) | audioFrame.buffer[idx] );
points[i] = new Vector3(
xStretch * (2.0f * i / (lineRenderer.positionCount - 1.0f) - 1.0f),
yAmp * (audioLevel / 32768f) + yOffset,
0.0f
);
}
lineRenderer.SetPositions(points);
}
}