#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_SWITCH || UNITY_IOS #define PHOTON_AUDIO_CHANGE_IN_NOTIFIER #endif namespace Photon.Voice.Unity { using Voice; using UnityEngine; /// /// This component is useful to handle audio device and config changes. /// [RequireComponent(typeof(Recorder))] public class AudioChangesHandler : VoiceComponent { private IAudioInChangeNotifier photonMicChangeNotifier; private AudioConfiguration audioConfiguration; private Recorder recorder; /// /// Try to start recording when we get devices change notification and recording is not started. /// /// /// On some platforms we can't make sure that a device change notification could mean that at least a microphone device is now available. /// Besides, the auto start of the recording might not happen if other necessary conditions set in Recorder are not met: /// e.g. or etc. /// or if the Recorder has been stopped explicitly via call or set to false. /// [Tooltip("Try to start recording when we get devices change notification and recording is not started.")] public bool StartWhenDeviceChange = true; /// /// Try to react to device change notification when Recorder is started. /// /// /// This requires or to be true. /// [Tooltip("Try to react to device change notification when Recorder is started.")] public bool HandleDeviceChange = true; /// /// Try to react to audio config change notification when Recorder is started. /// /// /// This requires to be true. /// [Tooltip("Try to react to audio config change notification when Recorder is started.")] public bool HandleConfigChange = true; /// /// Whether or not to make use of Photon's AudioInChangeNotifier native plugin. /// /// /// This may disable if this and are both false. /// [Tooltip("Whether or not to make use of Photon's AudioInChangeNotifier native plugin.")] public bool UseNativePluginChangeNotifier = true; /// /// Whether or not to make use of Unity's OnAudioConfigurationChanged. /// /// /// This is needed for and may also disable /// if this and are both false. /// [Tooltip("Whether or not to make use of Unity's OnAudioConfigurationChanged.")] public bool UseOnAudioConfigurationChanged = true; #if UNITY_EDITOR || UNITY_ANDROID /// /// If the recorder is set to use microphone as source with type Photon, audio device changes are handled within the native plugin by default. /// If you set this to true, it will also be handled via this component logic. /// [Tooltip("If the recorder is set to use microphone as source with type Photon, audio device changes are handled within the native plugin by default. " + "If you set this to true, it will also be handled via this component logic.")] public bool Android_AlwaysHandleDeviceChange; #endif #if UNITY_EDITOR || UNITY_IOS /// /// If the recorder is set to use microphone as source with type Photon, audio device changes are handled within the native plugin by default. /// If you set this to true, it will also be handled via this component logic. /// [Tooltip("If the recorder is set to use microphone as source with type Photon, audio device changes are handled within the native plugin by default. " + "If you set this to true, it will also be handled via this component logic.")] public bool iOS_AlwaysHandleDeviceChange; #endif private bool subscribedToSystemChangesPhoton, subscribedToSystemChangesUnity; protected override void Awake() { base.Awake(); this.recorder = this.GetComponent(); this.recorder.ReactOnSystemChanges = false; //if (this.recorder.AutoStart) { // if (this.AutoStartWhenMicrophoneMightBeAvailable && this.CheckIfMicrophoneIsAvailable) { // this.recorder.AutoStart = false; // if (this.micAvailable) { // this.recorder.CheckAndAutoStart(true); // } // } else if (this.recorder.IsRecording) { // this.recorder.AutoStart = false; // } //} this.audioConfiguration = AudioSettings.GetConfiguration(); this.SubscribeToSystemChanges(); } private void OnDestroy() { this.UnsubscribeFromSystemChanges(); } #if PHOTON_AUDIO_CHANGE_IN_NOTIFIER private void PhotonMicrophoneChangeDetected() { if (this.Logger.IsDebugEnabled) { this.Logger.LogDebug("Microphones change detected by Photon native plugin."); } if (!this.recorder.MicrophoneDeviceChangeDetected && this.UseNativePluginChangeNotifier) { this.OnDeviceChange(); } } #endif private void OnDeviceChange() { if (!this.recorder.IsRecording) { if (this.StartWhenDeviceChange) { this.recorder.MicrophoneDeviceChangeDetected = true; if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("An attempt to auto start recording should follow shortly."); } } else if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Device change detected but will not try to start recording as StartWhenDeviceChange is false."); } } else if (this.HandleDeviceChange) { #if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS) if (this.recorder.SourceType == Recorder.InputSourceType.Microphone && this.recorder.MicrophoneType == Recorder.MicType.Photon) { #if UNITY_ANDROID if (!this.Android_AlwaysHandleDeviceChange) { #elif UNITY_IOS if (!this.iOS_AlwaysHandleDeviceChange) { #endif if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Device change notification ignored when using Photon microphone type as this is handled internally for iOS and Android via native plugins."); } return; } } #endif this.recorder.MicrophoneDeviceChangeDetected = true; } else if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Device change detected but will not try to handle this as HandleDeviceChange is false."); } } private void SubscribeToSystemChanges() { if (this.Logger.IsDebugEnabled) { this.Logger.LogDebug("Subscribing to system (audio) changes."); } #if PHOTON_AUDIO_CHANGE_IN_NOTIFIER if (this.subscribedToSystemChangesPhoton) { if (this.Logger.IsWarningEnabled) { this.Logger.LogWarning("Already subscribed to audio changes via Photon."); } } else { this.photonMicChangeNotifier = Platform.CreateAudioInChangeNotifier(this.PhotonMicrophoneChangeDetected, this.Logger); if (this.photonMicChangeNotifier.IsSupported) { if (this.photonMicChangeNotifier.Error == null) { this.subscribedToSystemChangesPhoton = true; if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Subscribed to audio in change notifications via Photon plugin."); } } if (this.Logger.IsErrorEnabled) { this.Logger.LogError("Error creating instance of photonMicChangeNotifier: {0}", this.photonMicChangeNotifier.Error); } } else if (this.Logger.IsErrorEnabled) { this.Logger.LogError("Unexpected: Photon's AudioInChangeNotifier not supported on current platform: {0}", CurrentPlatform); } if (!this.subscribedToSystemChangesPhoton) { this.photonMicChangeNotifier.Dispose(); this.photonMicChangeNotifier = null; } } #else if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Skipped subscribing to audio change notifications via Photon's AudioInChangeNotifier as not supported on current platform: {0}", CurrentPlatform); } if (this.subscribedToSystemChangesPhoton) { if (this.Logger.IsErrorEnabled) { this.Logger.LogError("Unexpected: subscribedToSystemChangesPhoton is set to true while platform is not supported!."); } } #endif if (this.subscribedToSystemChangesUnity) { if (this.Logger.IsWarningEnabled) { this.Logger.LogWarning("Already subscribed to audio changes via Unity OnAudioConfigurationChanged callback."); } } else { AudioSettings.OnAudioConfigurationChanged += this.OnAudioConfigChanged; this.subscribedToSystemChangesUnity = true; if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Subscribed to audio configuration changes via Unity OnAudioConfigurationChanged callback."); } } } private void OnAudioConfigChanged(bool deviceWasChanged) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("OnAudioConfigurationChanged: {0}", deviceWasChanged ? "Device was changed." : "AudioSettings.Reset was called."); } AudioConfiguration config = AudioSettings.GetConfiguration(); bool audioConfigChanged = false; if (config.dspBufferSize != this.audioConfiguration.dspBufferSize) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("OnAudioConfigurationChanged: dspBufferSize old={0} new={1}", this.audioConfiguration.dspBufferSize, config.dspBufferSize); } audioConfigChanged = true; } if (config.numRealVoices != this.audioConfiguration.numRealVoices) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("OnAudioConfigurationChanged: numRealVoices old={0} new={1}", this.audioConfiguration.numRealVoices, config.numRealVoices); } audioConfigChanged = true; } if (config.numVirtualVoices != this.audioConfiguration.numVirtualVoices) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("OnAudioConfigurationChanged: numVirtualVoices old={0} new={1}", this.audioConfiguration.numVirtualVoices, config.numVirtualVoices); } audioConfigChanged = true; } if (config.sampleRate != this.audioConfiguration.sampleRate) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("OnAudioConfigurationChanged: sampleRate old={0} new={1}", this.audioConfiguration.sampleRate, config.sampleRate); } audioConfigChanged = true; } if (config.speakerMode != this.audioConfiguration.speakerMode) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("OnAudioConfigurationChanged: speakerMode old={0} new={1}", this.audioConfiguration.speakerMode, config.speakerMode); } audioConfigChanged = true; } if (audioConfigChanged) { this.audioConfiguration = config; } if (!this.recorder.MicrophoneDeviceChangeDetected) { if (audioConfigChanged) { if (this.recorder.IsRecording) { if (this.HandleConfigChange) { if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Config change detected; an attempt to auto start recording should follow shortly."); } this.recorder.MicrophoneDeviceChangeDetected = true; } else if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Config change detected but will not try to handle this as HandleConfigChange is false."); } } else if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Config change detected but ignored as recording not started."); } } else if (deviceWasChanged) { if (this.UseOnAudioConfigurationChanged) { this.OnDeviceChange(); } else if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Device change detected but will not try to handle this as UseOnAudioConfigurationChanged is false."); } } } } private void UnsubscribeFromSystemChanges() { if (this.subscribedToSystemChangesUnity) { AudioSettings.OnAudioConfigurationChanged -= this.OnAudioConfigChanged; this.subscribedToSystemChangesUnity = false; if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Unsubscribed from audio changes via Unity OnAudioConfigurationChanged callback."); } } if (this.subscribedToSystemChangesPhoton) { if (this.photonMicChangeNotifier == null) { if (this.Logger.IsErrorEnabled) { this.Logger.LogError("Unexpected: photonMicChangeNotifier is null while subscribedToSystemChangesPhoton is true."); } } else { this.photonMicChangeNotifier.Dispose(); this.photonMicChangeNotifier = null; } this.subscribedToSystemChangesPhoton = false; if (this.Logger.IsInfoEnabled) { this.Logger.LogInfo("Unsubscribed from audio in change notifications via Photon plugin."); } } } } }