C# SpeechRecognition: Sprachbefehl festgelegt durch Eingabe in Textbox

papanini

Ensign
Registriert
Feb. 2014
Beiträge
206
Hallo liebe Community,

bin gerade dabei ein sprachgesteuertes Programm zu programmieren, welches vom Benutzer festgelegte Programme starten soll. Dies funktioniert auch wunderbar.
Nun möchte ich das ganze noch erweitern, sodass nicht ein festgelegter Sprachbefehl (z.B. "program one") gesprochen werden muss, sondern ein ebenfals vom Benutzer über eine Textbox festgelegter Befehl.
Beispiel: Ich schreibe in die Textbox für Programm 1 "Minecraft". Nun soll dieser Text als Sprachbefehl benutzbar sein und dann durch sprechen von "Minecraft" das Programm 1 gestartet werden.

Habe schon überlegt für die Textbox eine Variable anzulegen, die sich dann durch die Eingabe der Textbox füllt. Sieht aktuell so aus:
Code:
   private void sRecognize_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)    //Annahme & Ausführen der eingegebenen Sprachbefehle
        {
F2.F1 = this;
string a = F2.textBox8.Text;
switch (e.Result.Text)
            { 
case a:           //Hier sollte der Sprachbefehl von der Textbox sein
textBox1.Text = textBox1.Text + "\r\n" + counter++ + ". " + e.Result.Text + " gestartet".ToString();
sSynth.Speak(F2.textBox8.Text + "is starting");
try { Process.Start(F2.textBox8.Text); }
//catch (Exception) { }                                            //Keine Fehlermeldung     
catch (Exception ex) { MessageBox.Show(ex.Message); }              //Fals der Benutzer das weitere ausführen des Programmes abbricht erscheint Fehlermeldung - verhindert Absturz
break;

case "program one":
textBox1.Text = textBox1.Text + "\r\n" + counter++ + ". " + e.Result.Text + " gestartet".ToString();
sSynth.Speak("program one is starting");
try { Process.Start(F2.textBox2.Text); }
//catch (Exception) { }                                            //Keine Fehlermeldung     
catch (Exception ex) { MessageBox.Show(ex.Message); }              //Fals der Benutzer das weitere ausführen des Programmes abbricht erscheint Fehlermeldung - verhindert Absturz
break;
//...

Hat jemand eine Idee? :D
Ergänzung ()

Niemand eine Idee? :freak:
 
Sollte doch kein Problem sein?

Debug mal Variable a

Debug.WriteLine(a);

und schau in den Output?

Für welches System entwickelst Du eigentlich?
 
Ich entwickle für Windows 7-HP.

Problem ist, dass er bringt: "Konstantenwert erwartet" für a in case. Sprich ich kann die Variable so nicht in den case einbauen, aber wie sonst?
 
Du kannst als case keine Variable angeben. Wenn du die Befehle jetzt eh dynamisch machen willst, dann brauchst du Switch doch eigentlich nicht mehr.
 
Stimmt den Switch brauche ich in Zukunft nicht mehr, den baue ich noch aus.
Dennoch weiß ich jetzt noch nicht wie ich mein Problem lösen kann :D
 
Die sRecognize_SpeechRecognized() koennte ggf. so aussehen.
Die Liste mit verfuegbaren Sprachbefehlen und dem jeweiligen Programmpfad kannst du dann beliebig bearbeiten/befuellen (z.B. aus einer Textdatei oder Datenbank).

Code:
//Annahme & Ausführen der eingegebenen Sprachbefehle
private void sRecognize_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
    // Das Dictionary kannst du wo anders erstellen
    Dictionary<string, string> programme = new Dictionary<string, string>();
    programme.Add("Minecraft", @"C:\Programme\Minecraft\Minecraft.exe");
    programme.Add("Taschenrechner", @"C:\Windows\System32\calc.exe");

    string programm;

    // Schlage Sprachbefehl im Dictionary nach und starte ggf. das Programme
    if (programme.TryGetValue(e.Result.Text, out programm))
    {
        Process.Start(programm); 
    }
}
 
Habe jetzt zum testen einfach mal alles in die SpeechRecognized reingeballert.
Ich habe es auch etwas umgeändert, da ich ja den Sprachbefehl (z.B. "Minecraft") und den Pfad des Programmes (@"C:...") nicht vorgegeben haben möchte, sondern aus einer Textbox ausgelesen haben will.
Jetzt sieht mein Code so aus, es funktioniert allerdings nicht:

Code:
 private void sRecognize_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)    //Annahme & Ausführen der eingegebenen Sprachbefehle
        {
            F2.F1 = this;
            string a = F2.textBox8.Text;
            //sSynth.SpeakAsync(F2.textBox8.Text);
                sSynth.Rate = -1;
                sSynth.Volume = 100;
                //sSynth.SelectVoice("Microsoft Anna");         //Legt die Stimme des Computers fest

                Dictionary<string, string> programme = new Dictionary<string, string>();
                programme.Add(F2.textBox8.Text, @F2.textBox7.Text);
                programme.Add("Taschenrechner", @"C:\Windows\System32\calc.exe");

                string program;

                // Schlage Sprachbefehl im Dictionary nach und starte ggf. das Programme
                if (programme.TryGetValue(e.Result.Text, out program))
                {
                    Process.Start(program);
                }

            switch (e.Result.Text)
            {
                case "exit":
                    sSynth.Speak("Good Bye");
                    Application.Exit(); //Beendet das Programm
                    break;
                case "google":
                    textBox1.Text = textBox1.Text +"\r\n" + counter++ + ". Google geöffnet".ToString(); //schreib den gesprochenen Text in die textBox1
                    System.Diagnostics.Process.Start("http://www.google.de");
                    sSynth.Speak("google is opening");
                    break;
//...
 
Was funktioniert denn nicht? Schon mal einen Stoppunkt bei Zeile 17 gesetzt und dann mit Einzelschritten geprueft was passiert?

Edit:
Habe mir gerade mal ein Beispiel bei Google angeschaut.
Ich vermute mal das du den Inhalt F2.textBox8.Text noch zum GrammarBuilder hinzufuegen musst.

Erst wenn man auf button1 klickt, wird der Inhalt der TextBox zu den verfuegbaren Befehlen hinzugefuegt und kann verwendet werden.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        SpeechSynthesizer synth = new SpeechSynthesizer();
        SpeechRecognitionEngine sRecognize = new SpeechRecognitionEngine();
        Dictionary<string, string> programme = new Dictionary<string, string>();


        private void ProgrammeLaden()
        {
            if (this.programme == null) this.programme = new Dictionary<string, string>();

            this.programme.Add("Minecraft", @"C:\Programme\Minecraft\Minecraft.exe");
            this.programme.Add("Taschenrechner", @"C:\Windows\System32\calc.exe");
        }

        private Grammar GrammarErstellen(params string[] weitereBefehle)
        {
            string[] festeBefehle = new String[] { "Exit", "Google", "Minecraft", "Taschenrechner" };

            string[] alleBefehle = festeBefehle.Union(weitereBefehle).ToArray();

            Choices sList = new Choices(alleBefehle);
            Grammar gr = new Grammar(new GrammarBuilder(sList));
            return gr;
        }


        public Form1()
        {

            InitializeComponent();

            sRecognize.LoadGrammar(this.GrammarErstellen());
            sRecognize.RequestRecognizerUpdate();
            sRecognize.SpeechRecognized += sRecognize_SpeechRecognized;
            sRecognize.SetInputToDefaultAudioDevice();
            sRecognize.RecognizeAsync(RecognizeMode.Multiple);
            sRecognize.SpeechRecognitionRejected += sRecognize_SpeechRecognitionRejected;
        }

        void sRecognize_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            programme.Add(this.textBox1.Text, this.textBox2.Text);

            string programm;

            this.listBox1.Items.Add(String.Format("Starte: {0}", e.Result.Text));

            // Schlage Sprachbefehl im Dictionary nach und starte ggf. das Programme
            if (programme.TryGetValue(e.Result.Text, out programm))
            {
                if (File.Exists(programm))
                {
                    Process.Start(programm);
                }
                else
                {
                    this.listBox1.Items.Add(String.Format("Programm nicht gefunden: {0}", e.Result.Text));
                }
            }

            synth.Rate = -1;
            synth.Volume = 25;

            switch (e.Result.Text.ToLower())
            {
                case "exit":
                    synth.Speak("Good Bye");
                    Application.Exit(); //Beendet das Programm
                    break;
                case "google":
                    this.listBox1.Items.Add(String.Format("Starte: {0}", e.Result.Text));
                    System.Diagnostics.Process.Start("http://www.google.de");
                    synth.Speak("google is opening");
                    break;
            }
        }

        void sRecognize_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            this.listBox1.Items.Add(String.Format("Ignoriere: {0}", e.Result.Text));
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            sRecognize.LoadGrammar(this.GrammarErstellen(this.textBox1.Text));
        }

        private void button1_Click(object sender, EventArgs e)
        {
            sRecognize.LoadGrammar(this.GrammarErstellen(this.textBox1.Text));
        }
    }
}
 
