Example #1
0
    void ApplyRotations(float yaw, float pitch)
    {
#pragma warning disable 0618
        if (yaw < 0)
        {
            // Left
            blendShapeValues[1] = 0;
            blendShapeValues[0] = Mathf.Clamp(Horizontal.Map(-yaw), 0, 1.0f);
        }
        else
        {
            // Right
            blendShapeValues[0] = 0;
            blendShapeValues[1] = Mathf.Clamp(Horizontal.Map(yaw), 0, 1.0f);
        }

        if (pitch < 0)
        {
            // Down
            blendShapeValues[2] = 0;
            blendShapeValues[3] = Mathf.Clamp(VerticalDown.Map(-pitch), 0, 1.0f);
        }
        else
        {
            // Up
            blendShapeValues[3] = 0;
            blendShapeValues[2] = Mathf.Clamp(VerticalUp.Map(pitch), 0, 1.0f);
        }
        faceController.MixPresets(nameof(VMC_VRMLookAtBlendShapeApplyer), presets, blendShapeValues);
#pragma warning restore 0618
    }
    void Update()
    {
        available = false;

        //全ノブを調べる
        if (faceController != null)
        {
            int valuecount = 0;
            for (int i = 0; i < MidiCCWrapper.KNOBS; i++)
            {
                //キーが登録されている場合
                if (KnobToBlendShape[i] != null && KnobToBlendShape[i] != "")
                {
                    //表情を反映する
                    var key = KnobToBlendShape[i];
                    if (nameToValueDictionary.ContainsKey(key) == false)
                    {
                        UpdateDictionary();
                    }
                    nameToValueDictionary[key] = midiCCWrapper.CCValue[i];
                    available = true;
                    valuecount++;
                }
            }
            if (keyArray.Length != valuecount)
            {
                UpdateDictionary();
            }
            if (valuecount > 0)
            {
                faceController.MixPresets(nameof(MIDICCBlendShape), keyArray, nameToValueDictionary.Values.ToArray());
            }
        }
    }
