Repository: dotnet/runtime
File: src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.cs (C#)

Code before:
            char[] source = new char[_lastComplete];
            _phonemes.CopyTo(0, source, 0, _lastComplete);
            _phonemes.Remove(0, _lastComplete);
            char[] target;
            if (_conversion == PhonemeConversion.IpaToSapi)
            {
                target = _alphabetConverter.IpaToSapi(source);
            }
            else
            {
                target = _alphabetConverter.SapiToIpa(source);
            }

            //
            // Convert the audio duration
            // Update the next phoneme id
            // Retain any other information based on the first TTS phoneme event.
            //
            TTSEvent ttsEvent, targetEvent, basePhonemeEvent = null;
            long totalDuration = 0;
            basePhonemeEvent = (TTSEvent)_phonemeQueue.Peek();
            for (int i = 0; i < _lastComplete;)
            {
                ttsEvent = (TTSEvent)_phonemeQueue.Dequeue();
                totalDuration += (long)ttsEvent.PhonemeDuration.TotalMilliseconds;
                i += ttsEvent.Phoneme.Length;
            }

            targetEvent = TTSEvent.CreatePhonemeEvent(new string(target), "",
                                                      TimeSpan.FromMilliseconds(totalDuration),
                                                      basePhonemeEvent.PhonemeEmphasis,
                                                      basePhonemeEvent.Prompt,
                                                      basePhonemeEvent.AudioPosition);
            SendToQueue(targetEvent);
        }

        private void Reset()
        {
            _phonemeQueue.Clear();
            _phonemes = new StringBuilder();
            _lastComplete = 0;
        }

        private void SendToQueue(TTSEvent evt)
        {
            if (evt.Id == TtsEventId.Phoneme)
            {
                TTSEvent firstEvent;
                if (_queue.Count > 0)
                {
                    firstEvent = _queue.Dequeue() as TTSEvent;

Review comment (suggestion):
This fix changes duration accumulation from an int millisecond component to TotalMilliseconds (double) and then truncates to long. To avoid floating-point rounding/truncation and preserve full TimeSpan precision, consider accumulating `PhonemeDuration.Ticks` and constructing the final duration via TimeSpan.FromTicks instead of going through TotalMilliseconds/FromMilliseconds.

See below for a potential fix:
```
            long totalTicks = 0;
            basePhonemeEvent = (TTSEvent)_phonemeQueue.Peek();
            for (int i = 0; i < _lastComplete;)
            {
                ttsEvent = (TTSEvent)_phonemeQueue.Dequeue();
                totalTicks += ttsEvent.PhonemeDuration.Ticks;
                i += ttsEvent.Phoneme.Length;
            }

            targetEvent = TTSEvent.CreatePhonemeEvent(new string(target), "",
                                                      TimeSpan.FromTicks(totalTicks),
```

Code after:
            char[] source = new char[_lastComplete];
            _phonemes.CopyTo(0, source, 0, _lastComplete);
            _phonemes.Remove(0, _lastComplete);
            char[] target;
            if (_conversion == PhonemeConversion.IpaToSapi)
            {
                target = _alphabetConverter.IpaToSapi(source);
            }
            else
            {
                target = _alphabetConverter.SapiToIpa(source);
            }

            //
            // Convert the audio duration
            // Update the next phoneme id
            // Retain any other information based on the first TTS phoneme event.
            //
            TTSEvent ttsEvent, targetEvent, basePhonemeEvent = null;
            long totalDurationTicks = 0;
            basePhonemeEvent = (TTSEvent)_phonemeQueue.Peek();
            for (int i = 0; i < _lastComplete;)
            {
                ttsEvent = (TTSEvent)_phonemeQueue.Dequeue();
                totalDurationTicks += ttsEvent.PhonemeDuration.Ticks;
                i += ttsEvent.Phoneme.Length;
            }

            targetEvent = TTSEvent.CreatePhonemeEvent(new string(target), "",
                                                      TimeSpan.FromTicks(totalDurationTicks),
                                                      basePhonemeEvent.PhonemeEmphasis,
                                                      basePhonemeEvent.Prompt,
                                                      basePhonemeEvent.AudioPosition);
            SendToQueue(targetEvent);
        }

        private void Reset()
        {
            _phonemeQueue.Clear();
            _phonemes = new StringBuilder();
            _lastComplete = 0;
        }

        private void SendToQueue(TTSEvent evt)
        {
            if (evt.Id == TtsEventId.Phoneme)
            {
                TTSEvent firstEvent;
                if (_queue.Count > 0)
                {
                    firstEvent = _queue.Dequeue() as TTSEvent;