In Büchern oder Filmen sprechen Menschen schon lange wie selbstverständlich mit Maschinen. Tatsächlich stehen wir mit universeller maschineller Spracherkennung im Vergleich dazu aber noch am Anfang. Dieser Beitrag beschäftigt sich am Beispiel von Vosk mit der Frage, wie eine universelle Spracherkennung für eigene Zwecke eingesetzt werden kann.

Spracherkennung

Der Bereich der Spracherkennung lässt sich für diesen Beitrag grob unterteilen in

  • Transskription und

  • simultane Spracherkennung.

Bei der Transskription wird zuvor aufgezeichnete Sprache zum Beispiel aus einer Audiodatei in Text umgewandelt. Die simultane Spracherkennung verwandelt Sprache noch während dem Sprechen direkt in Text und kann so zur Erkennung gesprochener Kommandos dienen.

Aktueller Stand

Transskriptionssoftware ist bereits seit Jahren als kommerzielles Produkt am Markt erhältlich. Für die simultane Spracherkennung gibt es inzwischen wohlbekannte Sprachassistenen wie beispielsweise Siri, Cortana oder Alexa, die auch Programmschnittstellen für individuelle eigene Anwendungen besitzen.

Überdies haben Hersteller wie zum Beispiel Apple, Microsoft, Google, Garmin oder Samsung die Spracherkennung in ihre Geräte eingebaut, so dass diese auch vom jeweiligen Gerät alleine ausgeführt werden kann, ganz ohne die Hilfe zentraler Rechenleistung und entsprechende Netzverbindungen. Damit können beliebige Texteingaben und sogar ganze Dokumente einfach dem Handy oder dem Laptop diktiert und direkt von Sprache zu Text umgewandelt werden. Und freilich ist so auch die Steuerung der Geräte mit gesprochenen Kommandos möglich.

Einschränkungen

Alle aufgezählten Möglichkeiten sind allerdings stark an die jeweiligen Hersteller gebunden. Sie funktionieren bestens mit den jeweiligen Geräten wie etwa einem iPhone, dem Mac oder einem Windows-Rechner.

Sollen diese Funktionen in einem eigenen Programm zum Einsatz kommen, erfordert es eine Neuprogrammierung, wenn die Lösung auf verschiedenen Geräten und Umgebungen gleichermaßen laufen soll. Das selbe gilt für Sprachassistenten, die auf einer zentralen KI beruhen (Siri, Cortana, usw.). Ein eigenes Programm, das auf der Programmschnittstelle eines dieser Angebote beruht, kann nicht ohne Neuprogrammierung zwischen den einzelnen 'Backends' portiert werden.

Erst mit einer universellen, nicht an Hersteller oder Geräte gebundene Spracherkennung ist eine unverändert auf verschiedenen Systemen verwendbare Lösung möglich. Dieser Beitrag beschäftigt sich am Beispiel von Vosk mit der Frage, wie eine universelle Spracherkennung für eigene Zwecke eingesetzt werden kann.

Vosk

Vosk ist eine Open-Source-Werkzeugsammlung zur Spracherkennung von Alpha Cephei. Die Webseite nennt die folgenden Punkte als wesentliche Vorteile

  1. Modelle für mehr als 20 Sprachen und Dialekte: Englisch, Indisches English, Deutsch, Französisch, Spanisch, Portugisisch, Chinesisch, Russisch, Türkisch, Vietnamesisch, Italienisch, Holländisch, Katalanisch, Arabisch, Griechisch, Farsi, Philipinisch, Ukrainisch, Kazachisch, Schwedisch. Weitere sollen folgen.

  2. Funktioniert offline, auch auf Geräten mit geringerer Rechenleistung wie Rapsberry Pi, Android, iOS

  3. Einfache Installation

  4. Portable Sprachmodelle mit nur 50MB bis hin zu weit größeren Modellen für Serveranwendungen

  5. Streaming API

  6. Untersützung vieler Programmiersprachen, neben Python z.B. Java, C#, JavaScript usw.

  7. Unterscheidung verschiedener Sprecher

Der eingangs erwähnten Unterteilung folgend lässt sich Vosk jeweils mit überschaubarem Aufwand für beide Einsatzmöglichkeiten der Spracherkennung nutzen, Transskription und simultane Spracherkennung.

Transskription

