Sapi.SpVoice

Allgemeines zum Thema EventGhost
MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Sun Mar 05, 2006 3:27 pm

So hat ich's mir vorgestellt ! :D

Lässt du EG mittlerweile vom Source-Code laufen? Ich glaube nämlich win32com.Dispatch läuft so nicht direkt bei den Leuten mit einer EXE Installation von EG, weil dieses dynamische Erzeugen der Wrapper über gen_py nicht drin ist. Dafür musste ich bisher immer das erzeugte Interface-File selber in das Plugin legen.
Momentan hab ich EG als exe am laufen. (Windows Image neu aufgespielt, nur Active Python und EG installiert.)

Soweit ich weiß, ist Sapi Voice ein Teil der XP Standart Installation, nur die Spracherkennung muß extra installiert werden.

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Sun Mar 05, 2006 3:57 pm

MonsterMagnet wrote:Momentan hab ich EG als exe am laufen. (Windows Image neu aufgespielt, nur Active Python und EG installiert.)
Hmm, dann scheint es doch dynamischen Dispatch zu machen. Müsste man nur mal auf einer Kiste ausprobieren, die sicher kein Python drauf hat.
MonsterMagnet wrote:Soweit ich weiß, ist Sapi Voice ein Teil der XP Standart Installation, nur die Spracherkennung muß extra installiert werden.
Spracherkennung? Da haben wir ja schon ein neues Plugin-Projekt. :)

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Sun Mar 05, 2006 5:08 pm

Bin schon dabei...

1.MS Agent(Sapi 4)

2.Sapi.SpSharedRecognizer(Sapi 5)

Meiner Meinung nach, ist die Erkennungsrate bei einem Wörterbuch bis 100 Einträge identisch zu MS Agent,
das Mikrofon und die Umgebungsgeräusche sind das Ausschlaggebende.

3.Alternative Spracherkennungs Engine

Auch wenn MS Agent total veraltet ist und auch nie den Durchbruch geschafft hat, würde ich diese Variante bevorzugen(Resourcenverbrauch).

Was meinst Du ?

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Sun Mar 05, 2006 5:10 pm

Keine Ahnung, ich hab damit noch nie experimentiert. Setzen alternative Sprach-Engines dann nicht auch auf SAPI auf?

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Sun Mar 05, 2006 5:34 pm

http://cmusphinx.sourceforge.net/html/cmusphinx.php

Sieht ziemlich eigenständig aus, aber auch kompliziert und um
vielleicht 20 Wörter zu erkennen reicht die MS Sapi 4/5 wohl.

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Sun Mar 05, 2006 5:39 pm

Ja, viel mehr als ein paar Kommando-Wörter wird man selten brauchen. Und wenn es tatsächlich mal einen Hype dafür gibt, kann man ja noch immer ein Plugin für eine andere Lib bauen. Hauptsache man hat zuerst mal was, dass wirklich etwas tut.

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Sun Mar 05, 2006 6:07 pm

Das hier funktioniert echt gut.

http://surguy.net/articles/speechrecognition.xml

Ich glaube darauf sollte man aufbauen.

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Mon Mar 06, 2006 11:29 am

Also bevor Du nachher wieder alles ins Reine bringen mußt, ist es vielleicht besser Du übernimmst ab hier.
Das ganze funktioniert echt besser als ich dachte.

Habe das Ergebniss von makepy Microsoft Speech Object Library 5.1 ins Plugin Verzeichniss gelegt und voice genannt.

C866CA3A-32F7-11D2-9602-00C04F8EE628x0x5x0.py

Code: Select all

from win32com.client import constants
import win32com.client
import voice
import eg

wordsToAdd = [ "One", "Two", "Three", "Four" ]

