File: Internal\Synthesis\SpeakInfo.cs
Web Access
Project: src\src\runtime\src\libraries\System.Speech\src\System.Speech.csproj (System.Speech)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Speech.Synthesis;
using System.Speech.Synthesis.TtsEngine;

#pragma warning disable 56524 // The _voiceSynthesis member is not created in this module and should not be disposed

namespace System.Speech.Internal.Synthesis
{
    internal sealed class SpeakInfo
    {
        #region Constructors
        /// <param name="voiceSynthesis">Voice synthesizer used</param>
        /// <param name="ttsVoice">Default engine to use</param>
        internal SpeakInfo(VoiceSynthesis voiceSynthesis, TTSVoice ttsVoice)
        {
            _voiceSynthesis = voiceSynthesis;
            _ttsVoice = ttsVoice;
        }

        #endregion

        #region Internal Properties

        internal TTSVoice Voice
        {
            get
            {
                return _ttsVoice;
            }
        }

        #endregion

        #region Internal Methods

        internal void SetVoice(string? name, CultureInfo? culture, VoiceGender gender, VoiceAge age, int variant)
        {
            TTSVoice ttsVoice = _voiceSynthesis.GetEngine(name, culture, gender, age, variant, false);
            if (!ttsVoice.Equals(_ttsVoice))
            {
                _ttsVoice = ttsVoice;
                NotInTextSeg = true;
            }
        }

        internal void AddAudio(AudioData audio)
        {
            AddNewSeg(new SpeechSeg(audio));
            NotInTextSeg = true;
        }

        internal void AddText(TTSVoice ttsVoice, TextFragment textFragment)
        {
            if (NotInTextSeg || ttsVoice != _ttsVoice)
            {
                AddNewSeg(new SpeechSeg(ttsVoice));
                NotInTextSeg = false;
            }
            _lastSeg.AddFrag(textFragment);
        }

        internal SpeechSeg? RemoveFirst()
        {
            SpeechSeg? speechSeg = null;
            if (_listSeg.Count > 0)
            {
                speechSeg = _listSeg[0];
                _listSeg.RemoveAt(0);
            }
            return speechSeg;
        }

        #endregion

        #region Private Method

        [MemberNotNull(nameof(_lastSeg))]
        private void AddNewSeg(SpeechSeg pNew)
        {
            _listSeg.Add(pNew);
            _lastSeg = pNew;
        }

        #endregion

        #region private Fields

        // default TTS voice
        private TTSVoice _ttsVoice;

        // If true then a new segment is required for the next Add Text
        [MemberNotNullWhen(false, nameof(_lastSeg))]
        private bool NotInTextSeg { get; set; } = true;

        // list of segments (text or audio)
        private List<SpeechSeg> _listSeg = new();

        // current segment
        private SpeechSeg? _lastSeg;

        // Reference to the VoiceSynthesizer that created it
        private VoiceSynthesis _voiceSynthesis;

        #endregion
    }

    #region Private Types

    internal class AudioData : IDisposable
    {
        internal AudioData(Uri uri, ResourceLoader resourceLoader)
        {
            _uri = uri;
            _resourceLoader = resourceLoader;
            Uri? baseAudio;
            _stream = _resourceLoader.LoadFile(uri, out _mimeType, out baseAudio, out _localFile);
        }

        /// <summary>
        /// Needed by IEnumerable!!!
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~AudioData()
        {
            Dispose(false);
        }

        internal Uri _uri;
        internal string? _mimeType;
        internal Stream _stream;

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // unload the file from the cache
                if (_localFile != null)
                {
                    _resourceLoader.UnloadFile(_localFile);
                }

                if (_stream != null)
                {
                    _stream.Dispose();
                    _stream = null!;
                    _localFile = null!;
                    _uri = null!;
                }
            }
        }

        private string? _localFile;
        private ResourceLoader _resourceLoader;
    }

    #endregion
}