This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

text to speech?
#1
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.
Reply
#2
UP...
Reply
#3
(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
Reply
#4
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
Reply
#5
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
Reply


Forum Jump: