Java
8 October 2014

Голосовое управление web-плеером, или скрещиваем CMU Sphinx с Selenium WebDriver

В этой статье я описывал создание веб mp3 плеера и домашней аудиосистемы.
Сам плеер можно увидеть здесь.

Возникла идея — прикрутить к плееру голосовое управление.
После часа-другого поиска в интернете решение нашлось:
CMU Sphinx — для распознавания речи + Selenium WebDriver — для программного управления браузером.

Итак, начнем.

Разработку велась в IDE Eclipse.
Для начала нужно преобразовать наш проект в проект Maven:
right click на проекте — Configure — Convert to Maven Project.

В файл pom.xml нужно добавить следующее:

<repositories>
  <repository>
    <id>snapshots-repo</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    <releases><enabled>false</enabled></releases>
    <snapshots><enabled>true</enabled></snapshots>
  </repository>
</repositories>
  
<dependencies>
  <dependency>
     <groupId>edu.cmu.sphinx</groupId>
     <artifactId>sphinx4-core</artifactId>
      <version>1.0-SNAPSHOT</version>
  </dependency>
</dependencies>


Также нам понадобится:

1. Русская акустическая модель (скачать можно здесь)
— скачиваем последнюю версию архива, и копируем папку zero_ru.cd_cont_4000 в нашу папку с исходниками.

2. Selenium WebDriver для Java (скачать) — подключаем к проекту jar-файл библиотеки из архива.

3. Эти файлы — для генерирования транскрипций русских слов используем dict2transcript.pl.

И так, можно начать работу над программой.

С помощью скрипта dict2transcript.pl составляем наш словарь — mydict.dict:

cranberries k r y n bb i rr i s
gromche g r oo m ch i
iskat i s k aa tt
kinoproby kk i n a p r oo b y
minus mm ii n u s
nautilus n ay u tt ii l u s
nazad n ay z aa t
number1 a dd ii n
number3 t rr ii
number10 dd je ss i tt
number30 t rr ii c ay tt
pausa p aa u z ay
plus p ll ju s
rammstein r aa m sh t ay j n
snaipery s n aa j pp i r y
start s t ay r t
tishe tt ii sh y


Затем составляем grammar-файл — mygrammar.gram:

#JSGF V1.0;

grammar mygrammar;

public <start> = start;

public <pausa> = pausa;

public <tishe> = tishe;
public <gromche> = gromche;

public <switch> = (plus|minus)(number1|number3|number10|number30);

public <find> = iskat;
public <changeartist> = <find>(cranberries|kinoproby|nautilus|rammstein|snaipery);


И вот, собственно, исходник программы на Java:

package jatx.sphinxtest;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

import edu.cmu.sphinx.api.Configuration;
import edu.cmu.sphinx.api.LiveSpeechRecognizer;
import edu.cmu.sphinx.api.SpeechResult;
import edu.cmu.sphinx.result.WordResult;

public class Main {
	private static final WebDriver driver = new FirefoxDriver();
	private static int volume = 100;
	private static final String[] prefixes =
		{"plus","minus","iskat"};
	private static final String[] artists =
		{"cranberries", "kinoproby","nautilus","rammstein","snaipery"};
	private static final String[] numbers =
		{"number1","number3","number10","number30"};
	static {
		driver.manage().window().maximize();
		music();
	}
	
	public static void main(String[] args) {
		
		Configuration configuration = new Configuration();
		 
		configuration.setAcousticModelPath("resource:/jatx/sphinxtest/zero_ru.cd_cont_4000");
		configuration.setDictionaryPath("resource:/jatx/sphinxtest/mydict.dict");
		configuration.setUseGrammar(true);
		configuration.setGrammarPath("resource:/jatx/sphinxtest");
		configuration.setGrammarName("mygrammar");
		
		try {
			LiveSpeechRecognizer recognizer = new LiveSpeechRecognizer(configuration);
			recognizer.startRecognition(true);
			
			String prefix = "";
			
			while (true) 
	        {
	            SpeechResult result = recognizer.getResult();

	            for (WordResult r : result.getWords()) 
	            { 
	            	try {
	            		String cmd = r.getWord().toString();
	            		if (cmd.equals("tishe")) volumeDown();
	            		if (cmd.equals("gromche")) volumeUp();
	            		if (cmd.equals("start")) play();
	            		if (cmd.equals("pausa")) pause();
	            		
	            		for (String pref: prefixes) {
	            			if (cmd.equals(pref)) prefix = pref;
	            		}
	            		
	            		for (String artist: artists) {
	            			if (cmd.equals(artist)&&prefix.equals("iskat")) changeArtist(artist);
	            		}
	            		            		
	            		for (String number: numbers) {
	            			if (cmd.equals(number)) {
	            				Integer num = Integer.parseInt(number.replace("number", ""));
	            				if (prefix.equals("minus")) rev(num);
	            				if (prefix.equals("plus")) fwd(num);
	            			}
	            		}
	            		
	            		//System.out.println(cmd);
	            	} catch (Exception e) {
	            		e.printStackTrace();
	            	}
	            }
	        }
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static void music() {
		System.out.println("music");

		driver.get("http://home.tabatsky.ru/mp3player/desktop.jsp");
	}
	
	private static void volumeDown() {
		System.out.println("volume down");
		volume = (volume>10?volume-20:volume);
		setVolume(volume);
	}

	private static void volumeUp() {
		System.out.println("volume up");
		volume = (volume<90?volume+20:volume);
		setVolume(volume);
	}
	
	private static void setVolume(int volume) {
		JavascriptExecutor js = (JavascriptExecutor) driver;
		js.executeScript("$('#volume_slider').slider('value',"
					+ Integer.valueOf(volume) + ")");
		js.executeScript("window.setVolume("+Double.valueOf(volume/100.0)+")");
	}
	
	private static void play() {
		System.out.println("start");
		
		WebElement track = driver.findElement(By.id("0"));
		track.click();
	}
	
	private static void pause() {
		System.out.println("pause");
		
		JavascriptExecutor js = (JavascriptExecutor) driver;
		js.executeScript("$('#toogle').trigger('click')");
	}
	
	private static void fwd(int n) {
		System.out.println("plus " + Integer.valueOf(n).toString());
		WebElement fwd = driver.findElement(By.id("fwd"));
		for (int i=0; i<n; i++) {
			fwd.click();
		}
	}
	
	private static void rev(int n) {
		System.out.println("minus " + Integer.valueOf(n).toString());
		WebElement rev = driver.findElement(By.id("rev"));
		for (int i=0; i<n; i++) {
			rev.click();
		}
	}
		
	private static void changeArtist(String artist) {
		System.out.println("Changing artist: "+artist);
		
		String query = "";
		
		switch (artist) {
		case "cranberries":
			query = "cranberries";
			break;
		case "kinoproby":
			query = "кинопробы";
			break;
		case "nautilus":
			query = "nautilus | нау бум | mutatis mutandis";
			break;
		case "rammstein":
			query = "rammstein made in germany | rammstein herzeleid | rammstein mtv music history";
			break;
		case "snaipery":
			query = "ночные снайперы | диана арбенина";
			break;
		}
		
		JavascriptExecutor js = (JavascriptExecutor) driver;
		js.executeScript("$('#query').val('"+query+"')");
		js.executeScript("$('#search').trigger('click')");
	}
}


По итогам тестирования: сфинкс время от времени путает команды, или принимает за команды посторонние шумы.

+7
7.8k 55
Comments 2