Example #3
0
    void Update()
    {
        if (SRanipal_Lip_Framework.Status != SRanipal_Lip_Framework.FrameworkStatus.WORKING)
        {
            return;
        }

        if (LipWeightings == null)
        {
            if (!SRanipal_Lip_Framework.Instance.EnableLip)
            {
                enabled = false;
                return;
            }

            //Get All Shapes
            SRanipal_Lip_v2.GetLipWeightings(out LipWeightings);
            foreach (var weighting in LipWeightings)
            {
                if (Enum.IsDefined(typeof(LipShape_v2), weighting.Key))
                {
                    LipShapeNameToEnumMap[weighting.Key.ToString()] = weighting.Key;
                }
            }
        }

        SRanipal_Lip_v2.GetLipWeightings(out LipWeightings);

        var keyvalues = new Dictionary <string, float>();

        foreach (var weighting in LipWeightings)
        {
            if (LipShapeToStringKeyMap.ContainsKey(weighting.Key))
            {
                keyvalues[LipShapeToStringKeyMap[weighting.Key]] = weighting.Value;
            }
        }
        if (keyvalues.Any())
        {
            faceController.MixPresets(nameof(LipTracking_Vive), keyvalues.Keys.ToArray(), keyvalues.Values.ToArray());
        }
    }
    void Update()
    {
        if (SRanipal_Lip_Framework.Status != SRanipal_Lip_Framework.FrameworkStatus.WORKING)
        {
            return;
        }

        SRanipal_Lip_v2.GetLipWeightings(out LipWeightings);

        var keyvalues = new Dictionary <BlendShapeKey, float>();

        foreach (var weighting in LipWeightings)
        {
            if (LipShapeToBlendShapeMap.ContainsKey(weighting.Key))
            {
                keyvalues[LipShapeToBlendShapeMap[weighting.Key]] = weighting.Value;
            }
        }
        if (keyvalues.Any())
        {
            faceController.MixPresets(nameof(LipTracking_Vive), keyvalues.Keys.ToArray(), keyvalues.Values.ToArray());
        }
    }
    void OnDataReceived(uOSC.Message message)
    {
        //有効なとき以外処理しない
        if (this.isActiveAndEnabled)
        {
            //仮想コントローラー V2.3
            if (message.address == "/VMC/Ext/Con/Pos" &&
                (message.values[0] is string) &&
                (message.values[1] is float) &&
                (message.values[2] is float) &&
                (message.values[3] is float) &&
                (message.values[4] is float) &&
                (message.values[5] is float) &&
                (message.values[6] is float) &&
                (message.values[7] is float)
                )
            {
                string serial         = (string)message.values[0];
                var    rigidTransform = SetTransform(ref pos, ref rot, ref message);

                lock (LockObject)
                {
                    if (virtualController.ContainsKey(serial))
                    {
                        virtualController[serial] = rigidTransform;
                    }
                    else
                    {
                        virtualController.Add(serial, rigidTransform);
                        virtualControllerFiltered.Add(serial, rigidTransform);
                    }
                }
            }
            //仮想トラッカー V2.3
            else if ((message.address == "/VMC/Ext/Hmd/Pos" ||
                      message.address == "/VMC/Ext/Tra/Pos") &&
                     (message.values[0] is string) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float) &&
                     (message.values[4] is float) &&
                     (message.values[5] is float) &&
                     (message.values[6] is float) &&
                     (message.values[7] is float)
                     )
            {
                string serial         = (string)message.values[0];
                var    rigidTransform = SetTransform(ref pos, ref rot, ref message);

                lock (LockObject)
                {
                    if (virtualTracker.ContainsKey(serial))
                    {
                        virtualTracker[serial] = rigidTransform;
                    }
                    else
                    {
                        virtualTracker.Add(serial, rigidTransform);
                        virtualTrackerFiltered.Add(serial, rigidTransform);
                    }
                }
            }
            //フレーム設定 V2.3
            else if (message.address == "/VMC/Ext/Set/Period" &&
                     (message.values[0] is int) &&
                     (message.values[1] is int) &&
                     (message.values[2] is int) &&
                     (message.values[3] is int) &&
                     (message.values[4] is int) &&
                     (message.values[5] is int)
                     )
            {
                externalSender.periodStatus     = (int)message.values[0];
                externalSender.periodRoot       = (int)message.values[1];
                externalSender.periodBone       = (int)message.values[2];
                externalSender.periodBlendShape = (int)message.values[3];
                externalSender.periodCamera     = (int)message.values[4];
                externalSender.periodDevices    = (int)message.values[5];
            }
            //Virtual MIDI CC V2.3
            else if (message.address == "/VMC/Ext/Midi/CC/Val" &&
                     (message.values[0] is int) &&
                     (message.values[1] is float)
                     )
            {
                MIDICCWrapper.KnobUpdated(0, (int)message.values[0], (float)message.values[1]);
            }
            //Camera Control V2.3
            else if (message.address == "/VMC/Ext/Cam" &&
                     (message.values[0] is string) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float) &&
                     (message.values[4] is float) &&
                     (message.values[5] is float) &&
                     (message.values[6] is float) &&
                     (message.values[7] is float) &&
                     (message.values[8] is float)
                     )
            {
                pos.x = (float)message.values[1];
                pos.y = (float)message.values[2];
                pos.z = (float)message.values[3];
                rot.x = (float)message.values[4];
                rot.y = (float)message.values[5];
                rot.z = (float)message.values[6];
                rot.w = (float)message.values[7];
                float fov = (float)message.values[8];

                //FreeCameraじゃなかったらFreeCameraにする
                if (ControlWPFWindow.CurrentSettings.CameraType != UnityMemoryMappedFile.CameraTypes.Free)
                {
                    window.ChangeCamera(UnityMemoryMappedFile.CameraTypes.Free);
                }

                //カメラ制御を切る
                window.FreeCamera.GetComponent <CameraMouseControl>().enabled = false;

                //座標とFOVを適用
                window.FreeCamera.transform.position = pos;
                window.FreeCamera.transform.rotation = rot;
                window.FreeCamera.fieldOfView        = fov;
            } //ブレンドシェープ同期
            else if (message.address == "/VMC/Ext/Blend/Val" &&
                     (message.values[0] is string) &&
                     (message.values[1] is float)
                     )
            {
                blendShapeBuffer[new BlendShapeKey((string)message.values[0])] = (float)message.values[1];
            }
            //ブレンドシェープ適用
            else if (message.address == "/VMC/Ext/Blend/Apply")
            {
                faceController.MixPresets(nameof(ExternalReceiverForVMC), blendShapeBuffer.Keys.ToArray(), blendShapeBuffer.Values.ToArray());
                blendShapeBuffer.Clear();
            }//外部アイトラ V2.3
            else if (message.address == "/VMC/Ext/Set/Eye" &&
                     (message.values[0] is int) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float)
                     )
            {
                bool enable = ((int)message.values[0]) != 0;
                pos.x = (float)message.values[1];
                pos.y = (float)message.values[2];
                pos.z = (float)message.values[3];

                if (enable)
                {
                    //ターゲットが存在しなければ作る
                    if (lookTargetOSC == null)
                    {
                        lookTargetOSC = new GameObject();
                        lookTargetOSC.transform.parent = null;
                        lookTargetOSC.name             = "lookTargetOSC";
                    }
                    //位置を書き込む
                    if (lookTargetOSC.transform != null)
                    {
                        lookTargetOSC.transform.position = pos;
                    }

                    //視線に書き込む
                    if (vrmLookAtHead != null && setFaceApplyAction == false)
                    {
                        faceController.BeforeApply += beforeFaceApply;
                        setFaceApplyAction          = true;
                    }
                }
                else
                {
                    //視線を止める
                    if (vrmLookAtHead != null && setFaceApplyAction == true)
                    {
                        faceController.BeforeApply -= beforeFaceApply;
                        setFaceApplyAction          = false;
                    }
                }
            }
            //情報要求 V2.4
            else if (message.address == "/VMC/Ext/Set/Req")
            {
                externalSender.SendPerLowRate(); //即時送信
            }
            //情報表示 V2.4
            else if (message.address == "/VMC/Ext/Set/Res" && (message.values[0] is string))
            {
                statusString = (string)message.values[0];
            }
            //キャリブレーション準備 V2.5
            else if (message.address == "/VMC/Ext/Set/Calib/Ready")
            {
                if (File.Exists(ControlWPFWindow.CurrentSettings.VRMPath))
                {
                    window.ImportVRM(ControlWPFWindow.CurrentSettings.VRMPath, true, true, true);
                }
            }
            //キャリブレーション実行 V2.5
            else if (message.address == "/VMC/Ext/Set/Calib/Exec" && (message.values[0] is int))
            {
                PipeCommands.CalibrateType calibrateType = PipeCommands.CalibrateType.Default;

                switch ((int)message.values[0])
                {
                case 0:
                    calibrateType = PipeCommands.CalibrateType.Default;
                    break;

                case 1:
                    calibrateType = PipeCommands.CalibrateType.FixedHand;
                    break;

                case 2:
                    calibrateType = PipeCommands.CalibrateType.FixedHandWithGround;
                    break;

                default: return;     //無視
                }
                StartCoroutine(window.Calibrate(calibrateType));
                Invoke("EndCalibrate", 2f);
            }
            //設定読み込み V2.5
            else if (message.address == "/VMC/Ext/Set/Config" && (message.values[0] is string))
            {
                string path = (string)message.values[0];
                if (File.Exists(path))
                {
                    //なぜか時間がかかる
                    window.LoadSettings(path);
                }
            }
        }
    }
    void OnDataReceived(uOSC.Message message)
    {
        //有効なとき以外処理しない
        if (this.isActiveAndEnabled)
        {
            //生存チェックのためのパケットカウンタ
            packets++;
            if (packets > int.MaxValue / 2)
            {
                packets = 0;
            }

            //仮想コントローラー V2.3
            if (message.address == "/VMC/Ext/Con/Pos" &&
                (message.values[0] is string) &&
                (message.values[1] is float) &&
                (message.values[2] is float) &&
                (message.values[3] is float) &&
                (message.values[4] is float) &&
                (message.values[5] is float) &&
                (message.values[6] is float) &&
                (message.values[7] is float)
                )
            {
                string serial         = (string)message.values[0];
                var    rigidTransform = SetTransform(ref pos, ref rot, ref message);

                lock (LockObject)
                {
                    if (virtualController.ContainsKey(serial))
                    {
                        virtualController[serial] = rigidTransform;
                    }
                    else
                    {
                        virtualController.Add(serial, rigidTransform);
                        virtualControllerFiltered.Add(serial, rigidTransform);
                    }
                }
            }
            //仮想トラッカー V2.3
            else if ((message.address == "/VMC/Ext/Hmd/Pos" ||
                      message.address == "/VMC/Ext/Tra/Pos") &&
                     (message.values[0] is string) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float) &&
                     (message.values[4] is float) &&
                     (message.values[5] is float) &&
                     (message.values[6] is float) &&
                     (message.values[7] is float)
                     )
            {
                string serial         = (string)message.values[0];
                var    rigidTransform = SetTransform(ref pos, ref rot, ref message);

                lock (LockObject)
                {
                    if (virtualTracker.ContainsKey(serial))
                    {
                        virtualTracker[serial] = rigidTransform;
                    }
                    else
                    {
                        virtualTracker.Add(serial, rigidTransform);
                        virtualTrackerFiltered.Add(serial, rigidTransform);
                    }
                }
            }
            //フレーム設定 V2.3
            else if (message.address == "/VMC/Ext/Set/Period" &&
                     (message.values[0] is int) &&
                     (message.values[1] is int) &&
                     (message.values[2] is int) &&
                     (message.values[3] is int) &&
                     (message.values[4] is int) &&
                     (message.values[5] is int)
                     )
            {
                externalSender.periodStatus     = (int)message.values[0];
                externalSender.periodRoot       = (int)message.values[1];
                externalSender.periodBone       = (int)message.values[2];
                externalSender.periodBlendShape = (int)message.values[3];
                externalSender.periodCamera     = (int)message.values[4];
                externalSender.periodDevices    = (int)message.values[5];
            }
            //Virtual MIDI CC V2.3
            else if (message.address == "/VMC/Ext/Midi/CC/Val" &&
                     (message.values[0] is int) &&
                     (message.values[1] is float)
                     )
            {
                MIDICCWrapper.KnobUpdated(0, (int)message.values[0], (float)message.values[1]);
            }
            //Camera Control V2.3
            else if (message.address == "/VMC/Ext/Cam" &&
                     (message.values[0] is string) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float) &&
                     (message.values[4] is float) &&
                     (message.values[5] is float) &&
                     (message.values[6] is float) &&
                     (message.values[7] is float) &&
                     (message.values[8] is float)
                     )
            {
                pos.x = (float)message.values[1];
                pos.y = (float)message.values[2];
                pos.z = (float)message.values[3];
                rot.x = (float)message.values[4];
                rot.y = (float)message.values[5];
                rot.z = (float)message.values[6];
                rot.w = (float)message.values[7];
                float fov = (float)message.values[8];

                //FreeCameraじゃなかったらFreeCameraにする
                if (ControlWPFWindow.CurrentSettings.CameraType != UnityMemoryMappedFile.CameraTypes.Free)
                {
                    window.ChangeCamera(UnityMemoryMappedFile.CameraTypes.Free);
                }

                //カメラ制御を切る
                window.FreeCamera.GetComponent <CameraMouseControl>().enabled = false;

                //座標とFOVを適用
                window.FreeCamera.transform.localPosition = pos;
                window.FreeCamera.transform.localRotation = rot;
                window.ControlCamera.fieldOfView          = fov;
            } //ブレンドシェープ同期
            else if (message.address == "/VMC/Ext/Blend/Val" &&
                     (message.values[0] is string) &&
                     (message.values[1] is float)
                     )
            {
                blendShapeBuffer[(string)message.values[0]] = (float)message.values[1];
            }
            //ブレンドシェープ適用
            else if (message.address == "/VMC/Ext/Blend/Apply")
            {
                faceController.MixPresets(nameof(ExternalReceiverForVMC), blendShapeBuffer.Keys.ToArray(), blendShapeBuffer.Values.ToArray());
                blendShapeBuffer.Clear();
            }//外部アイトラ V2.3
            else if (message.address == "/VMC/Ext/Set/Eye" &&
                     (message.values[0] is int) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float)
                     )
            {
                bool enable = ((int)message.values[0]) != 0;
                pos.x = (float)message.values[1];
                pos.y = (float)message.values[2];
                pos.z = (float)message.values[3];

                if (enable)
                {
                    //ターゲットが存在しなければ作る
                    if (lookTargetOSC == null)
                    {
                        lookTargetOSC      = new GameObject();
                        lookTargetOSC.name = "lookTargetOSC";
                    }
                    //位置を書き込む
                    if (lookTargetOSC.transform != null)
                    {
                        lookTargetOSC.transform.parent        = headTransform;
                        lookTargetOSC.transform.localPosition = pos;
                    }

                    //視線に書き込む
                    if (vrmLookAtHead != null && setFaceApplyAction == false)
                    {
                        faceController.BeforeApply += beforeFaceApply;
                        setFaceApplyAction          = true;
                    }
                }
                else
                {
                    //視線を止める
                    if (vrmLookAtHead != null && setFaceApplyAction == true)
                    {
                        faceController.BeforeApply -= beforeFaceApply;
                        setFaceApplyAction          = false;
                    }
                }
            }
            //情報要求 V2.4
            else if (message.address == "/VMC/Ext/Set/Req")
            {
                if (externalSender.isActiveAndEnabled && externalSender.uClient != null)
                {
                    externalSender.SendPerLowRate(); //即時送信
                }
            }
            //情報表示 V2.4
            else if (message.address == "/VMC/Ext/Set/Res" && (message.values[0] is string))
            {
                statusString = (string)message.values[0];
            }
            //キャリブレーション準備 V2.5
            else if (message.address == "/VMC/Ext/Set/Calib/Ready")
            {
                if (File.Exists(ControlWPFWindow.CurrentSettings.VRMPath))
                {
                    window.ImportVRM(ControlWPFWindow.CurrentSettings.VRMPath, true, true, true);
                }
            }
            //キャリブレーション実行 V2.5
            else if (message.address == "/VMC/Ext/Set/Calib/Exec" && (message.values[0] is int))
            {
                PipeCommands.CalibrateType calibrateType = PipeCommands.CalibrateType.Default;

                switch ((int)message.values[0])
                {
                case 0:
                    calibrateType = PipeCommands.CalibrateType.Default;
                    break;

                case 1:
                    calibrateType = PipeCommands.CalibrateType.FixedHand;
                    break;

                case 2:
                    calibrateType = PipeCommands.CalibrateType.FixedHandWithGround;
                    break;

                default: return;     //無視
                }
                StartCoroutine(window.Calibrate(calibrateType));
                Invoke("EndCalibrate", 2f);
            }
            //設定読み込み V2.5
            else if (message.address == "/VMC/Ext/Set/Config" && (message.values[0] is string))
            {
                string path = (string)message.values[0];
                if (File.Exists(path))
                {
                    //なぜか時間がかかる
                    window.LoadSettings(path);
                }
            }
            //スルー情報 V2.6
            else if (message.address != null && message.address.StartsWith("/VMC/Thru/"))
            {
                //転送する
                if (externalSender.isActiveAndEnabled && externalSender.uClient != null)
                {
                    externalSender.uClient.Send(message.address, message.values);
                }
            }
            //Directional Light V2.9
            else if (message.address == "/VMC/Ext/Light" &&
                     (message.values[0] is string) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float) &&
                     (message.values[4] is float) &&
                     (message.values[5] is float) &&
                     (message.values[6] is float) &&
                     (message.values[7] is float) &&
                     (message.values[8] is float) &&
                     (message.values[9] is float) &&
                     (message.values[10] is float) &&
                     (message.values[11] is float)
                     )
            {
                pos.x = (float)message.values[1];
                pos.y = (float)message.values[2];
                pos.z = (float)message.values[3];
                rot.x = (float)message.values[4];
                rot.y = (float)message.values[5];
                rot.z = (float)message.values[6];
                rot.w = (float)message.values[7];
                float r = (float)message.values[8];
                float g = (float)message.values[9];
                float b = (float)message.values[10];
                float a = (float)message.values[11];

                window.MainDirectionalLight.color             = new Color(r, g, b, a);
                window.MainDirectionalLightTransform.position = pos;
                window.MainDirectionalLightTransform.rotation = rot;
            }

            //ボーン姿勢
            else if (message.address == "/VMC/Ext/Bone/Pos" &&
                     (message.values[0] is string) &&
                     (message.values[1] is float) &&
                     (message.values[2] is float) &&
                     (message.values[3] is float) &&
                     (message.values[4] is float) &&
                     (message.values[5] is float) &&
                     (message.values[6] is float) &&
                     (message.values[7] is float)
                     )
            {
                string boneName = (string)message.values[0];
                pos.x = (float)message.values[1];
                pos.y = (float)message.values[2];
                pos.z = (float)message.values[3];
                rot.x = (float)message.values[4];
                rot.y = (float)message.values[5];
                rot.z = (float)message.values[6];
                rot.w = (float)message.values[7];

                //Humanoidボーンに該当するボーンがあるか調べる
                HumanBodyBones bone;
                if (HumanBodyBonesTryParse(ref boneName, out bone))
                {
                    //あれば位置と回転をキャッシュする
                    if (HumanBodyBonesPositionTable.ContainsKey(bone))
                    {
                        HumanBodyBonesPositionTable[bone] = pos;
                    }
                    else
                    {
                        HumanBodyBonesPositionTable.Add(bone, pos);
                    }

                    if (HumanBodyBonesRotationTable.ContainsKey(bone))
                    {
                        HumanBodyBonesRotationTable[bone] = rot;
                    }
                    else
                    {
                        HumanBodyBonesRotationTable.Add(bone, rot);
                    }
                }
                //受信と更新のタイミングは切り離した
            }
        }
    }