Bei einer Transskription werden zuvor aufgezeichnete Audiodaten mit Hilfe der Spracherkennung von Vosk in Text umgewandelt. Die Sprachquelle kann hierbei etwa die MP3-Datei eines Podcast oder ein mit der Diktier-App eines Handys aufgenommener und als Audiodatei exportierter Sprachmitschnitt sein.

Auf GitHub ist die Programmschnittstelle zu den Funktionen von Vosk mitsamt Bindungen an zahlreiche Programmiersprachen erhältlich. Neben Beispielen in C, C#, Python, Go, Nodejs uvm. liefert die dort auch befindliche Klasse DecoderDemo.java eine gute Grundlage für die Ausführung einer Transskription mit Java.

die Klasse DecoderDemo des Vosk-Projekts
package org.vosk.demo;

// importe ausgeblendet

public class DecoderDemo {

    public static void main(String[] argv) throws IOException,
    UnsupportedAudioFileException {
        LibVosk.setLogLevel(LogLevel.DEBUG);
        try (Model model = new Model("model");
                    InputStream ais = AudioSystem.getAudioInputStream(
                      new BufferedInputStream(
                        new FileInputStream("../../python/example/test.wav")));
                    Recognizer recognizer = new Recognizer(model, 16000)) {
            int nbytes;
            byte[] b = new byte[4096];
            while ((nbytes = ais.read(b)) >= 0) {
                if (recognizer.acceptWaveForm(b, nbytes)) {
                    System.out.println(recognizer.getResult());
                } else {
                    System.out.println(recognizer.getPartialResult());
                }
            }
            System.out.println(recognizer.getFinalResult());
        }
    }
}

In die Entwicklungsumgebung der Wahl geladen (IntelliJ, Eclipse, NetBeans, Visual Studio, …​) kann sie wie oben abgebildet mit dem Java Development Kit (JDK) ausprobiert, kompiliert und als Kommandozeilenprogramm ausgeführt werden. Damit die Ausführung gelingt, müssen zudem die folgenden Klassenbibliotheken heruntergeladen und eingebunden werden (vgl. Vosk-Installationsbeschreibung):

Nur wenige Anpassungen sind darüber hinaus nötig. Als Angabe des Sprachmodells in

Model model = new Model("model");

muss der Ordner angegeben sein, der das zuvor heruntergeladene Sprachmodell enthält. Der Teil

new FileInputStream("../../python/example/test.wav");

muss zu eigenen Testzwecken so abgewandelt werden, dass auf eine geeignete Audiodatei verwiesen wird. Wichtig hierbei ist, dass Vosk .wav-Dateien mit genau einem Audiokanal verarbeitet (Mono). Und auch die Abtastrate (sample rate) muss der Audioquelle entsprechen, sie wird mit folgender Angabe gesetzt.

Recognizer recognizer = new Recognizer(model, 16000)

Ist also die Audiodatei mit einer Abtastrate von 48 kHz aufgenommen, muss die 16000 oben auf 48000 geändert werden. Beim Ausführen der Klasse DecoderDemo mit den beschriebenen Einstellungen wird auf der Kommandozeile der Sprachinhalt als Text in JavaScript Object Notation (JSON) ausgegeben.

{
  "partial" : "Hallo"
}
{
  "partial" : "Hallo Welt"
}
{
  "text" : "Hallo Welt"
}

Mit Hilfe von Gson können aus den Antworten vom Server jeweils die Inhalte von "text" gelesen werden. Eine abgewandelte Form der Klasse DecoderDemo.java mit diesen Schritten sieht wie folgt aus.

die Klasse DecoderDemo erweitert um das Schreiben in eine Textdatei
package org.vosk.demo;

// importe ausgeblendet

public class DecoderDemo {

  // die folgenden Angaben werden z.B. als Parameter auf der
  // Kommandozeile uebergeben und waeren dann Strings
  private static final String MODEL_PATH = "/pfad/zum/modell";
  private static final String AUDIO_IN = "/pfad/zur/eingangsdatei";
  private static final String SAMPLE_RATE = "41000";
  private static final String TEXT_OUT = "/pfad/zur/ausgabedatei";

