├── 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 |
--------------------------------------------------------------------------------