Jump to content

XQuery/XHTML + Voice

From Wikibooks, open books for an open world

Motivation

[edit | edit source]

You want to have your browser read Twitter updates using a text-to-speech conversion extension that is built into your browser.

Method

[edit | edit source]

XHTML + Voice is supported by the Opera Browser with the Voice extension installed. In this simple application it is used as a browser-based Text-to-Speech engine.

Twitter Radio

[edit | edit source]

This script creates a simple text-to-speech version of Twitter Search.

Obama

Limitations

[edit | edit source]
  • Window has to be active for the T2S to play on refresh
  • The cleaned text to speak is held in a div which is rendered as white on white text. Initially it was output as a block in the header but it did not seen possible to apply styles. Applying a style display:none hid the text from the T2S engine as well!
  • Transforming the atom content to a a string suitable to speak needs more work.
  • Retweets and similar tweets could be removed using levenstein
  • Male and female voices are assigned randomly by tweet. I'd like to cache the voice assigned to a tweeter so that tweets are consistently spoken in the same voice
  • The initial load doesn't seem to trigger playing, hence the play button, but this also re-fetches the page.
  • This is an ideal situation to use AJAX instead of refresh
  • The T2S engine is quite good at rendering the text but it needs be helped in places, for example by replacing texting abbreviations with their expanded form.
declare namespace atom = "http://www.w3.org/2005/Atom"; declare variable $n := xs:integer( request:get-parameter("n",6)); declare variable $search := request:get-parameter("search",""); declare variable $timestamp := request:get-parameter("timestamp",()); declare variable $seconds := $n * 12; declare variable $noise := ( "<b>",  "</b>",  "&lt;.+?&gt;",  "http://[^ ]+",  "#\w+",  "RT *@\w+",  "@\w+",  "[\[\]\\=«»:;()_?!~\|]",  '"',  "\.\.+" ); declare function local:clean ($talk as xs:string, $noise as xs:string*) as xs:string {  if (empty($noise))  then $talk  else  local:clean(replace($talk,string($noise[1])," "),subsequence($noise,2)) }; declare function local:clean($talk as xs:string) as xs:string {  local:clean($talk,$noise) }; declare option exist:serialize "method=xhtml media-type=application/xv+xml"; let $entries := doc(concat("http://search.twitter.com/search.atom?lang=en&amp;q=",encode-for-uri($search)))//atom:entry let $entries := if (exists($timestamp))  then $entries[atom:published>$timestamp]  else $entries let $entries := $entries[position() <= $n]  let $newtimestamp := if (exists($entries)) then string($entries[1]/atom:published) else $timestamp let $entries := reverse($entries) return <html xmlns="http://www.w3.org/1999/xhtml"   xmlns:vxml="http://www.w3.org/2001/vxml"   xmlns:xv="http://www.voicexml.org/2002/xhtml+voice"  xmlns:ev="http://www.w3.org/2001/xml-events"  >  <head>  <meta http-equiv="refresh" content="{$seconds};url=?search={encode-for-uri($search)}&amp;timestamp={$newtimestamp}&amp;n={$n}"/>  <title>Tweets matching {$search}</title>  <vxml:form id="say">  <vxml:block>  <vxml:prompt src="#news"/>  </vxml:block>  </vxml:form>  <link rel="stylesheet" type="text/css" href="voice2.css" title="Normal"/>  </head>  <body ev:event="load" ev:handler="#say" >  <h1>Twitter radio, listening to tweets matching {$search}</h1>  <form method="get" >  Listen for <input type="text" name="search" value="{$search}" />  Max items <input type="text" name="n" value="{$n}" size="4" />   <input type="submit" value="Tune"/>  <button ev:event="click" ev:handler="#say">Play</button>  </form>   {for $entry in $entries  return   <div>   <a href="{$entry/atom:author/atom:uri}">{substring-before($entry/atom:author/atom:name, "(")}</a> &#160;  {util:parse(concat("<span xmlns='http://www.w3.org/1999/xhtml' >",$entry/atom:content/(text(),*),"</span>"))}  </div>  }   <div id="news">  {for $entry in $entries  return   <p class='{if (math:random ()< 0.5) then "male" else "female"}'>   { local:clean($entry/atom:content/text())}   </p>  }   </div>  </body> </html> 

With the style sheet:

.male {  voice-family: male;  pause-after:1.5s; } .female {  voice-family:female;  pause-after:1s } #news {  color:white;  background-color:white }