2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Runtime.InteropServices;
21 using static Interop.Tts;
23 namespace Tizen.Uix.Tts
26 /// Enumeration for the states.
28 /// <since_tizen> 3 </since_tizen>
34 /// <since_tizen> 3 </since_tizen>
40 /// <since_tizen> 3 </since_tizen>
46 /// <since_tizen> 3 </since_tizen>
52 /// <since_tizen> 3 </since_tizen>
56 /// Unavailable state.
58 /// <since_tizen> 3 </since_tizen>
63 /// Enumeration for TTS mode.
65 /// <since_tizen> 3 </since_tizen>
69 /// Default mode for normal application.
71 /// <since_tizen> 3 </since_tizen>
75 /// Notification mode.
77 /// <since_tizen> 3 </since_tizen>
81 /// Accessibility mode.
83 /// <since_tizen> 3 </since_tizen>
88 /// Enumeration for error values that can occur.
90 /// <since_tizen> 3 </since_tizen>
94 /// Successful, no error.
96 /// <since_tizen> 3 </since_tizen>
101 /// <since_tizen> 3 </since_tizen>
106 /// <since_tizen> 3 </since_tizen>
109 /// Invalid parameter.
111 /// <since_tizen> 3 </since_tizen>
114 /// No answer from the STT service.
116 /// <since_tizen> 3 </since_tizen>
121 /// <since_tizen> 3 </since_tizen>
124 /// Permission denied.
126 /// <since_tizen> 3 </since_tizen>
129 /// STT not supported.
131 /// <since_tizen> 3 </since_tizen>
136 /// <since_tizen> 3 </since_tizen>
141 /// <since_tizen> 3 </since_tizen>
144 /// No available engine.
146 /// <since_tizen> 3 </since_tizen>
149 /// Operation failed.
151 /// <since_tizen> 3 </since_tizen>
154 /// Audio policy blocked.
156 /// <since_tizen> 3 </since_tizen>
161 /// Enumeration for the voice types.
163 /// <since_tizen> 3 </since_tizen>
167 /// The automatic voice type.
169 /// <since_tizen> 3 </since_tizen>
173 /// The male voice type.
175 /// <since_tizen> 3 </since_tizen>
179 /// The female voice type.
181 /// <since_tizen> 3 </since_tizen>
185 /// The child voice type.
187 /// <since_tizen> 3 </since_tizen>
192 /// You can use Text-To-Speech (TTS) API's to read sound data transformed by the engine from input texts.
193 /// Applications can add input-text to queue for reading continuously and control the player that can play, pause, and stop sound data synthesized from text.
195 /// <since_tizen> 3 </since_tizen>
196 public class TtsClient : IDisposable
198 private IntPtr _handle;
199 private event EventHandler<StateChangedEventArgs> _stateChanged;
200 private event EventHandler<UtteranceEventArgs> _utteranceStarted;
201 private event EventHandler<UtteranceEventArgs> _utteranceCompleted;
202 private event EventHandler<ErrorOccurredEventArgs> _errorOccurred;
203 private event EventHandler<DefaultVoiceChangedEventArgs> _defaultVoiceChanged;
204 private event EventHandler<EngineChangedEventArgs> _engineChanged;
205 private bool disposedValue = false;
206 private readonly Object _stateChangedLock = new Object();
207 private readonly Object _utteranceStartedLock = new Object();
208 private readonly Object _utteranceCompletedLock = new Object();
209 private readonly Object _errorOccurredLock = new Object();
210 private readonly Object _defaultVoiceChangedLock = new Object();
211 private readonly Object _engineChangedLock = new Object();
212 private TtsStateChangedCB _stateDelegate;
213 private TtsUtteranceStartedCB _utteranceStartedResultDelegate;
214 private TtsUtteranceCompletedCB _utteranceCompletedResultDelegate;
215 private TtsErrorCB _errorDelegate;
216 private TtsDefaultVoiceChangedCB _voiceChangedDelegate;
217 private TtsEngineChangedCB _engineDelegate;
218 private TtsSupportedVoiceCB _supportedvoiceDelegate;
221 /// Constructor to create a TTS instance.
223 /// <since_tizen> 3 </since_tizen>
225 /// http://tizen.org/feature/speech.synthesis
227 /// <exception cref="InvalidOperationException">
228 /// This exception can be due to the following reasons:
229 /// 1. Operation Failed
230 /// 2. Engine Not Found
232 /// <exception cref="OutOfMemoryException">This exception can be due to out Of memory.</exception>
233 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
237 TtsError error = TtsCreate(out handle);
238 if (error != TtsError.None)
240 Log.Error(LogTag, "Create Failed with error " + error);
241 throw ExceptionFactory.CreateException(error);
248 /// Destructor to destroy TtsClient handle.
256 /// Event to be invoked when TTS state changes.
258 /// <since_tizen> 3 </since_tizen>
259 public event EventHandler<StateChangedEventArgs> StateChanged
263 lock (_stateChangedLock)
265 if (_stateChanged == null)
267 _stateDelegate = (IntPtr handle, State previous, State current, IntPtr userData) =>
269 StateChangedEventArgs args = new StateChangedEventArgs(previous, current);
270 _stateChanged?.Invoke(this, args);
273 TtsError error = TtsSetStateChangedCB(_handle, _stateDelegate, IntPtr.Zero);
274 if (error != TtsError.None)
276 Log.Error(LogTag, "Add StateChanged Failed with error " + error);
279 _stateChanged += value;
286 lock (_stateChangedLock)
288 _stateChanged -= value;
289 if (_stateChanged == null)
291 TtsError error = TtsUnsetStateChangedCB(_handle);
292 if (error != TtsError.None)
294 Log.Error(LogTag, "Remove StateChanged Failed with error " + error);
303 /// Event to be invoked when the utterance starts.
305 /// <since_tizen> 3 </since_tizen>
306 public event EventHandler<UtteranceEventArgs> UtteranceStarted
310 lock (_utteranceStartedLock)
312 if (_utteranceStarted == null)
314 _utteranceStartedResultDelegate = (IntPtr handle, int uttId, IntPtr userData) =>
316 UtteranceEventArgs args = new UtteranceEventArgs(uttId);
317 _utteranceStarted?.Invoke(this, args);
320 TtsError error = TtsSetUtteranceStartedCB(_handle, _utteranceStartedResultDelegate, IntPtr.Zero);
321 if (error != TtsError.None)
323 Log.Error(LogTag, "Add UtteranceStarted Failed with error " + error);
326 _utteranceStarted += value;
332 lock (_utteranceStartedLock)
334 _utteranceStarted -= value;
335 if (_utteranceStarted == null)
337 TtsError error = TtsUnsetUtteranceStartedCB(_handle);
338 if (error != TtsError.None)
340 Log.Error(LogTag, "Remove UtteranceStarted Failed with error " + error);
348 /// Event to be invoked when the utterance completes.
350 /// <since_tizen> 3 </since_tizen>
351 public event EventHandler<UtteranceEventArgs> UtteranceCompleted
355 lock (_utteranceCompletedLock)
357 if (_utteranceCompleted == null)
359 _utteranceCompletedResultDelegate = (IntPtr handle, int uttId, IntPtr userData) =>
361 UtteranceEventArgs args = new UtteranceEventArgs(uttId);
362 _utteranceCompleted?.Invoke(this, args);
365 TtsError error = TtsSetUtteranceCompletedCB(_handle, _utteranceCompletedResultDelegate, IntPtr.Zero);
366 if (error != TtsError.None)
368 Log.Error(LogTag, "Add UtteranceCompleted Failed with error " + error);
371 _utteranceCompleted += value;
377 lock (_utteranceCompletedLock)
379 _utteranceCompleted -= value;
380 if (_utteranceCompleted == null)
382 TtsError error = TtsUnsetUtteranceCompletedCB(_handle);
383 if (error != TtsError.None)
385 Log.Error(LogTag, "Remove UtteranceCompleted Failed with error " + error);
393 /// Event to be invoked when an error occurs.
395 /// <since_tizen> 4 </since_tizen>
396 public event EventHandler<ErrorOccurredEventArgs> ErrorOccurred
400 lock (_errorOccurredLock)
402 if (_errorOccurred == null)
404 _errorDelegate = (IntPtr handle, int uttId, TtsError reason, IntPtr userData) =>
406 ErrorOccurredEventArgs args = new ErrorOccurredEventArgs(handle, uttId, reason);
407 _errorOccurred?.Invoke(this, args);
410 TtsError error = TtsSetErrorCB(_handle, _errorDelegate, IntPtr.Zero);
411 if (error != TtsError.None)
413 Log.Error(LogTag, "Add ErrorOccurred Failed with error " + error);
416 _errorOccurred += value;
422 lock (_errorOccurredLock)
424 _errorOccurred -= value;
425 if (_errorOccurred == null)
427 TtsError error = TtsUnsetErrorCB(_handle);
428 if (error != TtsError.None)
430 Log.Error(LogTag, "Remove ErrorOccurred Failed with error " + error);
438 /// Event to be invoked when an error occurs.
440 /// <since_tizen> 3 </since_tizen>
441 public event EventHandler<DefaultVoiceChangedEventArgs> DefaultVoiceChanged
445 lock (_defaultVoiceChangedLock)
447 if (_defaultVoiceChanged == null)
449 _voiceChangedDelegate = (IntPtr handle, IntPtr previousLanguage, int previousVoiceType, IntPtr currentLanguage, int currentVoiceType, IntPtr userData) =>
451 string previousLanguageString = Marshal.PtrToStringAnsi(previousLanguage);
452 string currentLanguageString = Marshal.PtrToStringAnsi(currentLanguage);
453 DefaultVoiceChangedEventArgs args = new DefaultVoiceChangedEventArgs(previousLanguageString, previousVoiceType, currentLanguageString, currentVoiceType);
454 _defaultVoiceChanged?.Invoke(this, args);
457 TtsError error = TtsSetDefaultVoiceChangedCB(_handle, _voiceChangedDelegate, IntPtr.Zero);
458 if (error != TtsError.None)
460 Log.Error(LogTag, "Add DefaultVoiceChanged Failed with error " + error);
463 _defaultVoiceChanged += value;
469 lock (_defaultVoiceChangedLock)
471 _defaultVoiceChanged -= value;
472 if (_defaultVoiceChanged == null)
474 TtsError error = TtsUnsetDefaultVoiceChangedCB(_handle);
475 if (error != TtsError.None)
477 Log.Error(LogTag, "Remove DefaultVoiceChanged Failed with error " + error);
485 /// Event to be invoked to detect engine change.
487 /// <since_tizen> 3 </since_tizen>
488 public event EventHandler<EngineChangedEventArgs> EngineChanged
492 lock (_engineChangedLock)
494 if (_engineChanged == null)
496 _engineDelegate = (IntPtr handle, IntPtr engineId, IntPtr language, int voiceType, bool needCredential, IntPtr userData) =>
498 string engineIdString = Marshal.PtrToStringAnsi(engineId);
499 string languageString = Marshal.PtrToStringAnsi(language);
500 EngineChangedEventArgs args = new EngineChangedEventArgs(engineIdString, languageString, voiceType, needCredential);
501 _engineChanged?.Invoke(this, args);
503 TtsError error = TtsSetEngineChangedCB(_handle, _engineDelegate, IntPtr.Zero);
504 if (error != TtsError.None)
506 Log.Error(LogTag, "Add EngineChanged Failed with error " + error);
509 _engineChanged += value;
515 lock (_engineChangedLock)
517 _engineChanged -= value;
518 if (_engineChanged == null)
520 TtsError error = TtsUnsetEngineChangedCB(_handle);
521 if (error != TtsError.None)
523 Log.Error(LogTag, "Remove EngineChanged Failed with error " + error);
531 /// Gets the default voice set by the user.
533 /// <since_tizen> 3 </since_tizen>
535 /// The default voice in TTS.
538 /// The default voice SupportedVoice value.
540 public SupportedVoice DefaultVoice
546 TtsError error = TtsGetDefaultVoice(_handle, out language, out voiceType);
547 if (error != TtsError.None)
549 Log.Error(LogTag, "DefaultVoice Failed with error " + error);
550 return new SupportedVoice();
553 return new SupportedVoice(language, voiceType);
558 /// Gets the maximum byte size for text.
560 /// <since_tizen> 3 </since_tizen>
562 /// The Maximum byte size for text.
565 /// The Default Voice SupportedVoice value, 0 if unable to get the value.
568 /// The State should be ready.
570 public uint MaxTextSize
575 TtsError error = TtsGetMaxTextSize(_handle, out maxTextSize);
576 if (error != TtsError.None)
578 Log.Error(LogTag, "MaxTextSize Failed with error " + error);
588 /// Gets the current TTS state.
590 /// <since_tizen> 3 </since_tizen>
592 /// The current state of TTS.
595 /// Current TTS State value.
597 public State CurrentState
602 TtsError error = TtsGetState(_handle, out state);
603 if (error != TtsError.None)
605 Log.Error(LogTag, "CurrentState Failed with error " + error);
606 return State.Unavailable;
615 /// The TTS Mode can be set using this property.
617 /// <since_tizen> 3 </since_tizen>
619 /// The current TTS mode (default, screen-reader, notification).
624 /// <exception cref="InvalidOperationException">
625 /// This exception can be due to the following reasons while setting the value:
626 /// 1. Operation Failed
627 /// 2. Engine Not Found
629 /// <exception cref="OutOfMemoryException">This exception can be due to out Of memory.</exception>
630 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
632 /// The State should be created.
634 public Mode CurrentMode
638 Mode mode = Mode.Default;
639 TtsError error = TtsGetMode(_handle, out mode);
640 if (error != TtsError.None)
642 Log.Error(LogTag, "Get Mode Failed with error " + error);
651 error = TtsSetMode(_handle, value);
653 if (error != TtsError.None)
655 Log.Error(LogTag, "Set Mode Failed with error " + error);
656 throw ExceptionFactory.CreateException(error);
662 /// Sets the application credential.
664 /// <since_tizen> 3 </since_tizen>
665 /// <param name="credential">.
666 /// The credential string.
669 /// http://tizen.org/feature/speech.synthesis
671 /// <exception cref="InvalidOperationException">This exception can be due to an invalid state.</exception>
672 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
673 /// <exception cref="ArgumentException">This exception can be due to improper value provided while setting the value.</exception>
675 /// The State should be created or ready.
677 public void SetCredential(string credential)
679 TtsError error = TtsSetCredential(_handle, credential);
680 if (error != TtsError.None)
682 Tizen.Log.Error(LogTag, "SetCredential Failed with error " + error);
683 throw ExceptionFactory.CreateException(error);
688 /// Connects to the TTS service asynchronously.
690 /// <since_tizen> 3 </since_tizen>
692 /// http://tizen.org/feature/speech.synthesis
694 /// <exception cref="InvalidOperationException">This exception can be due to an invalid state.</exception>
695 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
697 /// The State must be Created.
700 /// If this function is successful, the TTS state will be ready.
701 /// If this function is unsuccessful, ErrorOccurred event will be invoked.
703 public void Prepare()
705 TtsError error = TtsPrepare(_handle);
706 if (error != TtsError.None)
708 Log.Error(LogTag, "Prepare Failed with error " + error);
709 throw ExceptionFactory.CreateException(error);
714 /// Disconnects from the STT service.
716 /// <since_tizen> 3 </since_tizen>
718 /// http://tizen.org/feature/speech.synthesis
720 /// <exception cref="InvalidOperationException">This exception can be due to an invalid state.</exception>
721 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
723 /// The state must be ready.
726 /// If this function is successful, the TTS state will be created.
728 public void Unprepare()
730 TtsError error = TtsUnprepare(_handle);
731 if (error != TtsError.None)
733 Log.Error(LogTag, "Unprepare Failed with error " + error);
734 throw ExceptionFactory.CreateException(error);
739 /// Retrieves all supported voices of the current engine.
741 /// <since_tizen> 3 </since_tizen>
743 /// The list of SupportedVoice.
746 /// http://tizen.org/feature/speech.synthesis
748 /// <exception cref="InvalidOperationException">
749 /// This exception can be due to the following reasons:
750 /// 1. Engine Not Found
751 /// 2. Operation Failed
753 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
754 public IEnumerable<SupportedVoice> GetSupportedVoices()
756 List<SupportedVoice> voicesList = new List<SupportedVoice>();
758 _supportedvoiceDelegate = (IntPtr handle, IntPtr language, int voiceType, IntPtr userData) =>
760 string lang = Marshal.PtrToStringAnsi(language);
761 SupportedVoice voice = new SupportedVoice(lang, voiceType);
762 voicesList.Add(voice);
765 TtsError error = TtsForeachSupportedVoices(_handle, _supportedvoiceDelegate, IntPtr.Zero);
766 if (error != TtsError.None)
768 Log.Error(LogTag, "GetSupportedVoices Failed with error " + error);
769 throw ExceptionFactory.CreateException(error);
776 /// Gets the private data from TTS engine.
778 /// <since_tizen> 3 </since_tizen>
779 /// <param name="key">
783 /// The data corresponding to the provided key.
786 /// http://tizen.org/feature/speech.synthesis
788 /// <exception cref="InvalidOperationException">
789 /// This exception can be due to the following reasons:
791 /// 2. Engine Not found
792 /// 3. Operation Failure
794 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
796 /// The state must be ready.
798 public string GetPrivateData(string key)
801 TtsError error = TtsGetPrivateData(_handle, key, out data);
802 if (error != TtsError.None)
804 Log.Error(LogTag, "GetPrivateData Failed with error " + error);
805 throw ExceptionFactory.CreateException(error);
812 /// Sets the private data to tts engine.
814 /// <since_tizen> 3 </since_tizen>
815 /// <param name="key">
818 /// <param name="data">
822 /// http://tizen.org/feature/speech.synthesis
824 /// <exception cref="InvalidOperationException">
825 /// This exception can be due to the following reasons:
827 /// 2. Engine Not found
828 /// 3. Operation Failure
830 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
831 /// <exception cref="ArgumentException">This exception can be due to improper value provided while setting the value.</exception>
833 /// The state must be ready.
835 public void SetPrivateData(string key, string data)
837 TtsError error = TtsSetPrivateData(_handle, key, data);
838 if (error != TtsError.None)
840 Log.Error(LogTag, "SetPrivateData Failed with error " + error);
841 throw ExceptionFactory.CreateException(error);
846 /// Gets the speed range.
848 /// <since_tizen> 3 </since_tizen>
850 /// The SpeedRange value.
853 /// http://tizen.org/feature/speech.synthesis
855 /// <exception cref="InvalidOperationException">
856 /// This exception can be due to the following reasons:
858 /// 2. Operation Failure
860 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
862 /// The state must be created.
864 public SpeedRange GetSpeedRange()
866 int min = 0, max = 0, normal = 0;
867 TtsError error = TtsGetSpeedRange(_handle, out min, out normal, out max);
868 if (error != TtsError.None)
870 Log.Error(LogTag, "GetSpeedRange Failed with error " + error);
871 throw ExceptionFactory.CreateException(error);
874 return new SpeedRange(min, normal, max);
878 /// Adds a text to the queue.
880 /// <since_tizen> 3 </since_tizen>
882 /// Locale MUST be set for UTF-8 text validation check.
884 /// <param name="text">
885 /// An input text based UTF-8.
887 /// <param name="language">
888 /// The language selected from the SupportedVoice.Language Property obtained from GetSupportedVoices()(e.g. 'NULL'(Automatic),'en_US').
890 /// <param name="voiceType">
891 /// The voice type selected from the SupportedVoice.VoiceType Property obtained from GetSupportedVoices().
893 /// <param name="speed">
894 /// A speaking speed (e.g.0 for Auto or the value from SpeedRange Property).
897 /// The utterance ID.
900 /// http://tizen.org/feature/speech.synthesis
902 /// <exception cref="InvalidOperationException">
903 /// This exception can be due to the following reasons:
905 /// 2. Operation Failure
908 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
909 /// <exception cref="UnauthorizedAccessException">This exception can be due to permission denied.</exception>
910 /// <exception cref="ArgumentException">This exception can be due to improper value provided while setting the value.</exception>
912 /// The state must be ready or playing or paused.
914 public int AddText(string text, string language, int voiceType, int speed)
917 TtsError error = TtsAddText(_handle, text, language, voiceType, speed, out id);
918 if (error != TtsError.None)
920 Log.Error(LogTag, "AddText Failed with error " + error);
921 throw ExceptionFactory.CreateException(error);
928 /// Starts synthesizing voice from the text and plays the synthesized audio data.
930 /// <since_tizen> 3 </since_tizen>
932 /// http://tizen.org/feature/speech.synthesis
934 /// <exception cref="InvalidOperationException">
935 /// This exception can be due to the following reasons:
937 /// 2. Operation Failure
938 /// 3. Out of Network
940 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
941 /// <exception cref="UnauthorizedAccessException">This exception can be due to permission denied.</exception>
943 /// The state must be ready or paused.
946 /// If this function succeeds, the TTS state will be playing.
950 TtsError error = TtsPlay(_handle);
951 if (error != TtsError.None)
953 Log.Error(LogTag, "Play Failed with error " + error);
954 throw ExceptionFactory.CreateException(error);
959 /// Stops playing the utterance and clears the queue.
961 /// <since_tizen> 3 </since_tizen>
963 /// http://tizen.org/feature/speech.synthesis
965 /// <exception cref="InvalidOperationException">
966 /// This exception can be due to the following reasons:
968 /// 2. Operation Failure
970 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
972 /// The state must be ready or playing or paused.
975 /// If this function succeeds, the TTS state will be ready.
976 /// This function will remove all text added via AddText() and synthesized sound data.
980 TtsError error = TtsStop(_handle);
981 if (error != TtsError.None)
983 Log.Error(LogTag, "Stop Failed with error " + error);
984 throw ExceptionFactory.CreateException(error);
989 /// Pauses the currently playing utterance.
991 /// <since_tizen> 3 </since_tizen>
993 /// http://tizen.org/feature/speech.synthesis
995 /// <exception cref="InvalidOperationException">
996 /// This exception can be due to the following reasons:
998 /// 2. Operation Failure
1000 /// <exception cref="NotSupportedException">This exception can be due to TTS not supported.</exception>
1002 /// The state must be playing.
1005 /// If this function succeeds, the TTS state will be Paused.
1009 TtsError error = TtsPause(_handle);
1010 if (error != TtsError.None)
1012 Log.Error(LogTag, "Pause Failed with error " + error);
1013 throw ExceptionFactory.CreateException(error);
1018 /// Method to release resources.
1020 /// <since_tizen> 3 </since_tizen>
1021 public void Dispose()
1024 GC.SuppressFinalize(this);
1028 /// Method to release resources.
1030 /// <since_tizen> 3 </since_tizen>
1031 /// <param name="disposing">
1032 /// The boolean value for destoying tts handle.
1034 protected virtual void Dispose(bool disposing)
1038 if (_handle != IntPtr.Zero)
1040 TtsError error = TtsDestroy(_handle);
1041 if (error != TtsError.None)
1043 Log.Error(LogTag, "Destroy Failed with error " + error);
1045 _handle = IntPtr.Zero;
1048 disposedValue = true;