forked from cgvr/DeltaVR
non-vr lobby, version fix
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_SWITCH || UNITY_IOS
|
||||
#define PHOTON_AUDIO_CHANGE_IN_NOTIFIER
|
||||
#endif
|
||||
|
||||
namespace Photon.Voice.Unity.Editor {
|
||||
using Unity;
|
||||
using UnityEditor;
|
||||
|
||||
[CustomEditor(typeof(AudioChangesHandler))]
|
||||
public class AudioChangesHandlerEditor : Editor {
|
||||
public override void OnInspectorGUI() {
|
||||
this.serializedObject.UpdateIfRequiredOrScript();
|
||||
VoiceLogger.ExposeLogLevel(this.serializedObject, this.target as ILoggable);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
this.DrawSerializedProperty("StartWhenDeviceChange");
|
||||
SerializedProperty useUnity = this.DrawSerializedProperty("UseOnAudioConfigurationChanged");
|
||||
bool showMore = useUnity.boolValue;
|
||||
if (showMore) {
|
||||
this.DrawSerializedProperty("HandleConfigChange");
|
||||
}
|
||||
#if PHOTON_AUDIO_CHANGE_IN_NOTIFIER
|
||||
SerializedProperty usePhoton = this.DrawSerializedProperty("UseNativePluginChangeNotifier");
|
||||
showMore |= usePhoton.boolValue;
|
||||
#endif
|
||||
if (showMore) {
|
||||
if (this.DrawSerializedProperty("HandleDeviceChange").boolValue) {
|
||||
this.DrawSerializedProperty("Android_AlwaysHandleDeviceChange");
|
||||
this.DrawSerializedProperty("iOS_AlwaysHandleDeviceChange");
|
||||
}
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck()) {
|
||||
this.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private SerializedProperty DrawSerializedProperty(string propertyName) {
|
||||
SerializedProperty serializedProperty = this.serializedObject.FindProperty(propertyName);
|
||||
EditorGUILayout.PropertyField(serializedProperty);
|
||||
return serializedProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a6ec05c84a196c429f5e48d3a770c22
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "PhotonVoice.Editor",
|
||||
"references": [
|
||||
"PhotonRealtime",
|
||||
"PhotonVoice",
|
||||
"PhotonVoice.API"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae8024269c736ee49ba1179cb00214e5
|
||||
timeCreated: 1538045250
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
321
Assets/Photon/PhotonVoice/Code/Editor/PhotonVoiceEditorUtils.cs
Normal file
321
Assets/Photon/PhotonVoice/Code/Editor/PhotonVoiceEditorUtils.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Photon.Voice.Unity.Editor
|
||||
{
|
||||
[InitializeOnLoad] // calls static constructor when script is recompiled
|
||||
public static class PhotonVoiceEditorUtils
|
||||
{
|
||||
public const string PHOTON_VIDEO_DEFINE_SYMBOL = "PHOTON_VOICE_VIDEO_ENABLE";
|
||||
public const string PHOTON_VIDEO_AVAILABLE_DEFINE_SYMBOL = "PHOTON_VOICE_VIDEO_AVAILABLE";
|
||||
|
||||
static PhotonVoiceEditorUtils()
|
||||
{
|
||||
if (HasVideo)
|
||||
{
|
||||
#if !PHOTON_VOICE_VIDEO_AVAILABLE
|
||||
Debug.Log("Photon Video is available");
|
||||
Realtime.PhotonEditorUtils.AddScriptingDefineSymbolToAllBuildTargetGroups(PHOTON_VIDEO_AVAILABLE_DEFINE_SYMBOL);
|
||||
TriggerRecompile();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PHOTON_VOICE_VIDEO_AVAILABLE
|
||||
RemoveScriptingDefineSymbolToAllBuildTargetGroups(PHOTON_VIDEO_AVAILABLE_DEFINE_SYMBOL);
|
||||
TriggerRecompile();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// triggers this calss recompilation after define symbols change
|
||||
private static void TriggerRecompile()
|
||||
{
|
||||
AssetDatabase.ImportAsset("Assets/Photon/PhotonVoice/Code/Editor/PhotonVoiceEditorUtils.cs");
|
||||
}
|
||||
|
||||
/// <summary>True if the ChatClient of the Photon Chat API is available. If so, the editor may (e.g.) show additional options in settings.</summary>
|
||||
public static bool HasChat
|
||||
{
|
||||
get
|
||||
{
|
||||
return Type.GetType("Photon.Chat.ChatClient, Assembly-CSharp") != null || Type.GetType("Photon.Chat.ChatClient, Assembly-CSharp-firstpass") != null || Type.GetType("Photon.Chat.ChatClient, PhotonChat") != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>True if the PhotonNetwork of the PUN is available. If so, the editor may (e.g.) show additional options in settings.</summary>
|
||||
public static bool HasPun
|
||||
{
|
||||
get
|
||||
{
|
||||
return Type.GetType("Photon.Pun.PhotonNetwork, Assembly-CSharp") != null || Type.GetType("Photon.Pun.PhotonNetwork, Assembly-CSharp-firstpass") != null || Type.GetType("Photon.Pun.PhotonNetwork, PhotonUnityNetworking") != null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasVideo
|
||||
{
|
||||
get
|
||||
{
|
||||
return Directory.Exists("Assets/Photon/PhotonVoice/PhotonVoiceApi/Core/Video");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Window/Photon Voice/Remove PUN", true, 1)]
|
||||
private static bool RemovePunValidate()
|
||||
{
|
||||
#if PHOTON_UNITY_NETWORKING
|
||||
return true;
|
||||
#else
|
||||
return HasPun;
|
||||
#endif
|
||||
}
|
||||
|
||||
[MenuItem("Window/Photon Voice/Remove PUN", false, 1)]
|
||||
private static void RemovePun()
|
||||
{
|
||||
DeleteDirectory("Assets/Photon/PhotonVoice/Demos/DemoProximityVoiceChat");
|
||||
DeleteDirectory("Assets/Photon/PhotonVoice/Demos/DemoVoicePun");
|
||||
DeleteDirectory("Assets/Photon/PhotonVoice/Code/PUN");
|
||||
DeleteDirectory("Assets/Photon/PhotonUnityNetworking");
|
||||
CleanUpPunDefineSymbols();
|
||||
}
|
||||
|
||||
[MenuItem("Window/Photon Voice/Remove Photon Chat", true, 2)]
|
||||
private static bool RemovePhotonChatValidate()
|
||||
{
|
||||
return HasChat;
|
||||
}
|
||||
|
||||
[MenuItem("Window/Photon Voice/Remove Photon Chat", false, 2)]
|
||||
private static void RemovePhotonChat()
|
||||
{
|
||||
DeleteDirectory("Assets/Photon/PhotonChat");
|
||||
}
|
||||
|
||||
[MenuItem("Window/Photon Voice/Leave a review", false, 3)]
|
||||
private static void OpenAssetStorePage()
|
||||
{
|
||||
Application.OpenURL("https://assetstore.unity.com/packages/tools/audio/photon-voice-2-130518");
|
||||
}
|
||||
|
||||
#if PHOTON_VOICE_VIDEO_AVAILABLE
|
||||
|
||||
#if PHOTON_VOICE_VIDEO_ENABLE
|
||||
[MenuItem("Window/Photon Voice/Disable Video", false, 4)]
|
||||
private static void DisableVideo()
|
||||
{
|
||||
RemoveScriptingDefineSymbolToAllBuildTargetGroups(PHOTON_VIDEO_DEFINE_SYMBOL);
|
||||
TriggerRecompile();
|
||||
}
|
||||
#else
|
||||
[MenuItem("Window/Photon Voice/Enable Video", false, 4)]
|
||||
private static void EnableVideo()
|
||||
{
|
||||
Realtime.PhotonEditorUtils.AddScriptingDefineSymbolToAllBuildTargetGroups(PHOTON_VIDEO_DEFINE_SYMBOL);
|
||||
TriggerRecompile();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
public static void DeleteDirectory(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
if (!FileUtil.DeleteFileOrDirectory(path))
|
||||
{
|
||||
Debug.LogWarningFormat("Directory \"{0}\" not deleted.", path);
|
||||
}
|
||||
DeleteFile(string.Concat(path, ".meta"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("Directory \"{0}\" does not exist.", path);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteFile(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
if (!FileUtil.DeleteFileOrDirectory(path))
|
||||
{
|
||||
Debug.LogWarningFormat("File \"{0}\" not deleted.", path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("File \"{0}\" does not exist.", path);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsInTheSceneInPlayMode(GameObject go)
|
||||
{
|
||||
return Application.isPlaying && !IsPrefab(go);
|
||||
}
|
||||
|
||||
public static void GetPhotonVoiceVersionsFromChangeLog(out string photonVoiceVersion, out string punChangelogVersion, out string photonVoiceApiVersion)
|
||||
{
|
||||
photonVoiceVersion = null;
|
||||
punChangelogVersion = null;
|
||||
photonVoiceApiVersion = null;
|
||||
string filePath = Path.Combine("Assets", "Photon", "PhotonVoice", "changes-voice.txt");
|
||||
const string guid = "63aaf8df43de62247af0bbdc549b31b5";
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
filePath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
Debug.LogFormat("Photon Voice 2's change log file was moved to this path \"{0}\"", filePath);
|
||||
}
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamReader file = new StreamReader(filePath))
|
||||
{
|
||||
while (!file.EndOfStream && (string.IsNullOrEmpty(photonVoiceVersion) || string.IsNullOrEmpty(punChangelogVersion) || string.IsNullOrEmpty(photonVoiceApiVersion)))
|
||||
{
|
||||
string line = file.ReadLine();
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
line = line.Trim();
|
||||
if (line.StartsWith("v2."))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(photonVoiceVersion))
|
||||
{
|
||||
break;
|
||||
}
|
||||
photonVoiceVersion = line.TrimStart('v');
|
||||
continue;
|
||||
}
|
||||
string[] parts = line.Split(null);
|
||||
if (line.StartsWith("PUN2: ") && parts.Length > 1)
|
||||
{
|
||||
punChangelogVersion = parts[1];
|
||||
continue;
|
||||
}
|
||||
if (line.StartsWith("PhotonVoiceApi: ") && parts.Length > 2)
|
||||
{
|
||||
photonVoiceApiVersion = string.Format("rev. {0}", parts[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Debug.LogErrorFormat("There was an error reading the file \"{0}\": ", filePath);
|
||||
Debug.LogError(e.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogErrorFormat("Photon Voice 2's change log file not found (moved or deleted or not imported? or meta file changed) \"{0}\": ", filePath);
|
||||
}
|
||||
if (string.IsNullOrEmpty(photonVoiceVersion))
|
||||
{
|
||||
Debug.LogError("There was an error retrieving Photon Voice version from changelog.");
|
||||
}
|
||||
if (string.IsNullOrEmpty(punChangelogVersion))
|
||||
{
|
||||
Debug.LogError("There was an error retrieving PUN2 version from changelog.");
|
||||
}
|
||||
if (string.IsNullOrEmpty(photonVoiceApiVersion))
|
||||
{
|
||||
Debug.LogError("There was an error retrieving Photon Voice API version from changelog.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a given scripting define symbol to all build target groups
|
||||
/// You can see all scripting define symbols ( not the internal ones, only the one for this project), in the PlayerSettings inspector
|
||||
/// </summary>
|
||||
/// <param name="defineSymbol">Define symbol.</param>
|
||||
public static void RemoveScriptingDefineSymbolToAllBuildTargetGroups(string defineSymbol)
|
||||
{
|
||||
foreach (BuildTarget target in Enum.GetValues(typeof(BuildTarget)))
|
||||
{
|
||||
BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target);
|
||||
|
||||
if (group == BuildTargetGroup.Unknown)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var defineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(group).Split(';').Select(d => d.Trim()).ToList();
|
||||
|
||||
if (defineSymbols.Contains(defineSymbol) && defineSymbols.Remove(defineSymbol))
|
||||
{
|
||||
try
|
||||
{
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, string.Join(";", defineSymbols.ToArray()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogErrorFormat("Could not remove \"{0}\" Scripting Define Symbol for build target: {1} group: {2} {3}", defineSymbol, target, group, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a GameObject is a prefab asset or part of a prefab asset, as opposed to an instance in the scene hierarchy
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if a prefab asset or part of it, <c>false</c> otherwise.</returns>
|
||||
/// <param name="go">The GameObject to check</param>
|
||||
public static bool IsPrefab(GameObject go)
|
||||
{
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
return UnityEditor.SceneManagement.PrefabStageUtility.GetPrefabStage(go) != null || EditorUtility.IsPersistent(go);
|
||||
#elif UNITY_2018_3_OR_NEWER
|
||||
return UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetPrefabStage(go) != null || EditorUtility.IsPersistent(go);
|
||||
#else
|
||||
return EditorUtility.IsPersistent(go);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes PUN2's Script Define Symbols from project
|
||||
/// </summary>
|
||||
public static void CleanUpPunDefineSymbols()
|
||||
{
|
||||
foreach (BuildTarget target in Enum.GetValues(typeof(BuildTarget)))
|
||||
{
|
||||
BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target);
|
||||
|
||||
if (group == BuildTargetGroup.Unknown)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var defineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(group)
|
||||
.Split(';')
|
||||
.Select(d => d.Trim())
|
||||
.ToList();
|
||||
|
||||
List<string> newDefineSymbols = new List<string>();
|
||||
foreach (var symbol in defineSymbols)
|
||||
{
|
||||
if ("PHOTON_UNITY_NETWORKING".Equals(symbol) || symbol.StartsWith("PUN_2_"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
newDefineSymbols.Add(symbol);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, string.Join(";", newDefineSymbols.ToArray()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogErrorFormat("Could not set clean up PUN2's define symbols for build target: {0} group: {1}, {2}", target, group, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e837e30d97d9fd4a984a83cc6db75a7
|
||||
timeCreated: 1550755478
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
741
Assets/Photon/PhotonVoice/Code/Editor/RecorderEditor.cs
Normal file
741
Assets/Photon/PhotonVoice/Code/Editor/RecorderEditor.cs
Normal file
@@ -0,0 +1,741 @@
|
||||
#if UNITY_EDITOR_OSX || UNITY_EDITOR_WIN
|
||||
#define PHOTON_MICROPHONE_ENUMERATOR
|
||||
#endif
|
||||
|
||||
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_ANDROID
|
||||
#define WEBRTC_AUDIO_DSP_SUPPORTED_PLATFORMS
|
||||
#endif
|
||||
|
||||
namespace Photon.Voice.Unity.Editor
|
||||
{
|
||||
using POpusCodec.Enums;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Unity;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_IOS
|
||||
using IOS;
|
||||
#endif
|
||||
|
||||
[CustomEditor(typeof(Recorder))]
|
||||
public class RecorderEditor : Editor
|
||||
{
|
||||
private Recorder recorder;
|
||||
|
||||
private int unityMicrophoneDeviceIndex;
|
||||
|
||||
#if PHOTON_MICROPHONE_ENUMERATOR
|
||||
private string[] photonDeviceNames;
|
||||
private int[] photonDeviceIDs;
|
||||
private int photonDeviceIndex;
|
||||
#endif
|
||||
|
||||
private int calibrationTime = 200;
|
||||
|
||||
private SerializedProperty voiceDetectionSp;
|
||||
private SerializedProperty voiceDetectionThresholdSp;
|
||||
private SerializedProperty voiceDetectionDelayMsSp;
|
||||
private SerializedProperty photonMicrophoneDeviceIdSp;
|
||||
private SerializedProperty interestGroupSp;
|
||||
private SerializedProperty debugEchoModeSp;
|
||||
private SerializedProperty reliableModeSp;
|
||||
private SerializedProperty encryptSp;
|
||||
private SerializedProperty transmitEnabledSp;
|
||||
private SerializedProperty samplingRateSp;
|
||||
private SerializedProperty frameDurationSp;
|
||||
private SerializedProperty bitrateSp;
|
||||
private SerializedProperty sourceTypeSp;
|
||||
private SerializedProperty microphoneTypeSp;
|
||||
private SerializedProperty audioClipSp;
|
||||
private SerializedProperty loopAudioClipSp;
|
||||
private SerializedProperty autoStartSp;
|
||||
private SerializedProperty recordOnlyWhenEnabledSp;
|
||||
private SerializedProperty stopRecordingWhenPausedSp;
|
||||
private SerializedProperty useMicrophoneTypeFallbackSp;
|
||||
private SerializedProperty recordOnlyWhenJoinedSp;
|
||||
|
||||
#if UNITY_IOS
|
||||
private SerializedProperty useCustomAudioSessionParametersSp;
|
||||
private SerializedProperty audioSessionParametersSp;
|
||||
private SerializedProperty audioSessionPresetIndexSp;
|
||||
private SerializedProperty audioSessionParametersCategorySp;
|
||||
private SerializedProperty audioSessionParametersModeSp;
|
||||
private SerializedProperty audioSessionParametersCategoryOptionsSp;
|
||||
|
||||
private string[] iOSAudioSessionPresetsNames = {"Game", "VoIP"};
|
||||
private AudioSessionParameters[] iOSAudioSessionPresetsValues =
|
||||
{AudioSessionParametersPresets.Game, AudioSessionParametersPresets.VoIP};
|
||||
#elif UNITY_ANDROID
|
||||
private SerializedProperty nativeAndroidMicrophoneSettingsSp;
|
||||
#else
|
||||
private SerializedProperty reactOnSystemChangesSp;
|
||||
private SerializedProperty skipDeviceChecksSp;
|
||||
#endif
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
this.recorder = this.target as Recorder;
|
||||
AudioSettings.OnAudioConfigurationChanged += this.OnAudioConfigChanged;
|
||||
this.RefreshMicrophones();
|
||||
this.voiceDetectionSp = this.serializedObject.FindProperty("voiceDetection");
|
||||
this.voiceDetectionThresholdSp = this.serializedObject.FindProperty("voiceDetectionThreshold");
|
||||
this.voiceDetectionDelayMsSp = this.serializedObject.FindProperty("voiceDetectionDelayMs");
|
||||
this.photonMicrophoneDeviceIdSp = this.serializedObject.FindProperty("photonMicrophoneDeviceId");
|
||||
this.interestGroupSp = this.serializedObject.FindProperty("interestGroup");
|
||||
this.debugEchoModeSp = this.serializedObject.FindProperty("debugEchoMode");
|
||||
this.reliableModeSp = this.serializedObject.FindProperty("reliableMode");
|
||||
this.encryptSp = this.serializedObject.FindProperty("encrypt");
|
||||
this.transmitEnabledSp = this.serializedObject.FindProperty("transmitEnabled");
|
||||
this.samplingRateSp = this.serializedObject.FindProperty("samplingRate");
|
||||
this.frameDurationSp = this.serializedObject.FindProperty("frameDuration");
|
||||
this.bitrateSp = this.serializedObject.FindProperty("bitrate");
|
||||
this.sourceTypeSp = this.serializedObject.FindProperty("sourceType");
|
||||
this.microphoneTypeSp = this.serializedObject.FindProperty("microphoneType");
|
||||
this.audioClipSp = this.serializedObject.FindProperty("audioClip");
|
||||
this.loopAudioClipSp = this.serializedObject.FindProperty("loopAudioClip");
|
||||
this.autoStartSp = this.serializedObject.FindProperty("autoStart");
|
||||
this.recordOnlyWhenEnabledSp = this.serializedObject.FindProperty("recordOnlyWhenEnabled");
|
||||
this.stopRecordingWhenPausedSp = this.serializedObject.FindProperty("stopRecordingWhenPaused");
|
||||
this.useMicrophoneTypeFallbackSp = this.serializedObject.FindProperty("useMicrophoneTypeFallback");
|
||||
this.recordOnlyWhenJoinedSp = this.serializedObject.FindProperty("recordOnlyWhenJoined");
|
||||
#if UNITY_IOS
|
||||
this.useCustomAudioSessionParametersSp = this.serializedObject.FindProperty("useCustomAudioSessionParameters");
|
||||
this.audioSessionPresetIndexSp = this.serializedObject.FindProperty("audioSessionPresetIndex");
|
||||
this.audioSessionParametersSp = this.serializedObject.FindProperty("audioSessionParameters");
|
||||
this.audioSessionParametersCategorySp = this.audioSessionParametersSp.FindPropertyRelative("Category");
|
||||
this.audioSessionParametersModeSp = this.audioSessionParametersSp.FindPropertyRelative("Mode");
|
||||
this.audioSessionParametersCategoryOptionsSp = this.audioSessionParametersSp.FindPropertyRelative("CategoryOptions");
|
||||
#elif UNITY_ANDROID
|
||||
this.nativeAndroidMicrophoneSettingsSp = this.serializedObject.FindProperty("nativeAndroidMicrophoneSettings");
|
||||
#else
|
||||
this.reactOnSystemChangesSp = this.serializedObject.FindProperty("reactOnSystemChanges");
|
||||
this.skipDeviceChecksSp = this.serializedObject.FindProperty("skipDeviceChangeChecks");
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
AudioSettings.OnAudioConfigurationChanged -= this.OnAudioConfigChanged;
|
||||
}
|
||||
|
||||
public override bool RequiresConstantRepaint() { return true; }
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
this.serializedObject.UpdateIfRequiredOrScript();
|
||||
//serializedObject.Update();
|
||||
WebRtcAudioDsp webRtcAudioDsp = this.recorder.GetComponent<WebRtcAudioDsp>();
|
||||
bool webRtcAudioDspAttached = webRtcAudioDsp && webRtcAudioDsp != null && webRtcAudioDsp.enabled;
|
||||
AudioChangesHandler audioChangesHandler = this.recorder.GetComponent<AudioChangesHandler>();
|
||||
bool audioChangesHandlerAttached = !ReferenceEquals(null, audioChangesHandler) && audioChangesHandler;
|
||||
if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.recorder.gameObject))
|
||||
{
|
||||
if (this.recorder.RequiresRestart)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Recorder requires restart. Call Recorder.RestartRecording().", MessageType.Warning);
|
||||
if (GUILayout.Button("RestartRecording"))
|
||||
{
|
||||
this.recorder.RestartRecording();
|
||||
}
|
||||
}
|
||||
else if (!this.recorder.IsInitialized)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Recorder requires initialization. Call Recorder.Init or VoiceConnection.InitRecorder.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
VoiceLogger.ExposeLogLevel(this.serializedObject, this.recorder);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.recorder.gameObject))
|
||||
{
|
||||
if (!audioChangesHandlerAttached)
|
||||
{
|
||||
#if !UNITY_ANDROID && !UNITY_IOS
|
||||
this.recorder.ReactOnSystemChanges = EditorGUILayout.Toggle(new GUIContent("React On System Changes", "If true, recording is restarted when Unity detects Audio Config. changes."), this.recorder.ReactOnSystemChanges);
|
||||
if (this.recorder.ReactOnSystemChanges)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.skipDeviceChecksSp, new GUIContent("Skip Device Checks", "If true, restarts recording without checking if audio config/device changes affected recording."));
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.HelpBox("ReactOnSystemChanges will be deprecated. AudioChangesHandler component is now the preferred solution to handle audio changes.", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("ReactOnSystemChanges will be deprecated. AudioChangesHandler component is now the preferred solution to handle audio changes.", MessageType.Info);
|
||||
}
|
||||
#endif
|
||||
if (GUILayout.Button("Add AudioChangesHandler component"))
|
||||
{
|
||||
this.recorder.gameObject.AddComponent<AudioChangesHandler>();
|
||||
}
|
||||
}
|
||||
this.recorder.RecordOnlyWhenJoined = EditorGUILayout.Toggle(new GUIContent("Record Only When Joined", "If true, recording can start only when client is joined to a room. Auto start is also delayed until client is joined to a room."),
|
||||
this.recorder.RecordOnlyWhenJoined);
|
||||
this.recorder.RecordOnlyWhenEnabled = EditorGUILayout.Toggle(new GUIContent("Record Only When Enabled", "If true, component will work only when enabled and active in hierarchy. Auto start is also delayed until these conditions are met."),
|
||||
this.recorder.RecordOnlyWhenEnabled);
|
||||
EditorGUILayout.PropertyField(this.stopRecordingWhenPausedSp,
|
||||
new GUIContent("Stop Recording When Paused",
|
||||
"If true, stop recording when paused resume/restart when un-paused."));
|
||||
this.recorder.TransmitEnabled = EditorGUILayout.Toggle(new GUIContent("Transmit Enabled", "If true, audio transmission is enabled."), this.recorder.TransmitEnabled);
|
||||
if (this.recorder.IsInitialized)
|
||||
{
|
||||
this.recorder.IsRecording = EditorGUILayout.Toggle(new GUIContent("IsRecording", "If true, audio recording is on."), this.recorder.IsRecording);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.recorder.AutoStart = EditorGUILayout.Toggle(new GUIContent("Auto Start", "If true, recording is started when Recorder is initialized."), this.recorder.AutoStart);
|
||||
}
|
||||
if (this.recorder.IsRecording && this.recorder.TransmitEnabled)
|
||||
{
|
||||
float amplitude = 0f;
|
||||
if (this.recorder.IsCurrentlyTransmitting)
|
||||
{
|
||||
amplitude = this.recorder.LevelMeter.CurrentPeakAmp;
|
||||
}
|
||||
EditorGUILayout.Slider("Level", amplitude, 0, 1);
|
||||
}
|
||||
this.recorder.Encrypt = EditorGUILayout.Toggle(new GUIContent("Encrypt", "If true, voice stream is sent encrypted."), this.recorder.Encrypt);
|
||||
this.recorder.InterestGroup = (byte)EditorGUILayout.IntField(new GUIContent("Interest Group", "Target interest group that will receive transmitted audio."), this.recorder.InterestGroup);
|
||||
if (this.recorder.InterestGroup == 0)
|
||||
{
|
||||
this.recorder.DebugEchoMode = EditorGUILayout.Toggle(new GUIContent("Debug Echo", "If true, outgoing stream routed back to client via server same way as for remote client's streams."), this.recorder.DebugEchoMode);
|
||||
}
|
||||
this.recorder.ReliableMode = EditorGUILayout.Toggle(new GUIContent("Reliable Mode", "If true, stream data sent in reliable mode."), this.recorder.ReliableMode);
|
||||
|
||||
EditorGUILayout.LabelField("Codec Parameters", EditorStyles.boldLabel);
|
||||
OpusCodec.FrameDuration frameDuration = (OpusCodec.FrameDuration)EditorGUILayout.EnumPopup(new GUIContent("Frame Duration", "Outgoing audio stream encoder delay."), this.recorder.FrameDuration);
|
||||
SamplingRate samplingRate = (SamplingRate)EditorGUILayout.EnumPopup(new GUIContent("Sampling Rate", "Outgoing audio stream sampling rate."), this.recorder.SamplingRate);
|
||||
if (webRtcAudioDspAttached)
|
||||
{
|
||||
int samplingRateInt = (int) samplingRate;
|
||||
if (Array.IndexOf(WebRTCAudioProcessor.SupportedSamplingRates, samplingRateInt) < 0)
|
||||
{
|
||||
if (this.recorder.SamplingRate == SamplingRate.Sampling48000)
|
||||
{
|
||||
Debug.LogWarningFormat("Sampling rate requested ({0}Hz) is not supported by WebRTC Audio DSP. Ignoring change.", samplingRateInt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("Sampling rate requested ({0}Hz) is not supported by WebRTC Audio DSP, switching to the closest supported value: {1}Hz.", samplingRateInt, "48k");
|
||||
}
|
||||
samplingRate = SamplingRate.Sampling48000;
|
||||
}
|
||||
switch (frameDuration)
|
||||
{
|
||||
case OpusCodec.FrameDuration.Frame2dot5ms:
|
||||
case OpusCodec.FrameDuration.Frame5ms:
|
||||
if (this.recorder.FrameDuration == OpusCodec.FrameDuration.Frame10ms)
|
||||
{
|
||||
Debug.LogWarningFormat("Frame duration requested ({0}ms) is not supported by WebRTC Audio DSP. Ignoring change.", frameDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("Frame duration requested ({0}ms) is not supported by WebRTC Audio DSP (it needs to be N x 10ms), switching to the closest supported value: {1}ms.", (int)frameDuration / 1000, 10);
|
||||
}
|
||||
frameDuration = OpusCodec.FrameDuration.Frame10ms;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.recorder.SamplingRate = samplingRate;
|
||||
this.recorder.FrameDuration = frameDuration;
|
||||
this.recorder.Bitrate = EditorGUILayout.IntSlider(new GUIContent("Bitrate", "Outgoing audio stream bitrate."), this.recorder.Bitrate, Recorder.MIN_OPUS_BITRATE, Recorder.MAX_OPUS_BITRATE);
|
||||
EditorGUILayout.LabelField("Audio Source Settings", EditorStyles.boldLabel);
|
||||
this.recorder.SourceType = (Recorder.InputSourceType) EditorGUILayout.EnumPopup(new GUIContent("Input Source Type", "Input audio data source type"), this.recorder.SourceType);
|
||||
switch (this.recorder.SourceType)
|
||||
{
|
||||
case Recorder.InputSourceType.Microphone:
|
||||
this.recorder.MicrophoneType = (Recorder.MicType) EditorGUILayout.EnumPopup(
|
||||
new GUIContent("Microphone Type",
|
||||
"Which microphone API to use when the Source is set to UnityMicrophone."),
|
||||
this.recorder.MicrophoneType);
|
||||
EditorGUILayout.PropertyField(this.useMicrophoneTypeFallbackSp, new GUIContent("Use Fallback", "If true, if recording fails to start with Unity microphone type, Photon microphone type is used -if available- as a fallback and vice versa."));
|
||||
switch (this.recorder.MicrophoneType)
|
||||
{
|
||||
case Recorder.MicType.Unity:
|
||||
if (UnityMicrophone.devices.Length == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No microphone device found", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Devices list and current selection is valid in Unity Editor only. In build, you need to set it via code preferably at runtime.", MessageType.Info);
|
||||
this.unityMicrophoneDeviceIndex = EditorGUILayout.Popup("Microphone Device", this.GetUnityMicrophoneDeviceIndex(), UnityMicrophone.devices);
|
||||
this.recorder.UnityMicrophoneDevice = UnityMicrophone.devices[this.unityMicrophoneDeviceIndex];
|
||||
int minFreq, maxFreq;
|
||||
UnityMicrophone.GetDeviceCaps(UnityMicrophone.devices[this.unityMicrophoneDeviceIndex], out minFreq, out maxFreq);
|
||||
EditorGUILayout.LabelField("Microphone Device Caps", string.Format("{0}..{1} Hz", minFreq, maxFreq));
|
||||
}
|
||||
break;
|
||||
case Recorder.MicType.Photon:
|
||||
#if PHOTON_MICROPHONE_ENUMERATOR
|
||||
if (this.recorder.MicrophonesEnumerator.IsSupported)
|
||||
{
|
||||
if (!this.recorder.MicrophonesEnumerator.Any())
|
||||
{
|
||||
EditorGUILayout.HelpBox("No microphone device found", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Devices list and current selection is valid in Unity Editor only. In build, you need to set it via code preferably at runtime.", MessageType.Info);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (this.photonDeviceIndex >= this.photonDeviceNames.Length)
|
||||
{
|
||||
Debug.LogWarningFormat("Unexpected photonDeviceIndex = {0} >= photonDeviceNames.Length = {1}, forcing refresh.", this.photonDeviceIndex, this.photonDeviceNames.Length);
|
||||
this.RefreshMicrophones();
|
||||
}
|
||||
this.photonDeviceIndex = EditorGUILayout.Popup("Microphone Device", this.photonDeviceIndex, this.photonDeviceNames);
|
||||
this.recorder.PhotonMicrophoneDeviceId = this.photonDeviceIDs[this.photonDeviceIndex];
|
||||
if (GUILayout.Button("Refresh", EditorStyles.miniButton, GUILayout.Width(70)))
|
||||
{
|
||||
this.RefreshPhotonMicrophoneDevices();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.recorder.PhotonMicrophoneDeviceId = -1;
|
||||
EditorGUILayout.HelpBox("PhotonMicrophoneEnumerator Not Supported", MessageType.Error);
|
||||
}
|
||||
#endif
|
||||
#if UNITY_IOS
|
||||
EditorGUILayout.LabelField("iOS Audio Session Parameters", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.useCustomAudioSessionParametersSp, new GUIContent("Use Custom"));
|
||||
if (this.useCustomAudioSessionParametersSp.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.audioSessionParametersCategorySp);
|
||||
EditorGUILayout.PropertyField(this.audioSessionParametersModeSp);
|
||||
EditorGUILayout.PropertyField(this.audioSessionParametersCategoryOptionsSp, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = EditorGUILayout.Popup("Preset", this.audioSessionPresetIndexSp.intValue, this.iOSAudioSessionPresetsNames);
|
||||
if (index != this.audioSessionPresetIndexSp.intValue)
|
||||
{
|
||||
this.audioSessionPresetIndexSp.intValue = index;
|
||||
AudioSessionParameters parameters = this.iOSAudioSessionPresetsValues[index];
|
||||
this.SetEnumIndex(this.audioSessionParametersCategorySp,
|
||||
typeof(AudioSessionCategory), parameters.Category);
|
||||
this.SetEnumIndex(this.audioSessionParametersModeSp,
|
||||
typeof(AudioSessionMode), parameters.Mode);
|
||||
if (parameters.CategoryOptions != null)
|
||||
{
|
||||
this.audioSessionParametersCategoryOptionsSp.ClearArray();
|
||||
this.audioSessionParametersCategoryOptionsSp.arraySize =
|
||||
parameters.CategoryOptions.Length;
|
||||
if (index == 0)
|
||||
{
|
||||
this.SetEnumIndex(this.audioSessionParametersCategoryOptionsSp
|
||||
.GetArrayElementAtIndex(0), typeof(AudioSessionCategoryOption), AudioSessionCategoryOption.DefaultToSpeaker);
|
||||
this.SetEnumIndex(this.audioSessionParametersCategoryOptionsSp
|
||||
.GetArrayElementAtIndex(1), typeof(AudioSessionCategoryOption), AudioSessionCategoryOption.AllowBluetooth);
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
this.SetEnumIndex(this.audioSessionParametersCategoryOptionsSp
|
||||
.GetArrayElementAtIndex(0), typeof(AudioSessionCategoryOption), AudioSessionCategoryOption.AllowBluetooth);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
#elif UNITY_ANDROID
|
||||
EditorGUILayout.LabelField("Android Native Microphone Settings", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.nativeAndroidMicrophoneSettingsSp.FindPropertyRelative("AcousticEchoCancellation"));
|
||||
EditorGUILayout.PropertyField(this.nativeAndroidMicrophoneSettingsSp.FindPropertyRelative("AutomaticGainControl"));
|
||||
EditorGUILayout.PropertyField(this.nativeAndroidMicrophoneSettingsSp.FindPropertyRelative("NoiseSuppression"));
|
||||
EditorGUI.indentLevel--;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
break;
|
||||
case Recorder.InputSourceType.AudioClip:
|
||||
this.recorder.AudioClip = EditorGUILayout.ObjectField(new GUIContent("Audio Clip", "Source audio clip."), this.recorder.AudioClip, typeof(AudioClip), false) as AudioClip;
|
||||
this.recorder.LoopAudioClip =
|
||||
EditorGUILayout.Toggle(new GUIContent("Loop", "Loop playback for audio clip sources."),
|
||||
this.recorder.LoopAudioClip);
|
||||
break;
|
||||
case Recorder.InputSourceType.Factory:
|
||||
EditorGUILayout.HelpBox("Add a custom InputFactory method in code.", MessageType.Info);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
EditorGUILayout.LabelField("Voice Activity Detection (VAD)", EditorStyles.boldLabel);
|
||||
if (webRtcAudioDspAttached)
|
||||
{
|
||||
if (webRtcAudioDsp.VAD)
|
||||
{
|
||||
EditorGUILayout.HelpBox("WebRtcAudioDsp.VAD is already enabled no need to use the built-in Recorder VAD", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("It's recommended to use VAD from WebRtcAudioDsp instead of built-in Recorder VAD", MessageType.Info);
|
||||
}
|
||||
}
|
||||
this.recorder.VoiceDetection = EditorGUILayout.Toggle(new GUIContent("Detect", "If true, voice detection enabled."), this.recorder.VoiceDetection);
|
||||
if (this.recorder.VoiceDetection)
|
||||
{
|
||||
if (webRtcAudioDspAttached && !webRtcAudioDsp.VAD && GUILayout.Button("Use WebRtcAudioDsp.VAD instead"))
|
||||
{
|
||||
this.recorder.VoiceDetection = false;
|
||||
webRtcAudioDsp.VAD = true;
|
||||
}
|
||||
this.recorder.VoiceDetectionThreshold =
|
||||
EditorGUILayout.Slider(
|
||||
new GUIContent("Threshold", "Voice detection threshold (0..1, where 1 is full amplitude)."),
|
||||
this.recorder.VoiceDetectionThreshold, 0f, 1f);
|
||||
this.recorder.VoiceDetectionDelayMs =
|
||||
EditorGUILayout.IntField(new GUIContent("Delay (ms)", "Keep detected state during this time after signal level dropped below threshold. Default is 500ms"), this.recorder.VoiceDetectionDelayMs);
|
||||
EditorGUILayout.HelpBox("Do not speak and stay in a silent environment when calibrating.", MessageType.Info);
|
||||
if (this.recorder.VoiceDetectorCalibrating)
|
||||
{
|
||||
EditorGUILayout.LabelField(string.Format("Calibrating {0} ms", this.calibrationTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.calibrationTime = EditorGUILayout.IntField("Calibration Time (ms)", this.calibrationTime);
|
||||
if (this.recorder.IsRecording && this.recorder.TransmitEnabled)
|
||||
{
|
||||
if (GUILayout.Button("Calibrate"))
|
||||
{
|
||||
this.recorder.VoiceDetectorCalibrate(this.calibrationTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!audioChangesHandlerAttached)
|
||||
{
|
||||
#if !UNITY_ANDROID && !UNITY_IOS
|
||||
EditorGUILayout.PropertyField(this.reactOnSystemChangesSp,
|
||||
new GUIContent("React On System Changes",
|
||||
"If true, recording is restarted when Unity detects Audio Config. changes."));
|
||||
if (this.reactOnSystemChangesSp.boolValue)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.skipDeviceChecksSp, new GUIContent("Skip Device Checks", "If true, restarts recording without checking if audio config/device changes affected recording."));
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.HelpBox("ReactOnSystemChanges will be deprecated. AudioChangesHandler component is now the preferred solution to handle audio changes.", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("ReactOnSystemChanges will be deprecated. AudioChangesHandler component is now the preferred solution to handle audio changes.", MessageType.Info);
|
||||
}
|
||||
#endif
|
||||
if (GUILayout.Button("Add AudioChangesHandler component"))
|
||||
{
|
||||
this.recorder.gameObject.AddComponent<AudioChangesHandler>();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.recordOnlyWhenEnabledSp,
|
||||
new GUIContent("Record Only When Enabled",
|
||||
"If true, component will work only when enabled and active in hierarchy."));
|
||||
EditorGUILayout.PropertyField(this.recordOnlyWhenJoinedSp,
|
||||
new GUIContent("Record Only When Joined",
|
||||
"If true, recording can start only when client is joined to a room. Auto start is also delayed until client is joined to a room."));
|
||||
EditorGUILayout.PropertyField(this.stopRecordingWhenPausedSp,
|
||||
new GUIContent("Stop Recording When Paused",
|
||||
"If true, stop recording when paused resume/restart when un-paused."));
|
||||
EditorGUILayout.PropertyField(this.transmitEnabledSp,
|
||||
new GUIContent("Transmit Enabled", "If true, audio transmission is enabled."));
|
||||
EditorGUILayout.PropertyField(this.autoStartSp,
|
||||
new GUIContent("Auto Start", "If true, recording is started when Recorder is initialized."));
|
||||
EditorGUILayout.PropertyField(this.encryptSp,
|
||||
new GUIContent("Encrypt", "If true, voice stream is sent encrypted."));
|
||||
EditorGUILayout.PropertyField(this.interestGroupSp,
|
||||
new GUIContent("Interest Group", "Target interest group that will receive transmitted audio."));
|
||||
if (this.interestGroupSp.intValue == 0)
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.debugEchoModeSp,
|
||||
new GUIContent("Debug Echo",
|
||||
"If true, outgoing stream routed back to client via server same way as for remote client's streams."));
|
||||
}
|
||||
else if (this.debugEchoModeSp.boolValue)
|
||||
{
|
||||
Debug.LogWarningFormat("DebugEchoMode disabled because InterestGroup changed to {0}. DebugEchoMode works only with Interest Group 0.", this.interestGroupSp.intValue);
|
||||
this.debugEchoModeSp.boolValue = false;
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.reliableModeSp, new GUIContent("Reliable Mode",
|
||||
"If true, stream data sent in reliable mode."));
|
||||
|
||||
EditorGUILayout.LabelField("Codec Parameters", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(this.frameDurationSp,
|
||||
new GUIContent("Frame Duration", "Outgoing audio stream encoder delay."));
|
||||
EditorGUILayout.PropertyField(this.samplingRateSp,
|
||||
new GUIContent("Sampling Rate", "Outgoing audio stream sampling rate."));
|
||||
EditorGUILayout.PropertyField(this.bitrateSp,
|
||||
new GUIContent("Bitrate", "Outgoing audio stream bitrate."));
|
||||
//if (webRtcAudioDspAttached)
|
||||
//{
|
||||
// SamplingRate samplingRate = (SamplingRate)Enum.GetValues(typeof(SamplingRate)).GetValue(this.samplingRateSp.enumValueIndex);
|
||||
// if (Array.IndexOf(WebRTCAudioProcessor.SupportedSamplingRates, samplingRate) < 0)
|
||||
// {
|
||||
// Debug.LogWarningFormat("Sampling rate requested ({0}Hz) is not supported by WebRTC Audio DSP, switching to the closest supported value: {1}ms.", samplingRate, "48k");
|
||||
// this.samplingRateSp.enumValueIndex = 4;
|
||||
// }
|
||||
// OpusCodec.FrameDuration frameDuration = (OpusCodec.FrameDuration)Enum.GetValues(typeof(OpusCodec.FrameDuration)).GetValue(this.frameDurationSp.enumValueIndex);
|
||||
// switch (frameDuration)
|
||||
// {
|
||||
// case OpusCodec.FrameDuration.Frame2dot5ms:
|
||||
// case OpusCodec.FrameDuration.Frame5ms:
|
||||
// Debug.LogWarningFormat("Frame duration requested ({0}ms) is not supported by WebRTC Audio DSP (it needs to be N x 10ms), switching to the closest supported value: {1}Hz.", (int)frameDuration / 1000, 10);
|
||||
// this.frameDurationSp.enumValueIndex = 2;
|
||||
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
EditorGUILayout.LabelField("Audio Source Settings", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(this.sourceTypeSp,
|
||||
new GUIContent("Input Source Type", "Input audio data source type"));
|
||||
switch ((Recorder.InputSourceType)this.sourceTypeSp.enumValueIndex)
|
||||
{
|
||||
case Recorder.InputSourceType.Microphone:
|
||||
EditorGUILayout.PropertyField(this.microphoneTypeSp, new GUIContent("Microphone Type",
|
||||
"Which microphone API to use when the Source is set to UnityMicrophone."));
|
||||
EditorGUILayout.PropertyField(this.useMicrophoneTypeFallbackSp, new GUIContent("Use Fallback", "If true, if recording fails to start with Unity microphone type, Photon microphone type is used -if available- as a fallback and vice versa."));
|
||||
switch (this.recorder.MicrophoneType)
|
||||
{
|
||||
case Recorder.MicType.Unity:
|
||||
break;
|
||||
case Recorder.MicType.Photon:
|
||||
#if PHOTON_MICROPHONE_ENUMERATOR
|
||||
if (this.recorder.MicrophonesEnumerator.IsSupported)
|
||||
{
|
||||
if (!this.recorder.MicrophonesEnumerator.Any())
|
||||
{
|
||||
EditorGUILayout.HelpBox("No microphone device found", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (this.photonDeviceIndex >= this.photonDeviceNames.Length)
|
||||
{
|
||||
Debug.LogWarningFormat("Unexpected photonDeviceIndex = {0} >= photonDeviceNames.Length = {1}, forcing refresh.", this.photonDeviceIndex, this.photonDeviceNames.Length);
|
||||
this.RefreshMicrophones();
|
||||
}
|
||||
this.photonDeviceIndex = EditorGUILayout.Popup("Microphone Device", this.photonDeviceIndex, this.photonDeviceNames);
|
||||
this.photonMicrophoneDeviceIdSp.intValue = this.photonDeviceIDs[this.photonDeviceIndex];
|
||||
if (GUILayout.Button("Refresh", EditorStyles.miniButton, GUILayout.Width(70)))
|
||||
{
|
||||
this.RefreshPhotonMicrophoneDevices();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.photonMicrophoneDeviceIdSp.intValue = -1;
|
||||
EditorGUILayout.HelpBox("PhotonMicrophoneEnumerator Not Supported", MessageType.Error);
|
||||
}
|
||||
#endif
|
||||
#if UNITY_IOS
|
||||
EditorGUILayout.LabelField("iOS Audio Session Parameters", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.useCustomAudioSessionParametersSp, new GUIContent("Use Custom"));
|
||||
if (this.useCustomAudioSessionParametersSp.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.audioSessionParametersCategorySp);
|
||||
EditorGUILayout.PropertyField(this.audioSessionParametersModeSp);
|
||||
EditorGUILayout.PropertyField(this.audioSessionParametersCategoryOptionsSp, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = EditorGUILayout.Popup("Preset", this.audioSessionPresetIndexSp.intValue, this.iOSAudioSessionPresetsNames);
|
||||
if (index != this.audioSessionPresetIndexSp.intValue)
|
||||
{
|
||||
this.audioSessionPresetIndexSp.intValue = index;
|
||||
AudioSessionParameters parameters = this.iOSAudioSessionPresetsValues[index];
|
||||
this.SetEnumIndex(this.audioSessionParametersCategorySp,
|
||||
typeof(AudioSessionCategory), parameters.Category);
|
||||
this.SetEnumIndex(this.audioSessionParametersModeSp,
|
||||
typeof(AudioSessionMode), parameters.Mode);
|
||||
if (parameters.CategoryOptions != null)
|
||||
{
|
||||
this.audioSessionParametersCategoryOptionsSp.ClearArray();
|
||||
this.audioSessionParametersCategoryOptionsSp.arraySize =
|
||||
parameters.CategoryOptions.Length;
|
||||
if (index == 0)
|
||||
{
|
||||
this.SetEnumIndex(this.audioSessionParametersCategoryOptionsSp
|
||||
.GetArrayElementAtIndex(0), typeof(AudioSessionCategoryOption), AudioSessionCategoryOption.DefaultToSpeaker);
|
||||
this.SetEnumIndex(this.audioSessionParametersCategoryOptionsSp
|
||||
.GetArrayElementAtIndex(1), typeof(AudioSessionCategoryOption), AudioSessionCategoryOption.AllowBluetooth);
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
this.SetEnumIndex(this.audioSessionParametersCategoryOptionsSp
|
||||
.GetArrayElementAtIndex(0), typeof(AudioSessionCategoryOption), AudioSessionCategoryOption.AllowBluetooth);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
#elif UNITY_ANDROID
|
||||
EditorGUILayout.LabelField("Android Native Microphone Settings", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(this.nativeAndroidMicrophoneSettingsSp.FindPropertyRelative("AcousticEchoCancellation"));
|
||||
EditorGUILayout.PropertyField(this.nativeAndroidMicrophoneSettingsSp.FindPropertyRelative("AutomaticGainControl"));
|
||||
EditorGUILayout.PropertyField(this.nativeAndroidMicrophoneSettingsSp.FindPropertyRelative("NoiseSuppression"));
|
||||
EditorGUI.indentLevel--;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
break;
|
||||
case Recorder.InputSourceType.AudioClip:
|
||||
EditorGUILayout.PropertyField(this.audioClipSp,
|
||||
new GUIContent("Audio Clip", "Source audio clip."));
|
||||
EditorGUILayout.PropertyField(this.loopAudioClipSp,
|
||||
new GUIContent("Loop", "Loop playback for audio clip sources."));
|
||||
break;
|
||||
case Recorder.InputSourceType.Factory:
|
||||
EditorGUILayout.HelpBox("Add a custom InputFactory method in code.", MessageType.Info);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
EditorGUILayout.LabelField("Voice Activity Detection (VAD)", EditorStyles.boldLabel);
|
||||
if (webRtcAudioDspAttached)
|
||||
{
|
||||
if (webRtcAudioDsp.VAD)
|
||||
{
|
||||
EditorGUILayout.HelpBox("WebRtcAudioDsp.VAD is already enabled no need to use the built-in Recorder VAD", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("It's recommended to use VAD from WebRtcAudioDsp instead of built-in Recorder VAD", MessageType.Info);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.voiceDetectionSp,
|
||||
new GUIContent("Detect", "If true, voice detection enabled."));
|
||||
if (this.voiceDetectionSp.boolValue)
|
||||
{
|
||||
if (webRtcAudioDspAttached && !webRtcAudioDsp.VAD && GUILayout.Button("Use WebRtcAudioDsp.VAD instead"))
|
||||
{
|
||||
this.recorder.VoiceDetection = false;
|
||||
webRtcAudioDsp.VAD = true;
|
||||
}
|
||||
this.voiceDetectionThresholdSp.floatValue = EditorGUILayout.Slider(
|
||||
new GUIContent("Threshold", "Voice detection threshold (0..1, where 1 is full amplitude)."),
|
||||
this.voiceDetectionThresholdSp.floatValue, 0f, 1f);
|
||||
this.voiceDetectionDelayMsSp.intValue =
|
||||
EditorGUILayout.IntField(new GUIContent("Delay (ms)", "Keep detected state during this time after signal level dropped below threshold. Default is 500ms"), this.voiceDetectionDelayMsSp.intValue);
|
||||
}
|
||||
}
|
||||
|
||||
#if WEBRTC_AUDIO_DSP_SUPPORTED_PLATFORMS
|
||||
if (!webRtcAudioDspAttached)
|
||||
{
|
||||
if (GUILayout.Button("Add WebRtcAudioDsp component"))
|
||||
{
|
||||
this.recorder.gameObject.AddComponent<WebRtcAudioDsp>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
this.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAudioConfigChanged(bool deviceWasChanged)
|
||||
{
|
||||
if (deviceWasChanged)
|
||||
{
|
||||
this.RefreshMicrophones();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshMicrophones()
|
||||
{
|
||||
if (UnityMicrophone.devices.Length == 0)
|
||||
{
|
||||
this.unityMicrophoneDeviceIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.unityMicrophoneDeviceIndex = Mathf.Clamp(ArrayUtility.IndexOf(UnityMicrophone.devices, this.recorder.UnityMicrophoneDevice), 0, UnityMicrophone.devices.Length - 1);
|
||||
}
|
||||
#if PHOTON_MICROPHONE_ENUMERATOR
|
||||
this.RefreshPhotonMicrophoneDevices();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PHOTON_MICROPHONE_ENUMERATOR
|
||||
private void RefreshPhotonMicrophoneDevices()
|
||||
{
|
||||
if (this.recorder.MicrophonesEnumerator.IsSupported)
|
||||
{
|
||||
this.recorder.MicrophonesEnumerator.Refresh();
|
||||
int count = this.recorder.MicrophonesEnumerator.Count();
|
||||
if (count == 0)
|
||||
{
|
||||
this.recorder.PhotonMicrophoneDeviceId = -1;
|
||||
this.photonDeviceNames = null;
|
||||
this.photonDeviceIDs = null;
|
||||
this.photonDeviceIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.photonDeviceNames = new string[count];
|
||||
this.photonDeviceIDs = new int[count];
|
||||
int i = 0;
|
||||
foreach (DeviceInfo deviceInfo in this.recorder.MicrophonesEnumerator)
|
||||
{
|
||||
this.photonDeviceIDs[i] = deviceInfo.IDInt;
|
||||
this.photonDeviceNames[i] = string.Format("{0} - {1} [{2}]", i, deviceInfo.Name.Replace('/', '_'), deviceInfo.IDInt);
|
||||
i++;
|
||||
}
|
||||
this.photonDeviceIndex = Mathf.Clamp(Array.IndexOf(this.photonDeviceIDs, this.recorder.PhotonMicrophoneDeviceId), 0, count - 1);
|
||||
this.recorder.PhotonMicrophoneDeviceId = this.photonDeviceIDs[this.photonDeviceIndex];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.recorder.PhotonMicrophoneDeviceId = -1;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_IOS
|
||||
private void SetEnumIndex(SerializedProperty property, Type enumType, object enumValue)
|
||||
{
|
||||
string enumName = Enum.GetName(enumType, enumValue);
|
||||
int index = Array.IndexOf(property.enumNames, enumName);
|
||||
if (index >= 0)
|
||||
{
|
||||
property.enumValueIndex = index;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private int GetUnityMicrophoneDeviceIndex()
|
||||
{
|
||||
if (this.unityMicrophoneDeviceIndex == 0 && !Recorder.IsDefaultUnityMic(this.recorder.UnityMicrophoneDevice) ||
|
||||
this.unityMicrophoneDeviceIndex > 0 && this.unityMicrophoneDeviceIndex < UnityMicrophone.devices.Length &&
|
||||
!Recorder.CompareUnityMicNames(UnityMicrophone.devices[this.unityMicrophoneDeviceIndex], this.recorder.UnityMicrophoneDevice))
|
||||
{
|
||||
int newIndex = Array.IndexOf(UnityMicrophone.devices, this.recorder.UnityMicrophoneDevice);
|
||||
if (newIndex >= 0)
|
||||
{
|
||||
return newIndex;
|
||||
}
|
||||
}
|
||||
return this.unityMicrophoneDeviceIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Photon/PhotonVoice/Code/Editor/RecorderEditor.cs.meta
Normal file
12
Assets/Photon/PhotonVoice/Code/Editor/RecorderEditor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ded9902b49048f04d8d9788d6a740eb7
|
||||
timeCreated: 1525441327
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
79
Assets/Photon/PhotonVoice/Code/Editor/SpeakerEditor.cs
Normal file
79
Assets/Photon/PhotonVoice/Code/Editor/SpeakerEditor.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
namespace Photon.Voice.Unity.Editor
|
||||
{
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Unity;
|
||||
|
||||
[CustomEditor(typeof(Speaker))]
|
||||
public class SpeakerEditor : Editor
|
||||
{
|
||||
private Speaker speaker;
|
||||
|
||||
private SerializedProperty playbackDelaySettingsSp;
|
||||
private SerializedProperty playbackOnlyWhenEnabledSp;
|
||||
|
||||
#region AnimationCurve
|
||||
|
||||
private AudioSource audioSource;
|
||||
private FFTWindow window = FFTWindow.Hanning;
|
||||
private float[] samples = new float[512];
|
||||
private AnimationCurve curve;
|
||||
|
||||
private void DrawAnimationCurve()
|
||||
{
|
||||
this.audioSource.GetSpectrumData(this.samples, 0, this.window);
|
||||
this.curve = new AnimationCurve();
|
||||
for (var i = 0; i < this.samples.Length; i++)
|
||||
{
|
||||
this.curve.AddKey(1.0f / this.samples.Length * i, this.samples[i] * 100);
|
||||
}
|
||||
EditorGUILayout.CurveField(this.curve, Color.green, new Rect(0, 0, 1.0f, 0.1f), GUILayout.Height(64));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
this.speaker = this.target as Speaker;
|
||||
this.audioSource = this.speaker.GetComponent<AudioSource>();
|
||||
this.playbackDelaySettingsSp = this.serializedObject.FindProperty("playbackDelaySettings");
|
||||
this.playbackOnlyWhenEnabledSp = this.serializedObject.FindProperty("playbackOnlyWhenEnabled");
|
||||
}
|
||||
|
||||
public override bool RequiresConstantRepaint()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
this.serializedObject.UpdateIfRequiredOrScript();
|
||||
VoiceLogger.ExposeLogLevel(this.serializedObject, this.speaker);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(this.playbackDelaySettingsSp, new GUIContent("Playback Delay Settings", "Remote audio stream playback delay to compensate packets latency variations."), true);
|
||||
if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.speaker.gameObject))
|
||||
{
|
||||
this.speaker.SetPlaybackDelaySettings(this.playbackDelaySettingsSp.FindPropertyRelative("MinDelaySoft").intValue, this.playbackDelaySettingsSp.FindPropertyRelative("MaxDelaySoft").intValue, this.playbackDelaySettingsSp.FindPropertyRelative("MaxDelayHard").intValue);
|
||||
this.speaker.PlaybackOnlyWhenEnabled = EditorGUILayout.Toggle(new GUIContent("Playback Only When Enabled", "If true, component will work only when enabled and active in hierarchy."),
|
||||
this.speaker.PlaybackOnlyWhenEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.playbackOnlyWhenEnabledSp, new GUIContent("Playback Only When Enabled", "If true, component will work only when enabled and active in hierarchy."));
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
this.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
if (this.speaker.IsPlaying)
|
||||
{
|
||||
EditorGUILayout.LabelField(string.Format("Current Buffer Lag: {0}", this.speaker.Lag));
|
||||
this.DrawAnimationCurve();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Photon/PhotonVoice/Code/Editor/SpeakerEditor.cs.meta
Normal file
12
Assets/Photon/PhotonVoice/Code/Editor/SpeakerEditor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10964642c52ad634da4de24866140e2d
|
||||
timeCreated: 1525867684
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
520
Assets/Photon/PhotonVoice/Code/Editor/VoiceConnectionEditor.cs
Normal file
520
Assets/Photon/PhotonVoice/Code/Editor/VoiceConnectionEditor.cs
Normal file
@@ -0,0 +1,520 @@
|
||||
//#define DEBUG_DISCARD
|
||||
|
||||
namespace Photon.Voice.Unity.Editor
|
||||
{
|
||||
using ExitGames.Client.Photon;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Unity;
|
||||
using Realtime;
|
||||
|
||||
[CustomEditor(typeof(VoiceConnection))]
|
||||
public class VoiceConnectionEditor : Editor
|
||||
{
|
||||
private VoiceConnection connection;
|
||||
|
||||
private SerializedProperty updateIntervalSp;
|
||||
private SerializedProperty enableSupportLoggerSp;
|
||||
private SerializedProperty settingsSp;
|
||||
#if !UNITY_ANDROID && !UNITY_IOS
|
||||
private SerializedProperty runInBackground;
|
||||
#endif
|
||||
#if !UNITY_IOS
|
||||
private SerializedProperty keepAliveInBackgroundSp;
|
||||
#endif
|
||||
private SerializedProperty applyDontDestroyOnLoadSp;
|
||||
private SerializedProperty statsResetInterval;
|
||||
private SerializedProperty primaryRecorderSp;
|
||||
private SerializedProperty speakerPrefabSp;
|
||||
private SerializedProperty autoCreateSpeakerIfNotFoundSp;
|
||||
private SerializedProperty globalRecordersLogLevelSp;
|
||||
private SerializedProperty globalSpeakersLogLevelSp;
|
||||
private SerializedProperty globalPlayDelaySettingsSp;
|
||||
|
||||
private const string notAvailable = "N/A?";
|
||||
protected string photonLibraryVersion;
|
||||
protected string photonVoiceVersion;
|
||||
protected string punChangelogVersion;
|
||||
protected string photonVoiceApiVersion;
|
||||
protected bool versionFoldout;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
this.connection = this.target as VoiceConnection;
|
||||
this.updateIntervalSp = this.serializedObject.FindProperty("updateInterval");
|
||||
this.enableSupportLoggerSp = this.serializedObject.FindProperty("enableSupportLogger");
|
||||
this.settingsSp = this.serializedObject.FindProperty("Settings");
|
||||
#if !UNITY_ANDROID && !UNITY_IOS
|
||||
this.runInBackground = this.serializedObject.FindProperty("runInBackground");
|
||||
#endif
|
||||
#if !UNITY_IOS
|
||||
this.keepAliveInBackgroundSp = this.serializedObject.FindProperty("KeepAliveInBackground");
|
||||
#endif
|
||||
this.applyDontDestroyOnLoadSp = this.serializedObject.FindProperty("ApplyDontDestroyOnLoad");
|
||||
this.statsResetInterval = this.serializedObject.FindProperty("statsResetInterval");
|
||||
this.primaryRecorderSp = this.serializedObject.FindProperty("primaryRecorder");
|
||||
if (this.primaryRecorderSp == null) // [FormerlySerializedAs("PrimaryRecorder")]
|
||||
{
|
||||
this.primaryRecorderSp = this.serializedObject.FindProperty("PrimaryRecorder");
|
||||
}
|
||||
this.speakerPrefabSp = this.serializedObject.FindProperty("speakerPrefab");
|
||||
this.autoCreateSpeakerIfNotFoundSp = this.serializedObject.FindProperty("AutoCreateSpeakerIfNotFound");
|
||||
this.globalRecordersLogLevelSp = this.serializedObject.FindProperty("globalRecordersLogLevel");
|
||||
this.globalSpeakersLogLevelSp = this.serializedObject.FindProperty("globalSpeakersLogLevel");
|
||||
this.globalPlayDelaySettingsSp = this.serializedObject.FindProperty("globalPlaybackDelaySettings");
|
||||
|
||||
PhotonVoiceEditorUtils.GetPhotonVoiceVersionsFromChangeLog(out this.photonVoiceVersion, out this.punChangelogVersion, out this.photonVoiceApiVersion);
|
||||
this.photonLibraryVersion = System.Reflection.Assembly.GetAssembly(typeof(ExitGames.Client.Photon.PhotonPeer)).GetName().Version.ToString();
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
this.ShowHeader();
|
||||
this.serializedObject.UpdateIfRequiredOrScript();
|
||||
|
||||
VoiceLogger.ExposeLogLevel(this.serializedObject, this.connection);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
this.connection.GlobalRecordersLogLevel = VoiceLogger.ExposeLogLevel(this.globalRecordersLogLevelSp);
|
||||
this.connection.GlobalSpeakersLogLevel = VoiceLogger.ExposeLogLevel(this.globalSpeakersLogLevelSp);
|
||||
EditorGUILayout.PropertyField(this.autoCreateSpeakerIfNotFoundSp, new GUIContent("Create Speaker If Not Found", "Auto instantiate a GameObject and attach a Speaker component to link to a remote audio stream if no candidate could be foun"));
|
||||
EditorGUILayout.PropertyField(this.updateIntervalSp, new GUIContent("Update Interval (ms)", "time [ms] between consecutive SendOutgoingCommands calls"));
|
||||
if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.connection.gameObject))
|
||||
{
|
||||
this.connection.PrimaryRecorder = EditorGUILayout.ObjectField(
|
||||
new GUIContent("Primary Recorder", "Main Recorder to be used for transmission by default"),
|
||||
this.connection.PrimaryRecorder, typeof(Recorder), true) as Recorder;
|
||||
if (this.connection.SpeakerPrefab == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Speaker prefab needs to have a Speaker component in the hierarchy.", MessageType.Info);
|
||||
}
|
||||
this.connection.SpeakerPrefab = EditorGUILayout.ObjectField(new GUIContent("Speaker Prefab",
|
||||
"Prefab that contains Speaker component to be instantiated when receiving a new remote audio source info"), this.connection.SpeakerPrefab,
|
||||
typeof(GameObject), false) as GameObject;
|
||||
EditorGUILayout.PropertyField(this.globalPlayDelaySettingsSp, new GUIContent("Global Playback Delay Configuration", "Remote audio stream playback delay to compensate packets latency variations."), true);
|
||||
this.connection.SetGlobalPlaybackDelaySettings(
|
||||
this.globalPlayDelaySettingsSp.FindPropertyRelative("MinDelaySoft").intValue,
|
||||
this.globalPlayDelaySettingsSp.FindPropertyRelative("MaxDelaySoft").intValue,
|
||||
this.globalPlayDelaySettingsSp.FindPropertyRelative("MaxDelayHard").intValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.enableSupportLoggerSp, new GUIContent("Support Logger", "Logs additional info for debugging.\nUse this when you submit bugs to the Photon Team."));
|
||||
#if !UNITY_ANDROID && !UNITY_IOS
|
||||
EditorGUILayout.PropertyField(this.runInBackground, new GUIContent("Run In Background", "Sets Unity's Application.runInBackground: Should the application keep running when the application is in the background?"));
|
||||
#endif
|
||||
#if !UNITY_IOS
|
||||
EditorGUILayout.PropertyField(this.keepAliveInBackgroundSp, new GUIContent("Background Timeout (ms)", "Defines for how long the Fallback Thread should keep the connection, before it may time out as usual."));
|
||||
#endif
|
||||
EditorGUILayout.PropertyField(this.applyDontDestroyOnLoadSp, new GUIContent("Don't Destroy On Load", "Persists the GameObject across scenes using Unity's GameObject.DontDestroyOnLoad"));
|
||||
if (this.applyDontDestroyOnLoadSp.boolValue && !PhotonVoiceEditorUtils.IsPrefab(this.connection.gameObject))
|
||||
{
|
||||
if (this.connection.transform.parent != null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("DontDestroyOnLoad only works for root GameObjects or components on root GameObjects.", MessageType.Warning);
|
||||
if (GUILayout.Button("Detach"))
|
||||
{
|
||||
this.connection.transform.parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.primaryRecorderSp,
|
||||
new GUIContent("Primary Recorder", "Main Recorder to be used for transmission by default"));
|
||||
GameObject prefab = this.speakerPrefabSp.objectReferenceValue as GameObject;
|
||||
if (prefab == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Speaker prefab needs to have a Speaker component in the hierarchy.", MessageType.Info);
|
||||
}
|
||||
prefab = EditorGUILayout.ObjectField(new GUIContent("Speaker Prefab",
|
||||
"Prefab that contains Speaker component to be instantiated when receiving a new remote audio source info"), prefab,
|
||||
typeof(GameObject), false) as GameObject;
|
||||
if (prefab == null || prefab.GetComponentInChildren<Speaker>() != null)
|
||||
{
|
||||
this.speakerPrefabSp.objectReferenceValue = prefab;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("SpeakerPrefab must have a component of type Speaker in its hierarchy.", this);
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.globalPlayDelaySettingsSp, new GUIContent("Global Playback Delay Settings", "Remote audio stream playback delay to compensate packets latency variations."), true);
|
||||
}
|
||||
if (!this.connection.Client.IsConnected)
|
||||
{
|
||||
this.DisplayAppSettings();
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.statsResetInterval, new GUIContent("Stats Reset Interval (ms)", "time [ms] between statistics calculations"));
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
this.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
if (PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.connection.gameObject))
|
||||
{
|
||||
this.DisplayVoiceStats();
|
||||
this.DisplayDebugInfo(this.connection.Client);
|
||||
this.DisplayCachedVoiceInfo();
|
||||
this.DisplayTrafficStats(this.connection.Client.LoadBalancingPeer);
|
||||
}
|
||||
}
|
||||
|
||||
private bool showVoiceStats;
|
||||
private bool showPlayersList;
|
||||
private bool showDebugInfo;
|
||||
private bool showCachedVoices;
|
||||
private bool showTrafficStats;
|
||||
|
||||
protected virtual void DisplayVoiceStats()
|
||||
{
|
||||
this.showVoiceStats =
|
||||
EditorGUILayout.Foldout(this.showVoiceStats, new GUIContent("Voice Frames Stats", "Show frames stats"));
|
||||
if (this.showVoiceStats)
|
||||
{
|
||||
this.DrawLabel("Frames Received /s", this.connection.FramesReceivedPerSecond.ToString());
|
||||
this.DrawLabel("Frames Lost /s", this.connection.FramesLostPerSecond.ToString());
|
||||
this.DrawLabel("Frames Lost %", this.connection.FramesLostPercent.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void DisplayDebugInfo(LoadBalancingClient client)
|
||||
{
|
||||
this.showDebugInfo = EditorGUILayout.Foldout(this.showDebugInfo, new GUIContent("Client Debug Info", "Debug info for Photon client"));
|
||||
if (this.showDebugInfo)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
this.DrawLabel("Client State", client.State.ToString());
|
||||
if (!string.IsNullOrEmpty(client.AppId))
|
||||
{
|
||||
this.DrawLabel("AppId", client.AppId);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(client.AppVersion))
|
||||
{
|
||||
this.DrawLabel("AppVersion", client.AppVersion);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(client.CloudRegion))
|
||||
{
|
||||
this.DrawLabel("Current Cloud Region", client.CloudRegion);
|
||||
}
|
||||
if (client.IsConnected)
|
||||
{
|
||||
this.DrawLabel("Current Server Address", client.CurrentServerAddress);
|
||||
}
|
||||
if (client.InRoom)
|
||||
{
|
||||
this.DrawLabel("Room Name", client.CurrentRoom.Name);
|
||||
this.showPlayersList = EditorGUILayout.Foldout(this.showPlayersList, new GUIContent("Players List", "List of players joined to the room"));
|
||||
if (this.showPlayersList)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
foreach (Player player in client.CurrentRoom.Players.Values)
|
||||
{
|
||||
this.DisplayPlayerDebugInfo(player);
|
||||
EditorGUILayout.LabelField(string.Empty, GUI.skin.horizontalSlider);
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void DisplayPlayerDebugInfo(Player player)
|
||||
{
|
||||
this.DrawLabel("Actor Number", player.ActorNumber.ToString());
|
||||
if (!string.IsNullOrEmpty(player.UserId))
|
||||
{
|
||||
this.DrawLabel("UserId", player.UserId);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(player.NickName))
|
||||
{
|
||||
this.DrawLabel("NickName", player.NickName);
|
||||
}
|
||||
if (player.IsMasterClient)
|
||||
{
|
||||
EditorGUILayout.LabelField("Master Client");
|
||||
}
|
||||
if (player.IsLocal)
|
||||
{
|
||||
EditorGUILayout.LabelField("Local");
|
||||
}
|
||||
if (player.IsInactive)
|
||||
{
|
||||
EditorGUILayout.LabelField("Inactive");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void DisplayCachedVoiceInfo()
|
||||
{
|
||||
this.showCachedVoices =
|
||||
EditorGUILayout.Foldout(this.showCachedVoices, new GUIContent("Cached Remote Voices' Info", "Show remote voices info cached by local client"));
|
||||
if (this.showCachedVoices)
|
||||
{
|
||||
List<RemoteVoiceLink> cachedVoices = this.connection.CachedRemoteVoices;
|
||||
Speaker[] speakers = FindObjectsOfType<Speaker>();
|
||||
for (int i = 0; i < cachedVoices.Count; i++)
|
||||
{
|
||||
//VoiceInfo info = cachedVoices[i].Info;
|
||||
EditorGUI.indentLevel++;
|
||||
this.DrawLabel("Voice #", cachedVoices[i].VoiceId.ToString());
|
||||
this.DrawLabel("Player #", cachedVoices[i].PlayerId.ToString());
|
||||
this.DrawLabel("Channel #", cachedVoices[i].ChannelId.ToString());
|
||||
if (cachedVoices[i].Info.UserData != null)
|
||||
{
|
||||
this.DrawLabel("UserData: ", cachedVoices[i].Info.UserData.ToString());
|
||||
}
|
||||
bool linked = false;
|
||||
for (int j = 0; j < speakers.Length; j++)
|
||||
{
|
||||
Speaker speaker = speakers[j];
|
||||
if (speaker.IsLinked && speaker.RemoteVoiceLink.PlayerId == cachedVoices[i].PlayerId &&
|
||||
speaker.RemoteVoiceLink.VoiceId == cachedVoices[i].VoiceId)
|
||||
{
|
||||
linked = true;
|
||||
EditorGUILayout.ObjectField(new GUIContent("Linked Speaker"), speaker, typeof(Speaker), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!linked)
|
||||
{
|
||||
EditorGUILayout.LabelField("Not Linked");
|
||||
}
|
||||
EditorGUILayout.LabelField(string.Empty, GUI.skin.horizontalSlider);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_DISCARD
|
||||
private int maxDeltaUnreliableNumber;
|
||||
private int maxCountDiscarded;
|
||||
#endif
|
||||
|
||||
// inspired by PhotonVoiceStatsGui.TrafficStatsWindow
|
||||
protected virtual void DisplayTrafficStats(LoadBalancingPeer peer)
|
||||
{
|
||||
this.showTrafficStats = EditorGUILayout.Foldout(this.showTrafficStats, new GUIContent("Traffic Stats", "Traffic Statistics for Photon Client"));
|
||||
if (this.showTrafficStats)
|
||||
{
|
||||
#if DEBUG_DISCARD
|
||||
if (peer.DeltaUnreliableNumber > this.maxDeltaUnreliableNumber) this.maxDeltaUnreliableNumber = peer.DeltaUnreliableNumber;
|
||||
if (peer.CountDiscarded > this.maxCountDiscarded) this.maxCountDiscarded = peer.CountDiscarded;
|
||||
GUILayout.Label(string.Format("Discarded: {0} (max: {1}) UnreliableDelta: {2} (max: {3})",peer.CountDiscarded, this.maxCountDiscarded, peer.DeltaUnreliableNumber, maxDeltaUnreliableNumber));
|
||||
#endif
|
||||
GUILayout.Label(string.Format("RTT (ping): {0}[+/-{1}]ms, last={2}ms", peer.RoundTripTime, peer.RoundTripTimeVariance, peer.LastRoundTripTime));
|
||||
//GUILayout.Label(string.Format("{0}ms since last ACK sent, {1}ms since last sent, {2}ms since last received", peer.ConnectionTime - peer.LastSendAckTime, peer.ConnectionTime - peer.LastSendOutgoingTime, peer.ConnectionTime - peer.TimestampOfLastSocketReceive)); //add
|
||||
GUILayout.Label(string.Format("Reliable Commands Resent: {0}", peer.ResentReliableCommands));
|
||||
//GUILayout.Label(string.Format("last operation={0}B current dispatch:{1}B", peer.ByteCountLastOperation, peer.ByteCountCurrentDispatch));
|
||||
//GUILayout.Label(string.Format("Packets Lost: by challenge={0} by CRC={1}", peer.PacketLossByChallenge, peer.PacketLossByCrc));
|
||||
//GUILayout.Label(string.Format("Total Traffic: In={0} - {1} Out={2} - {3}", this.FormatSize(peer.BytesIn, ti:string.Empty), this.FormatSize(this.connection.BytesReceivedPerSecond), this.FormatSize(peer.BytesOut, ti:string.Empty), this.FormatSize(this.connection.BytesSentPerSecond)));
|
||||
GUILayout.Label(string.Format("Total Traffic: In={0} Out={1}", this.FormatSize(peer.BytesIn, ti:string.Empty), this.FormatSize(peer.BytesOut, ti:string.Empty)));
|
||||
peer.TrafficStatsEnabled = EditorGUILayout.Toggle(new GUIContent("Advanced", "Enable or disable traffic Statistics for Photon Peer"), peer.TrafficStatsEnabled);
|
||||
if (peer.TrafficStatsEnabled)
|
||||
{
|
||||
long elapsedSeconds = peer.TrafficStatsElapsedMs / 1000;
|
||||
if (elapsedSeconds == 0)
|
||||
{
|
||||
elapsedSeconds = 1;
|
||||
}
|
||||
GUILayout.Label(string.Format("Time elapsed: {0} seconds", elapsedSeconds));
|
||||
this.DisplayTrafficStatsGameLevel(peer.TrafficStatsGameLevel, elapsedSeconds);
|
||||
TrafficStats trafficStats = peer.TrafficStatsIncoming;
|
||||
GUILayout.Label(string.Format("Protocol: {0} Package Header Size={1}B", peer.TransportProtocol, trafficStats.PackageHeaderSize));
|
||||
EditorGUILayout.LabelField("Commands/Packets Incoming", EditorStyles.boldLabel);
|
||||
this.DisplayTrafficStats(/*peer, */trafficStats, elapsedSeconds);
|
||||
EditorGUILayout.LabelField("Commands/Packets Outgoing", EditorStyles.boldLabel);
|
||||
trafficStats = peer.TrafficStatsOutgoing;
|
||||
this.DisplayTrafficStats(/*peer, */trafficStats, elapsedSeconds);
|
||||
if (GUILayout.Button("Reset"))
|
||||
{
|
||||
peer.TrafficStatsReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayTrafficStats(/*PhotonPeer peer, */TrafficStats trafficStats, long elapsedSeconds)
|
||||
{
|
||||
GUILayout.Label(string.Format("\tControl Commands: #={0} a#={1}/s s={2} as={3}", trafficStats.ControlCommandCount, trafficStats.ControlCommandCount/elapsedSeconds, this.FormatSize(trafficStats.ControlCommandBytes, ti:string.Empty), this.FormatSize(trafficStats.ControlCommandBytes/elapsedSeconds)));
|
||||
GUILayout.Label(string.Format("\tFragment Commands: #={0} a#={1}/s s={2} as={3}", trafficStats.FragmentCommandCount, trafficStats.FragmentCommandCount/elapsedSeconds, this.FormatSize(trafficStats.FragmentCommandBytes, ti:string.Empty), this.FormatSize(trafficStats.FragmentCommandBytes/elapsedSeconds)));
|
||||
GUILayout.Label(string.Format("\tReliable Commands: #={0} a#={1}/s s={2} as={3}", trafficStats.ReliableCommandCount, trafficStats.ReliableCommandCount/elapsedSeconds, this.FormatSize(trafficStats.ReliableCommandBytes, ti:string.Empty), this.FormatSize(trafficStats.ReliableCommandCount/elapsedSeconds)));
|
||||
GUILayout.Label(string.Format("\tUnreliable Commands: #={0} a#={1}/s s={2} as={3}", trafficStats.UnreliableCommandCount, trafficStats.UnreliableCommandCount/elapsedSeconds, this.FormatSize(trafficStats.UnreliableCommandBytes, ti:string.Empty), this.FormatSize(trafficStats.UnreliableCommandBytes/elapsedSeconds)));
|
||||
GUILayout.Label(string.Format("\tTotal Commands: #={0} a#={1}/s s={2} as={3}", trafficStats.TotalCommandCount, trafficStats.TotalCommandCount/elapsedSeconds, this.FormatSize(trafficStats.TotalCommandBytes, ti:string.Empty), this.FormatSize(trafficStats.TotalCommandBytes/elapsedSeconds)));
|
||||
GUILayout.Label(string.Format("\tTotal Packets: #={0} a#={1}/s s={2} as={3}", trafficStats.TotalPacketCount, trafficStats.TotalPacketCount/elapsedSeconds, this.FormatSize(trafficStats.TotalPacketBytes, ti:string.Empty), this.FormatSize(trafficStats.TotalPacketBytes/elapsedSeconds)));
|
||||
GUILayout.Label(string.Format("\tTotal Commands in Packets: {0}", trafficStats.TotalCommandsInPackets));
|
||||
//GUILayout.Label(string.Format("\t{0}ms since last ACK", peer.ConnectionTime - trafficStats.TimestampOfLastAck));
|
||||
//GUILayout.Label(string.Format("\t{0} ms since last reliable Command", peer.ConnectionTime - trafficStats.TimestampOfLastReliableCommand));
|
||||
}
|
||||
|
||||
private void DisplayTrafficStatsGameLevel(TrafficStatsGameLevel gls, long elapsedSeconds)
|
||||
{
|
||||
GUILayout.Label("In Game", EditorStyles.boldLabel);
|
||||
GUILayout.Label(string.Format("\tmax. delta between\n\t\tsend: {0,4}ms \n\t\tdispatch: {1,4}ms \n\tlongest dispatch for: \n\t\tev({3}):{2,3}ms \n\t\top({5}):{4,3}ms",
|
||||
gls.LongestDeltaBetweenSending,
|
||||
gls.LongestDeltaBetweenDispatching,
|
||||
gls.LongestEventCallback,
|
||||
gls.LongestEventCallbackCode,
|
||||
gls.LongestOpResponseCallback,
|
||||
gls.LongestOpResponseCallbackOpCode));
|
||||
GUILayout.Label("\tMessages", EditorStyles.boldLabel);
|
||||
GUILayout.Label(string.Format("\t\tTotal: Out {0,4}msg | In {1,4}msg | Sum {2,4}msg",
|
||||
gls.TotalOutgoingMessageCount,
|
||||
gls.TotalIncomingMessageCount,
|
||||
gls.TotalMessageCount));
|
||||
GUILayout.Label(string.Format("\t\tAverage: Out {0,4}msg/s | In {1,4}msg/s | Sum {2,4}msg/s",
|
||||
gls.TotalOutgoingMessageCount / elapsedSeconds,
|
||||
gls.TotalIncomingMessageCount / elapsedSeconds,
|
||||
gls.TotalMessageCount / elapsedSeconds));
|
||||
}
|
||||
|
||||
private void DrawLabel(string prefix, string text)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel(prefix);
|
||||
EditorGUILayout.LabelField(text);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected virtual void DisplayAppSettings()
|
||||
{
|
||||
this.connection.ShowSettings = EditorGUILayout.Foldout(this.connection.ShowSettings, new GUIContent("App Settings", "Settings to be used by this voice connection"));
|
||||
if (this.connection.ShowSettings)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
SerializedProperty sP = this.settingsSp.FindPropertyRelative("AppIdVoice");
|
||||
EditorGUILayout.PropertyField(sP);
|
||||
string appId = sP.stringValue;
|
||||
string url = "https://dashboard.photonengine.com/en-US/PublicCloud";
|
||||
if (!string.IsNullOrEmpty(appId))
|
||||
{
|
||||
url = string.Concat("https://dashboard.photonengine.com/en-US/App/Manage/", appId);
|
||||
}
|
||||
if (GUILayout.Button("Dashboard", EditorStyles.miniButton, GUILayout.Width(70)))
|
||||
{
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("AppVersion"));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("UseNameServer"), new GUIContent("Use Name Server", "Photon Cloud requires this checked.\nUncheck for Photon Server SDK (OnPremises)."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("FixedRegion"), new GUIContent("Fixed Region", "Photon Cloud setting, needs a Name Server.\nDefine one region to always connect to.\nLeave empty to use the best region from a server-side region list."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("Server"), new GUIContent("Server", "Typically empty for Photon Cloud.\nFor Photon Server, enter your host name or IP. Also uncheck \"Use Name Server\" for older Photon Server versions."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("Port"), new GUIContent("Port", "Use 0 for Photon Cloud.\nOnPremise uses 5055 for UDP and 4530 for TCP."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("ProxyServer"), new GUIContent("Proxy Server", "HTTP Proxy Server for WebSocket connection. See LoadBalancingClient.ProxyServerAddress for options."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("Protocol"), new GUIContent("Protocol", "Use UDP where possible.\nWSS works on WebGL and Xbox exports.\nDefine WEBSOCKET for use on other platforms."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("EnableProtocolFallback"), new GUIContent("Protocol Fallback", "Automatically try another network protocol, if initial connect fails.\nWill use default Name Server ports."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("EnableLobbyStatistics"), new GUIContent("Lobby Statistics", "When using multiple room lists (lobbies), the server can send info about their usage."));
|
||||
EditorGUILayout.PropertyField(this.settingsSp.FindPropertyRelative("NetworkLogging"), new GUIContent("Network Logging", "Log level for the Photon libraries."));
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
#region Best Region Box
|
||||
|
||||
GUIStyle verticalBoxStyle = new GUIStyle("HelpBox") { padding = new RectOffset(6, 6, 6, 6) };
|
||||
EditorGUILayout.BeginVertical(verticalBoxStyle);
|
||||
|
||||
string prefLabel;
|
||||
const string notAvailableLabel = "n/a";
|
||||
string bestRegionSummaryInPrefs = this.connection.BestRegionSummaryInPreferences;
|
||||
if (!string.IsNullOrEmpty(bestRegionSummaryInPrefs))
|
||||
{
|
||||
string[] regionsPrefsList = bestRegionSummaryInPrefs.Split(';');
|
||||
if (regionsPrefsList.Length < 2 || string.IsNullOrEmpty(regionsPrefsList[0]) || string.IsNullOrEmpty(regionsPrefsList[1]))
|
||||
{
|
||||
prefLabel = notAvailableLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
prefLabel = string.Format("'{0}' ping:{1}ms ", regionsPrefsList[0], regionsPrefsList[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prefLabel = notAvailableLabel;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent(string.Concat("Best Region Preference: ", prefLabel), "Best region is used if Fixed Region is empty."));
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
Rect resetRect = EditorGUILayout.GetControlRect(GUILayout.MinWidth(64));
|
||||
Rect editRect = EditorGUILayout.GetControlRect(GUILayout.MinWidth(64));
|
||||
if (GUI.Button(resetRect, "Reset", EditorStyles.miniButton))
|
||||
{
|
||||
this.connection.BestRegionSummaryInPreferences = null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(appId) && GUI.Button(editRect, "Edit Regions WhiteList", EditorStyles.miniButton))
|
||||
{
|
||||
url = string.Concat("https://dashboard.photonengine.com/en-US/App/RegionsWhitelistEdit/", appId);
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
#endregion Best Region Box
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ShowHeader()
|
||||
{
|
||||
this.ShowAssetVersionsFoldout();
|
||||
}
|
||||
|
||||
protected virtual void ShowAssetVersions()
|
||||
{
|
||||
EditorGUILayout.LabelField(string.Format("Photon Voice: {0}", this.GetVersionString(this.photonVoiceVersion)));
|
||||
EditorGUILayout.LabelField(string.Format("Photon Voice API: {0}", this.GetVersionString(this.photonVoiceApiVersion)));
|
||||
EditorGUILayout.LabelField(string.Format("Photon Realtime and Unity Library: {0}", this.GetVersionString(this.photonLibraryVersion)));
|
||||
}
|
||||
|
||||
private void ShowAssetVersionsFoldout()
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
this.versionFoldout = EditorGUILayout.Foldout(this.versionFoldout, "Asset Version Info");
|
||||
if (this.versionFoldout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.BeginVertical();
|
||||
this.ShowAssetVersions();
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
protected string GetVersionString(string versionString)
|
||||
{
|
||||
return string.IsNullOrEmpty(versionString) ? notAvailable : versionString;
|
||||
}
|
||||
|
||||
private string FormatSize(float bytes, string u = "B", string ti = "/s")
|
||||
{
|
||||
const long kb = 1024;
|
||||
const long mb = kb * 1024;
|
||||
const long gb = mb * 1024;
|
||||
const long tb = gb * 1024;
|
||||
long m = 1;
|
||||
if (bytes >= tb)
|
||||
{
|
||||
m = tb;
|
||||
u = "TB";
|
||||
}
|
||||
else if (bytes >= gb)
|
||||
{
|
||||
m = gb;
|
||||
u = "GB";
|
||||
}
|
||||
else if (bytes >= mb)
|
||||
{
|
||||
m = mb;
|
||||
u = "MB";
|
||||
}
|
||||
else if (bytes >= kb)
|
||||
{
|
||||
m = kb;
|
||||
u = "KB";
|
||||
}
|
||||
return string.Format("{0:0.0}{1}{2}", bytes/m, u, ti);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9496980b6639f4cb68abaed8fdda658e
|
||||
timeCreated: 1537180258
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
192
Assets/Photon/PhotonVoice/Code/Editor/WebRtcAudioDspEditor.cs
Normal file
192
Assets/Photon/PhotonVoice/Code/Editor/WebRtcAudioDspEditor.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
#if UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_IOS || UNITY_ANDROID || UNITY_WSA
|
||||
#define WEBRTC_AUDIO_DSP_SUPPORTED_PLATFORMS
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR_WIN || UNITY_EDITOR_OSX
|
||||
#define WEBRTC_AUDIO_DSP_SUPPORTED_EDITOR
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Photon.Voice.Unity.Editor
|
||||
{
|
||||
using UnityEditor;
|
||||
using Unity;
|
||||
|
||||
[CustomEditor(typeof(WebRtcAudioDsp))]
|
||||
public class WebRtcAudioDspEditor : Editor
|
||||
{
|
||||
private WebRtcAudioDsp processor;
|
||||
private Recorder recorder;
|
||||
|
||||
private SerializedProperty aecSp;
|
||||
private SerializedProperty aecHighPassSp;
|
||||
private SerializedProperty agcSp;
|
||||
private SerializedProperty agcCompressionGainSp;
|
||||
private SerializedProperty vadSp;
|
||||
private SerializedProperty highPassSp;
|
||||
private SerializedProperty bypassSp;
|
||||
private SerializedProperty noiseSuppressionSp;
|
||||
private SerializedProperty reverseStreamDelayMsSp;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
this.processor = this.target as WebRtcAudioDsp;
|
||||
this.recorder = this.processor.GetComponent<Recorder>();
|
||||
this.aecSp = this.serializedObject.FindProperty("aec");
|
||||
this.aecHighPassSp = this.serializedObject.FindProperty("aecHighPass");
|
||||
this.agcSp = this.serializedObject.FindProperty("agc");
|
||||
this.agcCompressionGainSp = this.serializedObject.FindProperty("agcCompressionGain");
|
||||
this.vadSp = this.serializedObject.FindProperty("vad");
|
||||
this.highPassSp = this.serializedObject.FindProperty("highPass");
|
||||
this.bypassSp = this.serializedObject.FindProperty("bypass");
|
||||
this.noiseSuppressionSp = this.serializedObject.FindProperty("noiseSuppression");
|
||||
this.reverseStreamDelayMsSp = this.serializedObject.FindProperty("reverseStreamDelayMs");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
this.serializedObject.UpdateIfRequiredOrScript();
|
||||
if (!PhotonVoiceEditorUtils.IsPrefab(this.processor.gameObject))
|
||||
{
|
||||
#if WEBRTC_AUDIO_DSP_SUPPORTED_PLATFORMS
|
||||
#elif WEBRTC_AUDIO_DSP_SUPPORTED_EDITOR
|
||||
string message = string.Format("WebRtcAudioDsp is not supported on this target platform {0}. The component will be disabled in build.", EditorUserBuildSettings.activeBuildTarget);
|
||||
EditorGUILayout.HelpBox(message, MessageType.Warning);
|
||||
#else
|
||||
string message = string.Format("WebRtcAudioDsp is not supported on this target platform {0}. This component is disabled.", EditorUserBuildSettings.activeBuildTarget);
|
||||
EditorGUILayout.HelpBox(message, MessageType.Warning);
|
||||
#endif
|
||||
}
|
||||
if (!this.processor.isActiveAndEnabled && this.processor.AecOnlyWhenEnabled && this.aecSp.boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("WebRtcAudioDsp is not enabled, AEC will not be used.", MessageType.Warning);
|
||||
}
|
||||
if (this.recorder != null && this.recorder.SourceType != Recorder.InputSourceType.Microphone)
|
||||
{
|
||||
EditorGUILayout.HelpBox("WebRtcAudioDsp is better suited to be used with Microphone as Recorder Input Source Type.", MessageType.Warning);
|
||||
}
|
||||
VoiceLogger.ExposeLogLevel(this.serializedObject, this.processor);
|
||||
bool bypassed;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool isInSceneInPlayMode = PhotonVoiceEditorUtils.IsInTheSceneInPlayMode(this.processor.gameObject);
|
||||
if (isInSceneInPlayMode)
|
||||
{
|
||||
this.processor.Bypass = EditorGUILayout.Toggle(new GUIContent("Bypass", "Bypass WebRTC Audio DSP"), this.processor.Bypass);
|
||||
bypassed = this.processor.Bypass;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.bypassSp, new GUIContent("Bypass", "Bypass WebRTC Audio DSP"));
|
||||
bypassed = this.bypassSp.boolValue;
|
||||
}
|
||||
#if UNITY_ANDROID
|
||||
SerializedObject serializedObject = new SerializedObject(this.recorder);
|
||||
SerializedProperty serializedProperty = serializedObject.FindProperty("nativeAndroidMicrophoneSettings");
|
||||
#endif
|
||||
if (!bypassed)
|
||||
{
|
||||
if (isInSceneInPlayMode)
|
||||
{
|
||||
this.processor.AEC = EditorGUILayout.Toggle(new GUIContent("AEC", "Acoustic Echo Cancellation"), this.processor.AEC);
|
||||
if (this.processor.AEC)
|
||||
{
|
||||
if (this.recorder.SourceType == Recorder.InputSourceType.Microphone && this.recorder.MicrophoneType == Recorder.MicType.Photon)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (serializedProperty.FindPropertyRelative("AcousticEchoCancellation").boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled AEC here and are using a Photon Mic as input on the Recorder, which might add its own echo cancellation. Please use only one AEC algorithm.", MessageType.Warning);
|
||||
}
|
||||
#else
|
||||
EditorGUILayout.HelpBox("You have enabled AEC here and are using a Photon Mic as input on the Recorder, which might add its own echo cancellation. Please use only one AEC algorithm.", MessageType.Warning);
|
||||
#endif
|
||||
}
|
||||
this.processor.ReverseStreamDelayMs = EditorGUILayout.IntField(new GUIContent("ReverseStreamDelayMs", "Reverse stream delay (hint for AEC) in Milliseconds"), this.processor.ReverseStreamDelayMs);
|
||||
this.processor.AecHighPass = EditorGUILayout.Toggle(new GUIContent("AEC High Pass"), this.processor.AecHighPass);
|
||||
}
|
||||
this.processor.AGC = EditorGUILayout.Toggle(new GUIContent("AGC", "Automatic Gain Control"), this.processor.AGC);
|
||||
if (this.processor.AGC)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (serializedProperty.FindPropertyRelative("AutomaticGainControl").boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled AGC here and are using a AGC from native plugin (Photon microphone type). Please use only one AGC algorithm.", MessageType.Warning);
|
||||
}
|
||||
#endif
|
||||
this.processor.AgcCompressionGain = EditorGUILayout.IntField(new GUIContent("AGC Compression Gain"), this.processor.AgcCompressionGain);
|
||||
}
|
||||
if (this.processor.VAD && this.recorder.VoiceDetection)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled VAD here and in the associated Recorder. Please use only one Voice Detection algorithm.", MessageType.Warning);
|
||||
}
|
||||
this.processor.VAD = EditorGUILayout.Toggle(new GUIContent("VAD", "Voice Activity Detection"), this.processor.VAD);
|
||||
this.processor.HighPass = EditorGUILayout.Toggle(new GUIContent("HighPass", "High Pass Filter"), this.processor.HighPass);
|
||||
this.processor.NoiseSuppression = EditorGUILayout.Toggle(new GUIContent("NoiseSuppression", "Noise Suppression"), this.processor.NoiseSuppression);
|
||||
if (this.processor.NoiseSuppression)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (serializedProperty.FindPropertyRelative("NoiseSuppression").boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled NS here and are using a NS from native plugin (Photon microphone type). Please use only one NS algorithm.", MessageType.Warning);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.PropertyField(this.aecSp, new GUIContent("AEC", "Acoustic Echo Cancellation"));
|
||||
if (this.aecSp.boolValue)
|
||||
{
|
||||
if (this.recorder.SourceType == Recorder.InputSourceType.Microphone && this.recorder.MicrophoneType == Recorder.MicType.Photon)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (serializedProperty.FindPropertyRelative("AcousticEchoCancellation").boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled AEC here and are using AEC from native plugin (Photon microphone type). Please use only one AEC algorithm.", MessageType.Warning);
|
||||
}
|
||||
#else
|
||||
EditorGUILayout.HelpBox("You have enabled AEC here and are using a Photon Mic as input on the Recorder, which might add its own echo cancellation. Please use only one AEC algorithm.", MessageType.Warning);
|
||||
#endif
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.reverseStreamDelayMsSp,
|
||||
new GUIContent("ReverseStreamDelayMs", "Reverse stream delay (hint for AEC) in Milliseconds"));
|
||||
EditorGUILayout.PropertyField(this.aecHighPassSp, new GUIContent("AEC High Pass"));
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.agcSp, new GUIContent("AGC", "Automatic Gain Control"));
|
||||
if (this.agcSp.boolValue)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (serializedProperty.FindPropertyRelative("AutomaticGainControl").boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled AGC here and are using a AGC from native plugin (Photon microphone type). Please use only one AGC algorithm.", MessageType.Warning);
|
||||
}
|
||||
#endif
|
||||
EditorGUILayout.PropertyField(this.agcCompressionGainSp, new GUIContent("AGC Compression Gain"));
|
||||
}
|
||||
if (this.vadSp.boolValue && this.recorder.VoiceDetection)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled VAD here and in the associated Recorder. Please use only one Voice Detection algorithm.", MessageType.Warning);
|
||||
}
|
||||
EditorGUILayout.PropertyField(this.vadSp, new GUIContent("VAD", "Voice Activity Detection"));
|
||||
EditorGUILayout.PropertyField(this.highPassSp, new GUIContent("HighPass", "High Pass Filter"));
|
||||
EditorGUILayout.PropertyField(this.noiseSuppressionSp, new GUIContent("NoiseSuppression", "Noise Suppression"));
|
||||
if (this.noiseSuppressionSp.boolValue)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (serializedProperty.FindPropertyRelative("NoiseSuppression").boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("You have enabled NS here and are using a NS from native plugin (Photon microphone type). Please use only one NS algorithm.", MessageType.Warning);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
this.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82948294db79e4c318c9b8815781f0d3
|
||||
timeCreated: 1538475591
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user