/// <summary>
        /// Immediately stop the entire transmission system
        /// </summary>
        private void StopTransmissionPipeline()
        {
            //Stop microphone
            if (_microphone != null && _microphone.IsRecording)
            {
                _microphone.StopCapture();
            }

            //Dispose preprocessor and encoder
            if (_preprocessor != null)
            {
                if (_microphone != null)
                {
                    _microphone.Unsubscribe(_preprocessor);
                }
                if (_encoder != null)
                {
                    _preprocessor.Unsubscribe(_encoder);
                }

                _preprocessor.Dispose();
                _preprocessor = null;
            }

            if (_encoder != null)
            {
                _encoder.Dispose();
                _encoder = null;
            }

            _encoderSubscribed = false;
        }
        /// <summary>
        /// (Re)Start the transmission pipeline, getting to a state where we *can* send voice (but aren't yet)
        /// </summary>
        private void RestartTransmissionPipeline(string reason)
        {
            Log.Debug("Restarting transmission pipeline: '{0}'", reason);

            StopTransmissionPipeline();

            //Clear the flag for requesting an explicit reset.
            //We're about to apply a reset so the request will be satisfied.
            _pendingResetRequest = false;

            //No point starting a transmission pipeline if the network is not a client
            if (_network == null || !_network.Mode.IsClientEnabled())
            {
                return;
            }

            //Create new mic capture system
            var format = _microphone.StartCapture(_micName);

            //If we created a mic (can be null if e.g. there is no mic)
            if (format != null)
            {
                //Close and re-open all channels, propogating this restart to the receiving end
                _roomChannels.Refresh();
                _playerChannels.Refresh();

                //Create preprocessor and subscribe it to microphone (webrtc preprocessor always wants audio to drive VAD+AEC)
                _preprocessor = CreatePreprocessor(format);
                _preprocessor.UpstreamLatency = _microphone.Latency;
                _preprocessor.Start();
                _microphone.Subscribe(_preprocessor);

                //Sub VAD listeners to preprocessor
                for (var i = 0; i < _activationListeners.Count; i++)
                {
                    _preprocessor.Subscribe(_activationListeners[i]);
                }

                //Sub audio listeners to the preprocessor output
                for (var i = 0; i < _audioListeners.Count; i++)
                {
                    var al = _audioListeners[i];
                    al.Reset();
                    _preprocessor.Subscribe(al);
                }

                //Create encoder (not yet subscribed to receive audio data, we'll do that later)
                Log.AssertAndThrowPossibleBug(_network != null, "5F33336B-15B5-4A85-9B54-54352C74768E", "Network object is unexpectedly null");
                _encoder = new EncoderPipeline(_preprocessor.OutputFormat, _codecSettingsLoader.CreateEncoder(), _network);
            }
            else
            {
                Log.Warn("Failed to start microphone capture; local voice transmission will be disabled.");
            }
        }
        /// <summary>
        /// Immediately stop the entire transmission system
        /// </summary>
        private void StopTransmissionPipeline()
        {
#if !NCRUNCH
            Profiler.BeginSample("CapturePipelineManager: StopTransmissionPipeline");
#endif

            //Stop microphone
            if (_microphone != null && _microphone.IsRecording)
            {
                _microphone.StopCapture();
            }

            //Dispose preprocessor and encoder
            if (_preprocessor != null)
            {
                if (_microphone != null)
                {
                    _microphone.Unsubscribe(_preprocessor);
                }
                if (_encoder != null)
                {
                    _preprocessor.Unsubscribe(_encoder);
                }

                _preprocessor.Dispose();
                _preprocessor = null;
            }

            if (_encoder != null)
            {
                _encoder.Dispose();
                _encoder = null;
            }

            _encoderSubscribed = false;

#if !NCRUNCH
            Profiler.EndSample();
#endif
        }
        /// <summary>
        /// (Re)Start the transmission pipeline, getting to a state where we *can* send voice (but aren't yet)
        /// </summary>
        private void RestartTransmissionPipeline(string reason)
        {
            Log.Debug("Restarting transmission pipeline: '{0}'", reason);

            StopTransmissionPipeline();

            //If capture has been specifically disabled, exit out of starting it
            if (_encounteredFatalException)
            {
                return;
            }

#if !NCRUNCH
            Profiler.BeginSample("CapturePipelineManager: RestartTransmissionPipeline");
#endif

            try
            {
                //Clear the flag for requesting an explicit reset.
                //We're about to apply a reset so the request will be satisfied.
                _pendingResetRequest = false;

                //No point starting a transmission pipeline if the network is not a client
                if (_network == null || !_network.Mode.IsClientEnabled())
                {
                    return;
                }

                //Create new mic capture system
                var format = _microphone.StartCapture(_micName);

                //If we created a mic (can be null if e.g. there is no mic)
                if (format != null)
                {
                    //Close and re-open all channels, propogating this restart to the receiving end
                    _roomChannels.Refresh();
                    _playerChannels.Refresh();

                    //Create preprocessor and subscribe it to microphone (webrtc preprocessor always wants audio to drive VAD+AEC)
                    _preprocessor = CreatePreprocessor(format);
                    _preprocessor.UpstreamLatency = _microphone.Latency;
                    _preprocessor.Start();
                    _microphone.Subscribe(_preprocessor);

                    //Sub VAD listeners to preprocessor
                    for (var i = 0; i < _activationListeners.Count; i++)
                    {
                        _preprocessor.Subscribe(_activationListeners[i]);
                    }

                    //Sub audio listeners to the preprocessor output
                    for (var i = 0; i < _audioListeners.Count; i++)
                    {
                        var al = _audioListeners[i];
                        al.Reset();
                        _preprocessor.Subscribe(al);
                    }

                    //Create encoder (not yet subscribed to receive audio data, we'll do that later)
                    Log.AssertAndThrowPossibleBug(_network != null, "5F33336B-15B5-4A85-9B54-54352C74768E", "Network object is unexpectedly null");
                    _encoder = new EncoderPipeline(_preprocessor.OutputFormat, _codecSettingsLoader.CreateEncoder(), _network);
                }
                else
                {
                    Log.Warn("Failed to start microphone capture; local voice transmission will be disabled.");
                    _cannotStartMic = true;
                }
            }
            catch (Exception ex)
            {
                //We don't know what happened, but something went wrong. As a precaution kill the transmission pipeline (it will be restarted if necessary)
                StopTransmissionPipeline();

                Log.Error("Unexpected exception encountered starting microphone capture; local voice transmission will be disabled: {0}", ex);
                _encounteredFatalException = true;
            }
            finally
            {
#if !NCRUNCH
                Profiler.EndSample();
#endif
            }
        }