Блог Влада Вильгельма

PocketSphinx в Паскале

2017-05-18 01:37:52 /программы/

 

Хочется голосового управления в программах? Не! Конечно не во всех! Но в части "тупого дома" вполне вариант.

Господа разработчики одной из опенсурсных библиотек распознавания речи (CMU Pocketsphinx) полагают, что использовать их продукт можно и должно токма на ведроиде и сях. Пришлось малость поковыряться в их кривом коде и адаптировать его под свои нужды...

Ну пасквилянт я! Не могу опуститься до уродливого синтаксиса Це--

Пример использования библиотеки тут:


program project1;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, pocketsphinx, CustApp;

type
 TMyApplication = class(TCustomApplication)
 protected
  procedure DoRun; override;
  procedure Say(txt: string);
 public
  procedure WriteHelp; virtual;
 end;

const
 cmdLn: array [0..3] of PChar = (
  '-jsgf', '/usr/share/hobgoblin/my.jsgf',
  '-dict', '/usr/share/hobgoblin/my.dict'
  );
 micBuffSize = 512;

procedure TMyApplication.DoRun;
var
 ErrorMsg: String;
 buff: array [0..micBuffSize - 1] of int16;
 ad: PpsAudioDevice;
 conf: PpsConfig;
 ps: PpsDecoder;
 b: integer;
 uttStarted, inSpeech: boolean;
 s, dt: string;
 us: WideString;
 s1: RawByteString;
begin
 // parse parameters
 if hasoption('h', 'help') then begin
  writehelp;
  terminate;
  exit;
 end;

 { add your program here }

 say('parse settings...');

 conf:= cmd_ln_parse_r(nil, psdefaultargs, length(cmdln), @cmdln, true);
 if not assigned(conf) then begin
  terminate;
  exit;
 end;

 say('apply settings...');

 ps_default_search_args(conf);

 say('init sphinx...');

 try // realy init stupid library
  ps_start_utt(nil)
 except end;

 ps:= nil;
 try
  ps:= ps_init(conf);
 except
  on e: exception do say(e.message);
 end;

 if not assigned(ps) then begin
  cmd_ln_free_r(conf);
  say('sphinx not started :(');
  terminate;
  exit;
 end;

 say('starting mic...');

 ad:= ad_open_sps(16000);
 if not assigned(ad) then begin
  ps_free(ps);
  cmd_ln_free_r(conf);
  say('mic not started :(');
  terminate;
  exit;
 end;

 ad_start_rec(ad);

 say('starting sphinx...');

 uttstarted:= false;
 try
  if ps_start_utt(ps) > -1 then while not Terminated do begin
  	b:= ad_read(ad, buff, micBuffSize);
   if b < 1 then break;
   ps_process_raw(ps, buff, b, 0, 0);
   inSpeech:= ps_get_in_speech(ps) > 0;
   if not uttStarted and inSpeech then Say('listen...');
   uttStarted:= uttStarted or inSpeech;
   if not inSpeech and uttStarted then begin
    ps_end_utt(ps);
    s:= string(ps_get_hyp(ps, nil));
    if (s > '') then try
     writeln(s);
     us:= Utf8Decode(s);
     s1:= '';
     widestringmanager.Unicode2AnsiMoveProc(punicodechar(us), s1, 1251, length(us));
     dt:= '';
     for b:= 1 to length(s1) do dt:= dt + IntToHex(byte(s1[b]), 2);
     //lineModule1.SendData('virt', 'T', dt);
    except end;
    if ps_start_utt(ps) < 0 then break;
    uttStarted:= False;
    Say('ready...');
    sleep(100);
   end;
  end;

 finally
  ad_stop_rec(ad);
  ad_close(ad);
  Terminate;
 end;

 ps_free(ps);
 cmd_ln_free_r(conf);

end;

procedure TMyApplication.WriteHelp;
begin
 writeln('Usage: ', ExeName, ' -h');
end;

procedure TMyApplication.Say(txt: string);
begin
 writeln(txt);
end;

var
 Application: TMyApplication;
begin
 Application:=TMyApplication.Create(nil);
 Application.Title:='My Application';
 Application.Run;
 Application.Free;
end.


За "изящество" кода извиняйте! Сие - быстрая адаптация куска мелкого робота. Но, как мне кажется, вполне достаточная для понимания.

Я влепил в библиотеку только те функции и типы данных, которые мне шкурно и сиюсекундно были нужны. Если есть надобность (во что я не верю), остальное можете быстренько накидать из описаний на их сайте.

Инструкция по установке - там же.

Самая нудная работа по адаптации этого чуда (сбор начальных параметров из разных источников в сырцах) таки сделана. Остальное - мелочь.

Пользуйтесь ахы :)


З.Ы. в примере есть мелкий кривой кусочек:


 try // realy init stupid library
  ps_start_utt(nil)
 except end;


Он нужен в случае инициализации библиотеки версии 5-prealpa для работы в консольных приложениях. Не знаю почему, но, без насильственного вызова сбоя при распознавании, библиотека выдает ошибку "Division by zero" во время передачи в нее настроек. Возможно, для пейсателей, хранящих настройки в коде в текстовом виде, это таки нормально.

Впрочем, если либу устанавливать из репозиториев Ubuntu, таких проблем не возникает и все работает как надо без этих извращений.

Для успешной компиляции проектов на линюхе (если библиотека устанавливалась из репозитория, а не компилировалась из исходников) выполните:

 sudo apt-get install libpocketsphinx-dev libsphinxbase-dev



Прикрепленные файлы:

замордобучить

powered by WILHELM.AZ