音声の送信と受信

Sora Unity SDK で音声を送信・受信する際に必要となる設定や API について説明します。

音声送信と受信の提供機能について

Sora Unity SDK では以下を提供しています。

  • デバイスマイクを音声送信に利用する

  • Unity 内で生成した音声を送信する

  • 音声送信を一時停止する (ソフトミュート)

  • 録音デバイスを利用しない (ハードミュート)

  • 利用可能な録音・再生デバイス一覧を取得する

  • 受信した音声を任意の方法で再生する

  • 音声コーデックやビットレートを指定する

  • ハンズフリー切り替えなど再生経路を制御する

利用時の注意事項

  • Sora.OnHandleAudio は Unity スレッドとは別スレッドで呼び出されます。バッファ操作時はロックなどで同期を取ってください

  • Sora.DispatchEvents() を呼び出さなくても Sora.OnHandleAudio は発火します

  • Sora.ProcessAudio() は 48000Hz Stereo のデータのみ受け取ることができます

  • Config.UnityAudioInputtrue にしている場合、NoAudioDevicetrue にしても Unity 側の音声入力は継続します

音声関連の API リファレンス

Sora.Config

Sora.Config クラスは Sora インスタンスの接続設定を定義します。音声送信・受信に関する主なプロパティは以下の通りです。

  • Audio: 音声トラックを有効にするかどうか。false にすると停止します

  • NoAudioDevice: 物理マイクデバイスを利用しない場合に true を設定します

  • UnityAudioInput: Unity で生成した音声を Sora.ProcessAudio() から送信する場合に true を設定します

  • UnityAudioOutput: OS の再生デバイスではなく Sora.OnHandleAudio コールバックで音声を受け取りたい場合に true を設定します

  • AudioRecordingDevice: 利用する録音デバイスの DeviceName または UniqueName (空文字ならデフォルトデバイス)

  • AudioPlayoutDevice: 利用する再生デバイスの DeviceName または UniqueName (空文字ならデフォルトデバイス)

  • AudioCodecType: 利用する音声コーデックを指定します (現状は Sora.AudioCodecType.OPUS のみ)

  • AudioBitRate: 音声のビットレート (kbps)。省略時は Sora のデフォルト値になります

  • AudioStreamingLanguageCode: 字幕用途などで利用する言語コードを指定します

Sora クラスの音声関連 API

  • Sora.Connect(Sora.Config config): 指定した設定で接続します

  • Sora.ProcessAudio(float[] data, int offset, int samples): UnityAudioInput 利用時に送信したいサンプルを渡します

  • Sora.OnHandleAudio: UnityAudioOutput 利用時に受信音声を取得するコールバック

  • Sora.AudioEnabled: ソフトミュートのためのトグルプロパティ

  • Sora.GetAudioRecordingDevices() / Sora.GetAudioPlayoutDevices(): 利用可能な録音・再生デバイス一覧を Sora.DeviceInfo[] で取得します (列挙に失敗した場合は null)

  • Sora.OnMediaStreamTrack: 受信したトラックを取得するコールバック。音声トラックの場合は AudioTrackconnectionId を取得できます。track.Kind で音声か映像かを判別できます

  • Sora.OnRemoveMediaStreamTrack: トラックの削除時に通知されます

  • Sora.DispatchEvents(): メインスレッドでイベントを処理します OnHandleAudio 以外のコールバックでは呼び出しが必要です

Sora.AudioTrack クラスの API

  • SetVolume(double volume): このトラックの再生音量を設定します (0.0〜10.0)

注釈

音声データを直接取得する機能は、音声データ取得機能 を参照してください。

利用方法

接続と送受信設定

以下は基本的な送受信設定の例です。例では既に初期化済みの Sora インスタンス (sora) を利用しています。