class SpeechRecognition(eg.PluginClass):
    
    def __start__(self):

        self.speaker = win32com.client.Dispatch("SAPI.SpVoice")

        self.listener = win32com.client.Dispatch("SAPI.SpSharedRecognizer")

        self.context = self.listener.CreateRecoContext()
        self.grammar = self.context.CreateGrammar()

        self.grammar.DictationSetState(0)

        self.wordsRule = self.grammar.Rules.Add("wordsRule",
                        constants.SRATopLevel + constants.SRADynamic, 0)

        self.wordsRule.Clear()

        [ self.wordsRule.InitialState.AddWordTransition(None, word) for word in wordsToAdd ]

        self.grammar.Rules.Commit()
        self.grammar.CmdSetRuleState("wordsRule", 1)

        self.grammar.Rules.Commit()

        self.eventHandler = ContextEvents(self.context)

        self.speaker.Speak("Speech Recognition started successfully")

class ContextEvents(win32com.client.getevents("SAPI.SpSharedRecoContext")):

    def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):
        newResult = win32com.client.Dispatch(Result)
        eg.TriggerEvent(newResult.PhraseInfo.GetText())

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Mon Mar 06, 2006 1:25 pm

Ich muss erstmal ein Mikrofon besorgen... :)
Mein Headset ist neulich abgeraucht.

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Sat Mar 25, 2006 12:21 pm

Also ich hab mich nochmal dran versucht, der folgende Code ist erstmal nur
ein Grundgerüst(hab mir das Speech SDK noch nicht genauer angesehen).

Jetzt hätt ich mal zwei Fragen:

1.Wie bekomme ich Zugriff auf

Code: Select all

win32com.client.getevents("SAPI.SpSharedRecoContext")
2.Wie ersetz ich eg.TriggerEvent mit myplugin.TriggerEvent

Sprich wie bekomm ich die ContextEvents Klasse in den Griff ?

Code: Select all

from win32com.client import constants
import win32com.client
import voice
import eg
import wx

class SpeechRecognition(eg.PluginClass):
   
    def __init__(self):

        self.speaker = win32com.client.Dispatch("SAPI.SpVoice")
        self.listener = win32com.client.Dispatch("SAPI.SpSharedRecognizer")

        self.context = self.listener.CreateRecoContext()
        self.grammar = self.context.CreateGrammar()

        self.grammar.DictationSetState(0) 
        self.eventHandler = self.ContextEvents(self.context)
        self.wordsRule = self.grammar.Rules.Add("wordsRule", constants.SRATopLevel + constants.SRADynamic, 0)
        
    def __start__(self,Commands):
        #self.grammar.CmdSetRuleState("wordsRule", 1)
        self.speaker.Speak("Speech Recognition started")
        
    def __stop__(self):
        #self.grammar.CmdSetRuleState("wordsRule", 0)
        self.speaker.Speak("Speech Recognition halted")

    def __close__(self):
        self.speaker = None
        self.listener = None
        self.eventHandler = None

    def SetupConfigDialog(self, dialog, Commands=["hello","computer"]):        
        sizer = dialog.sizer
        mySizer = wx.FlexGridSizer(cols=2)

        Edit = wx.TextCtrl(dialog, -1,)
        mySizer.Add(Edit, 0, wx.EXPAND|wx.ALL, 5)
        
        CommandsEdit = wx.ListBox(dialog, -1, choices=Commands, style=wx.LB_SINGLE)
        mySizer.Add(CommandsEdit, 0, wx.EXPAND|wx.ALL, 5)

        button1 = wx.Button(dialog, -1, label="Add Command")
        def OnButton1(event):
            newcommand = Edit.GetValue()
            if len(newcommand)> 0:               
                CommandsEdit.Append(newcommand)
                Commands.append(newcommand)
                Edit.Clear()
        button1.Bind(wx.EVT_BUTTON, OnButton1)
        mySizer.Add(button1, 0, wx.EXPAND|wx.ALL, 5)

        button2 = wx.Button(dialog, -1, label="Delete Command")
        def OnButton2(event):
            try:
                Commands.remove (CommandsEdit.GetStringSelection())
                CommandsEdit.Delete(CommandsEdit.GetSelection())
            except:
                pass
        button2.Bind(wx.EVT_BUTTON, OnButton2)
        mySizer.Add(button2, 0, wx.EXPAND|wx.ALL, 5)
        
        sizer.Add(mySizer, 1, wx.EXPAND)
        
        def ReturnResult():
            self.wordsRule.Clear()
            [self.wordsRule.InitialState.AddWordTransition(None, word) for word in Commands ]     
            self.grammar.Rules.Commit()        
            self.grammar.CmdSetRuleState("wordsRule", 1)
            self.grammar.Rules.Commit()
            return [Commands]            
        return ReturnResult
    
    class ContextEvents(win32com.client.getevents("SAPI.SpSharedRecoContext")):        

        def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):            
            newResult = win32com.client.Dispatch(Result)
            eg.TriggerEvent(newResult.PhraseInfo.GetText())

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Sat Mar 25, 2006 12:40 pm