Zuletzt bearbeitet: (funktionierendes Beispiel hinzugefuegt)
Vielen Dank für deine Mühe erstmal! Bin grad dabei es umzuschreiben, doch woher kommt die "listBox1"?
Soll das eine Variable sein? Ist nämlich undefiniert was soll ich damit anfangen?
Beispiel Zeile 63, 74, 88, 97

Fehlermeldung:
Fehler 2 'AB_Manager.Form1' enthält keine Definition für 'listBox1', und es konnte keine Erweiterungsmethode 'listBox1' gefunden werden, die ein erstes Argument vom Typ 'AB_Manager.Form1' akzeptiert (Fehlt eine Using-Direktive oder ein Assemblyverweis?). A:\C++ Projekte\AB Manager\AB Manager\Form1.cs 117 18 AB Manager
 
Zuletzt bearbeitet:
Ich habe mir eine listbox aufs form gezogen um darin die Events zu loggen. Kannst du rauswerfen.
 
Jetzt habe ich folgende Fehler ;)

Code:
 private Grammar GrammarErstellen(params string[] weitereBefehle)
        {
            string[] festeBefehle = new String[] ("Exit", "Google", "test", "program one");

            string[] alleBefehle = festeBefehle.Union(weitereBefehle).ToArray();

            Choices sList = new Choices(alleBefehle);
            Grammar gr = new Grammar(new GrammarBuilder (sList));
            return gr;
        }

