public AudioDeviceDataReader(MicrophoneDevice microphone)
        {
            this.microphone = microphone;

            // Initialize read and write head positions to the current position of the microphone write head.
            _previousLocalWriteHeadPosition = microphone.deviceWriteHeadPosition;
            _writeHeadLoopCount             = 0;
            _readHeadPosition = microphone.deviceWriteHeadPosition;
        }
예제 #2
0
        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;
            }
        }
예제 #3
0
        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 + ").");
                    }
                }
            }
        }