Sora sora; // 初期化済みの Sora インスタンス
var signalingUrl = "wss://example.com/signaling"; // シグナリング URL
var channelId = "sora"; // チャンネル ID
var unityAudioInput = false; // true なら Unity 内で生成した音声を送信
var unityAudioOutput = false; // true なら再生を Unity 側で処理
var disableDevice = false; // true なら NoAudioDevice を有効化
var recordingDevice = ""; // 空文字ならデフォルト録音デバイス
var playoutDevice = ""; // 空文字ならデフォルト再生デバイス

var config = new Sora.Config()
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    Role = Sora.Role.Sendrecv,
    Audio = true, // 音声送信を有効化
    NoAudioDevice = disableDevice, // 録音デバイスを利用しない場合は true
    UnityAudioInput = unityAudioInput, // Unity 生成音声を送信する場合に true
    UnityAudioOutput = unityAudioOutput, // Unity 側で再生する場合に true
    AudioRecordingDevice = recordingDevice, // 利用する録音デバイス
    AudioPlayoutDevice = playoutDevice, // 利用する再生デバイス
};

sora.Connect(config);

利用可能なオーディオデバイスの列挙

Sora.GetAudioRecordingDevices()Sora.GetAudioPlayoutDevices() を使うと利用可能なデバイスを取得できます。取得結果をログに出力する例です。

void DumpDeviceInfo(string label, Sora.DeviceInfo[] devices)
{
    if (devices == null || devices.Length == 0)
    {
        // デバイスが見つからない場合の処理
        Debug.LogWarning($"{label}: device not found");
        return;
    }

    foreach (var device in devices)
    {
        // デバイス情報をログに出力
        Debug.Log($"{label}: DeviceName={device.DeviceName}, UniqueName={device.UniqueName}");
    }
}

void Start()
{
    DumpDeviceInfo("audio recording devices", Sora.GetAudioRecordingDevices());
    DumpDeviceInfo("audio playout devices", Sora.GetAudioPlayoutDevices());
}

音声コーデックとビットレートの設定

音質に影響する主要な設定について説明します。

コーデックの指定

Sora.Config.AudioCodecType を使用して利用する音声コーデックを指定できます。省略した場合は Sora サーバーのデフォルト値が使用されます (現状 OPUS のみ)。

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    AudioCodecType = Sora.AudioCodecType.OPUS, // コーデックを指定
};

sora.Connect(config);

ビットレートの設定

Sora.Config.AudioBitRate で送信ビットレート (kbps) を指定できます。省略した場合は Sora サーバーのデフォルト値が使用されます。

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    AudioBitRate = 64, // 音声のビットレート (kbps) を指定
};

sora.Connect(config);

Unity 音声入力を送信する場合

Unity で生成した音声を送信する場合は UnityAudioInputtrue にし、Sora.ProcessAudio() にフレームデータを渡します。

以下は AudioRenderer を利用して 1 フレーム分のサンプルを送る例です。

void Start()
{
    StartCoroutine(RenderAudio());
}

IEnumerator RenderAudio()
{
    // Unity 内で音を鳴らしておくとキャプチャ状況を確認しやすい
    audioSourceInput.Play();
    // オーディオレンダラーを開始
    AudioRenderer.Start();

    while (true)
    {
        // Sora インスタンスが存在し、Unity 音声入力が有効な場合のみ処理する
        if (sora == null || !unityAudioInput) continue;
        // 1 フレーム分のサンプル数を取得する
        var samples = AudioRenderer.GetSampleCountForCaptureFrame();
        // ステレオモードでない場合はスキップ
        if (AudioSettings.speakerMode != AudioSpeakerMode.Stereo) continue;

        // ステレオ (2 チャンネル) 分のバッファを確保
        using (var buf = new Unity.Collections.NativeArray<float>(samples * 2, Unity.Collections.Allocator.Temp))
        {
            // オーディオデータをレンダリング
            AudioRenderer.Render(buf);
            // Sora 経由で音声データを送信
            sora.ProcessAudio(buf.ToArray(), 0, samples);
        }
    }
}

ソフトミュートとハードミュート

音声ミュートにはデバイスを維持するソフトミュートと、デバイス自体を利用しないハードミュートの 2 種類があります。

