diff --git a/HDT-Voice/HDT-Voice.vbproj b/HDT-Voice/HDT-Voice.vbproj index 1ab6b3d..06b7843 100644 --- a/HDT-Voice/HDT-Voice.vbproj +++ b/HDT-Voice/HDT-Voice.vbproj @@ -85,8 +85,8 @@ - - + + configMain.xaml @@ -95,7 +95,8 @@ - + + True @@ -122,6 +123,7 @@ + MyApplicationCodeGenerator Application.Designer.vb @@ -143,7 +145,7 @@ - + diff --git a/HDT-Voice/HDTPlugin/HDTVoicePlugin.vb b/HDT-Voice/HDTPlugin/HDTVoicePlugin.vb index 2110225..f56b0f1 100644 --- a/HDT-Voice/HDTPlugin/HDTVoicePlugin.vb +++ b/HDT-Voice/HDTPlugin/HDTVoicePlugin.vb @@ -9,7 +9,7 @@ Imports MahApps.Metro.Controls Public Class HDTVoicePlugin Implements IPlugin - Public Shared PluginVersion As New Version(0, 7, 3) + Public Shared PluginVersion As New Version(0, 7, 4) Private createdSettings As Boolean = False Private configRecog As configRecog diff --git a/HDT-Voice/HDTPlugin/configMain.xaml b/HDT-Voice/HDTPlugin/configMain.xaml index 48a213d..ad97878 100644 --- a/HDT-Voice/HDTPlugin/configMain.xaml +++ b/HDT-Voice/HDTPlugin/configMain.xaml @@ -22,6 +22,14 @@ + + diff --git a/HDT-Voice/HDTPlugin/configMain.xaml.vb b/HDT-Voice/HDTPlugin/configMain.xaml.vb index c65139f..10d9fda 100644 --- a/HDT-Voice/HDTPlugin/configMain.xaml.vb +++ b/HDT-Voice/HDTPlugin/configMain.xaml.vb @@ -14,6 +14,9 @@ Public Class configMain comboNotificationPos.SelectedIndex = My.Settings.intNotificationPos AddHandler comboNotificationPos.SelectionChanged, AddressOf SaveSettings + comboNotificationSize.SelectedIndex = My.Settings.intNotificationSize + AddHandler comboNotificationSize.SelectionChanged, AddressOf SaveSettings + checkSmoothMouse.IsChecked = My.Settings.boolSmoothCursor AddHandler checkSmoothMouse.Click, AddressOf SaveSettings @@ -36,9 +39,13 @@ Public Class configMain My.Settings.boolListenAtStartup = checkAutoStart.IsChecked My.Settings.boolShowNotification = checkShowNotification.IsChecked My.Settings.intNotificationPos = comboNotificationPos.SelectedIndex + My.Settings.intNotificationSize = comboNotificationSize.SelectedIndex My.Settings.boolSmoothCursor = checkSmoothMouse.IsChecked My.Settings.boolDebugLog = checkDebugLog.IsChecked My.Settings.Save() + + comboNotificationSize.IsEnabled = checkShowNotification.IsChecked + comboNotificationPos.IsEnabled = checkShowNotification.IsChecked End Sub Public Sub ClickUpdateButton() Process.Start("https://www.github.com/topher-au/HDT-Voice/releases/latest") diff --git a/HDT-Voice/HDTGrammarEngine.vb b/HDT-Voice/HDTVoice/HDTGrammarEngine.vb similarity index 99% rename from HDT-Voice/HDTGrammarEngine.vb rename to HDT-Voice/HDTVoice/HDTGrammarEngine.vb index 42780f7..4d3943b 100644 --- a/HDT-Voice/HDTGrammarEngine.vb +++ b/HDT-Voice/HDTVoice/HDTGrammarEngine.vb @@ -206,7 +206,6 @@ Public Class GrammarEngine Public ReadOnly Property MenuGrammar As Grammar Get Dim menuChoices As New Choices - Dim menuBuilder As New GrammarBuilder menuChoices.Add(New SemanticResultKey("menu", "play")) menuChoices.Add(New SemanticResultKey("menu", "casual mode")) @@ -267,9 +266,8 @@ Public Class GrammarEngine Next deckGrammar.Append(deckChoices) menuChoices.Add(deckGrammar) - menuBuilder.Append(menuChoices) - Return New Grammar(menuBuilder) + Return New Grammar(menuChoices) End Get End Property Public ReadOnly Property MulliganGrammar As Grammar diff --git a/HDT-Voice/HDTMouse.vb b/HDT-Voice/HDTVoice/HDTMouse.vb similarity index 100% rename from HDT-Voice/HDTMouse.vb rename to HDT-Voice/HDTVoice/HDTMouse.vb diff --git a/HDT-Voice/HDTVoice/HDTPopup.vb b/HDT-Voice/HDTVoice/HDTPopup.vb new file mode 100644 index 0000000..3764e28 --- /dev/null +++ b/HDT-Voice/HDTVoice/HDTPopup.vb @@ -0,0 +1,125 @@ +Imports System.Threading.Thread +Imports System.Windows.Controls +Imports System.Windows.Forms + +Imports Hearthstone_Deck_Tracker +Imports Hearthstone_Deck_Tracker.API +Public Class HDTPopup + Private popupText As String + Private popupDuration As Integer + Private hdtCanvas As Canvas + Private boolFadeComplete As Boolean = False + Public Sub New(Text As String, Duration As Integer) + popupText = Text + popupDuration = Duration + + hdtCanvas = Core.OverlayCanvas + ' Invoke popup from main canvas + hdtCanvas.Dispatcher.Invoke(AddressOf InvokePopup) + End Sub + Private Sub InvokePopup() + 'Spawn backgroundworker to avoid blocking HDT thread + + hdtCanvas.Dispatcher.Invoke(Sub() + Dim fadeWorker As New System.ComponentModel.BackgroundWorker + AddHandler fadeWorker.DoWork, AddressOf DoPopup + fadeWorker.RunWorkerAsync() + End Sub) + End Sub + Private Function CreatePopupCanvas(Text As String) As Canvas + Dim canvasPopup As New Canvas + canvasPopup.Tag = "HDTVoicePopup" + + 'Create text and add to popup + Dim htbPopupText As New HearthstoneTextBlock + Select Case My.Settings.intNotificationSize + Case 0 + htbPopupText.FontSize = 12 + Case 1 + htbPopupText.FontSize = 16 + Case 2 + htbPopupText.FontSize = 22 + End Select + hdtCanvas.Children.Add(canvasPopup) + htbPopupText.Text = Text + Canvas.SetZIndex(htbPopupText, 1) + Canvas.SetLeft(htbPopupText, 10) + Canvas.SetTop(htbPopupText, 8) + htbPopupText.UpdateLayout() + canvasPopup.Children.Add(htbPopupText) + canvasPopup.UpdateLayout() + + + 'Create background, size to text and add to popup + Dim rectPopupBG As New System.Windows.Shapes.Rectangle + rectPopupBG.RadiusX = 3 + rectPopupBG.RadiusY = 10 + rectPopupBG.Fill = New System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(110, 0, 0, 0)) + rectPopupBG.Height = htbPopupText.RenderSize.Height + 16 + rectPopupBG.Width = htbPopupText.RenderSize.Width + 20 + canvasPopup.Children.Add(rectPopupBG) + canvasPopup.UpdateLayout() + + Select Case My.Settings.intNotificationPos + Case 0 ' Top Left + Canvas.SetTop(canvasPopup, 16) + Canvas.SetLeft(canvasPopup, 16) + Case 1 ' Bottom Left + Canvas.SetTop(canvasPopup, hdtCanvas.ActualHeight * 0.93 - rectPopupBG.Height / 2) + Canvas.SetLeft(canvasPopup, 8) + Case 2 ' Top Right + Canvas.SetTop(canvasPopup, 16) + Canvas.SetLeft(canvasPopup, hdtCanvas.ActualWidth - rectPopupBG.ActualWidth - 16) + Case 3 ' Bottom Right + Canvas.SetTop(canvasPopup, hdtCanvas.ActualHeight * 0.93 - rectPopupBG.Height / 2) + Canvas.SetLeft(canvasPopup, hdtCanvas.ActualWidth - rectPopupBG.ActualWidth - 16) + End Select + + Return canvasPopup + End Function + Private Sub FadeIn(Popup As Canvas) + hdtCanvas.Children.Add(Popup) + For i = 0 To 1 Step 0.1 + Popup.Opacity = i + Application.DoEvents() + Sleep(10) + Next + End Sub + Private Sub FadeOut(Popup As Canvas) + For i = 1 To 0 Step -0.05 + Popup.Opacity = i + Application.DoEvents() + Sleep(15) + Next + hdtCanvas.Children.Remove(Popup) + End Sub + Private Sub DoPopup() + + Dim popupCanvas As Canvas = hdtCanvas.Dispatcher.Invoke(Function() + Return CreatePopupCanvas(popupText) + End Function) + + hdtCanvas.Dispatcher.Invoke(Sub() + RemoveAllPopups() + FadeIn(popupCanvas) + End Sub) + Sleep(popupDuration) + hdtCanvas.Dispatcher.Invoke(Sub() + FadeOut(popupCanvas) + End Sub) + End Sub + + Private Sub RemoveAllPopups() + Do + For Each child In hdtCanvas.Children + If TypeOf child Is Canvas Then + If child.Tag = "HDTVoicePopup" Then + hdtCanvas.Children.Remove(child) + Continue Do + End If + End If + Next + Exit Do + Loop + End Sub +End Class diff --git a/HDT-Voice/HDTVoice.vb b/HDT-Voice/HDTVoice/HDTVoice.vb similarity index 80% rename from HDT-Voice/HDTVoice.vb rename to HDT-Voice/HDTVoice/HDTVoice.vb index 1dbcb4a..56d51c3 100644 --- a/HDT-Voice/HDTVoice.vb +++ b/HDT-Voice/HDTVoice/HDTVoice.vb @@ -13,33 +13,30 @@ Imports Hearthstone_Deck_Tracker.Hearthstone.Entities Public Class HDTVoice ' Windows API Declarations - Public Declare Function GetForegroundWindow Lib "user32" () As System.IntPtr - Public Declare Auto Function GetWindowText Lib "user32" (ByVal hWnd As System.IntPtr, ByVal lpString As System.Text.StringBuilder, ByVal cch As Integer) As Integer - Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Integer) As Short + Private Declare Function GetForegroundWindow Lib "user32" () As System.IntPtr + Private Declare Auto Function GetWindowText Lib "user32" (ByVal hWnd As System.IntPtr, ByVal lpString As System.Text.StringBuilder, ByVal cch As Integer) As Integer + Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Integer) As Short ' Speech recognition objects - Public WithEvents recogVoice As SpeechRecognitionEngine - Public WithEvents workerHotkey As New BackgroundWorker - Public sreListen As Boolean ' Should we be listening? - Public boolUpdating As Boolean ' True when SRE update in progress, FALSE otherwise + Private WithEvents recogVoice As SpeechRecognitionEngine + Private WithEvents workerHotkey As New BackgroundWorker + Private sreListen As Boolean ' Should we be listening? + Private boolUpdating As Boolean ' True when SRE update in progress, FALSE otherwise ' Action processor list and worker - Public listActions As New List(Of SpeechRecognizedEventArgs) - Public WithEvents workerActions As New BackgroundWorker + Private listActions As New List(Of SpeechRecognizedEventArgs) + Private WithEvents workerActions As New BackgroundWorker 'HDT-Voice data objects - Public swDebugLog As IO.StreamWriter ' Debug log writer - Public strLastCommand As New String("none") ' Last command executed - Public WithEvents timerReset As New Timer ' Used to reset status text + Private swDebugLog As IO.StreamWriter ' Debug log writer 'Overlay elements - Public canvasOverlay As Canvas = Core.OverlayCanvas ' The main overlay object - Public textStatus As HearthstoneTextBlock ' Status text block + Private canvasOverlay As Canvas = Core.OverlayCanvas ' The main overlay object + Private rectStatusBG As Rectangle = Nothing - - Public intPlayerID As Integer = 0 - Public intOpponentID As Integer = 0 + Private intPlayerID As Integer = 0 + Private intOpponentID As Integer = 0 Public Shared GrammarEngine As New GrammarEngine Public Mouse As New Mouse @@ -78,14 +75,6 @@ Public Class HDTVoice writeLog("HDT-Voice {0}.{1} ({2}) | {3}x{4}", My.Application.Info.Version.Major, My.Application.Info.Version.Minor, My.Computer.Info.OSFullName, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height) writeLog("Initializing speech recognition object") - ' Initialize output displays - textStatus = New HearthstoneTextBlock - textStatus.Text = "HDT-Voice: Loading, please wait..." - textStatus.FontSize = 16 - Canvas.SetTop(textStatus, 22) - Canvas.SetLeft(textStatus, 4) - canvasOverlay.Children.Add(textStatus) - ' Attempt to initialize speech recognition Try @@ -129,22 +118,23 @@ Public Class HDTVoice GameEvents.OnPlayerPlayToHand.Add(New Action(Of Card)(AddressOf updateRecognizer)) GameEvents.OnOpponentPlay.Add(New Action(Of Card)(AddressOf updateRecognizer)) - 'Handlers for plugin settings and overlay size - AddHandler My.Settings.PropertyChanged, AddressOf updateOverlay - AddHandler Core.OverlayCanvas.SizeChanged, AddressOf updateOverlay - - timerReset.Interval = 2500 - timerReset.Enabled = True - 'Load default grammar and start recognition - recogVoice.LoadGrammar(New Grammar(New GrammarBuilder("default"))) + recogVoice.LoadGrammar(GrammarEngine.MenuGrammar) recogVoice.RecognizeAsync(RecognizeMode.Multiple) workerHotkey.RunWorkerAsync() ' Start listening for hotkey workerActions.RunWorkerAsync() ' Start action processor - ' Start listening if the option is enabled - If My.Settings.boolListenAtStartup And Not My.Settings.boolToggleOrPtt Then sreListen = True + If My.Settings.boolToggleOrPtt Then ' Push to talk enabled, don't start listening + sreListen = False + PopupNotification("HDT-Voice loaded (hold Left Shift to speak)", 4000) + ElseIf My.Settings.boolListenAtStartup Then ' Start listening + sreListen = True + PopupNotification("HDT-Voice loaded and enabled (F12 to disable)", 4000) + Else 'Don't start listening + sreListen = False + PopupNotification("HDT-Voice loaded (F12 to enable)", 4000) + End If End Sub ' Run when the plugin is first initialized Public Sub Unload() If Not swDebugLog Is Nothing Then @@ -198,7 +188,6 @@ Public Class HDTVoice End If If sreListen Then - updateStatusText("Heard """ & e.Result.Text & """") ' Speech was recognized, play audio If My.Settings.boolRecognizedAudio Then _ @@ -210,30 +199,38 @@ Public Class HDTVoice End Sub ' Handles processing recognized speech input Public Sub onRecognizerUpdateReached(sender As Object, e As RecognizerUpdateReachedEventArgs) Handles recogVoice.RecognizerUpdateReached - If Not Core.Game.IsRunning Then Exit Sub ' do nothing if the game is not running + If Not Core.Game.IsRunning Then + writeLog("Tried to update recog but game not running!") + Exit Sub ' do nothing if the game is not running + End If + boolUpdating = True recogVoice.UnloadAllGrammars() If Core.Game.IsInMenu Then - recogVoice.LoadGrammar(GrammarEngine.MenuGrammar) + Dim mG = GrammarEngine.MenuGrammar + recogVoice.LoadGrammar(mG) + boolUpdating = False + Return End If - ' if the player or opponent entity is unknown, try initiate a new game If intPlayerID = 0 Or intOpponentID = 0 Then onNewGame() End If - ' Check if we're at the mulligan, if so only the mulligan grammar will be returned If Not Core.Game.IsMulliganDone Then + recogVoice.LoadGrammar(GrammarEngine.MulliganGrammar) + Else + recogVoice.LoadGrammar(GrammarEngine.GameGrammar) End If - recogVoice.LoadGrammar(GrammarEngine.GameGrammar) boolUpdating = False End Sub ' Handles updating the grammar between commands Public Sub onSpeechRecognitionRejected() Handles recogVoice.SpeechRecognitionRejected ' If recognition fails, refresh Grammar updateRecognizer() + PopupNotification("Command not recognized", 3000) End Sub Public Sub hotkeyWorker_DoWork() Handles workerHotkey.DoWork Dim toggleHotkey = Keys.F12 @@ -253,33 +250,39 @@ Public Class HDTVoice sreListen = True End If End If - - If My.Settings.boolToggleOrPtt Then ' Push-to-talk - Dim hotkeyState As Short = GetAsyncKeyState(pttHotkey) - If hotkeyState <> 0 Then - sreListen = True - updateStatusText("Listening...") - Do While hotkeyState <> 0 - Sleep(5) - hotkeyState = GetAsyncKeyState(pttHotkey) - Loop - updateStatusText("Processing...") - Do While recogVoice.AudioState = AudioState.Speech - Sleep(5) - Loop - Sleep(500) - sreListen = False - updateStatusText(Nothing) - End If - Else ' Toggle - Dim hotkeyState As Short = GetAsyncKeyState(toggleHotkey) - If hotkeyState <> 0 Then - sreListen = Not sreListen - updateStatusText(Nothing) - Sleep(200) + If IsHearthstoneActive() Then + If My.Settings.boolToggleOrPtt Then ' Push-to-talk + Dim hotkeyState As Short = GetAsyncKeyState(pttHotkey) + If hotkeyState <> 0 Then + sreListen = True + PopupNotification("Listening...", 10000) + Do While hotkeyState <> 0 + Sleep(5) + hotkeyState = GetAsyncKeyState(pttHotkey) + Loop + PopupNotification("Processing...", 800) + Sleep(100) + Do While recogVoice.AudioState = AudioState.Speech + Sleep(5) + Loop + Sleep(700) + sreListen = False + End If + Else ' Toggle + + Dim hotkeyState As Short = GetAsyncKeyState(toggleHotkey) + If hotkeyState <> 0 Then + sreListen = Not sreListen + Select Case sreListen + Case False + PopupNotification("Voice control disabled. Press F12 to enable.", 4000) + Case True + PopupNotification("Voice control enabled. Press F12 to disable.", 4000) + End Select + Sleep(200) + End If End If End If - Sleep(50) Loop @@ -293,7 +296,9 @@ Public Class HDTVoice Loop If listActions.Count > 0 Then Dim currentAction As String = listActions.Item(0).Result.Text - writeLog("Processing action: ""{0}""", currentAction.ToUpper) + currentAction = currentAction.Substring(0, 1).ToUpper & currentAction.Substring(1) + writeLog("Processing action: ""{0}""", currentAction) + PopupNotification(String.Format("""{0}""", currentAction)) ProcessAction(listActions.Item(0)) ' Process action listActions.Remove(listActions.Item(0)) ' Remove from list Sleep(100) @@ -346,8 +351,6 @@ Public Class HDTVoice Mouse.SendClick(Mouse.Buttons.Left) End Select End If - - strLastCommand = e.Result.Text ' Set last command executed End Sub 'Voice command handlers @@ -694,53 +697,6 @@ Public Class HDTVoice Return False End If End Function 'Checks if the hearthstone window is active - Public Function updateOverlay() As System.Windows.SizeChangedEventHandler - 'Update positioning/visibility of status text - If My.Settings.boolShowNotification = False Then - textStatus.Visibility = System.Windows.Visibility.Hidden - Else - textStatus.Visibility = System.Windows.Visibility.Visible - End If - Select Case My.Settings.intNotificationPos ' position status text - Case 0 'Top left - Canvas.SetTop(textStatus, 32) - Canvas.SetLeft(textStatus, 8) - textStatus.TextAlignment = System.Windows.TextAlignment.Left - Case 1 'Bottom left - Canvas.SetTop(textStatus, canvasOverlay.Height - 72) - Canvas.SetLeft(textStatus, 8) - textStatus.TextAlignment = System.Windows.TextAlignment.Left - Case 2 'Top right - Canvas.SetTop(textStatus, 8) - Canvas.SetLeft(textStatus, canvasOverlay.Width - textStatus.ActualWidth - 8) - textStatus.TextAlignment = System.Windows.TextAlignment.Right - Case 3 'Bottom right - Canvas.SetTop(textStatus, canvasOverlay.Height - 72) - Canvas.SetLeft(textStatus, canvasOverlay.Width - textStatus.ActualWidth - 8) - textStatus.TextAlignment = System.Windows.TextAlignment.Right - End Select - Return Nothing - End Function 'Handles changing the overlay layout when it is resized - Public Sub updateStatusText(Status As String) - If Status = Nothing Then - onResetTimer() - Return - End If - Try - textStatus.Dispatcher.Invoke(Sub() - Dim newStatus = "HDT-Voice: " - newStatus &= Status - textStatus.Text = newStatus - timerReset.Enabled = False ' Reset interval - timerReset.Enabled = True - - End Sub) - canvasOverlay.UpdateLayout() - Catch ex As Exception - Return - End Try - - End Sub 'Updates the text on the status text block Public Sub writeLog(LogLine As String, ParamArray args As Object()) Dim formatLine As String = String.Format(LogLine, args) formatLine = String.Format("HDT-Voice: {0}", formatLine) @@ -759,13 +715,14 @@ Public Class HDTVoice End If End If End Sub 'Writes information to the debug output and the logfile if necessary - Public Sub onResetTimer() Handles timerReset.Tick - If sreListen = True Then - updateStatusText("Listening...") - Else - updateStatusText("Stopped") + Public Sub PopupNotification(Text As String, Optional Duration As Integer = 2000) + If My.Settings.boolShowNotification Then + Dim fadeWorker As New BackgroundWorker + ' Spawn backgroundworker to avoid blocking thread with popup + AddHandler fadeWorker.DoWork, Sub() + Dim myPopup As New HDTPopup(Text, Duration) + End Sub + fadeWorker.RunWorkerAsync() End If - - timerReset.Enabled = False - End Sub ' Resets the status text to default after a period + End Sub End Class diff --git a/HDT-Voice/My Project/Settings.Designer.vb b/HDT-Voice/My Project/Settings.Designer.vb index d6b806b..86d26bb 100644 --- a/HDT-Voice/My Project/Settings.Designer.vb +++ b/HDT-Voice/My Project/Settings.Designer.vb @@ -161,6 +161,18 @@ Namespace My Me("boolRecognizedAudio") = value End Set End Property + + _ + Public Property intNotificationSize() As Integer + Get + Return CType(Me("intNotificationSize"),Integer) + End Get + Set + Me("intNotificationSize") = value + End Set + End Property End Class End Namespace diff --git a/HDT-Voice/My Project/Settings.settings b/HDT-Voice/My Project/Settings.settings index f52a732..da089fa 100644 --- a/HDT-Voice/My Project/Settings.settings +++ b/HDT-Voice/My Project/Settings.settings @@ -29,5 +29,8 @@ True + + 1 + \ No newline at end of file diff --git a/HDT-Voice/Resources/app.config b/HDT-Voice/Resources/app.config new file mode 100644 index 0000000..5c95106 --- /dev/null +++ b/HDT-Voice/Resources/app.config @@ -0,0 +1,78 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + 90 + + + False + + + True + + + 0 + + + True + + + True + + + True + + + False + + + True + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HDT-Voice/packages.config b/HDT-Voice/Resources/packages.config similarity index 100% rename from HDT-Voice/packages.config rename to HDT-Voice/Resources/packages.config diff --git a/HDT-Voice/app.config b/HDT-Voice/app.config index 5c95106..9c64895 100644 --- a/HDT-Voice/app.config +++ b/HDT-Voice/app.config @@ -1,7 +1,7 @@ - + - +
@@ -10,7 +10,7 @@ - + @@ -20,7 +20,9 @@ - + @@ -54,25 +56,9 @@ True + + 1 + - - - - - - - - - - - - - - - - - - -