Action Plugin SAPI (source code)
Publicado: 12 Nov 2010 19:00
webultra escribió:He creado este post para ir discutiendo y desarrollando un actino plugin para el manejo de la librería SAPI.
Aca comienzo con algunas funciones globales que hice (solo quienes respondan a este post podrán ver el código, y para verlo correctamente peguenlo en el script editor ó en un lugar de un proyecto de ams):
Código: Seleccionar todo
--Primero creamos la tabla de información de idiomas if not langsss then langsss = {{LangID=\"en\", Idioma=\"Ingles\", LangCode=\"409\"},{LangID=\"es\", Idioma=\"Español\", LangCode=\"40A\"},{LangID=\"cn\", Idioma=\"Chinese\", LangCode=\"804\"},{LangID=\"jp\", Idioma=\"Japanese\", LangCode=\"411\"},{LangID=\"tcn\", Idioma=\"Traditional Chinese\", LangCode=\"404\"}};--You will be able to select any of these languages only if you have installed a voice for it end function CreateVoiceObject(handle) local handle = luacom.CreateObject(\"Sapi.SpVoice\") if type(handle) == \"table\" then return handle; else return nil; end end function DestroyVoiceObject(handle) handle = nil; end function GetVoices(handle) if not handle or type(handle) ~= \"table\" then return nil; else local tblToReturn = {}; for x=0, 30 do local voice = handle:GetVoices():Item(x); if voice then Table.Insert(tblToReturn, Table.Count(tblToReturn)+1, {Item = x, Voice=voice:GetDescription()}); else break; end end if Table.Count(tblToReturn) > 0 then return tblToReturn; else return nil; end end end function SetLang(lang, by)--Example: SetLang(\"es\", \"LangID\") or SetLang(\"40A\", \"LangCode\") or SetLang(\"Español\", \"Idioma\") if not by then return \"409\"; else for x, y in pairs(langsss) do if y[by] == lang then return y.LangCode; end end end end function GetLang(lang, by, get)--GetLang(\"es\", \"LangID\", \"Idioma\") or GetLang(\"Español\", \"Idioma\", \"LangCode\") or GetLang(\"40A\", \"LangCode\", \"LangID\") or GetLang(\"ALL\", \"string\") or GetLang(\"ALL\", \"table\", \"Idioma\") if lang == \"ALL\" then if not by then return \"You must especify a valid \"by\" argument\"; elseif by == \"string\" then local strLanguages = \"\"; for x, y in pairs(langsss) do for a, b in pairs(y) do if strLanguages == \"\" then strLanguages = a..\": \"..b else strLanguages = strLanguages..\"\r\n\"..a..\": \"..b end end strLanguages = strLanguages..\"\r\n\" end return strLanguages; elseif by == \"table\" then local t = {}; if not get then for x, y in pairs(langsss) do Table.Insert(t, Table.Count(t)+1, y); end else for x, y in pairs(langsss) do Table.Insert(t, Table.Count(t)+1, y[get]); end end if Table.Count(t) > 0 then return t; else return nil; end end else if not by then return \"You must especify a valid \"by\" argument\"; else for x, y in pairs(langsss) do if y[by] == lang then if not get then return y.Idioma else return y[get]; end end end end end end function SetRate(nRate) return nRate; end function talk(handle, txt, blast, nRate) if not handle or type(handle) ~= \"table\" then return false; else if not nRate then nRate = -2; end if blast then local tTexto = xDialog.DelimitedStringToTable(txt, \"\r\"); if tTexto then handle:Speak(\"<rate speed='\"..nRate..\"'/><lang langid='\"..lang..\"'>\"..tTexto[Table.Count(tTexto)]..\"</lang>\"); return true; else return false; end else handle:Speak(\"<rate speed='\"..nRate..\"'/><lang langid='\"..lang..\"'>\"..txt..\"</lang>\"); return true; end end end function talkbyte(cb, handle, txt, nRate) if not handle or type(handle) ~= \"table\" then return nil; else if not nRate then nRate = -2; end if not cb then cb = coroutine.create(talkby) coroutine.resume(cb, handle, txt, nRate); else if type(cb) == \"thread\" then coroutine.resume(cb) if coroutine.status(cb) == \"dead\" then return nil; end else return nil; end end return cb; end end function talkby(handle, txt, nRate) if not handle or type(handle) ~= \"table\" then return false; else if not nRate then nRate = -2; end local tText = xDialog.DelimitedStringToTable(txt, \"\r\n\r\n\"); for x=1, Table.Count(tText) do handle:Speak(\"<rate speed='\"..nRate..\"'/><lang langid='\"..lang..\"'>\"..tText[x]..\"</lang>\"); if x == Table.Count(tText) then handle:Speak(\"<rate speed='\"..nRate..\"'/><lang langid='\"..lang..\"'>.Ya ha terminado la lectura.</lang>\"); return false; else coroutine.yield(); end end end end function SetVoice(handle, nVoice) if not handle or type(handle) ~= \"table\" then return false; else local tVoices = GetVoices(handle); if tVoices then if tVoices[nVoice] then handle.Voice = handle:GetVoices():Item(tVoices[nVoice].Item) return true; else return false; end else return false; end end end
Ojo, la función talkby() sólo será llamada a través de la función talkbyte() que crea una co-rutina. Dicha función sirve para leer el texto por párrafos.
Por ejemplo:
NombredelaCorutina = talkbyte(NombredelaCorutina, MiObjetodeVoz, "Lee por párrafos\r\nEste Texto", -2);
Dicha función deberá ser colocada por ejemplo en un botón, y cada que se pulse pues se leerá el siguiente párrafo.
En la función talk(), el tercer argumento (blast) es boolean y le indica al programa leer sólo el último párrafo.
Aca un ejemplo de cómo estoy usando este código en un proyecto:
Es importante mencionar que sino existe una voz para el idioma seleccionado entonces se usará la voz predeterminada (Microsoft Sam o Microsoft Anna)Código: Seleccionar todo
vObj = CreateVoiceObject(\"vObj\");--Primero creo el objeto lang = SetLang(\"40A\", \"LangCode\");--Determino el idioma que quiero rate = SetRate(-2);--Determino la velocidad que quiero if SetVoice(vObj, 2) then--Elijo la segunda voz de mi sistema (en mi caso si existe) talk(vObj, \"Hola. Ha logrado iniciar\", false, rate); end
Espero sus comentarios y aportes a este source code.
Movido desde el antiguo foro.webultra escribió:Jejeje, recién me puse a revisar cosas que tenía yo sin sentido. Deja bajo el archivo rafa para echarle un ojo. Aca dejo el código había hecho pero ahora modificado para establecer el rate desde la propiedad del objeto com.
Así como dices Rafa, lo importante esta en sacar el estado del objeto (hablando, en pausa o terminado) lo cual ya hice jejeje. Revisa las funciones Pause, IsPaused, Resume e IsReading. Trabajan solo si el objeto esta leyendo (hablando) en modo asincrono, ya que en otro modo pues simplemente congela la app hasta que termina.
Por cierto, agregue una función para leer un archivo de texto (pero solo con la voz de default). Por ahora me estoy peleando más en hacer una función para leer un texto y guardarlo en wav jejeje porque creo debo crear antes 2 o tres objetos más.
Por cierto, al menos con este source puedes crear tantos objetos como quieras y así tener cualquier cantidad de voces hablando distintos textos a la vez.
Por cierto, no se que onda con la versión de SAPI porque en una al usar SetVoice no hay bronca, pero en otra da error. Igual pasa con GetVoices.
(source oculto):Código: Seleccionar todo
--My SAPI Plugin Globals _FlagsForTalk = 0; _langsss = {{LangID=\"en\", Idioma=\"Ingles\", LangCode=\"409\"},{LangID=\"es\", Idioma=\"Español\", LangCode=\"40A\"},{LangID=\"cn\", Idioma=\"Chinese\", LangCode=\"804\"},{LangID=\"jp\", Idioma=\"Japanese\", LangCode=\"411\"},{LangID=\"tcn\", Idioma=\"Traditional Chinese\", LangCode=\"404\"}};--You will be able to select any of these languages only if you have installed a voice for it --Voice Flags Globals _SVSFDefault = 0 _SVSFlagsAsync = 1 _SVSFPurgeBeforeSpeak = 2 _SVSFIsFilename = 4 _SVSFIsXML = 8 _SVSFIsNotXML = 16 _SVSFPersistXML = 32 --Normalizer Flags Globals _SVSFNLPSpeakPunc = 64 --Masks Flags Globals _SVSFNLPMask = 64 _SVSFVoiceMask = 127 _SVSFUnusedFlags = -128 function GetStatus(handle) if not handle or type(handle) ~= \"table\" then return nil; else return {Running = handle.Status.RunningState, Current = handle.Status.CurrentStreamNumber, BufferLenght = handle.Status.InputSentenceLength, BufferPos = handle.Status.InputSentencePosition, WordLenght = handle.Status.InputWordLength, WordPos = handle.Status.InputWordPosition, LastStream = handle.Status.LastStreamNumberQueued}; end end function IsReading(handle) if not handle or type(handle) ~= \"table\" then return false; else if _FlagsForTalk == 0 then return false; else if handle.Status.RunningState == 1 or handle.Status.RunningState == 0 then return false; elseif handle.Status.RunningState == 2 then return true; end end end end function IsPaused(handle) if not handle or type(handle) ~= \"table\" then return false; else if _FlagsForTalk == 0 then return false; else if handle.Status.RunningState == 0 then return true; else return false; end end end end function Pause(handle) if not handle or type(handle) ~= \"table\" then return false; else if _FlagsForTalk == 0 then return false; else if IsReading(handle) then handle:Pause(); return true; else return false; end end end end function Resume(handle) if not handle or type(handle) ~= \"table\" then return false; else if _FlagsForTalk == 0 then return false; else if IsPaused(handle) then handle:Resume(); return true; else return false; end end end end function SetReadMethod(nMethod) if type(nMethod) == \"number\" then _FlagsForTalk = nMethod return true; else return false; end end function ReadFile(handle, sFileName)--With default voice and language if not handle or type(handle) ~= \"table\" then return false; else if File.DoesExist(sFileName) then handle:Speak(sFileName, 5);--It's the same as _SVSFIsFilename + _SVSFlagsAsync return true; else return false; end end end function CreateVoiceObject(handle) local handle = luacom.CreateObject(\"Sapi.SpVoice\") if type(handle) == \"table\" then return handle; else return nil; end end function DestroyVoiceObject(handle) handle = nil; end function GetVoices(handle) if not handle or type(handle) ~= \"table\" then return nil; else local tblToReturn = {}; if System.GetOSName() == \"Windows XP\" then local tKeys = Registry.GetKeyNames(HKEY_LOCAL_MACHINE, \"Software\\Microsoft\\Speech\\Voices\\Tokens\"); if tKeys then for x, y in pairs(tKeys) do local voice = Registry.GetValue(HKEY_LOCAL_MACHINE, \"Software\\Microsoft\\Speech\\Voices\\Tokens\\"..y, \"\", false); Table.Insert(tblToReturn, Table.Count(tblToReturn)+1, {Item = x-1, Voice=voice}); end end if Table.Count(tblToReturn) > 0 then return tblToReturn; else return nil; end else for x=0, 30 do local voice = handle:GetVoices():Item(x); if voice then Table.Insert(tblToReturn, Table.Count(tblToReturn)+1, {Item = x, Voice=voice:GetDescription()}); else break; end end if Table.Count(tblToReturn) > 0 then return tblToReturn; else return nil; end end end end function SetLang(lang, by)--Example: SetLang(\"es\", \"LangID\") or SetLang(\"40A\", \"LangCode\") or SetLang(\"Español\", \"Idioma\") if not by then return \"409\"; else for x, y in pairs(_langsss) do if y[by] == lang then return y.LangCode; end end end end function GetLang(lang, by, get)--GetLang(\"es\", \"LangID\", \"Idioma\") or GetLang(\"Español\", \"Idioma\", \"LangCode\") or GetLang(\"40A\", \"LangCode\", \"LangID\") or GetLang(\"ALL\", \"string\") or GetLang(\"ALL\", \"table\", \"Idioma\") if lang == \"ALL\" then if not by then return \"You must especify a valid \"by\" argument\"; elseif by == \"string\" then local strLanguages = \"\"; for x, y in pairs(_langsss) do for a, b in pairs(y) do if strLanguages == \"\" then strLanguages = a..\": \"..b else strLanguages = strLanguages..\"\r\n\"..a..\": \"..b end end strLanguages = strLanguages..\"\r\n\" end return strLanguages; elseif by == \"table\" then local t = {}; if not get then for x, y in pairs(_langsss) do Table.Insert(t, Table.Count(t)+1, y); end else for x, y in pairs(_langsss) do Table.Insert(t, Table.Count(t)+1, y[get]); end end if Table.Count(t) > 0 then return t; else return nil; end end else if not by then return \"You must especify a valid \"by\" argument\"; else for x, y in pairs(_langsss) do if y[by] == lang then if not get then return y.Idioma else return y[get]; end end end end end end function SetRate(handle, nRate) if not handle or type(handle) ~= \"table\" then return false; else handle.Rate = nRate; if handle.Rate then return true; else return false; end end end function GetRate(handle) if not handle or type(handle) ~= \"table\" then return nil; else return handle.Rate; end end function talk(handle, lang, txt, blast) if not handle or type(handle) ~= \"table\" then return false; else if blast then local tTexto = xDialog.DelimitedStringToTable(txt, \"\r\"); if tTexto then handle:Speak(\"<lang langid='\"..lang..\"'>\"..tTexto[Table.Count(tTexto)]..\"</lang>\", _FlagsForTalk); return true; else Audio.Load(CHANNEL_NARRATION, _SourceFolder..\"\\AutoPlay\\Audio\\No se puede dic.ogg\", true, false); return false; end else handle:Speak(\"<lang langid='\"..lang..\"'>\"..txt..\"</lang>\", _FlagsForTalk); return true; end end end function talkbyte(cb, handle, lang, txt) if not handle or type(handle) ~= \"table\" then return nil; else if not nRate then nRate = -2; end if not cb then cb = coroutine.create(talkby) coroutine.resume(cb, handle, lang, txt); else if type(cb) == \"thread\" then coroutine.resume(cb) if coroutine.status(cb) == \"dead\" then return nil; end else return nil; end end return cb; end end function talkby(handle, lang, txt) if not handle or type(handle) ~= \"table\" then return false; else local tText = xDialog.DelimitedStringToTable(txt, \"\r\n\r\n\"); for x=1, Table.Count(tText) do handle:Speak(\"<lang langid='\"..lang..\"'>\"..tText[x]..\"</lang>\", _FlagsForTalk ); if x == Table.Count(tText) then handle:Speak(\"<lang langid='\"..lang..\"'>.Ya ha terminado la lectura.</lang>\", _FlagsForTalk); return false; else coroutine.yield(); end end end end function linea(handle, lang, txt, nRate) if not handle or type(handle) ~= \"table\" then return false; else if not nRate then nRate = -2; end local nF = String.ReverseFind(txt, \".\", false); if nF ~= -1 then return talk(handle, String.Mid(txt, nF+1, -1), false); else return talk(handle, txt, true); end end end function deletrear(handle, lang, txt, bword) if not handle or type(handle) ~= \"table\" then return false; else if not nRate then nRate = -2; end local nF = String.ReverseFind(txt, \" \", false); if nF ~= -1 then local word = String.Mid(txt, nF+1, -1); local nLargo = String.Length(word); if bword then return talk(handle, lang, word, false); else for x = 1, nLargo do handle:Speak(\"<lang langid='\"..lang..\"'><spell>\"..String.Mid(word, x, 1)..\"</spell></lang>\", _FlagsForTalk ); end return true; end else local nF = String.ReverseFind(txt, \"\r\", false); if nF ~= -1 then local word = String.Mid(txt, nF+1, -1); local nLargo = String.Length(word); if bword then return talk(handle, lang, word, false); else for x = 1, nLargo do handle:Speak(\"<lang langid='\"..lang..\"'><spell>\"..String.Mid(word, x, 1)..\"</spell></lang>\", _FlagsForTalk ); end return true; end else local nLargo = String.Length(txt); if bword then return talk(handle, lang, txt, false); else for x = 1, nLargo do handle:Speak(\"<lang langid='\"..lang..\"'><spell>\"..String.Mid(txt, x, 1)..\"</spell></lang>\", _FlagsForTalk ); end return true end end end end end function SetVoice(handle, nVoice)---SetVoice(vObj, 2); if not handle or type(handle) ~= \"table\" then return false; else if System.GetOSName() == \"Windows XP\" then return false; else local tVoices = GetVoices(handle); if tVoices then if tVoices[nVoice] then handle.Voice = handle:GetVoices():Item(tVoices[nVoice].Item) return true; else return false; end else return false; end end end end