Example #7
0
    // Update is called once per frame
    void Update()
    {
        if (EnableLipSync)
        {
            if (Context != 0)
            {
                if (faceController == null)
                {
                    return;
                }
                else
                {
                    // get the current viseme frame
                    OVRLipSync.Frame frame = GetCurrentPhonemeFrame();
                    if (frame != null)
                    {
                        //あ OVRLipSync.Viseme.aa; BlendShapePreset.A;
                        //い OVRLipSync.Viseme.ih; BlendShapePreset.I;
                        //う OVRLipSync.Viseme.ou; BlendShapePreset.U;
                        //え OVRLipSync.Viseme.E;  BlendShapePreset.E;
                        //お OVRLipSync.Viseme.oh; BlendShapePreset.O;
                        var presets = new BlendShapePreset[] {
                            BlendShapePreset.A,
                            BlendShapePreset.I,
                            BlendShapePreset.U,
                            BlendShapePreset.E,
                            BlendShapePreset.O,
                        };
                        var visemes = new float[] {
                            frame.Visemes[(int)OVRLipSync.Viseme.aa],
                            frame.Visemes[(int)OVRLipSync.Viseme.ih],
                            frame.Visemes[(int)OVRLipSync.Viseme.ou],
                            frame.Visemes[(int)OVRLipSync.Viseme.E],
                            frame.Visemes[(int)OVRLipSync.Viseme.oh],
                        };

                        int   maxindex   = 0;
                        float maxvisemes = 0;
                        for (int i = 0; i < presets.Length; i++)
                        {
                            if (visemes[i] < WeightThreashold)
                            {
                                visemes[i] = 0;
                            }
                            if (maxvisemes < visemes[i])
                            {
                                maxindex   = i;
                                maxvisemes = visemes[i];
                            }
                        }

                        if (MaxWeightEmphasis)
                        {
                            visemes[maxindex] = Mathf.Clamp(visemes[maxindex] * 3, 0.0f, 1.0f);
                        }

                        if (MaxWeightEnable)
                        {
                            for (int i = 0; i < presets.Length; i++)
                            {
                                if (i != maxindex)
                                {
                                    visemes[i] = 0.0f;
                                }
                            }
                        }

                        for (int i = 0; i < presets.Length; i++)
                        {
                            visemes[i] *= MaxLevel;
                        }
                        faceController.MixPresets(nameof(DynamicOVRLipSync), presets, visemes);

                        //Debug.Log("Visemes:" + string.Join(",", frame.Visemes.Select(d => d.ToString())));
                    }
                }
            }

            if (string.IsNullOrEmpty(selectedDevice) == false)
            {
                audioSource.volume = (sourceVolume / 100);
                if (!Microphone.IsRecording(selectedDevice))
                {
                    StartMicrophone();
                }

                if (EnableLowLatency)
                {
                    var position = Microphone.GetPosition(selectedDevice);
                    if (position < 0 || head == position)
                    {
                        return;
                    }

                    audioSource.clip.GetData(microphoneBuffer, 0);
                    while (GetDataLength(microphoneBuffer.Length, head, position) > processBuffer.Length)
                    {
                        var remain = microphoneBuffer.Length - head;
                        if (remain < processBuffer.Length)
                        {
                            Array.Copy(microphoneBuffer, head, processBuffer, 0, remain);
                            Array.Copy(microphoneBuffer, 0, processBuffer, remain, processBuffer.Length - remain);
                        }
                        else
                        {
                            Array.Copy(microphoneBuffer, head, processBuffer, 0, processBuffer.Length);
                        }

                        OVRLipSync.ProcessFrame(Context, processBuffer, Frame, OVRLipSync.AudioDataType.F32_Mono);

                        head += processBuffer.Length;
                        if (head > microphoneBuffer.Length)
                        {
                            head -= microphoneBuffer.Length;
                        }
                    }
                }
            }
        }
    }