LogicMachine Forum
text to speech? - Printable Version

+- LogicMachine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: text to speech? (/showthread.php?tid=497)



text to speech? - gjniewenhuijse - 07.12.2016

Has anyone a working solution for text to speech (and send it to sonos)?

Before i used: translate.google.com, but now its banned by google.
Code:
function sonosSay(ip, lang, speak)   local url = require("socket.url")   local speak = url.escape(speak)   uri = string.format("x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&q=%s", lang, speak)   upnpavcmd(ip, 1400, 'SetAVTransportURI', '<CurrentURI>'..uri..'</CurrentURI><CurrentURIMetaData/>')   upnpavcmd(ip, 1400, 'Play', '<Speed>1</Speed>') end

I like to use the dutch language.


RE: text to speech? - gjniewenhuijse - 09.01.2017

UP...


RE: text to speech? - bmodeco - 21.03.2017

(07.12.2016, 19:20)gjniewenhuijse Wrote: Has anyone a working solution for text to speech (and send it to sonos)?

Before i used: translate.google.com, but now its banned by google.
Code:
function sonosSay(ip, lang, speak)  local url = require("socket.url")  local speak = url.escape(speak)  uri = string.format("x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&q=%s", lang, speak)  upnpavcmd(ip, 1400, 'SetAVTransportURI', '<CurrentURI>'..uri..'</CurrentURI><CurrentURIMetaData/>')  upnpavcmd(ip, 1400, 'Play', '<Speed>1</Speed>') end

I like to use the dutch language.

Hi,
I am looking into this also...thinking of using VoiceRSS since it will require minimal change to use.
See weblink - http://www.voicerss.org/api/documentation.aspx
I already have my code available to continue playing music/playlist after playing my doorbell sound or tts.
Mvg, B
B


RE: text to speech? - gjniewenhuijse - 21.03.2017

I tested also with voicerss and that doesn't work for me. I think it failed because:
- i opened a direct mp3 stream into my sonos from google/voicerss
- but voicerss failed because it doesn't use mp3 as extension from the returning url

dirty workaround:
save voicerss file into lm
rename to .mp3
play with sonos
remove mp3 file


RE: text to speech? - bmodeco - 26.04.2017

As requested my code using voicerss.
First the user lib I use for controlling Sonos based on what existed - added some new functions (track info)

