├── Speak ├── FonixTalk.dll ├── ftalk_us.dic ├── ftalk_us.dll ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── Speak.csproj ├── SharpTalk ├── lib │ ├── FonixTalk.dll │ ├── ftalk_us.dic │ └── ftalk_us.dll ├── SpeakFlags.cs ├── TTS_INDEX_T.cs ├── TTSMessageType.cs ├── TTS_PHONEME_T.cs ├── DeviceOptions.cs ├── PhonemeEventArgs.cs ├── MMRESULT.cs ├── TTSVoice.cs ├── LanguageCode.cs ├── Properties │ └── AssemblyInfo.cs ├── FonixTalkException.cs ├── TTS_BUFFER_T.cs ├── SharpTalk.csproj ├── SpeakerParams.cs └── FonixTalkEngine.cs ├── .gitattributes ├── SharpTalk.sln ├── README.md └── .gitignore /Speak/FonixTalk.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatsecretproject/SharpTalk/HEAD/Speak/FonixTalk.dll -------------------------------------------------------------------------------- /Speak/ftalk_us.dic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatsecretproject/SharpTalk/HEAD/Speak/ftalk_us.dic -------------------------------------------------------------------------------- /Speak/ftalk_us.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatsecretproject/SharpTalk/HEAD/Speak/ftalk_us.dll -------------------------------------------------------------------------------- /SharpTalk/lib/FonixTalk.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatsecretproject/SharpTalk/HEAD/SharpTalk/lib/FonixTalk.dll -------------------------------------------------------------------------------- /SharpTalk/lib/ftalk_us.dic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatsecretproject/SharpTalk/HEAD/SharpTalk/lib/ftalk_us.dic -------------------------------------------------------------------------------- /SharpTalk/lib/ftalk_us.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whatsecretproject/SharpTalk/HEAD/SharpTalk/lib/ftalk_us.dll -------------------------------------------------------------------------------- /Speak/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SharpTalk/SpeakFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpTalk 8 | { 9 | [Flags] 10 | internal enum SpeakFlags : uint 11 | { 12 | Normal = 0, 13 | Force = 1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SharpTalk/TTS_INDEX_T.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SharpTalk 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | // ReSharper disable once InconsistentNaming 7 | struct TTS_INDEX_T 8 | { 9 | public uint IndexValue; 10 | public uint SampleNumber; 11 | readonly uint _reserved; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SharpTalk/TTSMessageType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpTalk 8 | { 9 | internal enum TTSMessageType : uint 10 | { 11 | Buffer = 0, 12 | IndexMarker = 1, 13 | Status = 2, 14 | Visual = 3 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SharpTalk/TTS_PHONEME_T.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SharpTalk 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | // ReSharper disable once InconsistentNaming 7 | struct TTS_PHONEME_T 8 | { 9 | public uint Phoneme; 10 | public uint PhonemeSampleNumber; 11 | public uint PhonemeDuration; 12 | private readonly uint _reserved; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SharpTalk/DeviceOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace SharpTalk 9 | { 10 | [Flags] 11 | internal enum DeviceOptions : uint 12 | { 13 | OwnAudioDevice = 0x00000001, 14 | ReportOpenError = 0x00000002, 15 | UseSapi5AudioDevice = 0x40000000, 16 | DoNotUseAudioDevice = 0x80000000 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Speak/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SharpTalk; 3 | 4 | namespace Speak 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | Console.Title = "SharpTalk Speaking Terminal"; 11 | using (var tts = new FonixTalkEngine()) 12 | { 13 | string msg; 14 | while ((msg = Console.ReadLine()) != "exit") 15 | { 16 | tts.Speak(msg); 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /SharpTalk/PhonemeEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpTalk 8 | { 9 | /// 10 | /// Holds information about phoneme events fired by the TTS engine. 11 | /// 12 | public class PhonemeEventArgs : EventArgs 13 | { 14 | /// 15 | /// Indicates the phoneme being spoken. 16 | /// 17 | public readonly char Phoneme; 18 | /// 19 | /// The duration of the phoneme in milliseconds. 20 | /// 21 | public readonly uint Duration; 22 | 23 | internal PhonemeEventArgs(char phoneme, uint duration) 24 | { 25 | this.Phoneme = phoneme; 26 | this.Duration = duration; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SharpTalk/MMRESULT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpTalk 8 | { 9 | internal enum MMRESULT : uint 10 | { 11 | MMSYSERR_NOERROR = 0, 12 | MMSYSERR_ERROR = 1, 13 | MMSYSERR_BADDEVICEID = 2, 14 | MMSYSERR_NOTENABLED = 3, 15 | MMSYSERR_ALLOCATED = 4, 16 | MMSYSERR_INVALHANDLE = 5, 17 | MMSYSERR_NODRIVER = 6, 18 | MMSYSERR_NOMEM = 7, 19 | MMSYSERR_NOTSUPPORTED = 8, 20 | MMSYSERR_BADERRNUM = 9, 21 | MMSYSERR_INVALFLAG = 10, 22 | MMSYSERR_INVALPARAM = 11, 23 | MMSYSERR_HANDLEBUSY = 12, 24 | MMSYSERR_INVALIDALIAS = 13, 25 | MMSYSERR_BADDB = 14, 26 | MMSYSERR_KEYNOTFOUND = 15, 27 | MMSYSERR_READERROR = 16, 28 | MMSYSERR_WRITEERROR = 17, 29 | MMSYSERR_DELETEERROR = 18, 30 | MMSYSERR_VALNOTFOUND = 19, 31 | MMSYSERR_NODRIVERCB = 20, 32 | WAVERR_BADFORMAT = 32, 33 | WAVERR_STILLPLAYING = 33, 34 | WAVERR_UNPREPARED = 34 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SharpTalk/TTSVoice.cs: -------------------------------------------------------------------------------- 1 | namespace SharpTalk 2 | { 3 | /// 4 | /// Enumerates the available voices for DECtalk. 5 | /// 6 | public enum TtsVoice : uint 7 | { 8 | /// 9 | /// Default (male) voice. 10 | /// 11 | Paul = 0, 12 | /// 13 | /// Full female voice. 14 | /// 15 | Betty = 1, 16 | /// 17 | /// Full male voice. 18 | /// 19 | Harry = 2, 20 | /// 21 | /// Aged male voice. 22 | /// 23 | Frank = 3, 24 | /// 25 | /// Male voice. 26 | /// 27 | Dennis = 4, 28 | /// 29 | /// Child's voice. 30 | /// 31 | Kit = 5, 32 | /// 33 | /// Aged female voice. 34 | /// 35 | Ursula = 6, 36 | /// 37 | /// Female voice. 38 | /// 39 | Rita = 7, 40 | /// 41 | /// Whispering female voice. 42 | /// 43 | Wendy = 8 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SharpTalk/LanguageCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpTalk 8 | { 9 | /// 10 | /// Common language codes used for loading DECtalk dictionaries. 11 | /// 12 | public static class LanguageCode 13 | { 14 | /// 15 | /// United States English 16 | /// 17 | public const string EnglishUS = "US"; 18 | /// 19 | /// United Kingdom English 20 | /// 21 | public const string EnglishUK = "UK"; 22 | /// 23 | /// Castilian Spanish 24 | /// 25 | public const string SpanishCastilian = "SP"; 26 | /// 27 | /// Latin-American Spanish 28 | /// 29 | public const string SpanishLatinAmerican = "LA"; 30 | /// 31 | /// German 32 | /// 33 | public const string German = "GR"; 34 | /// 35 | /// French 36 | /// 37 | public const string French = "FR"; 38 | /// 39 | /// A special language code that tells SharpTalk not to load any language files. 40 | /// 41 | public const string None = "XX"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SharpTalk/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SharpTalk")] 9 | [assembly: AssemblyDescription("A .NET wrapper for the DECtalk TTS engine.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SharpTalk")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a8dbd00f-6cfd-4d00-a4d6-b0059b2eb887")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.*")] 36 | -------------------------------------------------------------------------------- /SharpTalk.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpTalk", "SharpTalk\SharpTalk.csproj", "{339EA1D4-4CDE-4342-AAA0-81A35FCF87DC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speak", "Speak\Speak.csproj", "{FC185343-12C9-4013-BB40-37E9DD5DD49C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {339EA1D4-4CDE-4342-AAA0-81A35FCF87DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {339EA1D4-4CDE-4342-AAA0-81A35FCF87DC}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {339EA1D4-4CDE-4342-AAA0-81A35FCF87DC}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {339EA1D4-4CDE-4342-AAA0-81A35FCF87DC}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {FC185343-12C9-4013-BB40-37E9DD5DD49C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {FC185343-12C9-4013-BB40-37E9DD5DD49C}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {FC185343-12C9-4013-BB40-37E9DD5DD49C}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {FC185343-12C9-4013-BB40-37E9DD5DD49C}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Speak/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Speak")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Hewlett-Packard Company")] 12 | [assembly: AssemblyProduct("Speak")] 13 | [assembly: AssemblyCopyright("Copyright © Hewlett-Packard Company 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0caf451e-e63c-441a-bcae-d6aa60f5c063")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SharpTalk/FonixTalkException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpTalk 8 | { 9 | /// 10 | /// Contains information related to errors thrown by the TTS engine. 11 | /// 12 | public sealed class FonixTalkException : Exception 13 | { 14 | internal FonixTalkException(MMRESULT code) : base(GetMessage(code)) 15 | { 16 | } 17 | 18 | internal FonixTalkException(string message) : base(message) 19 | { 20 | } 21 | 22 | private static string GetMessage(MMRESULT code) 23 | { 24 | switch(code) 25 | { 26 | case MMRESULT.MMSYSERR_INVALPARAM: 27 | return "An invalid parameter was passed to the function."; 28 | case MMRESULT.MMSYSERR_INVALHANDLE: 29 | return "The associated handle is invalid. Did you dispose it?"; 30 | case MMRESULT.MMSYSERR_ERROR: 31 | return "The function returned a generic error. Please check that you are using the functions correctly."; 32 | case MMRESULT.MMSYSERR_NOERROR: 33 | return "The function did not throw an error. If you are seeing this, Berkin was obviously high while coding."; 34 | case MMRESULT.MMSYSERR_NOMEM: 35 | return "There was insufficnent memory available to allocate the requested resources."; 36 | case MMRESULT.MMSYSERR_ALLOCATED: 37 | return "The requested resources are already in use somewhere else."; 38 | case MMRESULT.WAVERR_BADFORMAT: 39 | return "Wave output device does not support request format."; 40 | case MMRESULT.MMSYSERR_BADDEVICEID: 41 | return "Device ID out of range."; 42 | case MMRESULT.MMSYSERR_NODRIVER: 43 | return "No wave output device present."; 44 | default: 45 | return code.ToString(); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SharpTalk 2 | ========= 3 | 4 | A .NET wrapper for the FonixTalk TTS engine. 5 | 6 | 7 | This project was inspired by the creative antics of those utilizing Moonbase Alpha's TTS feature. Aeiou. 8 | 9 | I searched around exhausively for a decent TTS engine apart from Microsoft's SAPI, which has a .NET implementation. I don't like SAPI because its features are complicated, it depends on having custom voices installed, and SSML generally makes me want to puke. 10 | 11 | Eventually, I came across DECtalk and its accompanying SDK, from which I was able to get documentation for its functions. I spent countless hours implementing these in C# using P/Invoke, and I eventually got it working. 12 | 13 | After noticing some issues with DECtalk, I migrated the library to its successor, FonixTalk. 14 | 15 | 16 | Features 17 | ----- 18 | * Asynchronous speaking 19 | * Phoneme events for mouth movements in games/animations 20 | * Stream output for exporting voice audio as PCM data 21 | * Sync() method makes it easy to synchronize voice output 22 | * Adjustable voice and speaking rate 23 | * Multiple engines can be independently controlled and talking at the same time 24 | * Voices can be paused/resumed 25 | 26 | 27 | How to use 28 | ------ 29 | 30 | Using the library is very simple and straightforward. Here is the basic code to make the engine speak: 31 | 32 | ```cs 33 | var tts = new FonixTalkEngine(); 34 | tts.Speak("John Madden!"); 35 | ``` 36 | 37 | You can easily change the voice of the engine, too! For example, to use the Frank voice, just add one line: 38 | 39 | ```cs 40 | var tts = new FonixTalkEngine(); 41 | tts.Voice = TTSVoice.Frank; 42 | tts.Speak("Here comes another Chinese earthquake! [brbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbr]"); 43 | ``` 44 | 45 | Exporting speech audio to memory is just as simple: 46 | 47 | ```cs 48 | var tts = new FonixTalkEngine(); 49 | byte[] audioBytes = tts.SpeakToMemory("Eat your heart out, SAPI!"); 50 | // Process audio data here 51 | ``` 52 | 53 | Or if you'd like a WAV file instead, SharpTalk has you covered. 54 | ```cs 55 | var tts = new FonixTalkEngine(); 56 | tts.SpeakToWAVFile("speech.wav", "[:dv gv 0 br 120][:rate 300]I'm BatMan."); 57 | ``` 58 | -------------------------------------------------------------------------------- /SharpTalk/TTS_BUFFER_T.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SharpTalk 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal unsafe class TtsBufferManaged : IDisposable 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | // ReSharper disable once InconsistentNaming 11 | public struct TTS_BUFFER_T 12 | { 13 | public const int BufferSize = 16384; 14 | 15 | public IntPtr DataPtr; 16 | public TTS_PHONEME_T* PhonemeArrayPtr; 17 | public TTS_INDEX_T* IndexArrayPtr; 18 | 19 | public uint MaxBufferLength; 20 | public uint MaxPhonemeChanges; 21 | public uint MaxIndexMarks; 22 | 23 | public uint BufferLength; 24 | public uint PhonemeChangeCount; 25 | public uint IndexMarkCount; 26 | 27 | public uint _reserved; 28 | } 29 | 30 | TTS_BUFFER_T _value; 31 | GCHandle _pinHandle; 32 | 33 | public TtsBufferManaged() 34 | { 35 | _value = new TTS_BUFFER_T(); 36 | _pinHandle = GCHandle.Alloc(this, GCHandleType.Pinned); 37 | _value.MaxBufferLength = TTS_BUFFER_T.BufferSize; 38 | _value.DataPtr = Marshal.AllocHGlobal(TTS_BUFFER_T.BufferSize); 39 | } 40 | 41 | public bool Full 42 | { 43 | get { return _value.BufferLength == _value.MaxBufferLength; } 44 | } 45 | 46 | public byte[] GetBufferBytes() 47 | { 48 | var buffer = new byte[_value.BufferLength]; 49 | Marshal.Copy(_value.DataPtr, buffer, 0, (int)_value.BufferLength); 50 | return buffer; 51 | } 52 | 53 | public uint Length 54 | { 55 | get { return _value.BufferLength; } 56 | } 57 | 58 | public void Reset() 59 | { 60 | _value.BufferLength = 0; 61 | _value.PhonemeChangeCount = 0; 62 | _value.IndexMarkCount = 0; 63 | } 64 | 65 | public TTS_BUFFER_T* ValuePointer 66 | { 67 | get 68 | { 69 | fixed (TTS_BUFFER_T* p = &_value) 70 | { 71 | // This is fine here only because the class is always pinned. 72 | return p; 73 | } 74 | } 75 | } 76 | 77 | public void Dispose() 78 | { 79 | // No managed resources 80 | Marshal.FreeHGlobal(_value.DataPtr); 81 | _pinHandle.Free(); 82 | GC.SuppressFinalize(this); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Speak/Speak.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FC185343-12C9-4013-BB40-37E9DD5DD49C} 8 | Exe 9 | Properties 10 | Speak 11 | Speak 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | PreserveNewest 51 | 52 | 53 | 54 | 55 | PreserveNewest 56 | 57 | 58 | PreserveNewest 59 | 60 | 61 | 62 | 63 | {339ea1d4-4cde-4342-aaa0-81a35fcf87dc} 64 | SharpTalk 65 | 66 | 67 | {339ea1d4-4cde-4342-aaa0-81a35fcf87dc} 68 | SharpTalk 69 | 70 | 71 | 72 | 79 | -------------------------------------------------------------------------------- /SharpTalk/SharpTalk.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {339EA1D4-4CDE-4342-AAA0-81A35FCF87DC} 8 | Library 9 | Properties 10 | SharpTalk 11 | SharpTalk 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | bin\Debug\SharpTalk.XML 24 | true 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | bin\Release\SharpTalk.XML 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Always 64 | 65 | 66 | Always 67 | 68 | 69 | 70 | 71 | Always 72 | 73 | 74 | 75 | 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /SharpTalk/SpeakerParams.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SharpTalk 4 | { 5 | /// 6 | /// Contains parameters used to modify the sound of a TTS voice. 7 | /// 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct SpeakerParams 10 | { 11 | /// 12 | /// The sex of the speaker 13 | /// Design voice alias: sx 14 | /// 15 | [MarshalAs(UnmanagedType.I2)] 16 | public Sex Sex; 17 | 18 | /// 19 | /// Smoothness, in % 20 | /// Design voice alias: sm 21 | /// 22 | public short Smoothness; 23 | 24 | /// 25 | /// Assertiveness, in % 26 | /// Design voice alias: as 27 | /// 28 | public short Assertiveness; 29 | 30 | /// 31 | /// Average pitch, in Hz 32 | /// Design voice alias: ap 33 | /// 34 | public short AveragePitch; 35 | 36 | /// 37 | /// Breathiness, in decibels (dB) 38 | /// Design voice alias: br 39 | /// 40 | public short Breathiness; 41 | 42 | /// 43 | /// Richness, in % 44 | /// Design voice alias: ri 45 | /// 46 | public short Richness; 47 | 48 | /// 49 | /// Number of fixed samples of open glottis 50 | /// Design voice alias: nf 51 | /// 52 | public short NumFixedSampOG; 53 | 54 | /// 55 | /// Laryngealization, in % 56 | /// Design voice alias: la 57 | /// 58 | public short Laryngealization; 59 | 60 | /// 61 | /// Head size, in % 62 | /// Design voice alias: hs 63 | /// 64 | public short HeadSize; 65 | 66 | /// 67 | /// Fourth formant resonance frequency, in Hz 68 | /// Design voice alias: f4 69 | /// 70 | public short Formant4ResFreq; 71 | 72 | /// 73 | /// Fourth formant bandwidth, in Hz 74 | /// Design voice alias: b4 75 | /// 76 | public short Formant4Bandwidth; 77 | 78 | /// 79 | /// Fifth formant resonance frequency, in Hz 80 | /// Design voice alias: f5 81 | /// 82 | public short Formant5ResFreq; 83 | 84 | /// 85 | /// Fifth formant bandwidth, in Hz 86 | /// Design voice alias: b5 87 | /// 88 | public short Formant5Bandwidth; 89 | 90 | /// 91 | /// Parallel fourth formant frequency, in Hz 92 | /// 93 | public short Parallel4Freq; 94 | 95 | /// 96 | /// Parallel fifth formant frequency, in Hz 97 | /// 98 | public short Parallel5Freq; 99 | 100 | /// 101 | /// Gain of frication source, in dB 102 | /// Design voice alias: gf 103 | /// 104 | public short GainFrication; 105 | 106 | /// 107 | /// Gain of aspiration source, in dB 108 | /// Design voice alias: gh 109 | /// 110 | public short GainAspiration; 111 | 112 | /// 113 | /// Gain of voicing source, in dB 114 | /// Design voice alias: gv 115 | /// 116 | public short GainVoicing; 117 | 118 | /// 119 | /// Gain of nasalization, in dB 120 | /// Design voice alias: gn 121 | /// 122 | public short GainNasalization; 123 | 124 | /// 125 | /// Gain of cascade formant resonator 1, in dB 126 | /// Design voice alias: g1 127 | /// 128 | public short GainCFR1; 129 | 130 | /// 131 | /// Gain of cascade formant resonator 2, in dB 132 | /// Design voice alias: g2 133 | /// 134 | public short GainCFR2; 135 | 136 | /// 137 | /// Gain of cascade formant resonator 3, in dB 138 | /// Design voice alias: g3 139 | /// 140 | public short GainCFR3; 141 | 142 | /// 143 | /// Gain of cascade formant resonator 4, in dB 144 | /// Design voice alias: g4 145 | /// 146 | public short GainCFR4; 147 | 148 | /// 149 | /// Loudness, gain input to cascade 1st formant in dB 150 | /// Design voice alias: g5 151 | /// 152 | public short Loudness; 153 | 154 | /// 155 | /// Not the slightest clue what this does. 156 | /// 157 | public short SpectralTilt; 158 | 159 | /// 160 | /// Baseline fall, in Hz 161 | /// Design voice alias: bf 162 | /// 163 | public short BaselineFall; 164 | 165 | /// 166 | /// Lax breathiness, in % 167 | /// Design voice alias: lx 168 | /// 169 | public short LaxBreathiness; 170 | 171 | /// 172 | /// Quickness, in % 173 | /// Design voice alias: qu 174 | /// 175 | public short Quickness; 176 | 177 | /// 178 | /// Hat rise, in Hz 179 | /// Design voice alias: hr 180 | /// 181 | public short HatRise; 182 | 183 | /// 184 | /// Stress rise, in Hz 185 | /// Design voice alias: sr 186 | /// 187 | public short StressRise; 188 | 189 | /// 190 | /// Glottal speed 191 | /// 192 | public short GlottalSpeed; 193 | 194 | /// 195 | /// Output gain multiplier for FVTM 196 | /// 197 | public short OutputGainMultiplier; 198 | } 199 | 200 | /// 201 | /// Provides gender selection options for SpeakerParams. 202 | /// 203 | public enum Sex : short 204 | { 205 | /// 206 | /// Indicates a female voice. 207 | /// Design voice value: 0 208 | /// 209 | Female = 0, 210 | /// 211 | /// Indicates a male voice. 212 | /// Design voice value: 1 213 | /// 214 | Male = 1 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /SharpTalk/FonixTalkEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.IO; 4 | 5 | using System.Runtime.InteropServices; 6 | 7 | namespace SharpTalk 8 | { 9 | /// 10 | /// Wraps the functions contained in the FonixTalk TTS engine. 11 | /// 12 | public class FonixTalkEngine : IDisposable 13 | { 14 | #region Events 15 | /// 16 | /// Fired when a phoneme event is invoked by the engine. 17 | /// 18 | public event EventHandler Phoneme; 19 | #endregion 20 | 21 | #region Defaults 22 | /// 23 | /// The default speaking rate assigned to new instances of the engine. 24 | /// 25 | public const uint DefaultRate = 200; 26 | 27 | /// 28 | /// The default voice assigned to new instances of the engine. 29 | /// 30 | public const TtsVoice DefaultSpeaker = TtsVoice.Paul; 31 | #endregion 32 | 33 | #region P/Invoke stuff 34 | #region FonixTalk functions 35 | 36 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 37 | private delegate void DtCallbackRoutine( 38 | int lParam1, 39 | int lParam2, 40 | uint drCallbackParameter, 41 | uint uiMsg); 42 | 43 | [DllImport("FonixTalk.dll")] 44 | static extern MMRESULT TextToSpeechStartupEx( 45 | out IntPtr handle, 46 | uint uiDeviceNumber, 47 | uint dwDeviceOptions, 48 | DtCallbackRoutine callback, 49 | ref IntPtr dwCallbackParameter); 50 | 51 | [DllImport("FonixTalk.dll")] 52 | [return: MarshalAs(UnmanagedType.Bool)] 53 | static extern bool TextToSpeechSelectLang(IntPtr handle, uint lang); 54 | 55 | [DllImport("FonixTalk.dll")] 56 | static extern uint TextToSpeechStartLang( 57 | [MarshalAs(UnmanagedType.LPStr)] 58 | string lang); 59 | 60 | [DllImport("FonixTalk.dll")] 61 | static extern MMRESULT TextToSpeechSetSpeaker(IntPtr handle, TtsVoice speaker); 62 | 63 | [DllImport("FonixTalk.dll")] 64 | static extern MMRESULT TextToSpeechGetSpeaker(IntPtr handle, out TtsVoice speaker); 65 | 66 | [DllImport("FonixTalk.dll")] 67 | static extern MMRESULT TextToSpeechGetRate(IntPtr handle, out uint rate); 68 | 69 | [DllImport("FonixTalk.dll")] 70 | static extern MMRESULT TextToSpeechSetRate(IntPtr handle, uint rate); 71 | 72 | [DllImport("FonixTalk.dll")] 73 | static extern MMRESULT TextToSpeechSpeakA(IntPtr handle, 74 | [MarshalAs(UnmanagedType.LPStr)] 75 | string msg, 76 | uint flags); 77 | 78 | [DllImport("FonixTalk.dll")] 79 | static extern MMRESULT TextToSpeechShutdown(IntPtr handle); 80 | 81 | [DllImport("FonixTalk.dll")] 82 | static extern MMRESULT TextToSpeechPause(IntPtr handle); 83 | 84 | [DllImport("FonixTalk.dll")] 85 | static extern MMRESULT TextToSpeechResume(IntPtr handle); 86 | 87 | [DllImport("FonixTalk.dll")] 88 | static extern MMRESULT TextToSpeechReset(IntPtr handle, 89 | [MarshalAs(UnmanagedType.Bool)] 90 | bool bReset); 91 | 92 | [DllImport("FonixTalk.dll")] 93 | static extern MMRESULT TextToSpeechSync(IntPtr handle); 94 | 95 | /* These don't seem to have any effect, but I'll keep them here in case a fix is found. 96 | * 97 | [DllImport("FonixTalk.dll")] 98 | static extern MMRESULT TextToSpeechSetVolume(IntPtr handle, int type, int volume); 99 | 100 | [DllImport("FonixTalk.dll")] 101 | static extern MMRESULT TextToSpeechGetVolume(IntPtr handle, int type, out int volume); 102 | * 103 | */ 104 | 105 | [DllImport("FonixTalk.dll")] 106 | static extern MMRESULT TextToSpeechSetSpeakerParams(IntPtr handle, IntPtr spDefs); 107 | 108 | [DllImport("FonixTalk.dll")] 109 | static extern MMRESULT TextToSpeechGetSpeakerParams(IntPtr handle, uint uiIndex, 110 | out IntPtr ppspCur, 111 | out IntPtr ppspLoLimit, 112 | out IntPtr ppspHiLimit, 113 | out IntPtr ppspDefault); 114 | 115 | [DllImport("FonixTalk.dll")] 116 | static unsafe extern MMRESULT TextToSpeechAddBuffer(IntPtr handle, TtsBufferManaged.TTS_BUFFER_T* buffer); 117 | 118 | [DllImport("FonixTalk.dll")] 119 | static extern MMRESULT TextToSpeechOpenInMemory(IntPtr handle, uint format); 120 | 121 | [DllImport("FonixTalk.dll")] 122 | static extern MMRESULT TextToSpeechCloseInMemory(IntPtr handle); 123 | 124 | /* 125 | [DllImport("FonixTalk.dll")] 126 | static unsafe extern MMRESULT TextToSpeechReturnBuffer(IntPtr handle, TtsBufferManaged.TTS_BUFFER_T* buffer); 127 | */ 128 | #endregion 129 | 130 | #region Win32 functions 131 | [DllImport("user32.dll")] 132 | private static extern uint RegisterWindowMessage( 133 | [MarshalAs(UnmanagedType.LPStr)] 134 | string lpString); 135 | 136 | [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] 137 | private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); 138 | 139 | #endregion 140 | #endregion 141 | 142 | #region API Constants 143 | private const uint WaveFormat_1M16 = 0x00000004; 144 | private const uint TtsNotSupported = 0x7FFF; 145 | private const uint TtsNotAvailable = 0x7FFE; 146 | private const uint TtsLangError = 0x4000; 147 | #endregion 148 | 149 | #region Window messages 150 | 151 | // Message types 152 | private static readonly uint UiIndexMsg = RegisterWindowMessage("DECtalkIndexMessage"); 153 | private static readonly uint UiErrorMsg = RegisterWindowMessage("DECtalkErrorMessage"); 154 | private static readonly uint UiBufferMsg = RegisterWindowMessage("DECtalkBufferMessage"); 155 | private static readonly uint UiPhonemeMsg = RegisterWindowMessage("DECtalkVisualMessage"); 156 | 157 | #endregion 158 | 159 | #region Non-public fields 160 | private IntPtr _handle; 161 | private IntPtr _speakerParamsPtr, _dummy1, _dummy2, _dummy3; 162 | private DtCallbackRoutine _callback; 163 | private TtsBufferManaged _buffer; 164 | private Stream _bufferStream; 165 | #endregion 166 | 167 | #region Constructors and Initialization logic 168 | /// 169 | /// Initializes a new instance of the engine. 170 | /// 171 | /// The language to load. 172 | public FonixTalkEngine(string language) 173 | { 174 | Init(language); 175 | } 176 | 177 | /// 178 | /// Initializes a new instance of the engine in US English. 179 | /// 180 | public FonixTalkEngine() 181 | { 182 | Init("US"); 183 | } 184 | 185 | /// 186 | /// Initialize a new instance of the engine with the specified language, rate, and speaker voice. 187 | /// 188 | /// The language ID. 189 | /// The speaking rate to set. 190 | /// The speaker voice to set. 191 | public FonixTalkEngine(string language, uint rate, TtsVoice speaker) 192 | { 193 | Init(language); 194 | Voice = speaker; 195 | Rate = rate; 196 | } 197 | 198 | /// 199 | /// Initializes a new instance of the engine in US English with the specified rate and speaker voice. 200 | /// 201 | /// The speaking rate to set. 202 | /// The speaker voice to set. 203 | public FonixTalkEngine(uint rate, TtsVoice speaker) 204 | { 205 | Init(LanguageCode.EnglishUS); 206 | Voice = speaker; 207 | Rate = rate; 208 | } 209 | 210 | private void Init(string lang) 211 | { 212 | _callback = TtsCallback; 213 | _buffer = null; 214 | _bufferStream = null; 215 | 216 | if (lang != LanguageCode.None) 217 | { 218 | var langid = TextToSpeechStartLang(lang); 219 | 220 | if ((langid & TtsLangError) != 0) 221 | { 222 | switch (langid) 223 | { 224 | case TtsNotSupported: 225 | throw new FonixTalkException("This version of DECtalk does not support multiple languages."); 226 | case TtsNotAvailable: 227 | throw new FonixTalkException("The specified language was not found."); 228 | } 229 | } 230 | 231 | if (!TextToSpeechSelectLang(IntPtr.Zero, langid)) 232 | { 233 | throw new FonixTalkException("The specified language failed to load."); 234 | } 235 | } 236 | 237 | Check(TextToSpeechStartupEx(out _handle, 0xFFFFFFFF, 0, _callback, ref _handle)); 238 | 239 | Speak("[:phone on]"); // Enable singing by default 240 | } 241 | 242 | #endregion 243 | 244 | #region Callback and phoneme structures 245 | [StructLayout(LayoutKind.Sequential)] 246 | private struct PhonemeMark 247 | { 248 | public byte ThisPhoneme; 249 | public byte NextPhoneme; 250 | public ushort Duration; 251 | } 252 | 253 | [StructLayout(LayoutKind.Explicit)] 254 | private struct PhonemeTag 255 | { 256 | [FieldOffset(0)] 257 | public PhonemeMark PMData; 258 | [FieldOffset(0)] 259 | public int DWData; 260 | } 261 | 262 | private void TtsCallback(int lParam1, int lParam2, uint drCallbackParameter, uint uiMsg) 263 | { 264 | if (uiMsg == UiPhonemeMsg && Phoneme != null) 265 | { 266 | var tag = new PhonemeTag { DWData = lParam2 }; 267 | Phoneme(this, new PhonemeEventArgs((char)tag.PMData.ThisPhoneme, tag.PMData.Duration)); 268 | } 269 | else if (uiMsg == UiBufferMsg) 270 | { 271 | _bufferStream.Write(_buffer.GetBufferBytes(), 0, (int)_buffer.Length); 272 | var full = _buffer.Full; 273 | _buffer.Reset(); 274 | unsafe { Check(TextToSpeechAddBuffer(_handle, _buffer.ValuePointer)); } 275 | } 276 | else if (uiMsg == UiErrorMsg) 277 | { 278 | // You fucked up! 279 | } 280 | else if (uiMsg == UiIndexMsg) 281 | { 282 | // I don't even know what index messages are for... 283 | } 284 | } 285 | #endregion 286 | 287 | #region Properties 288 | /// 289 | /// Gets or sets the voice currently assigned to the engine. 290 | /// 291 | public TtsVoice Voice 292 | { 293 | get 294 | { 295 | TtsVoice voice; 296 | Check(TextToSpeechGetSpeaker(_handle, out voice)); 297 | return voice; 298 | } 299 | set 300 | { 301 | Check(TextToSpeechSetSpeaker(_handle, value)); 302 | } 303 | } 304 | 305 | /// 306 | /// Gets or sets the current speaking rate of the TTS voice. 307 | /// 308 | public uint Rate 309 | { 310 | get 311 | { 312 | uint rate; 313 | Check(TextToSpeechGetRate(_handle, out rate)); 314 | return rate; 315 | } 316 | set 317 | { 318 | Check(TextToSpeechSetRate(_handle, value)); 319 | } 320 | } 321 | 322 | #endregion 323 | 324 | #region Public methods 325 | 326 | /// 327 | /// Writes speech data to an internal buffer and returns it as a byte array containing 16-bit 11025Hz mono PCM data. 328 | /// 329 | /// The input text to process. 330 | /// 331 | public byte[] SpeakToMemory(string input) 332 | { 333 | using (_bufferStream = new MemoryStream()) 334 | { 335 | using (OpenInMemory(WaveFormat_1M16)) 336 | using (ReadyBuffer()) 337 | { 338 | Speak(input); 339 | Sync(); 340 | TextToSpeechReset(_handle, false); 341 | } 342 | return ((MemoryStream)_bufferStream).ToArray(); 343 | } 344 | } 345 | 346 | /// 347 | /// Writes speech data to the specified stream as 16-bit 11025Hz mono PCM data. 348 | /// 349 | /// The stream to write to. 350 | /// The input text to process. 351 | /// 352 | public void SpeakToStream(Stream stream, string input) 353 | { 354 | _bufferStream = stream; 355 | using (OpenInMemory(WaveFormat_1M16)) 356 | using (ReadyBuffer()) 357 | { 358 | Speak(input); 359 | Sync(); 360 | TextToSpeechReset(_handle, false); 361 | } 362 | _bufferStream = null; 363 | } 364 | 365 | /// 366 | /// Writes speech data to a PCM WAV file. 367 | /// 368 | /// The path to the file. 369 | /// The input text to process. 370 | public void SpeakToWavFile(string path, string input) 371 | { 372 | const int headerSize = 44; 373 | const int formatChunkSize = 16; 374 | const short waveAudioFormat = 1; 375 | const short numChannels = 1; 376 | const int sampleRate = 11025; 377 | const short bitsPerSample = 16; 378 | const int byteRate = (numChannels * bitsPerSample * sampleRate) / 8; 379 | const short blockAlign = numChannels * bitsPerSample / 8; 380 | 381 | using (var dataStream = new MemoryStream()) 382 | { 383 | SpeakToStream(dataStream, input); 384 | var sizeInBytes = (int)dataStream.Length; 385 | using (var writer = new BinaryWriter(File.Create(path), Encoding.ASCII)) 386 | { 387 | writer.Write("RIFF".ToCharArray()); 388 | writer.Write(sizeInBytes + headerSize - 8); 389 | writer.Write("WAVE".ToCharArray()); 390 | writer.Write("fmt ".ToCharArray()); 391 | writer.Write(formatChunkSize); 392 | writer.Write(waveAudioFormat); 393 | writer.Write(numChannels); 394 | writer.Write(sampleRate); 395 | writer.Write(byteRate); 396 | writer.Write(blockAlign); 397 | writer.Write(bitsPerSample); 398 | writer.Write("data".ToCharArray()); 399 | writer.Write(sizeInBytes); 400 | dataStream.Position = 0; 401 | dataStream.CopyTo(writer.BaseStream); 402 | } 403 | } 404 | } 405 | 406 | /// 407 | /// Gets or sets the current speaker parameters. 408 | /// 409 | public SpeakerParams SpeakerParams 410 | { 411 | get 412 | { 413 | Check(TextToSpeechGetSpeakerParams(_handle, 0, out _speakerParamsPtr, out _dummy1, out _dummy2, out _dummy3)); 414 | return (SpeakerParams)Marshal.PtrToStructure(_speakerParamsPtr, typeof(SpeakerParams)); 415 | } 416 | set 417 | { 418 | Check(TextToSpeechGetSpeakerParams(_handle, 0, out _speakerParamsPtr, out _dummy1, out _dummy2, out _dummy3)); 419 | Marshal.StructureToPtr(value, _speakerParamsPtr, false); 420 | Check(TextToSpeechSetSpeakerParams(_handle, _speakerParamsPtr)); 421 | } 422 | } 423 | 424 | /// 425 | /// Pauses TTS audio output. 426 | /// 427 | public void Pause() 428 | { 429 | Check(TextToSpeechPause(_handle)); 430 | } 431 | 432 | /// 433 | /// Resumes previously paused TTS audio output. 434 | /// 435 | public void Resume() 436 | { 437 | Check(TextToSpeechResume(_handle)); 438 | } 439 | 440 | /// 441 | /// Flushes all previously queued text from the TTS system and stops any audio output. 442 | /// 443 | public void Reset() 444 | { 445 | Check(TextToSpeechReset(_handle, false)); 446 | } 447 | 448 | /// 449 | /// Blocks until all previously queued text is processed. 450 | /// 451 | public void Sync() 452 | { 453 | Check(TextToSpeechSync(_handle)); 454 | } 455 | 456 | /// 457 | /// Causes the engine to begin asynchronously speaking a specified phrase. If the engine is in the middle of speaking, the message passed will be queued. 458 | /// 459 | /// The phrase for the engine to speak. 460 | public void Speak(string msg) 461 | { 462 | Check(TextToSpeechSpeakA(_handle, msg, (uint)SpeakFlags.Force)); 463 | } 464 | 465 | #endregion 466 | 467 | #region Non-public methods 468 | private static void Check(MMRESULT code) 469 | { 470 | if (code != MMRESULT.MMSYSERR_NOERROR) 471 | { 472 | throw new FonixTalkException(code); 473 | } 474 | } 475 | 476 | #region OpenCloseInMemory 477 | private InMemoryRaiiHelper OpenInMemory(uint format) 478 | { 479 | Check(TextToSpeechOpenInMemory(_handle, format)); 480 | return new InMemoryRaiiHelper(this); 481 | } 482 | 483 | private void CloseInMemory() 484 | { 485 | Check(TextToSpeechCloseInMemory(_handle)); 486 | } 487 | 488 | private struct InMemoryRaiiHelper : IDisposable 489 | { 490 | private readonly FonixTalkEngine _engine; 491 | 492 | public InMemoryRaiiHelper(FonixTalkEngine engine) 493 | { 494 | _engine = engine; 495 | } 496 | 497 | public void Dispose() 498 | { 499 | _engine.CloseInMemory(); 500 | } 501 | } 502 | #endregion 503 | 504 | #region Buffer 505 | private BufferRaiiHelper ReadyBuffer() 506 | { 507 | if (_buffer != null) 508 | { 509 | // Buffer was created by previous call to this method 510 | throw new InvalidOperationException("Buffer already exists."); 511 | } 512 | _buffer = new TtsBufferManaged(); 513 | unsafe { Check(TextToSpeechAddBuffer(_handle, _buffer.ValuePointer)); } 514 | return new BufferRaiiHelper(this); 515 | } 516 | 517 | private void FreeBuffer() 518 | { 519 | _buffer.Dispose(); 520 | _buffer = null; 521 | } 522 | 523 | // I'm putting this here because it's the only place in this file I can think of it fits. 524 | private struct BufferRaiiHelper : IDisposable 525 | { 526 | private readonly FonixTalkEngine _engine; 527 | 528 | public BufferRaiiHelper(FonixTalkEngine engine) 529 | { 530 | _engine = engine; 531 | } 532 | 533 | public void Dispose() 534 | { 535 | _engine.FreeBuffer(); 536 | } 537 | } 538 | #endregion 539 | #endregion 540 | 541 | #region Disposal 542 | 543 | /// 544 | /// Releases all resources used by this instance. 545 | /// 546 | ~FonixTalkEngine() 547 | { 548 | Dispose(false); 549 | } 550 | 551 | /// 552 | /// Releases all resources used by this instance. 553 | /// 554 | public void Dispose() 555 | { 556 | Dispose(true); 557 | } 558 | 559 | private void Dispose(bool disposing) 560 | { 561 | TextToSpeechShutdown(_handle); 562 | if (_buffer != null) 563 | { 564 | // This is probably never called. 565 | _buffer.Dispose(); 566 | } 567 | if (disposing) 568 | { 569 | GC.SuppressFinalize(this); 570 | } 571 | } 572 | #endregion 573 | } 574 | } 575 | --------------------------------------------------------------------------------