/// <summary>
        /// Initializes a new instance of the <see cref="DialogManager{TInputType}"/> class.
        /// </summary>
        /// <param name="dialogBackend"> The dialog backend for the manager to use. </param>
        /// <param name="keywordRegistration"> The keyword registration with the current keyword file information.</param>
        /// <param name="dialogAudioInput"> The input audio provider. </param>
        /// <param name="agentSessionManager"> The manager that provides the instance of agent session wrapper. </param>
        /// <param name="dialogAudioOutput"> The dialog audio output sink to use. </param>
        public DialogManager(
            IDialogBackend <TInputType> dialogBackend,
            IKeywordRegistration keywordRegistration,
            IDialogAudioInputProvider <TInputType> dialogAudioInput,
            IAgentSessionManager agentSessionManager,
            IDialogAudioOutputAdapter dialogAudioOutput = null)
        {
            Contract.Requires(dialogBackend != null);
            Contract.Requires(agentSessionManager != null);
            this.logger        = LogRouter.GetClassLogger();
            this.dialogBackend = dialogBackend;
            this.dialogBackend.SessionStarted += (id)
                                                 => this.logger.Log($"DialogManager: Session start: {id}");
            this.dialogBackend.SessionStopped += (id)
                                                 => this.logger.Log($"DialogManager: Session stop: {id}");
            this.dialogBackend.KeywordRecognizing += this.OnKeywordRecognizing;
            this.dialogBackend.KeywordRecognized  += this.OnKeywordRecognized;
            this.dialogBackend.SpeechRecognizing  += this.OnSpeechRecognizing;
            this.dialogBackend.SpeechRecognized   += async(text)
                                                     => await this.OnSpeechRecognizedAsync(text);

            this.dialogBackend.DialogResponseReceived += this.OnActivityReceived;
            this.dialogBackend.ErrorReceived          += async(errorInformation)
                                                         => await this.OnErrorReceivedAsync(errorInformation);

            this.dialogAudioInput    = dialogAudioInput;
            this.keywordRegistration = keywordRegistration;
            this.agentSessionManager = agentSessionManager;

            this.agentSessionManager.SignalDetected += (sender, args) => this.HandleSignalDetection(args);
            this.InitializeSignalDetectionHelper();

            _ = this.InitializeAsync(dialogAudioOutput);
        }
        /// <summary>
        /// Completes async initialization for dialog manager, including initialization of dialog output.
        /// </summary>
        /// <param name="dialogAudioOutput"> The dialog audio output sink to use. </param>
        /// <returns> A task that completes once the state is updated and consumers are notified. </returns>
        public async Task InitializeAsync(IDialogAudioOutputAdapter dialogAudioOutput = null)
        {
            this.dialogAudioOutput = dialogAudioOutput ?? await DialogAudioOutputAdapter.CreateAsync();

            this.dialogResponseQueue = new DialogResponseQueue(this.dialogAudioOutput);

            this.dialogResponseQueue.ExecutingResponse += async(DialogResponse response) =>
            {
                if (response.MessageMedia != null)
                {
                    await this.ChangeAgentStateAsync(ConversationalAgentState.Speaking);
                }

                if (response.TurnEndIndicated)
                {
                    await this.FinishTurnAsync();
                }
            };

            if (this.dialogAudioOutput != null)
            {
                this.dialogAudioOutput.OutputEnded += async() =>
                {
                    var session = await this.agentSessionManager.GetSessionAsync();

                    if (session.AgentState == ConversationalAgentState.Speaking)
                    {
                        await this.ChangeAgentStateAsync(ConversationalAgentState.Inactive);
                    }
                };
            }
        }
        /// <summary>
        /// Free disposable resources per the IDisposable interface.
        /// </summary>
        /// <param name="disposing"> Whether managed state is being disposed. </param>
        protected virtual async void Dispose(bool disposing)
        {
            if (!this.AlreadyDisposed)
            {
                if (disposing)
                {
                    await this.StopAudioCaptureAsync();

                    await this.StopAudioPlaybackAsync();

                    this.dialogBackend?.Dispose();
                    this.dialogAudioInput?.Dispose();
                    this.dialogAudioOutput?.Dispose();
                }

                this.dialogBackend     = null;
                this.dialogAudioInput  = null;
                this.dialogAudioOutput = null;
                this.AlreadyDisposed   = true;
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="DialogResponseQueue"/> class.
 /// </summary>
 /// <param name="outputAdapter">Output adapter to play audio from dialog responses.</param>
 public DialogResponseQueue(IDialogAudioOutputAdapter outputAdapter)
 {
     this.dialogResponseQueue = new ConcurrentQueue <DialogResponse>();
     this.outputAdapter       = outputAdapter;
     this.actionQueueLock     = new object();
 }