void DisconnectAudioStream() { if (_microphoneStream != null) { // Destroy AudioPreprocessorPlaybackListener if (_audioPreprocessorPlaybackListener != null) { Destroy(_audioPreprocessorPlaybackListener); _audioPreprocessorPlaybackListener = null; } // Dispose of audio preprocessor if (_audioPreprocessor != null) { _audioPreprocessor.Dispose(); _audioPreprocessor = null; } // Close microphone stream _microphoneStream.Close(); // Dispose microphone device if (_oculusMicrophoneDevice != null) { _oculusMicrophoneDevice.Stop(); _oculusMicrophoneDevice.Dispose(); _oculusMicrophoneDevice = null; } if (_nativeMicrophoneDevice != null) { _nativeMicrophoneDevice.Stop(); _nativeMicrophoneDevice.Dispose(); _nativeMicrophoneDevice = null; } if (_unityMicrophoneDevice != null) { _unityMicrophoneDevice.Dispose(); _unityMicrophoneDevice = null; } // Clean up _unityMicrophoneDeviceDataReader = null; _microphoneStream = null; } // Remove audio output if (_audioOutput != null) { _audioOutput.Stop(); Destroy(_audioOutput); _audioOutput = null; } }
void ConnectAudioStream() { // Delete the old audio stream DisconnectAudioStream(); if (_model == null) { return; } if (isOwnedLocally) { // Local player, create microphone stream // First check if this platform supports our native microphone wrapper (lower latency + native echo cancellation if available) _microphoneSampleRate = 48000; _microphoneChannels = 1; // Check for Oculus native microphone device API bool foundOculusMicrophoneDevice = false; if (OculusMicrophoneDevice.IsOculusPlatformAvailable()) { foundOculusMicrophoneDevice = OculusMicrophoneDevice.IsOculusPlatformInitialized(); if (!foundOculusMicrophoneDevice && Application.platform == RuntimePlatform.Android) { Debug.LogWarning("Normcore: Oculus Platform SDK found, but it's not initialized. Oculus Quest native echo cancellation will be unavailable."); } } if (foundOculusMicrophoneDevice) { // Create Oculus microphone device _oculusMicrophoneDevice = new OculusMicrophoneDevice(); _oculusMicrophoneDevice.Start(); _microphoneSampleRate = 48000; _microphoneChannels = 1; } else if (Native.Microphone.PlatformSupported()) { _nativeMicrophoneDevice = new Native.Microphone(); // If we failed to connect to the local microphone, bail. if (!_nativeMicrophoneDevice.Start()) { Debug.LogError("Failed to connect to default microphone device. Make sure it is plugged in and functioning properly."); _nativeMicrophoneDevice.Dispose(); _nativeMicrophoneDevice = null; return; } _microphoneSampleRate = _nativeMicrophoneDevice.SampleRate(); _microphoneChannels = _nativeMicrophoneDevice.Channels(); } else { // Create a microphone device _unityMicrophoneDevice = MicrophoneDevice.Start(""); // If we failed to connect to the local microphone, bail. if (_unityMicrophoneDevice == null) { Debug.LogError("Failed to connect to default microphone device. Make sure it is plugged in and functioning properly."); return; } _unityMicrophoneDeviceDataReader = new AudioDeviceDataReader(_unityMicrophoneDevice); _microphoneSampleRate = _unityMicrophoneDevice.sampleRate; _microphoneChannels = _unityMicrophoneDevice.numberOfChannels; } // Compute frame size with the sample rate of the microphone we received _microphoneFrameSize = _microphoneSampleRate / 100; // Create microphone stream with this sample rate (stream will automatically resample to 48000 before encoding with OPUS) _microphoneStream = room.CreateAudioInputStream(true, _microphoneSampleRate, _microphoneChannels); // Audio Preprocessor bool createAudioPreprocessor = Application.platform != RuntimePlatform.IPhonePlayer; // Create it for all platforms except iOS. iOS provides a nice built-in one. if (createAudioPreprocessor) { // Turn on echo cancellation for mobile devices; bool echoCancellation = Application.isMobilePlatform && Application.platform != RuntimePlatform.IPhonePlayer; _audioPreprocessor = new AudioPreprocessor(_microphoneSampleRate, _microphoneFrameSize, // Input stream true, // Automatic gain control true, // Noise suppression true, // Reverb suppression echoCancellation, AudioSettings.outputSampleRate, 2, 0.28f); // Echo cancellation if (echoCancellation) { // Find the audio listener in the scene so we can perform echo cancellation with it AudioListener[] audioListeners = FindObjectsOfType <AudioListener>(); if (audioListeners.Length <= 0) { Debug.LogWarning("RealtimeAvatarVoice: Unable to find any AudioListeners in the scene. RealtimeAvatarVoice will not be able to perform echo cancellation."); } else { AudioListener audioListener = audioListeners[0]; if (audioListeners.Length > 1) { Debug.LogWarning("RealtimeAvatarVoice: Multiple AudioListeners found in the scene. Performing echo cancellation with the first one: " + audioListener.gameObject.name); } _audioPreprocessorPlaybackListener = audioListener.gameObject.AddComponent <AudioPreprocessorPlaybackListener>(); _audioPreprocessorPlaybackListener.audioPreprocessor = _audioPreprocessor; } } } } else { // Remote player, lookup audio stream and create audio output int clientID = _model.clientID; int streamID = _model.streamID; if (clientID >= 0 && streamID >= 0) { // Find AudioOutputStream AudioOutputStream audioOutputStream = room.GetAudioOutputStream(clientID, streamID); if (audioOutputStream != null) { _audioOutput = gameObject.AddComponent <AudioOutput>(); _audioOutput.mute = mute; _audioOutput.StartWithAudioOutputStream(audioOutputStream); } else { Debug.LogError("RealtimeAvatarVoice: Unable to find matching audio stream for avatar (clientID: " + clientID + ", streamID: " + streamID + ")."); } } } }