Голосовое управление 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')");
    	}
    }
    


    По итогам тестирования: сфинкс время от времени путает команды, или принимает за команды посторонние шумы.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 2

      0
      По поводу шумов — в конфигурации есть компонент SpeechClassifier, у него есть праметр threshold, можете попробовать его настроить. Ещё есть SpeechMarcker. Оба этих компонента отвечают за то, что называется voice activity detection.

      А вообще, sphinx4 больше подходит для пакетной обработки. Для веб-экспериментов попробуйте github.com/syl22-00/pocketsphinx.js и github.com/mbait/pocketsphinx-nacl.
        0
        Спасибо за совет, попробую, когда свободное время будет.

      Only users with full accounts can post comments. Log in, please.