Frage 1. habe ich nicht ganz verstanden. Zugriff brauchst du darauf ja nicht, weil die Klasse einfach nur als eine Art Callback registriert wird und dann automatisch aufgerufen wird. Interessant ist ja nur, wie man dann in OnRecognition wieder auf das Plugin verweisen kann. Dafür fallen mir auf die Schnelle zwei Lösungen ein:

1. Die ContextEvents-Klasse innerhalb der Plugin.__init__ (oder __start__) definieren und dann einem Attribut die Referenz auf das Plugin zuweisen:

Code: Select all

class SpeechRecognition(eg.PluginClass):
   
    def __start__(self):
        ...
        class ContextEvents(win32com.client.getevents("SAPI.SpSharedRecoContext")):
            plugin = self # <= self ist hier die Plugin-Instanz
            
            def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):
                # self ist hier die ContextEvents-Instanz
                newResult = win32com.client.Dispatch(Result)
                self.plugin.TriggerEvent(newResult.PhraseInfo.GetText())
        ...
        self.eventHandler = ContextEvents(self.context) 


Die zweite Möglichkeit wäre, dem Konstruktor von ContextEvents die Plugin-Instanz mitzugeben:

Code: Select all

class SpeechRecognition(eg.PluginClass):
   
    def __start__(self):
        ...
        self.eventHandler = ContextEvents(self.context, self) 


class ContextEvents(win32com.client.getevents("SAPI.SpSharedRecoContext")):

    def __init__(self, context, plugin):
        self.plugin = plugin
        ContextEvents.__init__(self, context)
    
    def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):
        newResult = win32com.client.Dispatch(Result)
        self.plugin.TriggerEvent(newResult.PhraseInfo.GetText())

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Sat Mar 25, 2006 12:48 pm

Danke, ich glaub das hilft mir erst mal weiter, bis das ganze so läuft wie ich mir das vorstelle wird es wohl noch ein paar Fragen dauern.

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Sat Mar 25, 2006 1:28 pm

Ähm, die zweite Lösung ist Schwachsinn. Die würde zu einer Endlosschleife führen. :)
Man müsste die dann eher so in der Art schreiben:

Code: Select all

class ContextEventsBase(win32com.client.getevents("SAPI.SpSharedRecoContext")):
    pass


class ContextEvents(ContextEventsBase):

    def __init__(self, context, plugin):
        self.plugin = plugin
        ContextEventsBase.__init__(self, context)
   
    def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):
        newResult = win32com.client.Dispatch(Result)
        self.plugin.TriggerEvent(newResult.PhraseInfo.GetText())

MonsterMagnet
Plugin Developer
Posts: 137
Joined: Fri Feb 10, 2006 12:04 pm

Post by MonsterMagnet » Wed Mar 29, 2006 11:18 am

Das ist fürs Erste mal bei rausgekommen:

Code: Select all

from win32com.client import constants
import win32com.client
import voice
import eg
import wx

