Autor Beitrag
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2279
Erhaltene Danke: 419

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Do 19.05.16 21:11 
Hallo Forum

Für eines meiner kommenden Projekte habe ich mir heute Abend einen Art Wrapper für Sapi 5.1 geschrieben, der vielmehr eine kleine Vereinfachung von der SpeechLib_TLB (unter Typbibliothek importieren) ist.

Im Moment beschränkt sich die Benutzung auf folgende Punkte:
- Abspielen / Pausieren / Fortsetzen und Abbrechen
- Abspielgeschwindigkeits - Regelung
- Lautstärke - Regelung
- Auflistung aller installierten Stimmen (können mit der Eigenschaft VoiceIndex gesetzt werden)
- Auflistung derer Beschreibung (Name, Geschlecht, Sprache und Hersteller der Stimme)
- Konvertieren und Speichern eines Textes nach WAV

Dazu verwendet ihr nur die Klasse TfrSAPI.

Was ihr vielleicht noch brauchen könntet:
- SDK: Speech SDK 5.1
- Stimmen: Runtime Languages

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
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:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
unit frSapiWrapper;

interface

uses
  Classes, Windows, SysUtils, SpeechLib_TLB;

type
  TAudioFormat = class
  const
    COUNT_MAX = 65;
  private
    FCount: Integer;
    FAudioIndex: Integer;
    function GetValue(Index: Integer): string;
    procedure SetAudioIndex(Value: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    property Count: Integer read FCount;
    property Value[Index: Integer]: String read GetValue;
    property AudioIndex: Integer read FAudioIndex write SetAudioIndex default 36;
  end;

  TVoiceMember = record
    Name, Language, Gender, Vendor, RegKey: string;
  end;

  TVoices = class
  private
    FSpeech: TSpVoice;
    FTokens: ISpeechObjectTokens;
    FCount: Integer;
    FVoiceIndex: Integer;
    FVoiceMembers: array of TVoiceMember;
    function GetLanguageName(LCID: string): string;
    function GetVoiceMember(Index: Integer): TVoiceMember;
    procedure SetVoiceIndex(Value: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    property Count: Integer read FCount;
    property Member[Index: Integer]: TVoiceMember read GetVoiceMember;
    property VoiceIndex: Integer read FVoiceIndex write SetVoiceIndex default 0;
  end;

  TSpeechState = (spNone, spPlay, spPause, spStop);

  TSpeechStateEvent = procedure(Sender: TObject; State: TSpeechState) of object;

  TfrSAPI = class
  private
    FRate: Integer;
    FVolume: Integer;
    FVoices: TVoices;
    FAudioFormat: TAudioFormat;
    FState: TSpeechState;
    FOnSpeechState: TSpeechStateEvent;
    FOnWord: TSpVoiceWord;
    FOnSentence: TSpVoiceSentence;
    function GetIsSpeaking: Boolean;
    function GetIsDone: Boolean;
    procedure SetRate(Value: Integer);
    procedure SetVolume(Value: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Play(const Text: WideString);
    procedure Stop();
    procedure Pause();
    procedure Resume();
    procedure TextToWAV(Text, FileName: string;
      FileMode: SpeechStreamFileMode;
      AudioType: SpeechAudioFormatType); overload;
    procedure TextToWAV(Text, FileName: string); overload;
    procedure DoSpeechState();
    property Rate: Integer read FRate write SetRate default 0// -10..10
    property Volume: Integer read FVolume write SetVolume default 100// 0..100
    property Voices: TVoices read FVoices;
    property AudioFormat: TAudioFormat read FAudioFormat;
    property State: TSpeechState read FState write FState default spNone;
    property IsSpeaking: Boolean read GetIsSpeaking;
    property IsDone: Boolean read GetIsDone;
    property OnSpeechState: TSpeechStateEvent read FOnSpeechState
                            write FOnSpeechState;
    property OnWord: TSpVoiceWord read FOnWord write FOnWord;
    property OnSentence: TSpVoiceSentence read FOnSentence write FOnSentence;
  end;

implementation

{ TAudioFormat }

constructor TAudioFormat.Create;
begin
  inherited;
  FCount := COUNT_MAX;
end;

destructor TAudioFormat.Destroy;
begin
  inherited;
end;

function TAudioFormat.GetValue(Index: Integer): String;
const
  VALUES: array[0..COUNT_MAX - 1of string = (
    '8kHz8BitMono''8kHz8BitStereo''8kHz12BitMono',
    '8kHz16BitStereo''11kHz4BitMono''11kHz8BitStereo''66kHz66BitMono',
    '77kHz76BitStereo''82kHz8BitMono''92kHz8BitStereo''102kHz106BitMono',
    '112kHz116BitStereo''126kHz8BitMono''136kHz8BitStereo',
    '146kHz146BitMono''156kHz156BitStereo''1616kHz8BitMono',
    '1717kHz8BitStereo''1818kHz16BitMono''1919kHz16BitStereo',
    '204kHz8BitMono''214kHz8BitStereo''224kHz16BitMono',
    '234kHz16BitStereo''324kHz8BitMono''325kHz8BitStereo',
    '262kHz16BitMono''272kHz16BitStereo''44kHz8BitMono',
    '44kHz8BitStereo''44kHz16BitMono''44kHz16BitStereo',
    '48kHz8BitMono''48kHz8BitStereo''48kHz16BitMono',
    '48kHz16BitStereo''TrueSpeech_8kHz1BitMono''CCITT_ALaw_8kHzMono',
    'CCITT_ALaw_8kHzStereo''CCITT_ALaw_11kHzMono''CCITT_ALaw_11kHzStereo',
    'CCITT_ALaw_22kHzMono''CCITT_ALaw_22kHzStereo''CCITT_ALaw_4343kHzMono',
    'CCITT_ALaw_4444kHzStereo''CCITT_uLaw_8kHzMono''CCITT_uLaw_8kHzStereo',
    'CCITT_uLaw_11kHzMono''CCITT_uLaw_11kHzStereo''CCITT_uLaw_22kHzMono',
    'CCITT_uLaw_22kHzStereo''CCITT_uLaw_44kHzMono''CCITT_uLaw_44kHzStereo',
    'ADPCM_8kHzMono''ADPCM_8kHzStereo''ADPCM_11kHzMono',
    'ADPCM_11kHzStereo''ADPCM_22kHzMono''ADPCM_22kHzStereo',
    'ADPCM_44kHzMono''ADPCM_44kHzStereo''GSM6110_8kHzMono',
    'GSM6210_11kHzMono''GSM6310_22kHzMono''GSM6410_44kHzMono'
  );
begin
  result := VALUES[Index];
end;

procedure TAudioFormat.SetAudioIndex(Value: Integer);
begin
  FAudioIndex := Value;
end;

{ TVoices }

// Gibt den vollen Namen einer LCID (Locale ID) einer Sprache/Region zurück
function TVoices.GetLanguageName(LCID: string): string;
var
  s: string;
  APos: Integer;
begin
  try
    s := '$' + LCID;
    APos := Pos(';', s);
    if APos <> 0 then
      s := Copy(s, 1, APos - 1);
    SetLength(result, MAX_PATH);
    SetLength(result, VerLanguageName(StrToInt(s), @result[1],
              Length(result)));
  except
    result := LCID;
  end;
end;

// Ermittelt die Beschreibung einer ausgewählten Stimme
function TVoices.GetVoiceMember(Index: Integer): TVoiceMember;
begin
  result := FVoiceMembers[Index];
end;

// Legt fest welche Stimme benutzt werden soll
procedure TVoices.SetVoiceIndex(Value: Integer);
begin
  FVoiceIndex := Value;
  FSpeech.Voice := FTokens.Item(FVoiceIndex);
end;

constructor TVoices.Create;
var
  i: Integer;
begin
  inherited;
  FSpeech := TSpVoice.Create(nil);

  // Ermittelt alle installierten Stimmen auf dem System
  FTokens := FSpeech.GetVoices('''');
  // Ermittelt die Anzahlt der Stimmen
  FCount := FTokens.Count;

  SetLength(FVoiceMembers, FCount);

  // Ermittelt die Beschreibung der Stimmen
  // und weist die Werte der Eigenschaft "Member" zu
  for i := 0 to FCount - 1 do
  begin
    with FVoiceMembers[i] do
    begin
      Name := FTokens.Item(i).GetDescription(i);
      Language := GetLanguageName(FTokens.Item(i).GetAttribute('Language'));
      Gender := FTokens.Item(i).GetAttribute('Gender');
      Vendor := FTokens.Item(i).GetAttribute('Vendor');
      RegKey := FTokens.Item(i).Id;
    end;
  end;
end;

destructor TVoices.Destroy;
begin
  FSpeech.Free;
  inherited;
end;

{ TfrSAPI }

constructor TfrSAPI.Create;
begin
  inherited;
  FVoices := TVoices.Create;
  FAudioFormat := TAudioFormat.Create;
end;

destructor TfrSAPI.Destroy;
begin
  FVoices.Free;
  FAudioFormat.Free;
  inherited;
end;

// Spielt den Text ab
procedure TfrSAPI.Play(const Text: WideString);
begin
  with FVoices do
  begin
    if FVoiceIndex >= 0 then
    begin
      self.FRate  := FSpeech.Rate;
      self.Volume := FSpeech.Volume;

      FSpeech.Disconnect;

      FSpeech.Rate   := self.FRate;
      FSpeech.Volume := self.Volume;

      FSpeech.Voice := FTokens.Item(FVoiceIndex);
      FSpeech.Speak(Text, SVSFlagsAsync);

      self.FState := spPlay;
    end;
  end;
end;

// Bricht das Abspielen ab
procedure TfrSAPI.Stop;
begin
  FVoices.FSpeech.Speak('', SVSFPurgeBeforeSpeak);

  self.FRate  := FVoices.FSpeech.Rate;
  self.Volume := FVoices.FSpeech.Volume;

  FVoices.FSpeech.Disconnect;

  FVoices.FSpeech.Rate   := self.FRate;
  FVoices.FSpeech.Volume := self.Volume;

  self.FState := spStop;
end;

// Pausiert das Abspielen
procedure TfrSAPI.Pause;
begin
  FVoices.FSpeech.Pause;
  self.FState := spPause;
end;

// Setzt das Abspielen fort
procedure TfrSAPI.Resume;
begin
  FVoices.FSpeech.Resume;
  self.FState := spPlay;
end;

// Gibt True zurück, solange der Text angesagt wird
// Gibt False zurück, sobald der angesagte Text angehalten wird
function TfrSAPI.GetIsSpeaking: Boolean;
begin
  result := FVoices.FSpeech.Status.RunningState = SRSEIsSpeaking;
end;

// Gibt True zurück, sobald der Text fertig gelesen wurde
function TfrSAPI.GetIsDone: Boolean;
begin
  result := FVoices.FSpeech.Status.RunningState = SRSEDone;
end;

// Setzt die Abspielgeschwindigkeit des gelesenen Textes
procedure TfrSAPI.SetRate(Value: Integer);
begin
  FRate := Value;
  FVoices.FSpeech.Rate := FRate;
end;

// Setzt die Lautstärke der Stimme
procedure TfrSAPI.SetVolume(Value: Integer);
begin
  FVolume := Value;
  FVoices.FSpeech.Volume := FVolume;
end;

// Speichert den Text in eine Audiodatei (.wav)
// (Parameter)
//   - Text: Text der gesprochen werden soll
//   - FileName: Voller Dateiname mit Endung
//   - FileMode: Legt fest, ob die Datei gelesen/geschrieben/erzeugt werden soll
//   - AudioType: Legt die Übertragungsrate fest
procedure TfrSAPI.TextToWAV(Text: string; FileName: string;
  FileMode: SpeechStreamFileMode; AudioType: SpeechAudioFormatType);
var
  ASpFileStream: TSpFileStream;
begin
  ASpFileStream := TSpFileStream.Create(nil);
  try
    with FVoices do
    begin
      FAudioFormat.FAudioIndex := AudioType;
      ASpFileStream.Format.type_ := FAudioFormat.FAudioIndex;
      ASpFileStream.Open(FileName, FileMode, false);
      FSpeech.AudioOutputStream := ASpFileStream.DefaultInterface;
      FSpeech.Speak(Text, SVSFDefault);
      ASpFileStream.Close;
    end;
  finally
    ASpFileStream.Free;
  end;
end;

// Speichert den Text in eine Audiodatei (.wav)
// (Parameter)
//   - Text: Text der gesprochen werden soll
//   - FileName: Voller Dateiname mit Endung
procedure TfrSAPI.TextToWAV(Text: string; FileName: string);
begin
  TextToWAV(Text, FileName, SSFMCreateForWrite, FAudioFormat.FAudioIndex);
end;

// Diese Methode sollte aufgerufen werden,
// nachdem sich der Status "TfrSAPI.State" verändert hat.
// Somit kann das Ereignis "OnSpeechState" in Kraft treten.
procedure TfrSAPI.DoSpeechState;
begin
  if Assigned(FOnSpeechState) then
    FOnSpeechState(self, FState);
end;

end.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)