Code:
function upnpavcmd(host, port, cmd, param)    local client, soap, reqs, service, res, err    require('socket')    client = socket.tcp()    client:settimeout(3)    -- try connecting to upnp endpoint    res, err = client:connect(host, port)    if not res then    return nil, err    end    -- guess service name based on command  if cmd =='SetGroupVolume' or cmd == 'SetGroupMute' or cmd == 'SnapshotGroupVolume' then    service = 'GroupRenderingControl'  elseif cmd == 'SetVolume' or cmd == 'GetVolume' or cmd == 'SetMute' or cmd == 'GetMute' then    service = 'RenderingControl'    else    service = 'AVTransport'    end      -- soap envelope    soap = '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' ..    '<s:Body>' ..    '<u:' .. cmd .. ' xmlns:u="urn:schemas-upnp-org:service:' .. service .. ':1">' ..    '<InstanceID>0</InstanceID>' ..    (param or '') ..    '</u:' .. cmd .. '>' ..    '</s:Body>' ..    '</s:Envelope>'        -- http request    reqs = 'POST /MediaRenderer/' .. service .. '/Control HTTP/1.1\r\n' ..    'CONNECTION: close\r\n' ..    'HOST: ' .. host .. ':' ..port.. '\r\n' ..    'CONTENT-LENGTH: ' .. soap:len() .. '\r\n' ..    'CONTENT-TYPE: text/xml; charset="utf-8"\r\n' ..    'SOAPACTION: "urn:schemas-upnp-org:service:' .. service .. ':1#' .. cmd .. '"\r\n' ..    '\r\n' .. soap        -- send http request    res, err = client:send(reqs)    if not res then    return nil, err    end        -- get reply and close connection    res, err = client:receive('*a')    client:close()        return res, err    end function Sonos_Find(sonos_name)  require("socket")  require('socket.http')  local udp = socket.udp()  if udp == nil then    return  else    local result, message    local datagram = "M-SEARCH * HTTP/1.1\r\n"                      .. "HOST: 239.255.255.250:1900\r\n"                      .. "ST: urn:schemas-upnp-org:device:ZonePlayer:1\r\n"                      .. "\r\n"    result, message = udp:sendto(datagram, "239.255.255.250", 1900)    for i = 1, 254 do      datagram, message = udp:receivefrom()        ip = datagram:match("http://(.-):1400")        if datagram == nil or ip == nil then          break        else        data = socket.http.request("http://" .. ip .. ":1400/status/zp")        zonename = data:match([[<ZoneName>(.-)</ZoneName>]])        ipaddress = data:match([[<IPAddress>(.-)</IPAddress>]])        macaddress = data:match([[<MACAddress>(.-)</MACAddress>]])        rincon = data:match([[<LocalUID>(.-)</LocalUID>]])        if zonename == sonos_name then          return {sonos_name = zonename, sonos_ipaddress = ipaddress, sonos_macaddress = macaddress, sonos_rincon = rincon}        end      end    end  end     udp:close() end function Sonos_Getpositioninfo(sonos_ipaddress) -- Get Sonos Player Position Info  position_info = upnpavcmd(sonos_ipaddress,1400,'GetPositionInfo')  -- Get Track  for i in string.gmatch(position_info, '<Track.-</Track>') do      if i ~= nil then      track_nr =  i:match([[<Track>(.-)</Track>]])      end     end  -- Get Track  for i in string.gmatch(position_info, '<TrackDuration.-</TrackDuration>') do      if i ~= nil then      track_duration =  i:match([[<TrackDuration>(.-)</TrackDuration>]])      end     end  -- Get Track Playing Time  for i in string.gmatch(position_info, '<RelTime.-</RelTime>') do      if i ~= nil then      track_playing_time =  i:match([[<RelTime>(.-)</RelTime>]])      end     end  -- Get Track Title  for i in string.gmatch(position_info, 'dc:title.-/dc:title') do      if i ~= nil then      track_title =  i:match([[dc:title&gt;(.-)&lt;/dc:title]])      end     end  -- Get Track Artist  for i in string.gmatch(position_info, 'dc:creator.-/dc:creator') do      if i ~= nil then      creator =  i:match([[dc:creator&gt;(.-)&lt;/dc:creator]])      end     end  if creator == nil then    for i in string.gmatch(position_info, '<TrackMetaData.-</TrackMetaData>') do      if i ~= nil then        creator =  i:match([[streamContent&gt;(.-)&lt;/r:streamContent]])      end    end  end  -- Get Track Album  for i in string.gmatch(position_info, 'upnp:album.-/upnp:album') do      if i ~= nil then      album =  i:match([[upnp:album&gt;(.-)&lt;/upnp:album]])      end     end --  -- Get Track Image URL --  for i in string.gmatch(position_info, 'upnp:albumArtURI.-/upnp:albumArtURI') do --      if i ~= nil then --      albumart =  i:match([[upnp:albumArtURI&gt;(.-)&lt;/upnp:albumArtURI]]) --      end --    end --  if albumart == nil then --    for i in string.gmatch(position_info, '<TrackURI.-</TrackURI>') do --      if i ~= nil then --        albumart =  i:match([[<TrackURI>(.-)</TrackURI>]]) --      end --    end --  end  return {TrackNr = track_nr, TrackDuration = track_duration, TrackRelTime = track_playing_time, TrackTitle = track_title, TrackCreator = creator, TrackAlbum = album}         end function Sonos_Gettransportinfo(sonos_ipaddress)  -- Get Sonos Player Transport Info  reply = upnpavcmd(sonos_ipaddress,1400,'GetTransportInfo')     for i in string.gmatch(reply, '<CurrentTransportState.-</CurrentTransportState>') do      if i ~= nil then      play_pause =  i:match([[<CurrentTransportState>(.-)</CurrentTransportState>]])      end     end  -- 'TRANSITIONING' , 'PLAYING' , 'PAUSED_PLAYBACK' or 'STOPPED'  return {Transportmode = play_pause}       end   function Sonos_GetVolume(sonos_ipaddress)  -- Get Sonos Player Volume Info  reply = upnpavcmd(sonos_ipaddress,1400,'GetVolume','<Channel>Master</Channel>')  for i in string.gmatch(reply, '<CurrentVolume.-</CurrentVolume>') do      if i ~= nil then      volume =  i:match([[<CurrentVolume>(.-)</CurrentVolume>]])      end     end  return {Volume = volume}       end function Sonos_Getmediainfo(sonos_ipaddress)  -- Get Sonos Player Media Info  reply = upnpavcmd(sonos_ipaddress,1400,'GetMediaInfo')     for i in string.gmatch(reply, '<CurrentURI.-</CurrentURI>') do      if i ~= nil then      currenturi =  i:match([[<CurrentURI>(.-)</CurrentURI>]])      end     end  -- 'TRANSITIONING' , 'PLAYING' , 'PAUSED_PLAYBACK' or 'STOPPED'    return {CurrentURI = currenturi}       end


