Action Plugin SAPI (source code)

Esta sección esta especialmente diseñada para todos aquellos usuarios que estés desarrollando plugins o aplicaciones y quieran ayuda para dichos plugins o aplicaciones.
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:

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
Es importante mencionar que sino existe una voz para el idioma seleccionado entonces se usará la voz predeterminada (Microsoft Sam o Microsoft Anna)

Espero sus comentarios y aportes a este source code.
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
Movido desde el antiguo foro.
:SOS: thanksssssssss
Una Re Chimba!!!

Gracias
is it speech recognition or speech to text sample ? really i am not understanding....
muchas gracias