성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Windows 10 운영체제의 데스크톱 앱에서 TTS(SpeechSynthesizer) 사용하는 방법</h1> <p> (업데이트: 2023-03-30) 이 글에 대한 질문은 더 이상 받지 않습니다. (하시다 보면, 제가 왜 이 기술에 대해 흥미를 못 느끼는 지 아시게 될 것입니다. ^^ SpeechRecognitionEngine에 특별한 업데이트가 없는 한 다루지 않을 것입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 예전에도 이야기했지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 데스크톱 윈도우 응용 프로그램에서 UWP 라이브러리를 이용한 비디오 장치 열람하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11284'>http://www.sysnet.pe.kr/2/0/11284</a> </pre> <br /> 데스크톱 응용 프로그램에서도 UWP 앱에 제공되는 타입들을 사용할 수 있습니다. 이번에는 그중에서 TTS(Text-to-speech) 기능을 제공하는 SpeechSynthesizer 타입을 알아보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > SpeechSynthesizer Class (Namespace: Windows.Media.SpeechSynthesis) ; <a target='tab' href='https://learn.microsoft.com/en-us/uwp/api/windows.media.speechsynthesis.speechsynthesizer'>https://learn.microsoft.com/en-us/uwp/api/windows.media.speechsynthesis.speechsynthesizer</a> * 최소 요구 사항: Windows 10(v10.0.10240.0) </pre> <br /> 이 타입을 사용하는 예제 코드는 마이크로소프트의 UWP 앱 샘플에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Speech recognition and synthesis sample ; <a target='tab' href='https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/SpeechRecognitionAndSynthesis'>https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/SpeechRecognitionAndSynthesis</a> Windows-universal-samples/Samples/SpeechRecognitionAndSynthesis/cs/ ; <a target='tab' href='https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/SpeechRecognitionAndSynthesis/cs'>https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/SpeechRecognitionAndSynthesis/cs</a> </pre> <br /> .\Samples\SpeechRecognitionAndSynthesis\cs\SpeechAndTTS.sln 솔루션 파일을 열어 테스트할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 사용해 볼까요? ^^<br /> <br /> 우선 SpeechSynthesizer 정의를 비롯해 UWP의 기본 타입들을 담고 있는 라이브러리를 먼저 참조해야 합니다. 원래 UWP 용으로는 다음과 같은 식의 경로에 있는 Windows.Foundation.UniversalApiContract.winmd 파일을 참조하는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\Program Files (x86)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.UniversalApiContract\5.0.0.0\Windows.Foundation.UniversalApiContract.winmd </pre> <br /> 데스크톱 응용 프로그램은 저 파일을 참조해서는 안되고 대신 다음의 파일 2개를 참조합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll </pre> <br /> 그럼 소스 코드에서 다음과 같이 SpeechSynthesizer 클래스를 생성하고, 텍스트가 음성으로 바뀌어 출력될 Stream을 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Windows.ApplicationModel.Resources.Core; using Windows.Media.SpeechSynthesis; namespace Listener10 { public partial class MainWindow : Window { private SpeechSynthesizer <span style='color: blue; font-weight: bold'>synthesizer = new SpeechSynthesizer();</span> public MainWindow() { InitializeComponent(); Speak("test is good"); } private async void Speak(string text) { if (!string.IsNullOrEmpty(text)) { try { SpeechSynthesisStream synthesisStream = await <span style='color: blue; font-weight: bold'>synthesizer.SynthesizeTextToStreamAsync(text);</span> } catch (Exception e1) { System.Diagnostics.Trace.WriteLine(e1.ToString()); } } } } } </pre> <br /> 문제는 여기서부터입니다. 음성이 출력될 synthesisStream을 재생해야 하는데 UWP 앱 예제에서는 이를 위해 (Windows.UI.Xaml.Controls.)MediaPlayerElement 타입을 사용합니다. 그래서 UWP 앱에서와 같이 WPF의 xaml에서 이를 추가하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Grid> <<span style='color: blue; font-weight: bold'>MediaPlayerElement</span> x:Name="media"/> </Grid> </pre> <br /> 오류 선이 그어지면서 다음과 같은 메시지를 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MediaPlayerElement is not supported in a Windows Presentation Foundation (WPF) project. </pre> <br /> 또는 웹 상에서 검색하면 (Windows.UI.Xaml.Controls.)MediaElement 타입을 사용하는 예제를 볼 수 있는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Grid> <<span style='color: blue; font-weight: bold'>MediaElement</span> x:Name="media"/> </Grid> mediaElement.SetSource(stream, synthesisStream.ContentType); mediaElement.Play(); </pre> <br /> 역시 WPF에서는 사용할 수 없습니다. 문제는 UWP 앱의 컨트롤들이 WPF 구조와는 맞지 않기 때문인데 이러한 제약은 UI 컨트롤이 아니면 해당하지 않습니다. 따라서 (Windows.Media.Playback.)MediaPlayer 타입을 이용해 다음과 같이 직접 음성 출력을 해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows.Media.Playback.MediaPlayer mp = new Windows.Media.Playback.MediaPlayer(); mp.SetStreamSource(synthesisStream); mp.Play(); </pre> <br /> 또는 (System.Media.)SoundPlayer 타입을 이용해서도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.Media.SoundPlayer sp = new System.Media.SoundPlayer(); sp.Stream = = synthesisStream.AsStream(); sp.Play(); </pre> <br /> <hr style='width: 50%' /><br /> <br /> 참고로 SSML 구문을 사용하면 보다 더 세밀한 음성 제어가 가능합니다. "<a target='tab' href='https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/SpeechRecognitionAndSynthesis/cs'>Windows-universal-samples/Samples/SpeechRecognitionAndSynthesis/cs/</a>" 예제에 보면 다음과 같은 SSML 텍스트가 리소스로 포함되어 있는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://www.w3.org/2001/10/synthesis http://www.w3.org/TR/speech-synthesis/synthesis.xsd' xml:lang='en-US'> <mark name='phonetic'/>This is an example of a phonetic pronunciation: <phoneme alphabet='x-microsoft-ups' ph='S1 W AA T . CH AX . M AX . S2 K AA L . IH T'> whatchamacallit </phoneme>. <mark name='date'/>This is an example of a date: <say-as interpret-as='date' format='mdy'> 04/30/2013 </say-as>. <mark name='number'/>This is an example of an ordinal number: <say-as interpret-as='ordinal'> 4 </say-as>. <mark name='end'/> </speak> </pre> <br /> 보는 바와 같이 발음 기호, 날짜, 숫자를 인식하도록 출력을 제어할 수 있습니다. 이를 위해 SynthesizeTextToStreamAsync가 아닌 Synthesize<span style='color: blue; font-weight: bold'>Ssml</span>ToStreamAsync 메서드를 이용해 출력해야 합니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > SpeechSynthesisStream synthesisStream = await synthesizer.SynthesizeTextToStreamAsync(text); Windows.Media.Playback.MediaPlayer mp = new Windows.Media.Playback.MediaPlayer(); mp.SetStreamSource(synthesisStream); mp.Play(); </pre> <br /> 정리해 보면, <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1205&boardid=331301885'>다음의 코드는 첨부 파일에 포함된 예제 프로젝트의 xaml, xaml.cs 내용</a>입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Window x:Class="Listener10.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Listener10" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <ListBox x:Name="voiceList"> </ListBox> </Grid> </Window> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using Windows.Media.SpeechSynthesis; namespace Listener10 { public partial class MainWindow : Window { private SpeechSynthesizer _synthesizer = new SpeechSynthesizer(); public MainWindow() { InitializeComponent(); InitializeListboxVoiceChooser(); Speak(); } private async void Speak() { string text = "test is good"; if (!string.IsNullOrEmpty(text)) { try { SpeechSynthesisStream synthesisStream = await _synthesizer.SynthesizeTextToStreamAsync(text); // SpeechSynthesisStream synthesisStream = await synthesizer.SynthesizeSsmlToStreamAsync(Listener10.Properties.Resources.TTSText); //Windows.Media.Playback.MediaPlayer mp = new Windows.Media.Playback.MediaPlayer(); //mp.SetStreamSource(synthesisStream); //mp.Play(); System.Media.SoundPlayer sp = new System.Media.SoundPlayer(); sp.Stream = synthesisStream.AsStream(); sp.Play(); } catch (Exception e) { System.Diagnostics.Trace.WriteLine(e.ToString()); } } } private void InitializeListboxVoiceChooser() { var voices = SpeechSynthesizer.AllVoices; VoiceInformation currentVoice = _synthesizer.Voice; foreach (VoiceInformation voice in voices.OrderBy(p => p.Language)) { ListBoxItem item = new ListBoxItem(); item.Content = voice.DisplayName + " (Language: " + voice.Language + ")"; voiceList.Items.Add(item); if (currentVoice.Id == voice.Id) { item.IsSelected = true; voiceList.SelectedItem = item; } } } } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그럼 오류 유형을 한번 알아볼까요? ^^<br /> <br /> (Windows.UI.Xaml.Controls.)MediaElement나 (Windows.UI.Xaml.Controls.)MediaPlayerElement가 .xaml 파일에서 생성할 수 없기 때문에 .xaml.cs에서 new를 이용해 생성하려고 시도하면 다음과 같은 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.Windows.Controls.MediaElement me = new System.Windows.Controls.MediaElement(); /* {"The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"} */ </pre> <br /> 분명히 UI 스레드인데도 불구하고 저런 오류가 발생합니다. 실제로 UWP의 Dispatcher 역시 WPF에서 사용하게 되면 0x8000000e 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { // Your UI update code goes here! }); {"A method was called at an unexpected time.\r\n\r\nCould not create a new view because the main window has not yet been created"} </pre> <br /> 왜냐하면 WPF의 UI 엔진이 UWP의 UI 엔진과 호환하지 않기 때문입니다. 이 외에도 UWP에서만 가능한 구성 요소들은 WPF에서 사용할 수 없습니다. 안 그러면 다음과 같은 식의 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ResourceContext context = ResourceContext.GetForCurrentView(); {"Unsupported MRT profile type. (Exception from HRESULT: 0x80073B20)"} </pre> <br /> <hr style='width: 50%' /><br /> <br /> MediaElement 타입의 경우 WPF에서 System.Windows.Controls 네임스페이스를 통해 제공되긴 합니다. 하지만 UWP의 것과 완전히 다르기 때문에 UWP 예제에 나온 식으로 사용할 수 없습니다. 가령 UWP의 Windows.UI.Xaml.Controls.MediaElement의 경우 IRandomAccessStream을 인자로 받는 SetSource 메서드가 있지만 WPF에서 제공하는 System.Windows.Controls.MediaElement에는 Uri 타입만을 받는 Source 속성이 있을 뿐입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, Windows 10 미만의 버전이라면 System.Speech.Synthesis.SpeechSynthesizer를 사용하는 것도 방법입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C#으로 만드는 음성인식/TTS 프로그램 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1228'>http://www.sysnet.pe.kr/2/0/1228</a> </pre> </p><br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9109
(왼쪽의 숫자를 입력해야 합니다.)