Fehler 1 Für die Arrayerstellung ist eine Arraygröße oder ein Arrayinitialisierer erforderlich. A:\C++ Projekte\AB Manager\AB Manager\Form1.cs 68 47 AB Manager
-> new String[]


Code:
 void sRecognize_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)    //Annahme & Ausführen der eingegebenen Sprachbefehle
        {
            F2.F1 = this;
            programme.Add(this.textBox1.Text, this.F2.textBox2.Text);

            string program;

            this.Items.Add(String.Format("Starte: {0}", e.Result.Text));

            //Schlage Sprachbefehl im Dictionary nach und starte ggf. das Programm
            if (programme.TryGetValue(e.Result.Text, out program))
            {
                if (File.Exists(program))
                {
                    Process.Start(program);
                }
                else
                {
                    this.Items.Add(String.Format("Programm nicht gefunden: {0}", e.Result.Text));
                }
            }

Fehler 2 'AB_Manager.Form1' enthält keine Definition für 'Items', und es konnte keine Erweiterungsmethode 'Items' gefunden werden, die ein erstes Argument vom Typ 'AB_Manager.Form1' akzeptiert (Fehlt eine Using-Direktive oder ein Assemblyverweis?). A:\C++ Projekte\AB Manager\AB Manager\Form1.cs 117 18 AB Manager

-> .Items.

Und entsprechend bei den anderen "Items" wo vorher die "listBox1" davor war.
 
Zuletzt bearbeitet:
Erster Fehler: Es muessen geschweifte Klammer sein.
Code:
string[] festeBefehle = new String[] {"Exit", "Google", "test", "program one"};

Zweiter Fehler:
Die ListBox ist ein Control wie ein Button, TextBox etc., welche ich auf mein Form zum testen gezogen habe. Du hast auf deinem Form aber keine ListBox, weshalb die Variable this.listBox1 nicht zur Verfuegung steht (this bezieht sich auf das Form | this.listBox1 bezieht sich auf eine ListBox namens listBox1 auf dem Form).
Mit
Code:
this.listBox1.Items.Add("Beliebiger Text");
fuegt man Elemente zur ListBox hinzu. Diese werden dann einfach untereinander aufgelistet. In diesem Fall habe ich das nur gemacht um zu sehen was ausgefuehrt wird.
Da du aber keine ListBox auf deinem Form hast kannst du die Zeilen entweder alle entfernen, oder du fuegst eine ListBox zu deinem Form hinzu.
 
Guten Abend :D

Sorry für die späte Antwort, aber ich hatte leider keine Zeit die letzten Tage.
Habe die Fehler korrigiert und mit dem Debugger mal gecheckt, doch leider ist der Erfolg immer noch nicht ganz da.
Die unter
Code:
switch (e.Result.Text.ToLower())
festgelegten
Code:
case
Situationen nimmt er nach wie vor wunderbar an, doch wenn ich versuche einen Sprachbefehl, welchen ich in die Textbox eingebe, auszuführen bekomme ich zwei Ausnahmefehler und danach beendet das Programm einfach.



Er erkennt auf jeden Fall schonmal meinen Befehl richtig, doch für die Fehler die da im Hintergrund ablaufen fehlt mir leider die Kenntnis um dies einzustufen.
Vlt. sagen dir die Bilder ja was.


Gruß
Patrick
 
Du musst die Exceptions auch lesen. Dort steht der Fehler beschrieben. Ein Element mit dem gleichen Schluessel wurde bereits hinzugefuegt bedeutet, dass in der Liste bereits ein gleichnamiger Eintrag vorhanden ist.
Man kann nun entweder pruefen ob der Eintrag schon vorhanden ist und ihn dann nicht mehr hinzufuegen, oder vorher die Liste leeren.

Liste leeren und neuaufbauen: aendere die ProgrammeLaden() Funktion so ab.
Code:
private void ProgrammeLaden()
{
    if (this.programme == null) this.programme = new Dictionary<string, string>();

    this.programme.Clear();
    this.programme.Add("Minecraft", @"C:\Programme\Minecraft\Minecraft.exe");
    this.programme.Add("Taschenrechner", @"C:\Windows\System32\calc.exe");
    this.programme.Add(this.textBox1.Text, this.textBox2.Text); // Deine Textbox
}
Und ersetze Zeile 113 mit:
Code:
this.ProgrammeLaden();

Nun wird die Liste mit den verfuegbaren Programmen, geleert und komplett neu aufgebaut.


Klick bei den Exceptions auf "Unterbrechen", dann kannst du per MouseOver in die Variablen schauen und sehen was verkehrt ist.
In diesem Fall haettest du sehen koennen das in der Liste bereits das Element vorhanden ist.
Die zweite Exception erscheint, weil das Programm durch den anderen Fehler abgestuerzt ist.
 
Juten Abend ;)