class SpeechRecognition(eg.PluginClass):
   
    def __init__(self):        
        try:
            self.listener = win32com.client.Dispatch("SAPI.SpSharedRecognizer")
        except:
            eg.PrintError ("Cannot create SpeechRecognition object")
            return
        
        maingroup = self.AddGroup(self.name)
        maingroup.AddAction(self.SpeechRules)
        
        self.context = self.listener.CreateRecoContext()
        self.grammar = self.context.CreateGrammar()

        self.grammar.DictationSetState(0) 
        self.eventHandler = ContextEvents(self.context, self)
        self.wordsRule = self.grammar.Rules.Add("wordsRule", constants.SRATopLevel + constants.SRADynamic, 0)
   
    def __start__(self):        
        try:
            self.grammar.CmdSetRuleState("wordsRule", 1)
        except:
            pass
                
    def __stop__(self):        
        self.grammar.CmdSetRuleState("wordsRule", 0)
        
    def __close__(self):        
        self.listener = None
        self.eventHandler = None
        self.grammar = None
        self.wordsRule = None
        
    class SpeechRules(eg.ActionClass):
       
        def __call__(self, Commands, Rule):
            wordsRule = self.plugin.wordsRule
            grammar = self.plugin.grammar            
            wordsRule.Clear()
            [wordsRule.InitialState.AddWordTransition(None, word)for word in Commands ]     
            grammar.Rules.Commit()        
            grammar.CmdSetRuleState("wordsRule", 1)
            grammar.Rules.Commit()        
           
        def GetLabel(self, Commands, Rule):
            return "SpeechRecognition: %s" % Rule
       
        def SetupConfigDialog(self, dialog, Commands=["hello"], Rule="Rule1"):
        
            sizer = dialog.sizer
            mySizer = wx.FlexGridSizer(cols=2)
                        
            desc1 = wx.StaticText(dialog, -1, "Rule Name:")
            mySizer.Add(desc1, 0,  wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5)
            
            Name = wx.TextCtrl(dialog, -1,Rule)
            mySizer.Add(Name, 0, wx.EXPAND|wx.ALL, 5)
            
            Edit = wx.TextCtrl(dialog, -1,)
            mySizer.Add(Edit, 0, wx.EXPAND|wx.ALL, 5)
        
            CommandsEdit = wx.ListBox(dialog, -1, choices=Commands, style=wx.LB_SINGLE)
            mySizer.Add(CommandsEdit, 0, wx.EXPAND|wx.ALL, 5)

            button1 = wx.Button(dialog, -1, label="Add Command")
            def OnButton1(event):
                newcommand = Edit.GetValue()
                if len(newcommand)> 0:               
                    CommandsEdit.Append(newcommand)
                    Edit.Clear()
            button1.Bind(wx.EVT_BUTTON, OnButton1)
            mySizer.Add(button1, 0, wx.EXPAND|wx.ALL, 5)

            button2 = wx.Button(dialog, -1, label="Delete Command")
            def OnButton2(event):
                try:
                    CommandsEdit.Delete(CommandsEdit.GetSelection())
                except:
                    pass
            button2.Bind(wx.EVT_BUTTON, OnButton2)
            mySizer.Add(button2, 0, wx.EXPAND|wx.ALL, 5)
        
            sizer.Add(mySizer, 1, wx.EXPAND)
        
            def ReturnResult():
                Commands =[]
                for i in range (CommandsEdit.GetCount()):
                    Commands.append(CommandsEdit.GetString(i))
                return (Commands, Name.GetValue(),)       
            return ReturnResult

        
class ContextEventsBase(win32com.client.getevents("SAPI.SpSharedRecoContext")):
    
    pass

class ContextEvents(ContextEventsBase):

    def __init__(self, context, plugin):
        self.plugin = plugin
        ContextEventsBase.__init__(self, context)
   
    def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):
        newResult = win32com.client.Dispatch(Result)
        self.plugin.TriggerEvent(newResult.PhraseInfo.GetText())
Man kann also für jede Gruppe ob z.B. Musik oder Video eine eigene
Sprachregel erstellen, was die Erkennungsgenauigkeit sehr erhöht.

User avatar
Bitmonster
Site Admin
Posts: 2239
Joined: Mon Feb 06, 2006 10:28 pm

Post by Bitmonster » Wed Mar 29, 2006 11:26 am

Wenn ich mal wieder ein Headset habe, probiere ich es mal aus. :)
Werde mir heute mal eines bestellen...

Post Reply