<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ssml="http://www.w3.org/2001/10/synthesis" xmlns:tts="http://www.daisy.org/ns/pipeline/tts" exclude-result-prefixes="#all" version="2.0"> <xsl:import href="flatten-css.xsl"/> <xsl:param name="lang"/> <xsl:param name="style-ns"/> <xsl:key name="bindings" match="*[@sent]" use="@sent"/> <xsl:key name="parent-to-child" match="*[@ancestor]" use="@ancestor"/> <xsl:variable name="sent-ancestors"> <tts:parents> <xsl:for-each select="//ssml:s"> <xsl:variable name="sent-id" select="@id"/> <xsl:for-each select="ancestor-or-self::*[@tts:cue-before or @tts:cue-after or @tts:pause-before or @tts:pause-after]"> <tts:parenting ancestor="{generate-id(.)}" sent="{$sent-id}"/> </xsl:for-each> </xsl:for-each> </tts:parents> </xsl:variable> <xsl:variable name="bindings"> <tts:bindings> <xsl:apply-templates mode="build-bindings" select="/*"/> </tts:bindings> </xsl:variable> <xsl:template name="bind-one-sent"> <xsl:param name="sent-id"/> <xsl:param name="pause"/> <xsl:param name="cue"/> <xsl:if test="$pause != '' or $cue != ''"> <bind sent="{$sent-id}"> <xsl:if test="$pause != ''"> <xsl:attribute name="pause"><xsl:value-of select="$pause"/></xsl:attribute> </xsl:if> <xsl:if test="$cue != ''"> <xsl:attribute name="cue"><xsl:value-of select="$cue"/></xsl:attribute> </xsl:if> </bind> </xsl:if> </xsl:template> <xsl:template match="*" mode="bind-all-the-way-through"> <xsl:param name="sent-id"/> <xsl:call-template name="bind-one-sent"> <xsl:with-param name="sent-id" select="$sent-id"/> <xsl:with-param name="pause" select="@tts:pause-before"/> <xsl:with-param name="cue" select="@tts:cue-before"/> </xsl:call-template> <xsl:apply-templates select="*" mode="bind-all-the-way-through"> <xsl:with-param name="sent-id" select="$sent-id"/> </xsl:apply-templates> <xsl:call-template name="bind-one-sent"> <xsl:with-param name="sent-id" select="$sent-id"/> <xsl:with-param name="pause" select="@tts:pause-after"/> <xsl:with-param name="cue" select="@tts:cue-after"/> </xsl:call-template> </xsl:template> <xsl:template match="ssml:s[@id]" mode="build-bindings" priority="2"> <xsl:call-template name="bind-one-sent"> <xsl:with-param name="sent-id" select="@id"/> <xsl:with-param name="pause" select="@tts:pause-before"/> <xsl:with-param name="cue" select="@tts:cue-before"/> </xsl:call-template> <content sent="{@id}"/> <xsl:call-template name="bind-one-sent"> <xsl:with-param name="sent-id" select="@id"/> <xsl:with-param name="pause" select="@tts:pause-after"/> <xsl:with-param name="cue" select="@tts:cue-after"/> </xsl:call-template> </xsl:template> <xsl:template match="*" mode="build-bindings" priority="1"> <xsl:variable name="descendants" select="key('parent-to-child', generate-id(), $sent-ancestors)"/> <xsl:choose> <xsl:when test="$descendants"> <xsl:call-template name="bind-one-sent"> <xsl:with-param name="sent-id" select="$descendants[1]/@sent"/> <xsl:with-param name="pause" select="@tts:pause-before"/> <xsl:with-param name="cue" select="@tts:cue-before"/> </xsl:call-template> <xsl:apply-templates select="*" mode="build-bindings"/> <xsl:call-template name="bind-one-sent"> <xsl:with-param name="sent-id" select="$descendants[last()]/@sent"/> <xsl:with-param name="pause" select="@tts:pause-after"/> <xsl:with-param name="cue" select="@tts:cue-after"/> </xsl:call-template> </xsl:when> <xsl:when test="@tts:pause-before or @tts:cue-before or @tts:pause-after or @tts:cue-after"> <xsl:variable name="neighbor" select="preceding::ssml:s[@id][1]|following::ssml:s[@id][1]"/> <xsl:if test="$neighbor"> <xsl:apply-templates select="." mode="bind-all-the-way-through"> <xsl:with-param name="sent-id" select="$neighbor[1]/@id"/> </xsl:apply-templates> </xsl:if> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="*" mode="build-bindings"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="/"> <ssml:speak version="1.1"> <xsl:for-each select="//ssml:s"> <s> <xsl:variable name="sentence" select="current()"/> <xsl:copy-of select="$sentence/@*"/> <xsl:apply-templates select="$sentence" mode="flatten-css-properties"> <xsl:with-param name="style-ns" select="$style-ns"/> <xsl:with-param name="lang" select="$lang"/> </xsl:apply-templates> <xsl:for-each select="key('bindings', $sentence/@id, $bindings)"> <xsl:choose> <xsl:when test="current()/@pause or current()/@cue"> <xsl:apply-templates select="current()/@pause" mode="pause"/> <xsl:apply-templates select="current()/@cue" mode="cue"/> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="$sentence/node()" mode="inside-sentence"/> <xsl:variable name="next-text-node" select="$sentence/following-sibling::text()[normalize-space(.) != ''][1]"/> <xsl:if test="$next-text-node"> <xsl:analyze-string select="$next-text-node" regex="^[\p{{Z}}]*([.!?…])"> <xsl:matching-substring> <xsl:value-of select="regex-group(1)"/> </xsl:matching-substring> <xsl:non-matching-substring/> </xsl:analyze-string> </xsl:if> </xsl:otherwise> </xsl:choose> </xsl:for-each> </s> </xsl:for-each> </ssml:speak> </xsl:template> <xsl:template match="*" mode="inside-sentence"> <xsl:apply-templates select="@tts:pause-before" mode="pause"/> <xsl:apply-templates select="@tts:cue-before" mode="cue"/> <xsl:element name="{name()}" namespace="{namespace-uri()}"> <xsl:copy-of select="@*"/> <xsl:apply-templates select="node()" mode="inside-sentence"/> </xsl:element> <xsl:apply-templates select="@tts:cue-after" mode="cue"/> <xsl:apply-templates select="@tts:pause-after" mode="pause"/> </xsl:template> <xsl:template match="text()" mode="inside-sentence"> <xsl:value-of select="."/> </xsl:template> <xsl:template match="@*" mode="pause"> <ssml:break time="{.}"/> </xsl:template> <xsl:template match="@*" mode="cue"> <xsl:choose> <xsl:when test="starts-with(., 'file:///')"> <ssml:audio src="{substring-after(., 'file://')}"/> </xsl:when> <xsl:when test="starts-with(., 'file:/')"> <ssml:audio src="{substring-after(., 'file:')}"/> </xsl:when> <xsl:when test="starts-with(., '/')"> <ssml:audio src="{.}"/> </xsl:when> </xsl:choose> </xsl:template> </xsl:stylesheet>