public static string WrapAmazonPitchParam(this TTSPitch pitch, string text) { int pitchShift; switch (pitch) { case TTSPitch.X_Low: case TTSPitch.Low: case TTSPitch.High: case TTSPitch.X_High: //proceed break; case TTSPitch.Medium: return(text); case TTSPitch.Unassigned: goto case TTSPitch.Medium; default: BGC.Debug.LogError($"TTS Pitch not supported {pitch}"); goto case TTSPitch.Unassigned; } //pitchShift = (int)Math.Round(100 * Math.Pow(2, pitch.GetSemitoneShift() / 12.0)) - 100; pitchShift = (int)Math.Round(100 * (pitch.GetSemitoneShift() / 12.0)); return($"<prosody pitch=\"{pitchShift:+0;-#}%\">{text}</prosody>"); }
public static double GetSemitoneShift(this TTSPitch pitch) { switch (pitch) { case TTSPitch.X_Low: return(-10); case TTSPitch.Low: return(-5); case TTSPitch.Medium: return(0); case TTSPitch.High: return(5); case TTSPitch.X_High: return(10); case TTSPitch.Unassigned: goto case TTSPitch.Medium; default: BGC.Debug.LogError($"TTS Pitch not supported {pitch}"); goto case TTSPitch.Unassigned; } }
public async Task <AudioRequest> TTSRequest(TTSVoice voicePreference, TTSPitch pitchPreference, Effect effectsChain, string[] splitTTSText) { if (splitTTSText.Any(x => x.Contains('/') || x.Contains('!'))) { List <AudioRequest> audioRequestSegments = new List <AudioRequest>(); //Complex parsing StringBuilder stringbuilder = new StringBuilder(); foreach (string ttsWord in splitTTSText) { if (ttsWord.Contains('/') || ttsWord.Contains('!')) { foreach (string ttsWordSegment in SplitStringByCommandRegex(ttsWord)) { if (ttsWordSegment.StartsWith('/')) { //Sound Effect SoundEffect soundEffect = soundEffectSystem.GetSoundEffectByAlias(ttsWordSegment); if (soundEffect is null) { //Unrecognized, append as is stringbuilder.Append(ttsWordSegment); } else { //Output current if (stringbuilder.Length > 0) { string filename = await GetSynthSpeech(stringbuilder.ToString(), voicePreference, pitchPreference); audioRequestSegments.Add(new AudioFileRequest(filename, effectsChain)); stringbuilder.Clear(); } audioRequestSegments.Add(new SoundEffectRequest(soundEffect)); } } else if (ttsWordSegment.StartsWith('!')) { //Command AudioRequest request = AudioRequest.ParseCommand(ttsWordSegment.ToLower()); if (request is null) { //Unrecognized, append as is stringbuilder.Append(ttsWordSegment); } else { //Output current if (stringbuilder.Length > 0) { string filename = await GetSynthSpeech(stringbuilder.ToString(), voicePreference, pitchPreference); audioRequestSegments.Add(new AudioFileRequest(filename, effectsChain)); stringbuilder.Clear(); } audioRequestSegments.Add(request); } } else { stringbuilder.Append(ttsWordSegment); } } if (stringbuilder.Length > 0) { stringbuilder.Append(' '); } } else { stringbuilder.Append(ttsWord); stringbuilder.Append(' '); } } if (stringbuilder.Length > 0) { string filename = await GetSynthSpeech(stringbuilder.ToString(), voicePreference, pitchPreference); audioRequestSegments.Add(new AudioFileRequest(filename, effectsChain)); stringbuilder.Clear(); } return(new ConcatenatedAudioRequest(audioRequestSegments)); } else { //Simple parsing string ttsSpeech = string.Join(' ', splitTTSText); string filename = await GetSynthSpeech(ttsSpeech, voicePreference, pitchPreference); return(new AudioFileRequest(filename, effectsChain)); } }
public Task <AudioRequest> TTSRequest(TTSVoice voicePreference, TTSPitch pitchPreference, Effect effectsChain, string ttsText) => TTSRequest(voicePreference, pitchPreference, effectsChain, ttsText.Split(' ', options: StringSplitOptions.RemoveEmptyEntries));
protected async Task <string> GetGoogleSynthSpeech(string text, TTSVoice voicePreference, TTSPitch pitchPreference, string filename = null) { VoiceSelectionParams voice = voicePreference.GetGoogleVoiceSelectionParams(); AudioConfig config = new AudioConfig { AudioEncoding = AudioEncoding.Mp3, Pitch = pitchPreference.GetSemitoneShift() }; //TTS SynthesisInput input = new SynthesisInput { Ssml = PrepareGoogleSSML(text) }; // Perform the Text-to-Speech request, passing the text input // with the selected voice parameters and audio file type GoogleSynthesizeSpeechResponse response = await googleClient.SynthesizeSpeechAsync(input, voice, config); // Write the binary AudioContent of the response to file. string filepath; if (string.IsNullOrWhiteSpace(filename)) { filepath = Path.Combine(TTSFilesPath, $"{Guid.NewGuid()}.mp3"); } else { filepath = Path.Combine(TTSFilesPath, $"{filename}.mp3"); } using (Stream file = new FileStream(filepath, FileMode.Create)) { response.AudioContent.WriteTo(file); } return(filepath); }
protected async Task <string> GetAmazonSynthSpeech(string text, TTSVoice voicePreference, TTSPitch pitchPreference, string filename = null) { AmazonSynthesizeSpeechRequest synthesisRequest = voicePreference.GetAmazonTTSSpeechRequest(); synthesisRequest.TextType = TextType.Ssml; synthesisRequest.Text = PrepareAmazonSSML(text, pitchPreference); // Perform the Text-to-Speech request, passing the text input // with the selected voice parameters and audio file type AmazonSynthesizeSpeechResponse synthesisResponse = await amazonClient.SynthesizeSpeechAsync(synthesisRequest); // Write the binary AudioContent of the response to file. string filepath; if (string.IsNullOrWhiteSpace(filename)) { filepath = Path.Combine(TTSFilesPath, $"{Guid.NewGuid()}.mp3"); } else { filepath = Path.Combine(TTSFilesPath, $"{filename}.mp3"); } using (Stream file = new FileStream(filepath, FileMode.Create)) { await synthesisResponse.AudioStream.CopyToAsync(file); await file.FlushAsync(); file.Close(); } return(filepath); }
protected async Task <string> GetSynthSpeech(string text, TTSVoice voicePreference, TTSPitch pitchPreference) { switch (voicePreference.GetTTSService()) { case TTSService.Amazon: return(await GetAmazonSynthSpeech(text, voicePreference, pitchPreference)); case TTSService.Google: return(await GetGoogleSynthSpeech(text, voicePreference, pitchPreference)); default: communication.SendErrorMessage($"Unsupported TTSVoice for TTSService {voicePreference}"); goto case TTSService.Google; } }