File: Internal\Synthesis\TTSVoice.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.Runtime.InteropServices;
using System.Speech.Synthesis;

namespace System.Speech.Internal.Synthesis
{
    internal class TTSVoice
    {
        #region Constructors

        internal TTSVoice(ITtsEngineProxy engine, VoiceInfo voiceId)
        {
            _engine = engine;
            _voiceId = voiceId;
        }

        #endregion

        #region public Methods

        /// <summary>
        /// Tests whether two objects are equivalent
        /// </summary>
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            return obj is TTSVoice voice && (_voiceId.Equals(voice.VoiceInfo));
        }

        /// <summary>
        /// Overrides Object.GetHashCode()
        /// </summary>
        public override int GetHashCode()
        {
            return _voiceId.GetHashCode();
        }

        #endregion

        #region Internal Methods

        internal void UpdateLexicons(List<LexiconEntry> lexicons)
        {
            // Remove the lexicons that are defined in this voice but are not in the list
            for (int i = _lexicons.Count - 1; i >= 0; i--)
            {
                LexiconEntry entry = _lexicons[i];
                if (!lexicons.Contains(entry))
                {
                    // Remove the entry first, just in case the RemoveLexicon throws
                    _lexicons.RemoveAt(i);
                    TtsEngine.RemoveLexicon(entry._uri);
                }
            }

            // Add the lexicons that are defined in this voice but are not in the list
            foreach (LexiconEntry entry in lexicons)
            {
                if (!_lexicons.Contains(entry))
                {
                    // Remove the entry first, just in case the RemoveLexicon throws
                    TtsEngine.AddLexicon(entry._uri, entry._mediaType);
                    _lexicons.Add(entry);
                }
            }
        }

        internal byte[] WaveFormat(byte[]? targetWaveFormat)
        {
            // Get the Wave header if it has not been set by the user
            if (targetWaveFormat == null && _waveFormat == null)
            {
                // The registry values contains a default rate
                if (VoiceInfo.SupportedAudioFormats?.Count > 0)
                {
                    // Create the array of bytes containing the format
                    targetWaveFormat = VoiceInfo.SupportedAudioFormats[0].WaveFormat;
                }
            }

            // No input specified and we already got the default
            if (targetWaveFormat == null && _waveFormat != null)
            {
                return _waveFormat;
            }

            // New waveFormat provided?
            if (_waveFormat == null || !Array.Equals(targetWaveFormat, _waveFormat))
            {
                IntPtr waveFormat = IntPtr.Zero;
                GCHandle targetFormat = new();

                if (targetWaveFormat != null)
                {
                    targetFormat = GCHandle.Alloc(targetWaveFormat, GCHandleType.Pinned);
                }
                try
                {
                    waveFormat = _engine.GetOutputFormat(targetWaveFormat != null ? targetFormat.AddrOfPinnedObject() : IntPtr.Zero);
                }
                finally
                {
                    if (targetWaveFormat != null)
                    {
                        targetFormat.Free();
                    }
                }

                if (waveFormat != IntPtr.Zero)
                {
                    _waveFormat = WAVEFORMATEX.ToBytes(waveFormat);

                    // Free the buffer
                    Marshal.FreeCoTaskMem(waveFormat);
                }
                else
                {
                    _waveFormat = WAVEFORMATEX.Default.ToBytes();
                }
            }
            return _waveFormat;
        }

        #endregion

        #region Internal Properties

        internal ITtsEngineProxy TtsEngine
        {
            get
            {
                return _engine;
            }
        }

        internal VoiceInfo VoiceInfo
        {
            get
            {
                return _voiceId;
            }
        }

        #endregion

        #region private Fields

        private ITtsEngineProxy _engine;
        private VoiceInfo _voiceId;
        private List<LexiconEntry> _lexicons = new();
        private byte[]? _waveFormat;

        #endregion
    }
}