  public static void main(String[] argv) throws IOException,
    UnsupportedAudioFileException {
    LibVosk.setLogLevel(LogLevel.DEBUG);
    try {
      Model model = new Model(MODEL_PATH);
      InputStream ais = AudioSystem.getAudioInputStream(
              new BufferedInputStream(new FileInputStream(AUDIO_IN)));
      Recognizer recognizer = new Recognizer(model, Long.parseLong(SAMPLE_RATE));
      int nbytes;
      byte[] b = new byte[4096];
      FileOutputStream out = new FileOutputStream(new File(TEXT_OUT));
      Gson gson = new Gson();
      VoskResult vres;
      while ((nbytes = ais.read(b)) >= 0) {
        if (recognizer.acceptWaveForm(b, nbytes)) {
          String result = recognizer.getResult();
          vres = gson.fromJson(result, VoskResult.class);
          System.out.println("Text: " + vres.getText());
          out.write(vres.getText().getBytes());
        } else {
          // ..
        }
      }
      out.flush();
      out.close();
      System.out.println(recognizer.getFinalResult());
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  private class VoskResult {
    private String text;
    public String getText() {
      return text;
    }
    public void setText(String text) {
      this.text = text;
    }
  }
}

Im Codebeispiel sind die Variablen MODEL_PATH, AUDIO_IN, SAMPLE_RATE und TEXT_OUT als Strings hart codiert. Sie würden gewöhnlich als Parameter auf der Kommandozeile übergeben. Die Ergänzungen an der Klasse DecoderDemo bewirken, dass die Ergebnisse der Spracherkennung als Text in eine Datei herausgeschrieben werden.

Zur Aufbereitung des zuvor aufgezeichneten Audiomaterials kann vorab ffmpeg mit folgendem Kommando dienen:

ffmpeg -i audio-in.m4a -ac 1 audio-out-mono.wav

// oder mit Verringerung der Abtastrate auf 8kHz
ffmpeg -i audio-in.m4a -ac 1 -b:a 8k -ar 8000 audio-out-mono.wav

Mit der wie oben gezeigten Abwandlung des einfachen DecoderDemo-Beispiels ist bereits eine Lösung entstanden, die mit Hilfe von Vosk beliebige Sprachmitschnitte zu Text transskribiert.

Simultane Spracherkennung

Bei der simultanen Spracherkennung wird die Sprache noch während dem Sprechen in Text verwandelt. Ein Anwendungsfall für diese Form der Spracherkennung ist beispielsweise die Ausführung gesprochener Kommandos. Hier lauscht eine Maschine auf Sprache und muss die erfassten Audiosignale fortlaufend auf gesprochene Kommandos hin überprüfen.

Soll bei diesem Anwendungsfall anders als im vorangegangenen Beispiel der Client zudem ohne lokal installiertes Sprachmodell und ohne lokal installierte Spracherkennungslogik funktionieren, muss die Spracherkennung als Dienst eingebunden werden, der über das Netz angesprochen wird.

Vosk Server

Die Webseite von Vosk hält hierfür weitere zahlreiche Beispiele bis hin zu einem Vosk Server bereit, der mit einem einzelnen Docker-Kommando gestartet werden kann. Die Server-Variante von Vosk enthält zahlreiche Beispiele, wie der Server aus verschiedenen Programmiersprachen wie Python oder Nodejs heraus verwendet wird.

Webclient

Hier in diesem Artikel wird ein Webclient beschrieben, der ohne weitere Abhängigkeiten den containerisierten Vosk Server nutzt. Unser Webclient ist nicht als 'best practise' Code-Beispiel gedacht, das für bestimmte Formen oder Stile der Umsetzung steht, er soll gewissermaßen als Durchstich lediglich die simultane Spracherkennung als eigenständig funktionierende 'Live-Demo' veranschaulichen.

Vosk Server ausführen

Die containerisierte Form des Vosk Servers hat den Vorteil, ohne größere Umstände mit einem Kommando sofort ausführbar zu sein. Zum Ausprobieren muss allerdings Docker installiert sein. Je nach verwendetem Betriebssystem finden sich Installationanweisungen für Linux, Mac und Windows.

Soll für eine derartige Erprobung nicht eigens Docker direkt auf der eigenen Maschine installiert werden, kann stattdessen auch eine virtuelle Maschine mit VirtualBox verwendet werden, in der Alpine Linux installiert wird. Auf Alpine Linux gelingt die Installation von Docker dann so, wie im Alpine Wiki beschrieben.

Sobald Docker verfügbar ist, genügt das folgende Kommando für einen Start des Vosk Servers:

docker run -d -p 2700:2700 alphacep/kaldi-de:latest

Nach gelungenem Start ist der Server über Websockets unter dem URL ws://localhost:2700 ansprechbar. Im Container sind zudem einige Beispiele, die dessen Verwendung mit unterschiedlichen Programmiersprachen bzw. Ablaufumgebungen zeigen, z.B. Python, Go, Nodejs uvm. In diesem Artikel soll allerdings anstelle dieser Beispiele eine Nutzung aus dem Browser heraus ohne sonstige Abhängigkeiten betrachtet werden.

Demo

Unser Webclient geht davon aus, dass ein wie zuvor beschriebener Vosk-Server gestartet wurde und via Websocket ansprechbar ist. Er besteht aus einer HTML-Seite und etwas JavaScript, die Bestandteile finden sich auf GitHub. Wird der Inhalt des Ordners public_html von dort auf die lokale Maschine geladen muss nur die Datei index.html im Browser geöffnet werden.

ein Webclient zum Ausprobieren des Vosk-Servers

Vosk-Client

Mit dem Webclient kann ein eingebautes oder angeschlossenes Mikrofon aus dem Browser heraus ein- und ausgeschaltet werden. Nach dem Einschalten des Mikrofons lauscht der Client auf Sprache und sendet diese an den Vosk Server. Antworten des Servers mit erkannter Sprache als Text werden simultan während des Sprechens im Client angezeigt.

Steuerung

So lange auf gesprochene Kommandos reagiert werden soll muss das Mikrofon 'offen' sein und auf Kommandos lauschen. An den Server sollen aber nur Signale weitergereicht werden, wenn gerade etwas gesagt wird, wenn also beim Eingangssignal eine bestimmte Lautsärke gemessen werden kann. Diese Steuerung geschieht in der gleichnamigen Funktion steuerung des Clients.

Werden Tonsignale erkannt, verwandelt die Funktion mitschnittSenden die Tonsignale ins WAV-Format und schickt sie via Websocket-Verbindung zum Server.

Die Antwort des Servers mit dem aus der Sprache erkannten Text wird auf die Webseite geschrieben und es ist zu sehen, was der Server aus den gesprochenen Signalen erkannt hat. Der Ablauf wiederholt sich, so lange das Mikrofon eingeschaltet ist und es ist so noch während dem Weitersprechen das Ergebnis der Spracherkennung simultan in Echtzeit zu sehen.

Qualität und Trefferquote

Normal gesprochenes Hochdeutsch wird von Vosk fast zu hundert Prozent erkannt, es kann dafür ganz natürlich gesprochen werden. Die Spracherkennung geschieht so schnell, dass noch während dem Sprechen das Ergebnis als Text vorliegt. Gesprochene Kommandos werden mit wenig Zeitverzug erkannt und können damit in einen flüssigen Programmablauf eingehen. Mit der Anwendung des passenden Modells gelingt die Erkennung von Kommandos, die in unterschiedlichen Sprachen gesprochen werden.

Schlussbemerkungen

Die hier genutzte containerisierte Fassung des Vosk Servers hat den Vorteil, mit nur einem Kommando ausführbar sowie portabel für skalierbare Lösungen einsetzbar zu sein.

Anstelle der containerisierten Fassung kann mit einfachen Mitteln auch ein eigener Server gebaut werden, der eine Websocket-Implementierung mit einer ähnlichen Logik kombiniert, wie sie oben bei der Transskription mit der Klasse DecoderDemo beschrieben ist. Das erlaubt mehr Eingriffsmöglichkeiten in Funktionsweise und Konfiguration und ist bei Bedarf ebenfalls containerisierbar.

Aus der großen Zahl von möglichen Implementierungen in den unterschiedlichsten Programmiersprachen und Ablaufumgebungen zeigt dieser Beitrag zwei Beispiele und lässt Raum für Erweiterungen und Verbesserungen in Richtung eigener Einsatzmöglichkeiten der Spracherkennung.

Fazit

Mit Vosk lässt sich Sprache maschinell erkennen und für ganz unterschiedliche "speech to text" Lösungen in eigene Programme einbauen. Das Werkzeug ist unabhängig von Geräten und proprietärer Software aus einer großen Zahl von Programmiersprachen heraus einsetzbar und kann als zentrale Server-Funktion oder auf einer lokalen Maschine offline genutzt werden.

Wird ein wie hier vorgestellter Webclient verwendet, genügt ein Browser, um Anwendungen mit gesprochenen Kommandos steuerbar zu machen. Zur Steuerung von Anwendungen reicht im einfachsten Fall ein Handy und das gesprochene Wort.