• Gmod TTS: 3D voice chatbox Text To Speech with accents!
    49 replies, posted
In light of lots of programmers jumping on the text to speech train abusing Gran PC's peniscorp API's bandwidth, disk space, and CPU, and also the generally low quality of such scripts, I made my own for all of you. This uses the google API as much as possible, giving each player a unique voice based on the player's current name. When too many people are talking in a short span, it will use Gran PC's peniscorp robot voice as a backup server. Voices are 3D, and follow the player. Accents used: Robot English (USA) English (British) French Russian Pitch, speed, and accent are all affected by the speaking player's name. This also accounts for TTT and DarkRP. In TTT, being a spectator or being dead means you don't get TTS. In DarkRP, PMs, OOC, etc, do not have TTS. In DarkRP, TTS is not heard from over 500 units away. The DarkRP default non-OOC chat range is 250. Put this code in autorun/client/gmodtts.lua [lua] local accents = { "http://translate.google.co.uk/translate_tts?tl=en&q=", "http://translate.google.com/translate_tts?tl=en&q=", "http://translate.google.com/translate_tts?tl=fr&q=", "http://translate.google.com/translate_tts?tl=ru&q=", "http://tts.peniscorp.com/speak.lua?" } local wait = false local channels = {} local function playvoice(ply,text,accent,speed) if wait then accent = "http://tts.peniscorp.com/speak.lua?" end sound.PlayURL(accent..text,"3d noplay",function(channel,errorid,errorname) if channel == nil || errorid != nil || errorname != nil then --peniscorp backup; google is upset if wait == false then wait = true timer.Simple(125,function() wait = false end) playvoice(ply,text,accent,speed) end return end channel:SetPos(ply:GetPos(),Vector(0,0,0)) channel:Set3DFadeDistance(300,9999999999) channel:SetPlaybackRate(math.Clamp(.5+speed,.5,2)) channel:Play() table.insert(channels,{channel,ply}) end) end hook.Add("Think","TTSThink",function() for k, v in pairs(channels) do if !v[1]:IsValid() then table.remove(channels,k) return end if v[2] == nil || v[1]:GetState() != GMOD_CHANNEL_PLAYING || v[1]:GetTime() >= v[1]:GetLength() then v[1]:Stop() table.remove(channels,k) return end v[1]:SetPos(v[2]:GetPos(),Vector(0,0,0)) end end) hook.Add("OnPlayerChat","TTSPlayerChat",function(ply,text,team,dead) --firstly, checking for popular gamemode limitations local dotts = true if engine.ActiveGamemode() == "terrortown" then if dead then dotts = false end if ply.IsSpec != nil && ply:IsSpec() then dotts = false end end if engine.ActiveGamemode() == "darkrp" then if text[1] == "/" then dotts = false end if ply:GetPos():Distance(LocalPlayer():GetPos()) > 500 then dotts = false end --250 = default chat range end if dotts then local speed,accent,nick = 1, accents[1], ply:Nick() local nlen = #nick local vowels, vow, notvow, vrat = {"a","e","i","o","u","y"}, 0, 0, 1 for i = 1, nlen do if table.HasValue(vowels,nick[i]) then vow = vow+1 else notvow = notvow+1 end end if vow == 0 then notvow = nlen end vrat = vow/notvow accent = accents[math.Clamp(vow,1,#accents)] speed = vrat playvoice(ply,text,accent,speed) end end) [/lua]
Better than mine by a long shot, nice :)
Text To Speech has a special place in my heart - I don't quite know why I love it so much, I just do Thank you for this, and great work!
1 crappy drawback is how low it sounds in-game versus in a web browser you can barely hear it sometimes but this is really cool, good job.
So can I remove the Russian and French voices if I don't want them without an issue?
yes, but that'll only hurt the unique voices aspect it isn't a translation, just a different way of saying the words
sweet american tts. Nice job tho.
Its better to set up an own TTS servers, because i had problems with 3 players continuesly using google translator tts. (spam protection)
[QUOTE=gamerpaddy;47018958]Its better to set up an own TTS servers, because i had problems with 3 players continuesly using google translator tts. (spam protection)[/QUOTE] Yes, as did I. Sometimes it would not play, sometimes it would.
[QUOTE=bitches;47018729]yes, but that'll only hurt the unique voices aspect it isn't a translation, just a different way of saying the words[/QUOTE] Non-English voices pronounce numbers in their native language, not English.
[QUOTE=gamerpaddy;47018958]Its better to set up an own TTS servers, because i had problems with 3 players continuesly using google translator tts. (spam protection)[/QUOTE] [QUOTE=mib999;47019527]Yes, as did I. Sometimes it would not play, sometimes it would.[/QUOTE] If a sound is not received, the script falls back on Gran PC's server instead of google. I don't know what problem you're having, other than Gran PC's latency issues.
[vid]http://ph129.net/i/L46XfM.mp4[/vid] Fun non-the-less.
I heard in WAYWO that Google's unofficial tts "api" requires a captcha to be filled out after a few requests (hence not getting the sound) in addition to having a 100 character limit [editline]e[/editline] Yeah, I read that. I knew there was a fallback; I was just pointing out the reasons that might be the case.
[QUOTE=zerf;47023632]I heard in WAYWO that Google's unofficial tts "api" requires a captcha to be filled out after a few requests (hence not getting the sound) in addition to having a 100 character limit[/QUOTE] [QUOTE=bitches;47022549]If a sound is not received, the script falls back on Gran PC's server instead of google. I don't know what problem you're having, other than Gran PC's latency issues.[/QUOTE] [QUOTE=bitches;47016412] This uses the google API as much as possible, giving each player a unique voice based on the player's current name. When too many people are talking in a short span, it will use Gran PC's peniscorp robot voice as a backup server. [/QUOTE] [editline]27th January 2015[/editline] come on [editline]27th January 2015[/editline] [QUOTE=zerf;47023632] [editline]e[/editline] Yeah, I read that. I knew there was a fallback; I was just pointing out the reasons that might be the case.[/QUOTE] yeah but that can only happen once every few minutes at most, and when it does, it repeats the message under peniscorp
Works like a sharm! I see there is also a funny example on API page ([URL]http://tts.peniscorp.com/):[/URL] :v: :dance: [URL="http://tts.peniscorp.com/speak.lua?[dah<500,20>][dah<500,20>][dah<500,20>][dah<400,16>][dah<130,23>][dah<500,20>][dah<400,16>][dah<130,23>][dah<600,20>][_<300>][dah<500,27>][dah<500,27>][dah<500,27>][dah<400,28>][dah<130,23>][dah<500,19>][dah<400,16>][dah<130,23>][dah<500,20>]"]dah-dah-dah-dah......[/URL]
This will go great with my gag space gamemode
Does typing soisoisoisoisoi behave the same way?
[QUOTE=nickster50;47028977]Does typing soisoisoisoisoi behave the same way?[/QUOTE] That was Microsoft Sam, I'm pretty sure.
Peniscorp uses DECTalk, the same from moonbase alpha. The google accents are something different, and sound far less robotic.
I want to use your script in my gamemode. Buttt... oh, there is no my gamemode, whilst I didn't make it yet. Wait for alpha version ;) OK, main idea of my GM is to construct quest (in single player), share it, place the players (in MP, or just one player in SP) and make them communicate and do other stuff to finish task. Now I only have HTML-interface of splash screen, so I can not show it at the moment. IT'S NOT whole idea, I just need some more time to learn other stuff, and there will be brand new colossal awesome gamemode. :D Best regards!
[QUOTE=bitches;47029864]Peniscorp uses DECTalk, the same from moonbase alpha. The google accents are something different, and sound far less robotic.[/QUOTE] I remember being a dick in moonbase alpha and typing this: :)! :)! :)! :)! :)! Until I hit the char limit. It's a bug that I discovered myself, and results in the densest talk-time-per-char. Each :)! Makes it say this: Ess Emm Aye Ell Ee Exclamation Point! Enjoy.
I wish there was a way to play this down the microphone :(
[QUOTE=Ylsid;47034096]I wish there was a way to play this down the microphone :([/QUOTE] there is? Virtual Audio Cable + DECTalk
[QUOTE=TylerB;47039530]there is? Virtual Audio Cable + DECTalk[/QUOTE] good luck getting an updated version of DTDemo :v: although i guess you could just play it using the API on the peniscorp site but that still has quite a bit of latency Actually heres a 4.4 version of DTDemo [url=http://theflameofhope.co/dectalk/440/]FlameOfHope.co/dectalk/440/[/url]
[:dv hs 200] ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? ! ? !
[QUOTE=TylerB;47039530]there is? Virtual Audio Cable + DECTalk[/QUOTE] through lua that is
How would I go about restricting the tts behavior to a certain Darkrp job instead of for everyone?
[lua]if (not ply:Team() == TEAM_DERP) then return end[/lua]
So I did some experimentation with the above code and tried working it out, and found something out. One, you can't use ply for this, it has to be LocalPlayer(). Second, after I set it as LocalPlayer (which gets rid of the following error); [lua][ERROR] lua/autorun/client/gmodtts.lua:2: attempt to index global 'ply' (a nil value) 1. unknown - lua/autorun/client/gmodtts.lua:2[/lua] this error then appears, stating that Team is nil, even though Team is shared. [lua][ERROR] addons/gmodtts/lua/autorun/client/gmodtts.lua:8: attempt to call method 'Team' (a nil value) 1. unknown - addons/gmodtts/lua/autorun/client/gmodtts.lua:8[/lua] Thirdly, this doesn't work, even fi you load it as an addon or DarkRP module instead of putting it into lua/autorun/client. The problem is that Team is showing up as nil in this script even though it isn't (probably), as the above error states. Ignore the fact that the lines are different in the second error, I was testing putting the code into different positions. The error that occurred earlier was the exact same as that otherwise. This is my current code; [lua]if (not LocalPlayer():Team() == TEAM_ASTRO) then return end local accents = { "http://tts.peniscorp.com/speak.lua?" } local wait = false local channels = {} local function playvoice(ply,text,accent,speed) if wait then accent = "http://tts.peniscorp.com/speak.lua?" end sound.PlayURL(accent..text,"3d noplay",function(channel,errorid,errorname) if channel == nil || errorid != nil || errorname != nil then --peniscorp backup; google is upset if wait == false then wait = true timer.Simple(125,function() wait = false end) playvoice(ply,text,accent,speed) end return end channel:SetPos(ply:GetPos(),Vector(0,0,0)) channel:Set3DFadeDistance(300,9999999999) channel:SetPlaybackRate(math.Clamp(.5+speed,.5,2)) channel:Play() table.insert(channels,{channel,ply}) end) end hook.Add("Think","TTSThink",function() for k, v in pairs(channels) do if !v[1]:IsValid() then table.remove(channels,k) return end if v[2] == nil || v[1]:GetState() != GMOD_CHANNEL_PLAYING || v[1]:GetTime() >= v[1]:GetLength() then v[1]:Stop() table.remove(channels,k) return end v[1]:SetPos(v[2]:GetPos(),Vector(0,0,0)) end end) hook.Add("OnPlayerChat","TTSPlayerChat",function(ply,text,team,dead) --firstly, checking for popular gamemode limitations local dotts = true if engine.ActiveGamemode() == "terrortown" then if dead then dotts = false end if ply.IsSpec != nil && ply:IsSpec() then dotts = false end end if engine.ActiveGamemode() == "darkrp" then if text[1] == "/" then dotts = false end if text[1] == "@" then dotts = false end if ply:GetPos():Distance(LocalPlayer():GetPos()) > 500 then dotts = false end --250 = default chat range end if dotts then local speed,accent,nick = 1, accents[1], ply:Nick() local nlen = #nick local vowels, vow, notvow, vrat = {"a","e","i","o","u","y"}, 0, 0, 1 for i = 1, nlen do if table.HasValue(vowels,nick[i]) then vow = vow+1 else notvow = notvow+1 end end if vow == 0 then notvow = nlen end vrat = vow/notvow accent = accents[math.Clamp(vow,1,#accents)] speed = vrat playvoice(ply,text,accent,speed) end end)[/lua]
So I found out that despite this code being clientside, if someone hosts a listenserver with this code, everyone can hear it. It's great, I wonder if the same applies to dedicated servers.
Sorry, you need to Log In to post a reply to this thread.