Below the actual script:
It gets te mp3info and saves it on my synologyNas so the sonos can play it.
Before pausing the music (if playing), I save the tracknum and relpos so I can continue afterwards - works as expected.
My code only makes a distinction between playlist and radio station stubru - either one of them can be playing.
Since currently testing - it is not yet a function to call with a custom text - easy to make it that way.
The following weblink details the parameters for voicerss. http://www.voicerss.org/api/documentation.aspx
Have fun, Bart


Code:
-- Load modules require('json') require('socket.http') require('socket.ftp') require('user.Sonos') -- Set SONOS Variables Sonos_Player_Name = 'Eetkamer'   action = 'none' -- Read IP address from controller data = io.readproc('if-json') data = json.decode(data) LMip = data.eth0.inetaddr -- Set timout socket.http.TIMEOUT = 5 -- Set volumes TTS_Volume = 80 Default_Volume = 25 --Check if player exists reply = Sonos_Find(Sonos_Player_Name) current_sonosname = reply.sonos_name current_sonosplayerip = reply.sonos_ipaddress current_sonosmacaddress = reply.sonos_macaddress current_sonosrincon = reply.sonos_rincon -- Check if Sonos is playing? reply = Sonos_Gettransportinfo(current_sonosplayerip) previous_transportmode = reply.Transportmode -- Get VolumeInfo reply = Sonos_GetVolume(current_sonosplayerip) current_volume = reply.Volume -- Get VolumeInfo reply = Sonos_Getmediainfo(current_sonosplayerip) current_URI = reply.CurrentURI local MP3_data = socket.http.request("http://api.voicerss.org/?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx&hl=nl-nl&f=16khz_16bit_stereo&src=Mooi%20weer%20vandaag%20ik%20doe%20alles%20vandaag%20een%20beetje%20traag") --Store on synology res, err = socket.ftp.put({    host = '192.168.1.200',    user = 'xxx',    password = 'xxx',    path = '/music/Domotica/MP3/TTS.mp3',      source = ltn12.source.string(MP3_data)  }) -- Get trackinfo reply=Sonos_Getpositioninfo(current_sonosplayerip) current_mediatype = reply.TrackTitle current_TrackRelTime = reply.TrackRelTime current_TrackNr = reply.TrackNr if previous_transportmode == 'PLAYING' then    upnpavcmd(current_sonosplayerip, 1400, 'Pause') end upnpavcmd(current_sonosplayerip, 1400, 'SetVolume', '<Channel>Master</Channel><DesiredVolume>' ..TTS_Volume.. '</DesiredVolume>') upnpavcmd(current_sonosplayerip, 1400, 'SetAVTransportURI', '<CurrentURI>x-file-cifs://DISKSTATION/music/Domotica/MP3/TTS.mp3</CurrentURI><CurrentURIMetaData/>') os.sleep(1) -- Check if Sonos is playing? upnpavcmd(current_sonosplayerip, 1400, 'Play', '<Speed>1</Speed>') os.sleep(1) reply = Sonos_Gettransportinfo(current_sonosplayerip) current_transportmode = reply.Transportmode --Wait until playing TTS-MP3 is finished while (current_transportmode == 'PLAYING') do  os.sleep(1)     reply = Sonos_Gettransportinfo(current_sonosplayerip)     current_transportmode = reply.Transportmode end upnpavcmd(current_sonosplayerip, 1400, 'Pause') if current_mediatype == 'stubru.aac' then  Sonos_Action = 'playuri'    Sonos_Listtype = 'favorites'  Sonos_Listnumber = 3  local reply = socket.http.request('http://' .. LMip .. '/apps/data/sonos/sonos.lp?action=' .. Sonos_Action .. '&name=' .. Sonos_Player_Name .. '&listtype=' .. Sonos_Listtype.. '&listnumber=' ..Sonos_Listnumber.. '') else     upnpavcmd(current_sonosplayerip, 1400, 'SetAVTransportURI', '<CurrentURI>' ..current_URI.. '</CurrentURI><CurrentURIMetaData/>')     upnpavcmd(current_sonosplayerip, 1400, 'Seek', '<InstanceID>0</InstanceID><Unit>TRACK_NR</Unit><Target>' ..current_TrackNr.. '</Target>')     upnpavcmd(current_sonosplayerip, 1400, 'Seek', '<InstanceID>0</InstanceID><Unit>REL_TIME</Unit><Target>' ..current_TrackRelTime.. '</Target>') end upnpavcmd(current_sonosplayerip, 1400, 'SetVolume', '<Channel>Master</Channel><DesiredVolume>' ..current_volume.. '</DesiredVolume>') upnpavcmd(current_sonosplayerip, 1400, 'Pause') if previous_transportmode == 'PLAYING' then  upnpavcmd(current_sonosplayerip, 1400, 'Play', '<Speed>1</Speed>') end script.disable(_SCRIPTNAME) -- loop while condition is met