FS2_Open
Open source remastering of the Freespace 2 engine
voicerec.cpp
Go to the documentation of this file.
1 /*
2  * Code created by Thomas Whittaker (Random Tiger) for a FreeSpace 2 source code project
3  *
4  * You may not sell or otherwise commercially exploit the source or things you
5  * created based on the source.
6  *
7 */
8 
9 #ifdef _WIN32
10 #ifndef FS2_VOICER
11 
12 #if NDEBUG
13  #pragma message( "WARNING: You have not compiled voice recognition into this build (use FS2_VOICER)" )
14 #endif // NDEBUG
15 
16 #else // to end-of-file ...
17 
18 #ifdef LAUNCHER
19 #include "stdafx.h"
20 #endif //LAUNCHER
21 
22 
23 
24 #include <sphelper.h> // Contains definitions of SAPI functions
25 #include <stdio.h>
26 
27 #include "voicerec.h"
28 #include "grammar.h"
29 
30 // FreeSpace includes
31 #include "cfile/cfile.h"
32 #include "hud/hudsquadmsg.h"
33 #include "io/keycontrol.h"
34 #include "playerman/player.h"
35 #include "ship/ship.h"
36 
37 CComPtr<ISpRecoGrammar> p_grammarObject; // Pointer to our grammar object
38 CComPtr<ISpRecoContext> p_recogContext; // Pointer to our recognition context
39 CComPtr<ISpRecognizer> p_recogEngine; // Pointer to our recognition engine instance
40 CComPtr<ISpAudio> cpAudio; // Pointer for Audio Input Device
41 
42 const bool DEBUG_ON = false;
43 
44 extern int button_function(int n);
45 extern void hud_squadmsg_msg_all_fighters();
46 extern void hud_squadmsg_shortcut( int command );
47 extern bool hud_squadmsg_ship_valid(ship *shipp, object *objp = nullptr);
48 extern int hud_squadmsg_wing_valid(wing *wingp);
49 
50 extern int Msg_instance;;
51 extern int Msg_shortcut_command;
52 extern int Squad_msg_mode;
53 
54 void doVid_Action(int action)
55 {
56  // If menu is up
58  {
59  switch(action)
60  {
61  case VID_DestroyTarget: Msg_shortcut_command = ATTACK_TARGET_ITEM; break;
62  case VID_DisableTarget: Msg_shortcut_command = DISABLE_TARGET_ITEM; break;
63  case VID_DisarmTarget: Msg_shortcut_command = DISARM_TARGET_ITEM; break;
64  case VID_DestroySubsys: Msg_shortcut_command = DISABLE_SUBSYSTEM_ITEM; break;
65  case VID_ProtectTarget: Msg_shortcut_command = PROTECT_TARGET_ITEM; break;
66  case VID_IgnoreTarget: Msg_shortcut_command = IGNORE_TARGET_ITEM; break;
67  case VID_FormWing: Msg_shortcut_command = FORMATION_ITEM; break;
68  case VID_CoverMe: Msg_shortcut_command = COVER_ME_ITEM; break;
69  case VID_EngageEnemy: Msg_shortcut_command = ENGAGE_ENEMY_ITEM; break;
70  case VID_Depart: Msg_shortcut_command = DEPART_ITEM; break;
71  default: Msg_shortcut_command = -1; break;
72 
73  }
74 
76  {
78  }
80  {
81  // skip if player cannot order this ship
84  }
86  {
87  // skip if player cannot order this wing
90  }
92  {
93  }
94 
96  }
97  else
98  {
99  switch(action)
100  {
101  case VID_DestroyTarget: button_function(ATTACK_MESSAGE); break;
102  case VID_DisableTarget: button_function(DISABLE_MESSAGE); break;
103  case VID_DisarmTarget: button_function(DISARM_MESSAGE); break;
104  case VID_DestroySubsys: button_function(ATTACK_SUBSYSTEM_MESSAGE); break;
105  case VID_ProtectTarget: button_function(PROTECT_MESSAGE); break;
106  case VID_IgnoreTarget: button_function(IGNORE_MESSAGE); break;
107  case VID_FormWing: button_function(FORM_MESSAGE); break;
108  case VID_CoverMe: button_function(COVER_MESSAGE); break;
109  case VID_EngageEnemy: button_function(ENGAGE_MESSAGE); break;
110  case VID_Depart: button_function(WARP_MESSAGE); break;
111  }
112  }
113 }
114 
115 
116 bool VOICEREC_init(HWND hWnd, int event_id, int grammar_id, int command_resource)
117 {
118  HRESULT hr = S_OK;
119 
120  while (true)
121  {
122  // create a recognition engine
123  hr = p_recogEngine.CoCreateInstance(CLSID_SpInprocRecognizer);
124  if (FAILED(hr))
125  {
126  MessageBox(hWnd,"Failed to create a recognition engine\n","Error",MB_OK);
127  printf("Failed to create a recognition engine\n");
128  break;
129  }
130 
131  // create the command recognition context
132  hr = p_recogEngine->CreateRecoContext( &p_recogContext );
133  if (FAILED(hr))
134  {
135  MessageBox(hWnd,"Failed to create the command recognition context\n","Error",MB_OK);
136  printf("Failed to create the command recognition context\n");
137  break;
138  }
139 
140  // Let SR know that window we want it to send event information to, and using
141  // what message
142  hr = p_recogContext->SetNotifyWindowMessage( hWnd, event_id, 0, 0 );
143  if (FAILED(hr))
144  {
145  MessageBox(hWnd,"Failed to SetNotifyWindowMessage\n","Error",MB_OK);
146  break;
147  }
148 
149  // Tell SR what types of events interest us. Here we only care about command
150  // recognition.
151  hr = p_recogContext->SetInterest( SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION) );
152  if (FAILED(hr))
153  {
154  MessageBox(hWnd,"Failed to set events\n","Error",MB_OK);
155  break;
156  }
157 
158  // Create a grammar
159  hr = p_recogContext->CreateGrammar(grammar_id, &p_grammarObject);
160  if (FAILED(hr))
161  {
162  MessageBox(hWnd,"Failed to create grammar\n","Error",MB_OK);
163  break;
164  }
165 
166  // Load our grammar from data\phrases.xml, or if that doesn't exist, from the compiled in
167  // user defined ("SRGRAMMAR") resource type.
168  hr = p_grammarObject->LoadCmdFromFile(L"data\\phrases.xml", SPLO_STATIC);
169  if (FAILED(hr))
170  {
171  hr = p_grammarObject->LoadCmdFromResource(NULL, MAKEINTRESOURCEW(command_resource),
172  L"SRGRAMMAR", MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
173  SPLO_STATIC);
174  if (FAILED(hr))
175  {
176  MessageBox(hWnd,"Failed to load resource SRGRAMMAR\n","Error",MB_OK);
177  break;
178  }
179  }
180 
181  hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);
182  if (FAILED(hr))
183  {
184  MessageBox(hWnd,"Failed to get default audio input\n", "Error", MB_OK);
185  break;
186  }
187 
188  // Set the audio input to our token.
189  hr = p_recogEngine->SetInput(cpAudio, TRUE);
190  if (FAILED(hr))
191  {
192  MessageBox(hWnd,"Failed to set audio input\n", "Error", MB_OK);
193  }
194 
195  // Set rules to active, we are now listening for commands
196  hr = p_grammarObject->SetRuleState(NULL, NULL, SPRS_ACTIVE );
197  if (FAILED(hr))
198  {
199  MessageBox(hWnd,"Failed to set listening for commands\n","Error",MB_OK);
200  break;
201  }
202 
203  break;
204  }
205 
206  // if we failed and have a partially setup SAPI, close it all down
207  if (FAILED(hr))
208  {
209  VOICEREC_deinit();
210  }
211 
212  return ( hr == S_OK);
213 }
214 
215 void VOICEREC_deinit()
216 {
217  if ( cpAudio )
218  {
219  cpAudio.Release();
220  }
221  // Release grammar, if loaded
222  if ( p_grammarObject )
223  {
224  p_grammarObject.Release();
225  }
226  // Release recognition context, if created
227  if ( p_recogContext )
228  {
229  p_recogContext->SetNotifySink(NULL);
230  p_recogContext.Release();
231  }
232  // Release recognition engine instance, if created
233  if ( p_recogEngine )
234  {
235  p_recogEngine.Release();
236  }
237 }
238 
239 void VOICEREC_execute_command(ISpPhrase *pPhrase, HWND hWnd);
240 void VOICEREC_process_event(HWND hWnd)
241 {
242  CSpEvent event; // Event helper class
243 
244  // Loop processing events while there are any in the queue
245  while (event.GetFrom(p_recogContext) == S_OK)
246  {
247  // Look at recognition event only
248  switch (event.eEventId)
249  {
250  case SPEI_RECOGNITION:
251  VOICEREC_execute_command(event.RecoResult(), hWnd);
252  break;
253 
254  }
255  }
256 }
257 
258 /******************************************************************************
259 * ExecuteCommand *
260 *----------------*
261 * Description:
262 * Called to Execute commands that have been identified by the speech engine.
263 *
264 ******************************************************************************/
265 
266 char VOICEREC_lastCommand[30];
267 
268 void VOICEREC_execute_command(ISpPhrase *pPhrase, HWND hWnd)
269 {
270  SPPHRASE *pElements;
271 
272  // Get the phrase elements, one of which is the rule id we specified in
273  // the grammar. Switch on it to figure out which command was recognized.
274  if (SUCCEEDED(pPhrase->GetPhrase(&pElements)))
275  {
276 #ifndef NDEBUG
277  if(DEBUG_ON)
278  {
279  WCHAR *pwszText;
280  char szText[255];
281  int i;
282 
283  pPhrase->GetText(static_cast<ULONG>(SP_GETWHOLEPHRASE), static_cast<ULONG>(SP_GETWHOLEPHRASE), TRUE, &pwszText, NULL);
284 
285  memset(szText, 0, 255);
286  for (i=0;i<254;i++)
287  {
288  if (*(pwszText + i) == 0)
289  {
290  break;
291  }
292  szText[i] = (char)(*(pwszText + i));
293  szText[i+1] = 0;
294  }
295 
296  mprintf(( "recognized speech : %s \n", szText ));
297  mprintf(( "speech Rule.ulId : %d \n", pElements->Rule.ulId ));
298  mprintf(( "confidence: %f \n", pElements->pProperties->SREngineConfidence));
299 
300  ::CoTaskMemFree(pwszText);
301  }
302 #endif
303  int part1, part2, part3;
304  part1 = part2 = part3 = -1;
305  switch ( pElements->Rule.ulId )
306  {
307 
308  case VID_WingName:
309  {
310 
311  part1 = pElements->pProperties->vValue.ulVal;
312  if (pElements->pProperties->pNextSibling)
313  {
314  part2 = pElements->pProperties->pNextSibling->vValue.ulVal;
315  if (pElements->pProperties->pNextSibling->pNextSibling)
316  {
317  part3 = pElements->pProperties->pNextSibling->pNextSibling->vValue.ulVal;
318  }
319  }
320  if (part2 == -1)
321  break; // no ship number or wing
322 
323  const wchar_t* valstr = pElements->pProperties->pszValue;
324  char* wing_name = new char[wcslen(valstr)+1];
325 
326  size_t j = 0;
327 
328  for (size_t i = 0; i < wcslen(valstr); i++) {
329  int c = wctob(valstr[i]);
330  if (c != EOF) {
331  wing_name[j++] = static_cast<char>(c);
332  }
333  }
334  wing_name[j] = '\0';
335 
336  if (part2 == 0)
337  {
338  Msg_instance = wing_lookup(wing_name);
339  delete[] wing_name;
340 
341  if (Msg_instance < 0)
342  break;
343 
345  {
347  }
348 
351  }
352  else
353  {
354  char shipName[NAME_LENGTH];
355  wing_bash_ship_name(shipName, wing_name, part2);
356  delete[] wing_name;
357 
358  Msg_instance = ship_name_lookup(shipName);
359 
360  // Can't issue commands to yourself or to nobody
361  if (Msg_instance < 0 || Msg_instance == Player_obj->instance)
362  {
363  break;
364  }
365 
367  {
369  }
370 
373 
374  }
375 
376  if (part3 == -1)
377  break;
378  }
379 
380  case VID_Action:
381  {
382  int action;
383  if (part3 == -1)
384  {
385  action = pElements->pProperties->vValue.ulVal;
386  }
387  else
388  {
389  action = part3;
390  }
391 
392  doVid_Action(action);
393 
394  break;
395  }
396 
397  // These commands run no matter what, and will even bring up the menu
398  case VID_TopMenu:
399  {
400  int action = pElements->pProperties->vValue.ulVal;
401  bool msgWindow = false;
403  {
404  msgWindow = true;
405  }
406 
407  // If the command window is not up, or it is and its a cancel request toggle
408  if((msgWindow && action == VID_Cancel) || (!msgWindow && action != VID_Cancel))
409  {
411  }
412 
413  switch(action)
414  {
415  case VID_Ships:
417  break;
418 
419  case VID_Wings:
421  break;
422 
423  case VID_AllFighters:
424  case VID_AllWings:
426  // can have the action to perform spoken directly afterwards
427  if (pElements->pProperties->pFirstChild) {
428  doVid_Action(pElements->pProperties->pFirstChild->vValue.ulVal);
429  }
430  break;
431 
432  case VID_Reinforcements:
434  break;
435 
436  case VID_SupportShip:
438  break;
439 
440  case VID_AbortSupport:
442  break;
443 
444  case VID_More:
445  break;
446  }
447 
448  break;
449  }
450  // phrases for transferring shield energy to different locations
451  case VID_shields:
452  {
453  int action = pElements->pProperties->vValue.ulVal;
454 
455  switch(action)
456  {
457  case 0: button_function( SHIELD_XFER_TOP ); break;
458  case 1: button_function( SHIELD_XFER_LEFT ); break;
459  case 2: button_function( SHIELD_XFER_RIGHT ); break;
460  case 3: button_function( SHIELD_XFER_BOTTOM ); break;
461  case 4: button_function( SHIELD_EQUALIZE ); break;
462 
463  }
464 
465  break;
466  }
467  // basic cheat, phrase as a value equivalent to the defines in ControlConfig/ControlsConfig.h
468  // it just calls the button_function with this value
469  case VID_speed:
470  case VID_targeting:
471  case VID_other:
472  {
473  int action = pElements->pProperties->vValue.ulVal;
474 
475  if (action > -1)
476  {
477  button_function( action );
478  }
479  break;
480  }
481  // nearly the same as the previous except it has some extra entries for
482  // maximising/minimising energy
483  case VID_power:
484  {
485  int action = pElements->pProperties->vValue.ulVal;
486 
487  if (action >= INCREASE_WEAPON && action <= ETS_EQUALIZE)
488  {
489  button_function( action );
490  }
491  else
492  {
493  // this is for the max engines etc.
494  for (int i=1; i<7; i++)
495  {
496  button_function( action - 132 );
497  }
498  }
499  break;
500  }
501  }
502  // Free the pElements memory which was allocated for us
503  ::CoTaskMemFree(pElements);
504  }
505 
506 }
507 
508 
509 #endif // VOICER
510 #endif // _WIN32
wing Wings[MAX_WINGS]
Definition: ship.cpp:128
void wing_bash_ship_name(char *ship_name, const char *wing_name, int index)
Definition: ship.cpp:12686
void * HWND
Definition: config.h:104
int i
Definition: multi_pxo.cpp:466
#define SM_MODE_ALL_FIGHTERS
Definition: hudsquadmsg.h:26
int flags
Definition: player.h:104
#define PROTECT_TARGET_ITEM
Definition: hudsquadmsg.h:41
wingman message: attack current target
void VOICEREC_deinit()
#define mprintf(args)
Definition: pstypes.h:238
IGNORE_MESSAGE.
int hud_squadmsg_send_wing_command(int wingnum, int command, int send_message, int update_history, int player_num)
int Msg_shortcut_command
Definition: hudsquadmsg.cpp:48
void hud_squadmsg_do_mode(int mode)
#define TRUE
Definition: pstypes.h:399
#define COVER_ME_ITEM
Definition: hudsquadmsg.h:44
Definition: ship.h:1516
#define MESSAGE_ALL_FIGHTERS
Definition: hudsquadmsg.h:29
object * objp
Definition: lua.cpp:3105
ship * shipp
Definition: lua.cpp:9162
transfer shield energy to right
#define SM_MODE_WING_SELECT
Definition: hudsquadmsg.h:20
#define DISARM_TARGET_ITEM
Definition: hudsquadmsg.h:40
int hud_squadmsg_wing_valid(wing *wingp)
#define IGNORE_TARGET_ITEM
Definition: hudsquadmsg.h:42
#define ATTACK_TARGET_ITEM
Definition: hudsquadmsg.h:38
#define ENGAGE_ENEMY_ITEM
Definition: hudsquadmsg.h:45
wingman message: cover me
void hud_squadmsg_msg_all_fighters()
void hud_squadmsg_send_to_all_fighters(int command, int player_num)
equalize recharge rates
wingman message: disarm current target
#define MB_OK
Definition: config.h:179
#define DISABLE_TARGET_ITEM
Definition: hudsquadmsg.h:39
wingman message: form on my wing
Definition: ship.h:534
#define SM_MODE_REPAIR_REARM
Definition: hudsquadmsg.h:24
int button_function(int n)
GLclampd n
Definition: Glext.h:7286
long HRESULT
Definition: vddraw.h:115
bool VOICEREC_init(HWND hWnd, int event_id, int grammar_id, int command_resource)
#define SM_MODE_WING_COMMAND
Definition: hudsquadmsg.h:22
wingman message: engage enemy
#define SM_MODE_SHIP_SELECT
Definition: hudsquadmsg.h:19
void hud_squadmsg_shortcut(int command)
int Squad_msg_mode
Definition: hudsquadmsg.cpp:42
#define SM_MODE_REPAIR_REARM_ABORT
Definition: hudsquadmsg.h:25
int hud_squadmsg_send_ship_command(int shipnum, int command, int send_message, int update_history, int player_num)
#define SM_MODE_REINFORCEMENTS
Definition: hudsquadmsg.h:23
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
HWND hWnd
Definition: vddraw.h:425
wingman message: protect current target
equalize shield energy to all quadrants
#define NAME_LENGTH
Definition: globals.h:15
player * Player
int Msg_instance
Definition: hudsquadmsg.cpp:47
#define DEPART_ITEM
Definition: hudsquadmsg.h:56
struct _cl_event * event
Definition: Glext.h:7296
int MessageBox(HWND h, const char *s1, const char *s2, int i)
transfer shield energy to left
wingman message: disable current target
wingman message: warp out
#define DISABLE_SUBSYSTEM_ITEM
Definition: hudsquadmsg.h:59
int ship_name_lookup(const char *name, int inc_players)
Definition: ship.cpp:12900
#define FORMATION_ITEM
Definition: hudsquadmsg.h:43
wingman message: disable current target
increase weapon recharge rate
#define PLAYER_FLAGS_MSG_MODE
Definition: player.h:37
transfer shield energy to rear
void hud_squadmsg_toggle()
int wing_lookup(const char *name)
Definition: ship.cpp:12736
const GLubyte * c
Definition: Glext.h:8376
transfer shield energy to front
#define SM_MODE_SHIP_COMMAND
Definition: hudsquadmsg.h:21
bool hud_squadmsg_ship_valid(ship *shipp, object *objp)
void VOICEREC_process_event(HWND hWnd)