Nach laaaanger Pause aus zeitlichen Gründen mal wieder konnte ich mich mit meinem Programm auseinandersetzen.
Zuerst mal vielen Dank für die tollen Tipps bisher @r15ch13! :D

Nach ein bischen hin und her debuggen und anpassen der vielen textBox'en konnte ich schonmal einen großen Teilerfolg erreichen! Nun funktioniert es perfekt mit EINER Sprach- und Pfadbox.
Code:
private void ProgrammLaden()
 {
if (this.programme == null) this.programme = new Dictionary<string, string>();

this.programme.Clear();
//this.programme.Add("test", @"A:\IVAO Dateien\Programme\Macro\MacroIvAc.exe");
this.programme.Add(this.F2.F2textBox9.Text, this.F2.textBox2.Text);
+
Code:
sRecognize.LoadGrammar(this.GrammarErstellen(this.F2.F2textBox9.Text));

So gebe ich nun in TextBox 9 der Form F2 z.B. Photo ein und in TextBox 2 steht der Pfad. Tada das Programm öffnet sich :freaky:

Natürlich auch ein ABER:
Nun wäre ja eine Box zu langweilig und ich will mein Programm erweitern auf mehrere Sprachboxen. Sieht aktuell so aus:
Code:
private void ProgrammLaden()
{
if (this.programme == null) this.programme = new Dictionary<string, string>();
this.programme.Clear();
            //this.programme.Add("test", @"A:\IVAO Dateien\Programme\Macro\MacroIvAc.exe");
this.programme.Add(this.F2.F2textBox9.Text, this.F2.textBox2.Text);
this.programme.Add(this.F2.F2textBox9_2.Text, this.F2.textBox3.Text);
this.programme.Add(this.F2.F2textBox9_3.Text, this.F2.textBox4.Text);
this.programme.Add(this.F2.F2textBox9_4.Text, this.F2.textBox5.Text);
this.programme.Add(this.F2.F2textBox9_5.Text, this.F2.textBox6.Text);
this.programme.Add(this.F2.F2textBox9_6.Text, this.F2.textBox7.Text);
this.programme.Add(this.F2.F2textBox9_7.Text, this.F2.textBox8.Text);
+
Code:
private void button2_Click(object sender, EventArgs e) //Button2 = Starten der Spracherkennung
sRecognize.LoadGrammar(this.GrammarErstellen(this.F2.F2textBox9.Text, this.F2.F2textBox9_2.Text, this.F2.F2textBox9_3.Text, this.F2.F2textBox9_4.Text, this.F2.F2textBox9_5.Text, this.F2.F2textBox9_6.Text, this.F2.F2textBox9_7.Text));

Fehler kommt hier:
Code:
private Grammar GrammarErstellen(params string[] weitereBefehle)
{
string[] festeBefehle = new String[] {"Exit", "Google", "program one"};
string[] alleBefehle = festeBefehle.Union(weitereBefehle).ToArray();
Choices sList = new Choices(alleBefehle);
Grammar gr = new Grammar(new GrammarBuilder (sList));
return gr;
}
Sobald der Button gedrückt wird, noch vor der SpeechRecognition:
Ein Ausnahmefehler des Typs "System.ArgumentException" ist in System.Speech.dll aufgetreten.
Zusätzliche Informationen: Bei "phrase" darf es sich nicht um eine leere Zeichenfolge handeln.

Ich verstehe nicht ganz warum eine !?phrase!? leere Zeichenfolgen haben soll. Du bist bestimmt wieder schlauer :D


Grüßle
Ergänzung ()

Genau gesagt in Zeile "Choices sList..."
 
Mindestens eine deiner Textboxen ist leer. Deshalb hast du eine leere Zeichenfolge. :p

Die "alleBefehle" Zeile so anpassen, dann werden alle leeren Befehle entfernt.
Code:
// Befehlsliste mit LINQ erstellen
string[] alleBefehle = festeBefehle
    .Union(weitereBefehle) // fuegt alle Befehle zusammen
    .Where(s => !string.IsNullOrEmpty(s)) // schliesst leere Befehle aus
    .ToArray(); // erstellt ein Array aus dem Ergebnis
 
Servus ;)

Leider funktioniert deine Formel nicht. Sieht jetzt so aus:
Code:
        private Grammar GrammarErstellen(params string[] weitereBefehle)
        {
            string[] festeBefehle = new String[] {"two", "one"};

            //Befehlsliste mit LINQ erstellen
            string[] alleBefehle = festeBefehle
                .Union(weitereBefehle)                //fügt alle Befehle zusammen
                .Where(s => !string.IsNullOrEmpty(s)) //schließt leere Befehle aus
                .ToArray();                           //erstellt ein Array aus dem Ergebnis     //Array = Datenstruktur, Feld(Datentyp)

            Choices sList = new Choices(alleBefehle);
            Grammar gr = new Grammar(new GrammarBuilder (sList));
            return gr;

        }

Problem ist dennoch, dass er bei der ersten leere Zeile in
Code:
this.programme.Add(this.F2.F2textBox9_3.Text, this.F2.textBox4.Text);
this.programme.Add(this.F2.F2textBox9_4.Text, this.F2.textBox5.Text);
//...
stehen bleibt mit folgender Meldung:

In System.ArgumentException ist eine Ausnahme vom Typ "mscorlib.dll" aufgetreten, doch wurde diese im Benutzercode nicht verarbeitet.

Zusätzliche Informationen: Ein Element mit dem gleichen Schlüssel wurde bereits hinzugefügt.

Falls ein Handler für diese Ausnahme vorhanden ist, kann das Programm möglicherweise weiterhin sicher ausgeführt werden.


Bzw. gehe ich anhand des unterstrichenen Parts in der Fehlermeldung davon aus, dass die Formel von dir ihren Zweck zwar erfüllt, es jedoch mit einem falschen (doppelten, bereits vorhandenen Wert?) füllt?


MfG
Patrick
Ergänzung ()

Angehängte Frage: Wofür steht das "s" in den Klammern? Wird hierbei ein neuer string deklariert? :D
 
Tja in der Programmliste besteht das gleiche Problem. Es duerfen keine doppelten Schluessel vergeben werden.
Du musst jede Textbox pruefen und dann erst den Inhalt zum Programme-Array hinzu.
Wird bei deiner Anzahl an Textboxen aber ganz schoen haesslich :D

Code:
if (!this.programme.ContainsKey(this.F2.F2textBox9_3.Text.Trim()) &&
    !string.IsNullOrWhiteSpace(this.F2.F2textBox9_3.Text.Trim()) &&
    !string.IsNullOrWhiteSpace(this.F2.textBox4.Text.Trim()))
{
    this.programme.Add(this.F2.F2textBox9_3.Text.Trim(), this.F2.textBox4.Text.Trim());
}
if (!this.programme.ContainsKey(this.F2.F2textBox9_4.Text.Trim()) && 
    !string.IsNullOrWhiteSpace(this.F2.F2textBox9_4.Text.Trim()) && 
    !string.IsNullOrWhiteSpace(this.F2.textBox5.Text.Trim()))
{
    this.programme.Add(this.F2.F2textBox9_4.Text.Trim(), this.F2.textBox5.Text.Trim());
}
...


"das "s" in den Klammern" ist eine Lambda Expression.
Also eine Minifunktion mit dem Parameter "s". In der Funktion wird abgefragt ob der String in der Variable "s" null oder leer ist und das Ergebnis der Funktion "Where()" uebergeben.
 
@r15ch13: Fühl dich gedrückt :evillol:
Ist zwar bei meinen später insgesammt um die 25 Textboxen "etwas" viel Code, aber dennoch funktioniert es einwandfrei :D

Ich merke schon ich bin noch in den Startlöchern, aber solangsam ergibt alles einen Sinn und ich verstehe auch die "tiefen" Hintergründe meines Programmes.
Machst du des beruflich wenn ich fragen darf? :p

PS: Wenn du mir jetzt nur noch erklären würdest wozu die Ausrufezeichen in
Code:
!this
... sind dann wäre mein Hunger nach Wissen erstmal gesättigt :freak:

Ich hoffe ich komme dann erstmal alleine klar und muss dich nicht schon bald wieder nerven :D


Gruß
Patrick
 
Code:
if(!this.programme.ContainsKey(this.F2.F2textBox9_4.Text.Trim()))
{
}

Ist das gleiche wie:

Code:
if(this.programme.ContainsKey(this.F2.F2textBox9_4.Text.Trim()) != true)
{
}

Oder:

Code:
if(this.programme.ContainsKey(this.F2.F2textBox9_4.Text.Trim()) == false)
{
}
 
Zurück
Oben