Uwp SpeechSynthesizer/MediaPlayer memory leak
We have a Uwp app that uses Microsoft voices to speak and read the text as it speaks. I noticed that the app's memory usage increases with each bit of text that is spoken, and it will eventually run out of memory. It does not matter which voice is used or what text is spoken.
In order to highlight the text, I subscribe to events in the TimedMedatataTracks of the MediaPlaybackItem. When the text is finished speaking, I unsubscribe each event and dispose the MediaPlaybackItem.Source. The Visual Studio memory profiler does not show any leaks in managed memory, so I suspect something is not getting cleaned up in the unmanaged space.
Edit: I commented on this in the code but I'll call it out here -- if I do not subscribe to the TimedMetadataTrack events, the leak goes away. I am also able to reproduce this using the Windows sample app (Synthesize Text with Boundaries)
Am I missing something that needs to be disposed, or is this a bug in SpeechSynthesizer/MediaPlayer?
using System;
using System.Diagnostics;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Media.SpeechSynthesis;
namespace WindowsTts
public class UwpNativeVoice : IDisposable
private readonly object _activeSpeechLock;
private SpeechSynthesizer _synthesizer;
private MediaPlayer _mediaPlayer;
private SpeechCallback _activeSpeech;
public UwpNativeVoice(VoiceInformation platformInfo)
_activeSpeechLock = new object();
_synthesizer = new SpeechSynthesizer();
_synthesizer.Options.IncludeWordBoundaryMetadata = true;
_synthesizer.Voice = platformInfo;
_mediaPlayer = new MediaPlayer
RealTimePlayback = true,
AutoPlay = false,
Volume = 1.0f
;
_mediaPlayer.MediaOpened += OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded += OnMediaPlayerMediaEnded;
public void Dispose()
_mediaPlayer.MediaOpened -= OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded -= OnMediaPlayerMediaEnded;
(_mediaPlayer.Source as MediaPlaybackItem)?.Source?.Dispose();
_mediaPlayer.Source = null;
_mediaPlayer.Dispose();
_mediaPlayer = null;
_synthesizer?.Dispose();
_synthesizer = null;
public async void Speak(string text, SpeechDelegate speechDelegate)
if ( string.IsNullOrEmpty(text) )
// no-op; just fire events and bail
speechDelegate?.Invoke(text, ReadTextEvent.Start);
speechDelegate?.Invoke(text, ReadTextEvent.End);
return;
if (_activeSpeech != null)
// something currently speaking; halt it, fire events and then start anew
Halt();
// get synth stream, and add markers for bookmarks & word boundaries
var synthStream = await _synthesizer.SynthesizeTextToStreamAsync(text);
lock (_activeSpeechLock)
_activeSpeech = new SpeechCallback(text, speechDelegate);
try
var source = MediaSource.CreateFromStream(synthStream, synthStream.ContentType);
var playbackItem = new MediaPlaybackItem(source);
ConfigPlaybackEvents(playbackItem); //Comment this out and the leak goes away
_mediaPlayer.Source = playbackItem;
_mediaPlayer.Play();
catch (Exception e)
Debug.WriteLine(e);
_activeSpeech?.Invoke(ReadTextEvent.End);
_activeSpeech = null;
public bool Halt()
lock (_activeSpeechLock)
if (_activeSpeech == null)
return true;
_mediaPlayer.Pause();
DestroyMediaPlaybackItem(_mediaPlayer.Source as MediaPlaybackItem);
_mediaPlayer.Source = null;
SpeechCallback callback;
lock (_activeSpeechLock)
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
return true;
private void OnMediaPlayerMediaOpened(MediaPlayer sender, object args)
FireReadTextEvent(ReadTextEvent.Start);
private void OnTimedMetadataTrackEntered(TimedMetadataTrack track, MediaCueEventArgs args)
if ( track.TimedMetadataKind == TimedMetadataKind.Speech && args.Cue is SpeechCue speechCue )
var startIdx = speechCue.StartPositionInInput ?? 0;
var endIdx = speechCue.EndPositionInInput ?? -1;
FireReadTextEvent(ReadTextEvent.WordEvent(startIdx, (endIdx - startIdx) + 1));
private void OnMediaPlayerMediaEnded(MediaPlayer sender, object args)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
DestroyMediaPlaybackItem(sender.Source as MediaPlaybackItem);
sender.Source = null;
private void FireReadTextEvent(ReadTextEvent evt)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
callback?.Invoke(evt);
private void ConfigPlaybackEvents(MediaPlaybackItem playbackItem)
// see: https://docs.microsoft.com/en-us/uwp/api/windows.media.core.timedmetadatatrack
// iterate through existing tracks, registering callbacks for them
for ( int i = 0; i < playbackItem.TimedMetadataTracks.Count; i++ )
RegisterAction(playbackItem, i);
private void RegisterAction(MediaPlaybackItem item, int idx)
track.Label.Equals(speechWordIdentifier, StringComparison.Ordinal))
track.CueEntered += OnTimedMetadataTrackEntered;
item.TimedMetadataTracks.SetPresentationMode((uint)idx, TimedMetadataTrackPresentationMode.ApplicationPresented);
private void DestroyMediaPlaybackItem(MediaPlaybackItem item)
if ( item == null )
return;
foreach ( var track in item.TimedMetadataTracks )
track.CueEntered -= OnTimedMetadataTrackEntered;
item.Source?.Dispose();
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
memory uwp media-player text-to-speech speechsynthesizer
add a comment |
We have a Uwp app that uses Microsoft voices to speak and read the text as it speaks. I noticed that the app's memory usage increases with each bit of text that is spoken, and it will eventually run out of memory. It does not matter which voice is used or what text is spoken.
In order to highlight the text, I subscribe to events in the TimedMedatataTracks of the MediaPlaybackItem. When the text is finished speaking, I unsubscribe each event and dispose the MediaPlaybackItem.Source. The Visual Studio memory profiler does not show any leaks in managed memory, so I suspect something is not getting cleaned up in the unmanaged space.
Edit: I commented on this in the code but I'll call it out here -- if I do not subscribe to the TimedMetadataTrack events, the leak goes away. I am also able to reproduce this using the Windows sample app (Synthesize Text with Boundaries)
Am I missing something that needs to be disposed, or is this a bug in SpeechSynthesizer/MediaPlayer?
using System;
using System.Diagnostics;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Media.SpeechSynthesis;
namespace WindowsTts
public class UwpNativeVoice : IDisposable
private readonly object _activeSpeechLock;
private SpeechSynthesizer _synthesizer;
private MediaPlayer _mediaPlayer;
private SpeechCallback _activeSpeech;
public UwpNativeVoice(VoiceInformation platformInfo)
_activeSpeechLock = new object();
_synthesizer = new SpeechSynthesizer();
_synthesizer.Options.IncludeWordBoundaryMetadata = true;
_synthesizer.Voice = platformInfo;
_mediaPlayer = new MediaPlayer
RealTimePlayback = true,
AutoPlay = false,
Volume = 1.0f
;
_mediaPlayer.MediaOpened += OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded += OnMediaPlayerMediaEnded;
public void Dispose()
_mediaPlayer.MediaOpened -= OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded -= OnMediaPlayerMediaEnded;
(_mediaPlayer.Source as MediaPlaybackItem)?.Source?.Dispose();
_mediaPlayer.Source = null;
_mediaPlayer.Dispose();
_mediaPlayer = null;
_synthesizer?.Dispose();
_synthesizer = null;
public async void Speak(string text, SpeechDelegate speechDelegate)
if ( string.IsNullOrEmpty(text) )
// no-op; just fire events and bail
speechDelegate?.Invoke(text, ReadTextEvent.Start);
speechDelegate?.Invoke(text, ReadTextEvent.End);
return;
if (_activeSpeech != null)
// something currently speaking; halt it, fire events and then start anew
Halt();
// get synth stream, and add markers for bookmarks & word boundaries
var synthStream = await _synthesizer.SynthesizeTextToStreamAsync(text);
lock (_activeSpeechLock)
_activeSpeech = new SpeechCallback(text, speechDelegate);
try
var source = MediaSource.CreateFromStream(synthStream, synthStream.ContentType);
var playbackItem = new MediaPlaybackItem(source);
ConfigPlaybackEvents(playbackItem); //Comment this out and the leak goes away
_mediaPlayer.Source = playbackItem;
_mediaPlayer.Play();
catch (Exception e)
Debug.WriteLine(e);
_activeSpeech?.Invoke(ReadTextEvent.End);
_activeSpeech = null;
public bool Halt()
lock (_activeSpeechLock)
if (_activeSpeech == null)
return true;
_mediaPlayer.Pause();
DestroyMediaPlaybackItem(_mediaPlayer.Source as MediaPlaybackItem);
_mediaPlayer.Source = null;
SpeechCallback callback;
lock (_activeSpeechLock)
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
return true;
private void OnMediaPlayerMediaOpened(MediaPlayer sender, object args)
FireReadTextEvent(ReadTextEvent.Start);
private void OnTimedMetadataTrackEntered(TimedMetadataTrack track, MediaCueEventArgs args)
if ( track.TimedMetadataKind == TimedMetadataKind.Speech && args.Cue is SpeechCue speechCue )
var startIdx = speechCue.StartPositionInInput ?? 0;
var endIdx = speechCue.EndPositionInInput ?? -1;
FireReadTextEvent(ReadTextEvent.WordEvent(startIdx, (endIdx - startIdx) + 1));
private void OnMediaPlayerMediaEnded(MediaPlayer sender, object args)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
DestroyMediaPlaybackItem(sender.Source as MediaPlaybackItem);
sender.Source = null;
private void FireReadTextEvent(ReadTextEvent evt)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
callback?.Invoke(evt);
private void ConfigPlaybackEvents(MediaPlaybackItem playbackItem)
// see: https://docs.microsoft.com/en-us/uwp/api/windows.media.core.timedmetadatatrack
// iterate through existing tracks, registering callbacks for them
for ( int i = 0; i < playbackItem.TimedMetadataTracks.Count; i++ )
RegisterAction(playbackItem, i);
private void RegisterAction(MediaPlaybackItem item, int idx)
track.Label.Equals(speechWordIdentifier, StringComparison.Ordinal))
track.CueEntered += OnTimedMetadataTrackEntered;
item.TimedMetadataTracks.SetPresentationMode((uint)idx, TimedMetadataTrackPresentationMode.ApplicationPresented);
private void DestroyMediaPlaybackItem(MediaPlaybackItem item)
if ( item == null )
return;
foreach ( var track in item.TimedMetadataTracks )
track.CueEntered -= OnTimedMetadataTrackEntered;
item.Source?.Dispose();
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
memory uwp media-player text-to-speech speechsynthesizer
Have you checkedHaltmethod ? it does not work.
– Nico Zhu - MSFT
Nov 13 '18 at 6:56
You could check this official code sample, and it have no memory leak issue from my test.
– Nico Zhu - MSFT
Nov 13 '18 at 7:01
@NicoZhu-MSFT thanks, that's a good idea. However, I am still seeing a leak with that sample in the second scenario (Synthesize text with boundaries). Open the sample app side by side with task manager. Note the current memory usage. Use some short text in (e.g. "Sample text"), then press speak several times. I see the memory usage increasing several MB and it never returns to the baseline. I am confident the issue is in the metadata CueEntered events. If I comment those out of my code, it does not leak.
– J Nelson
Nov 13 '18 at 12:29
@NicoZhu-MSFT no, I haven't. Sorry, let me clarify: Removing the CueEntered events does fix the memory leak, meaning the leak must be related to those events. However, I need to subscribe to those events in order to highlight text in my app, so removing them is not a solution.
– J Nelson
Nov 14 '18 at 12:50
add a comment |
We have a Uwp app that uses Microsoft voices to speak and read the text as it speaks. I noticed that the app's memory usage increases with each bit of text that is spoken, and it will eventually run out of memory. It does not matter which voice is used or what text is spoken.
In order to highlight the text, I subscribe to events in the TimedMedatataTracks of the MediaPlaybackItem. When the text is finished speaking, I unsubscribe each event and dispose the MediaPlaybackItem.Source. The Visual Studio memory profiler does not show any leaks in managed memory, so I suspect something is not getting cleaned up in the unmanaged space.
Edit: I commented on this in the code but I'll call it out here -- if I do not subscribe to the TimedMetadataTrack events, the leak goes away. I am also able to reproduce this using the Windows sample app (Synthesize Text with Boundaries)
Am I missing something that needs to be disposed, or is this a bug in SpeechSynthesizer/MediaPlayer?
using System;
using System.Diagnostics;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Media.SpeechSynthesis;
namespace WindowsTts
public class UwpNativeVoice : IDisposable
private readonly object _activeSpeechLock;
private SpeechSynthesizer _synthesizer;
private MediaPlayer _mediaPlayer;
private SpeechCallback _activeSpeech;
public UwpNativeVoice(VoiceInformation platformInfo)
_activeSpeechLock = new object();
_synthesizer = new SpeechSynthesizer();
_synthesizer.Options.IncludeWordBoundaryMetadata = true;
_synthesizer.Voice = platformInfo;
_mediaPlayer = new MediaPlayer
RealTimePlayback = true,
AutoPlay = false,
Volume = 1.0f
;
_mediaPlayer.MediaOpened += OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded += OnMediaPlayerMediaEnded;
public void Dispose()
_mediaPlayer.MediaOpened -= OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded -= OnMediaPlayerMediaEnded;
(_mediaPlayer.Source as MediaPlaybackItem)?.Source?.Dispose();
_mediaPlayer.Source = null;
_mediaPlayer.Dispose();
_mediaPlayer = null;
_synthesizer?.Dispose();
_synthesizer = null;
public async void Speak(string text, SpeechDelegate speechDelegate)
if ( string.IsNullOrEmpty(text) )
// no-op; just fire events and bail
speechDelegate?.Invoke(text, ReadTextEvent.Start);
speechDelegate?.Invoke(text, ReadTextEvent.End);
return;
if (_activeSpeech != null)
// something currently speaking; halt it, fire events and then start anew
Halt();
// get synth stream, and add markers for bookmarks & word boundaries
var synthStream = await _synthesizer.SynthesizeTextToStreamAsync(text);
lock (_activeSpeechLock)
_activeSpeech = new SpeechCallback(text, speechDelegate);
try
var source = MediaSource.CreateFromStream(synthStream, synthStream.ContentType);
var playbackItem = new MediaPlaybackItem(source);
ConfigPlaybackEvents(playbackItem); //Comment this out and the leak goes away
_mediaPlayer.Source = playbackItem;
_mediaPlayer.Play();
catch (Exception e)
Debug.WriteLine(e);
_activeSpeech?.Invoke(ReadTextEvent.End);
_activeSpeech = null;
public bool Halt()
lock (_activeSpeechLock)
if (_activeSpeech == null)
return true;
_mediaPlayer.Pause();
DestroyMediaPlaybackItem(_mediaPlayer.Source as MediaPlaybackItem);
_mediaPlayer.Source = null;
SpeechCallback callback;
lock (_activeSpeechLock)
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
return true;
private void OnMediaPlayerMediaOpened(MediaPlayer sender, object args)
FireReadTextEvent(ReadTextEvent.Start);
private void OnTimedMetadataTrackEntered(TimedMetadataTrack track, MediaCueEventArgs args)
if ( track.TimedMetadataKind == TimedMetadataKind.Speech && args.Cue is SpeechCue speechCue )
var startIdx = speechCue.StartPositionInInput ?? 0;
var endIdx = speechCue.EndPositionInInput ?? -1;
FireReadTextEvent(ReadTextEvent.WordEvent(startIdx, (endIdx - startIdx) + 1));
private void OnMediaPlayerMediaEnded(MediaPlayer sender, object args)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
DestroyMediaPlaybackItem(sender.Source as MediaPlaybackItem);
sender.Source = null;
private void FireReadTextEvent(ReadTextEvent evt)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
callback?.Invoke(evt);
private void ConfigPlaybackEvents(MediaPlaybackItem playbackItem)
// see: https://docs.microsoft.com/en-us/uwp/api/windows.media.core.timedmetadatatrack
// iterate through existing tracks, registering callbacks for them
for ( int i = 0; i < playbackItem.TimedMetadataTracks.Count; i++ )
RegisterAction(playbackItem, i);
private void RegisterAction(MediaPlaybackItem item, int idx)
track.Label.Equals(speechWordIdentifier, StringComparison.Ordinal))
track.CueEntered += OnTimedMetadataTrackEntered;
item.TimedMetadataTracks.SetPresentationMode((uint)idx, TimedMetadataTrackPresentationMode.ApplicationPresented);
private void DestroyMediaPlaybackItem(MediaPlaybackItem item)
if ( item == null )
return;
foreach ( var track in item.TimedMetadataTracks )
track.CueEntered -= OnTimedMetadataTrackEntered;
item.Source?.Dispose();
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
memory uwp media-player text-to-speech speechsynthesizer
We have a Uwp app that uses Microsoft voices to speak and read the text as it speaks. I noticed that the app's memory usage increases with each bit of text that is spoken, and it will eventually run out of memory. It does not matter which voice is used or what text is spoken.
In order to highlight the text, I subscribe to events in the TimedMedatataTracks of the MediaPlaybackItem. When the text is finished speaking, I unsubscribe each event and dispose the MediaPlaybackItem.Source. The Visual Studio memory profiler does not show any leaks in managed memory, so I suspect something is not getting cleaned up in the unmanaged space.
Edit: I commented on this in the code but I'll call it out here -- if I do not subscribe to the TimedMetadataTrack events, the leak goes away. I am also able to reproduce this using the Windows sample app (Synthesize Text with Boundaries)
Am I missing something that needs to be disposed, or is this a bug in SpeechSynthesizer/MediaPlayer?
using System;
using System.Diagnostics;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Media.SpeechSynthesis;
namespace WindowsTts
public class UwpNativeVoice : IDisposable
private readonly object _activeSpeechLock;
private SpeechSynthesizer _synthesizer;
private MediaPlayer _mediaPlayer;
private SpeechCallback _activeSpeech;
public UwpNativeVoice(VoiceInformation platformInfo)
_activeSpeechLock = new object();
_synthesizer = new SpeechSynthesizer();
_synthesizer.Options.IncludeWordBoundaryMetadata = true;
_synthesizer.Voice = platformInfo;
_mediaPlayer = new MediaPlayer
RealTimePlayback = true,
AutoPlay = false,
Volume = 1.0f
;
_mediaPlayer.MediaOpened += OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded += OnMediaPlayerMediaEnded;
public void Dispose()
_mediaPlayer.MediaOpened -= OnMediaPlayerMediaOpened;
_mediaPlayer.MediaEnded -= OnMediaPlayerMediaEnded;
(_mediaPlayer.Source as MediaPlaybackItem)?.Source?.Dispose();
_mediaPlayer.Source = null;
_mediaPlayer.Dispose();
_mediaPlayer = null;
_synthesizer?.Dispose();
_synthesizer = null;
public async void Speak(string text, SpeechDelegate speechDelegate)
if ( string.IsNullOrEmpty(text) )
// no-op; just fire events and bail
speechDelegate?.Invoke(text, ReadTextEvent.Start);
speechDelegate?.Invoke(text, ReadTextEvent.End);
return;
if (_activeSpeech != null)
// something currently speaking; halt it, fire events and then start anew
Halt();
// get synth stream, and add markers for bookmarks & word boundaries
var synthStream = await _synthesizer.SynthesizeTextToStreamAsync(text);
lock (_activeSpeechLock)
_activeSpeech = new SpeechCallback(text, speechDelegate);
try
var source = MediaSource.CreateFromStream(synthStream, synthStream.ContentType);
var playbackItem = new MediaPlaybackItem(source);
ConfigPlaybackEvents(playbackItem); //Comment this out and the leak goes away
_mediaPlayer.Source = playbackItem;
_mediaPlayer.Play();
catch (Exception e)
Debug.WriteLine(e);
_activeSpeech?.Invoke(ReadTextEvent.End);
_activeSpeech = null;
public bool Halt()
lock (_activeSpeechLock)
if (_activeSpeech == null)
return true;
_mediaPlayer.Pause();
DestroyMediaPlaybackItem(_mediaPlayer.Source as MediaPlaybackItem);
_mediaPlayer.Source = null;
SpeechCallback callback;
lock (_activeSpeechLock)
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
return true;
private void OnMediaPlayerMediaOpened(MediaPlayer sender, object args)
FireReadTextEvent(ReadTextEvent.Start);
private void OnTimedMetadataTrackEntered(TimedMetadataTrack track, MediaCueEventArgs args)
if ( track.TimedMetadataKind == TimedMetadataKind.Speech && args.Cue is SpeechCue speechCue )
var startIdx = speechCue.StartPositionInInput ?? 0;
var endIdx = speechCue.EndPositionInInput ?? -1;
FireReadTextEvent(ReadTextEvent.WordEvent(startIdx, (endIdx - startIdx) + 1));
private void OnMediaPlayerMediaEnded(MediaPlayer sender, object args)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
_activeSpeech = null;
callback?.Invoke(ReadTextEvent.End);
DestroyMediaPlaybackItem(sender.Source as MediaPlaybackItem);
sender.Source = null;
private void FireReadTextEvent(ReadTextEvent evt)
SpeechCallback callback;
lock ( _activeSpeechLock )
callback = _activeSpeech;
callback?.Invoke(evt);
private void ConfigPlaybackEvents(MediaPlaybackItem playbackItem)
// see: https://docs.microsoft.com/en-us/uwp/api/windows.media.core.timedmetadatatrack
// iterate through existing tracks, registering callbacks for them
for ( int i = 0; i < playbackItem.TimedMetadataTracks.Count; i++ )
RegisterAction(playbackItem, i);
private void RegisterAction(MediaPlaybackItem item, int idx)
track.Label.Equals(speechWordIdentifier, StringComparison.Ordinal))
track.CueEntered += OnTimedMetadataTrackEntered;
item.TimedMetadataTracks.SetPresentationMode((uint)idx, TimedMetadataTrackPresentationMode.ApplicationPresented);
private void DestroyMediaPlaybackItem(MediaPlaybackItem item)
if ( item == null )
return;
foreach ( var track in item.TimedMetadataTracks )
track.CueEntered -= OnTimedMetadataTrackEntered;
item.Source?.Dispose();
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
namespace WindowsTts
/// <summary>Defines a trigger that caused the broadcasting of a ReadTextEvent.</summary>
public enum ReadTextTrigger
Start,
Bookmark,
Word,
End,
/// <summary>A ReadTextEvent encompasses the relevant information from the tts world and is passed to the api user as part of a ReadTextInfo's EventAction data. </summary>
public class ReadTextEvent
public static ReadTextEvent Start get; = new ReadTextEvent()
Trigger = ReadTextTrigger.Start,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent End get; = new ReadTextEvent()
Trigger = ReadTextTrigger.End,
BookmarkName = null,
TextOffset = -1,
TextLength = -1,
;
public ReadTextTrigger Trigger get; set;
public string BookmarkName get; set;
public int TextOffset get; set;
public int TextLength get; set;
/// <summary>Utility methods to pre-initialize some fields of this object.</summary>
public static ReadTextEvent Factory(ReadTextEvent src)
return new ReadTextEvent()
Trigger = src.Trigger,
BookmarkName = src.BookmarkName,
TextOffset = src.TextOffset,
TextLength = src.TextLength,
;
public static ReadTextEvent BookmarkEvent(string bookmark)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Bookmark,
BookmarkName = bookmark,
TextOffset = -1,
TextLength = -1,
;
public static ReadTextEvent WordEvent(int textOffset, int textLength)
return new ReadTextEvent()
Trigger = ReadTextTrigger.Word,
BookmarkName = null,
TextOffset = textOffset,
TextLength = textLength,
;
private ReadTextEvent()
/// <summary>
/// A SpeechDelegate is passed to the ITtsVoice.Speak() method, so that the caller may receive progress info as the text is being spoken.
/// </summary>
/// <param name="speechText"></param>
/// <param name="readTextEvent"></param>
public delegate void SpeechDelegate(string speechText, ReadTextEvent readTextEvent);
/// <summary>
/// This class encapsulates everything necessary to invoke a SpeechDelegate.
/// A SpeechCallback instance may be created each time a new string is enqueued for speaking,
/// and then invoked multiple times throughout the process, with an updated ReadTextEvent.
/// </summary>
public class SpeechCallback
private readonly SpeechDelegate _speechDelegate;
public SpeechCallback(string text, SpeechDelegate speechDelegate)
Text = text;
_speechDelegate = speechDelegate;
public string Text get;
public void Invoke(ReadTextEvent readTextEvent) => _speechDelegate?.Invoke(Text, readTextEvent);
memory uwp media-player text-to-speech speechsynthesizer
memory uwp media-player text-to-speech speechsynthesizer
edited Nov 13 '18 at 12:35
J Nelson
asked Nov 12 '18 at 16:40
J NelsonJ Nelson
467
467
Have you checkedHaltmethod ? it does not work.
– Nico Zhu - MSFT
Nov 13 '18 at 6:56
You could check this official code sample, and it have no memory leak issue from my test.
– Nico Zhu - MSFT
Nov 13 '18 at 7:01
@NicoZhu-MSFT thanks, that's a good idea. However, I am still seeing a leak with that sample in the second scenario (Synthesize text with boundaries). Open the sample app side by side with task manager. Note the current memory usage. Use some short text in (e.g. "Sample text"), then press speak several times. I see the memory usage increasing several MB and it never returns to the baseline. I am confident the issue is in the metadata CueEntered events. If I comment those out of my code, it does not leak.
– J Nelson
Nov 13 '18 at 12:29
@NicoZhu-MSFT no, I haven't. Sorry, let me clarify: Removing the CueEntered events does fix the memory leak, meaning the leak must be related to those events. However, I need to subscribe to those events in order to highlight text in my app, so removing them is not a solution.
– J Nelson
Nov 14 '18 at 12:50
add a comment |
Have you checkedHaltmethod ? it does not work.
– Nico Zhu - MSFT
Nov 13 '18 at 6:56
You could check this official code sample, and it have no memory leak issue from my test.
– Nico Zhu - MSFT
Nov 13 '18 at 7:01
@NicoZhu-MSFT thanks, that's a good idea. However, I am still seeing a leak with that sample in the second scenario (Synthesize text with boundaries). Open the sample app side by side with task manager. Note the current memory usage. Use some short text in (e.g. "Sample text"), then press speak several times. I see the memory usage increasing several MB and it never returns to the baseline. I am confident the issue is in the metadata CueEntered events. If I comment those out of my code, it does not leak.
– J Nelson
Nov 13 '18 at 12:29
@NicoZhu-MSFT no, I haven't. Sorry, let me clarify: Removing the CueEntered events does fix the memory leak, meaning the leak must be related to those events. However, I need to subscribe to those events in order to highlight text in my app, so removing them is not a solution.
– J Nelson
Nov 14 '18 at 12:50
Have you checked
Halt method ? it does not work.– Nico Zhu - MSFT
Nov 13 '18 at 6:56
Have you checked
Halt method ? it does not work.– Nico Zhu - MSFT
Nov 13 '18 at 6:56
You could check this official code sample, and it have no memory leak issue from my test.
– Nico Zhu - MSFT
Nov 13 '18 at 7:01
You could check this official code sample, and it have no memory leak issue from my test.
– Nico Zhu - MSFT
Nov 13 '18 at 7:01
@NicoZhu-MSFT thanks, that's a good idea. However, I am still seeing a leak with that sample in the second scenario (Synthesize text with boundaries). Open the sample app side by side with task manager. Note the current memory usage. Use some short text in (e.g. "Sample text"), then press speak several times. I see the memory usage increasing several MB and it never returns to the baseline. I am confident the issue is in the metadata CueEntered events. If I comment those out of my code, it does not leak.
– J Nelson
Nov 13 '18 at 12:29
@NicoZhu-MSFT thanks, that's a good idea. However, I am still seeing a leak with that sample in the second scenario (Synthesize text with boundaries). Open the sample app side by side with task manager. Note the current memory usage. Use some short text in (e.g. "Sample text"), then press speak several times. I see the memory usage increasing several MB and it never returns to the baseline. I am confident the issue is in the metadata CueEntered events. If I comment those out of my code, it does not leak.
– J Nelson
Nov 13 '18 at 12:29
@NicoZhu-MSFT no, I haven't. Sorry, let me clarify: Removing the CueEntered events does fix the memory leak, meaning the leak must be related to those events. However, I need to subscribe to those events in order to highlight text in my app, so removing them is not a solution.
– J Nelson
Nov 14 '18 at 12:50
@NicoZhu-MSFT no, I haven't. Sorry, let me clarify: Removing the CueEntered events does fix the memory leak, meaning the leak must be related to those events. However, I need to subscribe to those events in order to highlight text in my app, so removing them is not a solution.
– J Nelson
Nov 14 '18 at 12:50
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53266509%2fuwp-speechsynthesizer-mediaplayer-memory-leak%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53266509%2fuwp-speechsynthesizer-mediaplayer-memory-leak%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Have you checked
Haltmethod ? it does not work.– Nico Zhu - MSFT
Nov 13 '18 at 6:56
You could check this official code sample, and it have no memory leak issue from my test.
– Nico Zhu - MSFT
Nov 13 '18 at 7:01
@NicoZhu-MSFT thanks, that's a good idea. However, I am still seeing a leak with that sample in the second scenario (Synthesize text with boundaries). Open the sample app side by side with task manager. Note the current memory usage. Use some short text in (e.g. "Sample text"), then press speak several times. I see the memory usage increasing several MB and it never returns to the baseline. I am confident the issue is in the metadata CueEntered events. If I comment those out of my code, it does not leak.
– J Nelson
Nov 13 '18 at 12:29
@NicoZhu-MSFT no, I haven't. Sorry, let me clarify: Removing the CueEntered events does fix the memory leak, meaning the leak must be related to those events. However, I need to subscribe to those events in order to highlight text in my app, so removing them is not a solution.
– J Nelson
Nov 14 '18 at 12:50