/// <summary> /// Queue up an action to say. /// </summary> /// <param name="who"></param> /// <param name="what"></param> /// <param name="where"></param> /// <param name="v"></param> internal void SayAction( string who, string what, Vector3 where, AssignedVoice v, bool spatial) { if (queue == null) { return; } QueuedSpeech e = new QueuedSpeech( subs.FixExpressions(who), subs.FixExpressions(what), where, v, true, spatial, BeepType.None); lock (queue) { queue.Enqueue(e); Monitor.Pulse(queue); } }
/// <summary> /// Say the main part of the message. /// </summary> /// <param name="utterance"></param> private void SaySegment(QueuedSpeech utterance) { PromptStyle body = new PromptStyle(); switch (utterance.voice.rateModification) { case 00: body.Rate = PromptRate.Medium; break; case +1: body.Rate = PromptRate.Fast; break; case -1: body.Rate = PromptRate.Slow; break; } switch (utterance.voice.pitchModification) { case 00: body.Emphasis = PromptEmphasis.Moderate; break; case +1: body.Emphasis = PromptEmphasis.Strong; break; case -1: body.Emphasis = PromptEmphasis.Reduced; break; } pb.StartStyle(body); pb.StartSentence(); pb.AppendText(utterance.message); pb.EndSentence(); pb.EndStyle(); }
internal void Speak(QueuedSpeech utterance, string outputfile) { if (syn == null) return; // Got something to say. Initialize temp file. StartSpeech(utterance.voice, outputfile ); // Play the beep, if there is one. if (utterance.beep != BeepType.None) { pb.StartSentence(); pb.AppendAudio(BeepNames[(int)utterance.beep]); pb.EndSentence(); } // Say who is talking, if we know. if (utterance.speaker != null) { if (utterance.isAction) { SayPrompt(utterance.speaker); } else { // Speak the name of the person speaking. This is done in a neutral, // softer voice, speaking slightly rapidly. SayPrompt(utterance.speaker + "."); } } // Then synthesize the main part of the message. SaySegment(utterance); // Close the temporary WAV file. FinishSpeech(); }
internal void Speak(QueuedSpeech q, string outputfile) { control.osLayer?.Speak(q, outputfile); }
public void Speak( RadegastSpeech.Talk.QueuedSpeech utterance, string filename) { synth.Speak(utterance, filename); }
internal void Speak(QueuedSpeech q, string outputfile) { if (control.osLayer == null) return; control.osLayer.Speak(q, outputfile); }
/// <summary> /// Synthesize a speech with Festival /// </summary> /// <param name="utterance"></param> /// <param name="outputfile"></param> internal void Speak(QueuedSpeech utterance, string outputfile) { string args; // Construct the exact sequence to say in SABLE notation. // SABLE is a simple XML syntax for describing how to speak something. // First set voice name. string message = "<?xml version=\"1.0\"?>\n" + "<!DOCTYPE SABLE PUBLIC \"-//SABLE//DTD SABLE speech mark up//EN\"\n"+ "\"Sable.v0_2.dtd\" []>\n" + "<SABLE><SPEAKER NAME=\"" + utterance.voice.root.Name + "\">\n"; // Then pitch and rate variations on that for more variety. if (utterance.voice.pitchModification > 0) message += "<PITCH BASE=\"+30%\">"; else if (utterance.voice.pitchModification < 0) message += "<PITCH BASE=\"-30%\">"; int effectiveSpeed = utterance.voice.rateModification + rateBias; if (effectiveSpeed != 0) message += "<RATE SPEED=\"" + effectiveSpeed.ToString("+#;-#;0") + "%\">"; // For an action it all comes out at a constant speed. // For chat, the name is said quickly, with a break after it. if (utterance.isAction) message += utterance.speaker + " "; else message += "<RATE SPEED=\"+25%\">" + utterance.speaker + " </RATE><BREAK/>\n"; // Supress any embedded tags which would confuse SABLE. message += Regex.Replace(utterance.message, @"[<>]", "") + "\n"; // Close any modifications, in reverse order. if (utterance.voice.rateModification != 0) message += "</RATE>"; if (utterance.voice.pitchModification != 0) message += "</PITCH>"; message += "</SPEAKER></SABLE>\n"; // Write this to a temporary file. Filename is the output // filename with ".wav" changed to ".sable". // TODO talk directly to Festival server. string textfilename = Regex.Replace(outputfile, @"\.wav$", ".sable"); FileStream tstream = new FileStream(textfilename, FileMode.Create, FileAccess.Write); byte[] msgBytes = ToASCII.GetBytes(message); tstream.Write(msgBytes, 0, msgBytes.Length); tstream.Close(); // args = Regex.Replace( ActualArgs, @"%T", textfilename ); // Put the desired WAV file name in the command. args = Regex.Replace( ActualArgs, @"%F", outputfile ); // Run synthesizer externally // TODO Talk directly to the Festival Server on port 1314. Process proc = new Process( ); proc.StartInfo.FileName = SynthCommand; proc.StartInfo.Arguments = args; proc.StartInfo.UseShellExecute = false; proc.StartInfo.RedirectStandardInput = true; proc.StartInfo.RedirectStandardError = false; proc.StartInfo.RedirectStandardOutput = false; try { if( proc.Start( ) ) { // Send the SCHEME command to redirect output back here proc.StandardInput.WriteLine("(tts_return_to_client)"); // Send the SCHEME command to voice the SABLE file. proc.StandardInput.WriteLine("(tts \"" + textfilename + "\" 'sable)"); // proc.StandardInput.WriteLine("(tts_textall \"" + saythis + "\" 'nil)"); proc.StandardInput.Close(); proc.WaitForExit( ); } } catch( Exception e ) { System.Console.WriteLine( "Festival process error " + e.Message ); return; } // All done with the intermediate file. File.Delete(textfilename); }
/// <summary> /// Queue up an action to say. /// </summary> /// <param name="who"></param> /// <param name="what"></param> /// <param name="where"></param> /// <param name="v"></param> internal void SayAction( string who, string what, Vector3 where, AssignedVoice v, bool spatial) { if (queue == null) return; QueuedSpeech e = new QueuedSpeech( subs.FixExpressions(who), subs.FixExpressions(what), where, v, true, spatial, BeepType.None); lock (queue) { queue.Enqueue(e); Monitor.Pulse(queue); } }
internal void Say( string who, string what, Vector3 where, AssignedVoice v, bool doRotate, BeepType beep) { if (queue == null) return; QueuedSpeech e = new QueuedSpeech( subs.FixExpressions(who), subs.FixExpressions(what), where, v, false, doRotate, beep); // Put that on the queue and wake up the background thread. lock (queue) { queue.Enqueue(e); Monitor.Pulse(queue); } }
/// <summary> /// Say something as a named person or object at a location. /// </summary> /// <param name="who">Name of person speaking</param> /// <param name="what">What they said</param> /// <param name="where">Where they were standing when they said it</param> /// <param name="v">The voice they use</param> internal void Say( string who, string what, Vector3 where, AssignedVoice v) { if (queue == null) return; // Create queue entry from the information. QueuedSpeech e = new QueuedSpeech( subs.FixExpressions(who), subs.FixExpressions(what), where, v, false); // Put that on the queue and wake up the background thread. lock (queue) { queue.Enqueue(e); Monitor.Pulse(queue); } }
/// <summary> /// Tell OSX Speech Synthesizer to speak some text. /// </summary> /// <param name="utterance"></param> /// <param name="outputfile"></param> internal void Speak(QueuedSpeech utterance, string outputfile) { string message; // Remove any embedded command delimiters. string sayThis = Regex.Replace( utterance.message, @"\[\[", "" ); if (utterance.isAction) // Action statements are spoken all together. Such as // "/me looks nervous." The "/me" will have been substituted // with the correct name earlier. message = utterance.speaker + " " + sayThis; else // Normal speech has the name spoken quickly and slightly softer. message = "[[rate +1.0;volm -10.0]]" + utterance.speaker + "[[rate -1.0;volm +10.0;slnc 200]]" + // 200ms pause after name sayThis; syn.SetVoice(utterance.voice.root.Name); NSURL fileURL = new NSURL("file://" + outputfile); syn.StartSpeakingStringToURL(message, fileURL); // Wait for it to finish. This proceeds at faster than // speaking speed because output is to a file. // TODO use a callback to detect this. while (syn.IsSpeaking) { Thread.Sleep(200); // Check 5x per second. } }