ソフトミュートは Sora.AudioEnabled を切り替えて行います。この場合マイクデバイスは解放されません。

public void ToggleAudio()
{
    if (sora == null) return;
    sora.AudioEnabled = !sora.AudioEnabled;
}

ハードミュートは接続時に Sora.Config.NoAudioDevicetrue に設定します。送信中に切り替えることはできないため、設定を変える場合は再接続が必要です。

var config = new Sora.Config
{
    SignalingUrl = signalingUrl,
    ChannelId = channelId,
    NoAudioDevice = true, // 物理マイクデバイスを利用しない
};

sora.Connect(config);

警告

UnityAudioInputtrue の場合、NoAudioDevicetrue にしても Unity からの音声入力は継続します。

Unity 内で受信音声を再生する場合

Config.UnityAudioOutputtrue にすると、受信音声が Sora.OnHandleAudio でコールバックされます。以下はキューを使って AudioClip に流し込む例です。

// 受信した音声データを保持するキュー
Queue<short[]> audioBuffer = new Queue<short[]>();
// キューに格納されているサンプル数の合計
int audioBufferSamples = 0;
// 現在読み取り中のチャンク内の位置
int audioBufferPosition = 0;

void SetupAudioOutput()
{
    // 音声データ受信時のコールバックを設定
    sora.OnHandleAudio = (buf, samples, channels) =>
    {
        lock (audioBuffer)
        {
            // 受信したバッファをキューに追加
            audioBuffer.Enqueue(buf);
            // サンプル数を加算
            audioBufferSamples += samples;
        }
    };

    // 複数の受信者がいる場合は、重複にならないよう "Remote_{connectionId}" などを使用してください
    // 48000Hz Stereo のデータのみ受け取ることができます
    var clip = AudioClip.Create("Remote", 480000, 1, 48000, true, data =>
    {
        lock (audioBuffer)
        {
            // バッファが空、またはデータ不足の場合は無音を返す
            if (audioBuffer.Count == 0 || audioBufferSamples < data.Length)
            {
                Array.Fill(data, 0.0f);
                return;
            }

            // キューの先頭チャンクを取得
            var chunk = audioBuffer.Peek();
            // 要求されたデータ長分のサンプルを取り出す
            for (int i = 0; i < data.Length; ++i)
            {
                // 現在のチャンクを読み終えた場合、次のチャンクへ移動
                while (audioBufferPosition >= chunk.Length)
                {
                    audioBuffer.Dequeue();
                    chunk = audioBuffer.Peek();
                    audioBufferPosition = 0;
                }
                // short 型 (±32768) から float 型 (±1.0) に変換
                data[i] = chunk[audioBufferPosition] / 32768.0f;
                ++audioBufferPosition;
            }
            // 処理したサンプル数を減算
            audioBufferSamples -= data.Length;
        }
    });

    // AudioClip を AudioSource にセットして再生開始
    audioSourceOutput.clip = clip;
    audioSourceOutput.Play();
}

ハンズフリー制御 (Sora.IAudioOutputHelper)

Sora.AudioOutputHelperFactory.Create()IAudioOutputHelper を生成すると、ハンズフリー切り替えなどの再生経路制御が可能になります。詳細は ハンズフリー も参照してください。

Sora.IAudioOutputHelper audioOutputHelper;

void InitAudioOutputHelper()
{
    // ルート変更時に呼ばれるコールバックを指定して生成
    audioOutputHelper = Sora.AudioOutputHelperFactory.Create(OnChangeRoute);
}

void OnChangeRoute()
{
    if (audioOutputHelper == null) return;

    // ルート変更を検知して処理を行う
    Debug.Log("音声ルートが変更されました: " +
              (audioOutputHelper.IsHandsfree() ? "ハンズフリー ON" : "ハンズフリー OFF"));
}

public void OnClickHandsfree()
{
    if (audioOutputHelper == null) return;
    audioOutputHelper.SetHandsfree(!audioOutputHelper.IsHandsfree());
}
© Copyright 2024, Shiguredo Inc. Created using Sphinx 9.0.4