M src/DefaultCSS.hs => src/DefaultCSS.hs +4 -2
@@ 3,6 3,7 @@ module DefaultCSS(userAgentCSS) where
 userAgentCSS = unlines [
   "head, link, meta, style, script, title, base {speak: never}",
   "datalist, template {speak: never}",
+  "html {speak-as: no-punctuation} body {speak-as: normal}",
   "",
   "/** Forms **/",
   "/* Hide buttons that don't do anything */",
@@ 37,6 38,7 @@ userAgentCSS = unlines [
   "",
   "/** Sectioning **/",
   "footer, header {voice-volume: soft}",
+  "nav {speak: never} /* Expose the links for navigation, but not narration */",
   "h1, h2, h3, h4, h5, h6, legend, th, summary, dt {voice-stress: strong}",
   "h1 {pause: x-strong; voice-rate: x-slow}",
   "h2 {pause: strong; voice-rate: slow}",
@@ 49,7 51,7 @@ userAgentCSS = unlines [
   "hr {pause: x-strong}",
   "p, pre, samp, blockquote {pause: strong}",
   "pre, address, samp {speak-as: literal-punctuation}",
-  "",
+  "pre, samp, code {voice: neutral 2}",
   ":link {cue-before: url(link.wav); voice-pitch: low}",
   ":link:visited {cue-before: url(link.wav) -1db}",
   "",
@@ 96,7 98,7 @@ userAgentCSS = unlines [
   "abbr[title]::after {content: attr(title); voice-volume: x-soft}",
   "abbr {speak-as: spell-out}",
   "",
-  "q, blockquote {voice-family: neutral 2}",
+  "q, blockquote {voice-family: female 2}",
   "cite {voice-stress: reduce}",
   "dialog:not([open]) {speak: never}",
   "kbd {speak-as: spell-out}",
 
M src/Main.hs => src/Main.hs +7 -88
@@ 26,7 26,7 @@ import Data.Scientific (toRealFloat)
 
 import DefaultCSS
 import StyleTree
-import qualified SSML
+import SSML
 
 main :: IO ()
 main = do
@@ 40,6 40,12 @@ main = do
     let transcript = stylize style html
     C8.putStrLn $ renderElLBS $ styleToSSML transcript
 
+renderElLBS el = XML.renderLBS XML.def $ XML.Document {
+        XML.documentPrologue = XML.Prologue [] Nothing [],
+        XML.documentRoot = el,
+        XML.documentEpilogue = []
+    }
+
 retreiveStyles html manager base = do
     style <- H2C.externalStyles authorStyle testMedia html loadURL
     return style
@@ 62,90 68,3 @@ stylize styles html = H2C.traverseStyles buildNode buildText styles html
     where
         buildNode self children = self {children = children}
         buildText _ txt = Style.temp {content = txt}
-
-styleToSSML StyleTree {speak = False} =
-    XML.Element (XML.Name "blank" Nothing Nothing) M.empty []
-styleToSSML self =  buildEl0 "prosody" [
-            ("volume", volume self),
-            ("rate", rate self),
-            ("pitch", serializePitch $ pitch self),
-            ("range", serializePitch $ range self)
-        ] $ buildEl0 "prosody" [
-            ("volume", maybeAdjust $ volumeAdjust self),
-            ("rate", maybeAdjust $ rateAdjust self),
-            ("pitch", maybeAdjust $ pitchAdjust $ pitch self),
-            ("range", maybeAdjust $ pitchAdjust $ range self)
-        ] $ buildEl0 "emphasis" [("level", stress self)] $
-        buildEl0 "say-as" [("interpret-as", speakAs self)] $
-        buildEl0 "tts:style" [
-            ("field", maybeBool (punctuation self) "punctuation" "punctuation"),
-            ("mode", maybeBool (punctuation self) "all" "none")
-        ] $ buildVoices (voice self) (
-            XML.NodeContent (content self) :
-            map (\node -> XML.NodeElement $ styleToSSML node) (children self)
-        )
-
-serializePitch Inherit = Nothing
-serializePitch (Pitch kw _) = Just kw
-serializePitch (Absolute "khz" n) = serializePitch $ Absolute "hz" (1000 * n)
-serializePitch (Absolute _ n) | n < 0 = Nothing
-    | otherwise = Just $ Txt.pack (show n ++ "hz")
-serializePitch (Relative unit n) = Just $ relativeUnit unit n
-
-relativeUnit "khz" n = relativeUnit "hz" $ 1000 * n
-relativeUnit unit n | n < 0 = Txt.pack (show n) `Txt.append` unit
-    | otherwise = Txt.pack ('+':show n) `Txt.append` unit
-
-maybeBool (Just True) truthy _ = Just truthy
-maybeBool (Just False) _ falsy = Just falsy
-maybeBool Nothing _ _ = Nothing
-
-maybeCast (Just n) = Just $ Txt.pack $ show n
-maybeCast Nothing = Nothing
-
-maybeAdjust (Just (Unit' unit n)) = Just $ relativeUnit unit n
-maybeAdjust Nothing = Nothing
-
-buildVoice (Voice name) children = buildEl "voice" [("name", Just name)] children
-buildVoice (VoicePattern age gender variant) children = buildEl "voice" [
-        ("age", maybeCast age),
-        ("gender", Just gender),
-        ("variant", maybeCast variant)
-    ] children
-
-buildVoices Nothing = buildEl "blank" []
-buildVoices (Just voice) = buildVoice voice
-
---------
----- XML Output Utils
---------
-buildEl name attrs elChildren =
-    XML.Element (XML.Name name Nothing Nothing) (attrsFromList attrs) elChildren
-buildEl0 name attrs child = buildEl name attrs [XML.NodeElement child]
-
-attrsFromList ((name, Just value):attrs) =
-    M.insert (XML.Name name Nothing Nothing) value $ attrsFromList attrs
-attrsFromList ((_, Nothing):attrs) = attrsFromList attrs
-attrsFromList [] = M.empty
-
-stripEmptyEls (XML.Element name attrs elChildren) =
-    XML.Element name attrs $ stripEmptyNodes elChildren
-stripEmptyNodes (XML.NodeElement el@(XML.Element _ attrs elChildren ):nodes)
-    | M.null attrs = stripEmptyNodes (elChildren ++ nodes)
-    | otherwise = XML.NodeElement (stripEmptyEls el) : stripEmptyNodes nodes
-stripEmptyNodes (XML.NodeContent txt : nodes) = -- strip whitespace
-    XML.NodeContent (collapseSpaces txt) : stripEmptyNodes nodes
-stripEmptyNodes (node:nodes) = stripEmptyNodes nodes
-stripEmptyNodes [] = []
-
-collapseSpaces txt
-    | txt == "" = "" -- Avoids errors from head/tail tests.
-    | isSpace $ Txt.head txt = " " `Txt.append` collapseSpaces (Txt.stripStart txt)
-    | isSpace $ Txt.last txt = collapseSpaces (Txt.stripEnd txt) `Txt.append` " "
-    | otherwise = Txt.unwords $ Txt.words txt
-
-renderElLBS el = XML.renderLBS XML.def $ XML.Document {
-        XML.documentPrologue = XML.Prologue [] Nothing [],
-        XML.documentRoot = stripEmptyEls el,
-        XML.documentEpilogue = []
-    }
 
M src/SSML.hs => src/SSML.hs +5 -2
@@ 83,6 83,7 @@ buildBreak self = NodeElement $ buildEl "break" [
         ("strength", strength self),
         ("time", time self)
     ] []
+buildCue NoCue = NodeElement $ buildEl "blank" [] []
 buildCue self = NodeElement $
     buildEl0 "prosody" [("volume", maybeAdjust $ cueVolume self)] $
     buildEl "audio" [("src", Just $ src self)] []
@@ 118,6 119,8 @@ stripEmptyEls (Element name attrs elChildren) =
 stripEmptyNodes (NodeElement el@(Element _ attrs elChildren ):nodes)
     | M.null attrs = stripEmptyNodes (elChildren ++ nodes)
     | otherwise = NodeElement (stripEmptyEls el) : stripEmptyNodes nodes
+stripEmptyNodes (NodeContent a : NodeContent b : nodes) =
+    stripEmptyNodes (NodeContent (Txt.append a b) : nodes)
 stripEmptyNodes (NodeContent txt : nodes) -- strip whitespace
     | Txt.stripStart txt == "" = stripEmptyNodes nodes
     | otherwise = NodeContent (collapseSpaces txt) : stripEmptyNodes nodes
@@ 125,8 128,8 @@ stripEmptyNodes (node:nodes) = stripEmptyNodes nodes
 stripEmptyNodes [] = []
 
 collapseSpaces txt
-    | Txt.stripStart txt == "" = "" -- Avoids errors from head/tail tests.
-    | isSpace $ Txt.head txt = " " `Txt.append` collapseSpaces txt
+    | txt == "" = "" -- Avoids errors from head/tail tests.
+    | isSpace $ Txt.head txt = " " `Txt.append` collapseSpaces (Txt.stripStart txt)
     | isSpace $ Txt.last txt = collapseSpaces (Txt.stripEnd txt) `Txt.append` " "
     | otherwise = Txt.unwords $ Txt.words txt