1
0
forked from cgvr/DeltaVR

non-vr lobby, version fix

This commit is contained in:
joonasp
2022-06-29 14:45:17 +03:00
parent 5774be9822
commit 04baadfad1
1774 changed files with 573069 additions and 1533 deletions

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a6ec05c84a196c429f5e48d3a770c22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
{
"name": "PhotonVoice.Editor",
"references": [
"PhotonRealtime",
"PhotonVoice",
"PhotonVoice.API"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ae8024269c736ee49ba1179cb00214e5
timeCreated: 1538045250
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4e837e30d97d9fd4a984a83cc6db75a7
timeCreated: 1550755478
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}

View 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:

View 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();
}
}
}
}

View 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:

View 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);
}
}
}

View File

@@ -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:

View 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();
}
}
}
}

View File

@@ -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: