B - How to Use the Formatting Bus

This chapter provides an introduction to the Formatting bus and describes how custom Text-To-Audio (TTA) algorithms can be integrated into the VoiceObjects platform.

Typically TTA algorithms are used primarily in the voice channel, however with the more generic algorithm TTA-XML formatting settings can be defined for other channels as well. Refer to the Format object in the Object Reference for detailed information.

What Is a TTA Algorithm?

TTA algorithms can be used to generate a more human and natural output to the caller. This is done by transforming the content of a given Variable, Expression, Script, Collection, or Layer object into a sequence of prerecorded audio files (e.g. New York City will be interpreted to play back an audio file new_york_city.wav when choosing TTA – Complete Value). This can easily be done by defining a Format object for the object types mentioned above. Within the Format object the desired TTA algorithm can be selected. If none of the five TTA algorithms provided by VoiceObjects fits your needs you can also add a custom TTA algorithm. For detailed information on the existing methods refer to the Format object in the Object Reference.

A typical use case for a custom TTA algorithm is the playback of a specific custom value, e.g. a customer number or an invoice number. The example described in the paragraphs below will show how a TTA algorithm for a date structure could be created.

8     Caution: The Java code that is referenced by the TTA algorithm runs in the same JVM as VoiceObjects Server. You need to ensure that it is thread safe and will not cause any damage to the JVM, otherwise operations of VoiceObjects Server may be harmed.

i8    Note: If the TTA algorithm is going to be used within a multi-tenant installation, in which each site may have different resource directories applied to it, it is required to link relative resource locators instead of returning absolute paths by the Java code. If the TTA algorithm is used in a single site installation only, the code can return the absolute path.

How to Define a Custom TTA Algorithm

The way to define a custom TTA algorithm can be divided into three separate steps. In the first step the configuration files Eclipse_Configuration.xml, VODesktop_Configuration.xml and VOServer_Configuration.xml need to be adapted; see section Desktop and Server Configuration below. In the next step the TTA.xml file (or a custom file) has to be created by following the listed conventions. Finally the corresponding Java code, which will transform the content into a sequence of predefined audio files, must be programmed. This Java code must return a specific XML structure, so that VoiceObject Server can produce the correct VoiceXML in order to play the right audio files. This XML structure is similar to the one that is used when choosing TTA – XML.

Desktop and Server Configuration

Before a custom TTA algorithm can be created and integrated into the VoiceObjects platform, the corresponding feature needs to be activated. This is done by adding the following XML tag to the files Eclipse_Configuration.xml (only when using Desktop for Eclipse), VODesktop_Configuration.xml (only when using Desktop for Web) and VOServer_Configuration.xml. The configuration file Eclipse_Configuration.xml is located in the folder
…plugins/com.voiceobjects.eclipseDesktop_9.1.0/config

whereas VODesktop_Configuration.xml and VOServer_Configuration.xml are located in the folder
/VoiceObjects/Platform/WEB-INF/config:

<TTALibraries>

          <TTALibrary>TTA.xml</TTALibrary>

          <TTALibrary>TTA2.xml</TTALibrary>

          <TTALibrary>...</TTALibrary>

</TTALibraries>


The filenames that are referenced in the <TTALibraries> tag (TTA.xml in this example) can be changed, but the corresponding files have to follow the structure that is described in the section below.

TTA.xml File

The TTA.xml file (or a corresponding file) is the interface between the VoiceObjects platform and the provided custom Java code. It must be located in the folder …plugins/com.voiceobjects.eclipseDesktop_9.1.0/config (Desktop for Eclipse) and in the folder …/VoiceObjects/Platform/WEB-INF/config of your VoiceObjects installation. Within this file the custom TTA algorithms are described in an XML structure that consists of two parts, Header and Function.

i8    Note: The TTA.xml file has to be the same across the entire installation; especially in a cluster installation it is mandatory to use the same file for all server instances on all machines.

This paragraph describes the required XML structure and gives detailed information about the parameters and settings. A complete example for the TTA.xml file is listed in the paragraph Custom TTA Example at the end of this document. In combination with the corresponding Java code example it can be used to show how these two files depend on each other.

Header

In the header section general information such as the path to the documentation and library are defined.

Example:

<header>

    <defaultLocaleID>en</defaultLocaleID>

    <namePrefix>

        <locale id="en">SpeakAs - </locale>

               <locale id="de">SprichAls - </locale>

    </namePrefix>

    <docPath>http://myPC/docs/customTTA/doc/</docPath>

    <srcPath>./lib/ttaLibrary/</srcPath>

</header>

i8    Note: The VoiceObjects platform currently supports different locales for en (English) and de (German).

 

Tag

Description

<defaultLocaleID>

The <defaultLocaleID> is the locale that is used by default when displaying information in VoiceObjects Desktop.

Example: When VoiceObjects Desktop is opened in German, but no de (German) locale for the TTA algorithm is defined in this file, the defined default locale, which is en (English) in this case, will be taken and shown in VoiceObjects Desktop.

<namePrefix>

The <namePrefix> is used as a general prefix for all function names in VoiceObjects Desktop and may differ for different locales.
If you need to include blanks in the <namePrefix>, use the character sequence &amp;nbsp; to achieve this when working in Desktop for Web.

<docPath>

Contains a generic path, which will be concatenated with the specific <docFile> settings for each custom TTA algorithm defined.

<srcPath>

The <srcPath> is pointing to the directory with the class/jar files, in relation to the WEB-INF folder (for Desktop for Web and VoiceObjects Server) or the com.voiceobjects.eclipseDesktop folder (for Desktop for Eclipse).

Function

The second part can represent 1 to N individual TTA functions. Each function is split into three parts: Properties, Java and FilePostProcessing.

 

Properties

The properties section lists general parameters, which are used to define some basic settings like name, description, documentation reference, etc. for the custom TTA algorithm.

Example:

<function

    <properties>

        <id>custom_tta_date</id>

        <name>

            <locale id="en">TTA Date</locale>

            <locale id="de">TTA Datum</locale>

        </name>

        <description>

            <locale id="en">Speak as a Date (input is DD/MM/YYYY).</locale>

            <locale id="de">Sprich als Datum (Eingabe ist DD/MM/YYYY).</locale>

        </description>

        <docFile>

            <locale id="en">en/custom_tta_date.html</locale>

            <locale id="de">de/custom_tta_date.html</locale>

        </docFile>

    </properties>

 

Tag

Description

<id>

The <id> is stored as the name of the TTA algorithm in the metadata of the Format object. It is mandatory and needs to be unique across all custom formatting algorithms of the VoiceObjects installation.

The <id> is used to reference a custom TTA algorithm in VoiceObjectsXML.

<name>

Inside the <name> tag different names can be defined for different languages. The defined name of the active locale will be shown in the Type field of the Format object in VoiceObjects Desktop. If the tag <namePrefix> was defined before, a concatenation of <namePrefix> and <name> will be shown in VoiceObjects Desktop.

<description>

Inside the <description> tag different descriptions can be defined for different languages (locales). The defined description of the active locale is displayed in the description field below the Type field in the Format editor in VoiceObjects Desktop.

<docFile>

Inside the <docFile> tag different documentation files together with an optional HTML reference can be defined for different languages (locales). This value is interpreted relative to the defined <docPath>. In Desktop for Web the defined HTML file of the active locale is linked to the question mark button of the Type field in the Format editor in VoiceObjects Desktop.

 

Java

The Java section is used to set up the connection to the provided custom Java code. The parameters <srcFile>, <class> and <method> define the Java file, class and method to be invoked. Inside the <parameterSet> the parameters for the invoked method can be defined.

Example:

    <java>

        <srcFile>TTATest.jar</srcFile>

        <class>TTAExample</class>

        <method>process</method>

        <parameterSet>

            <result>XMLResult</result>

            <data>Data</data>

            <resourceLocator>ResourceLocator</resourceLocator>

            <language>Language</language>

            <encoding>Encoding</encoding>

            <options>Options</options>

            <channel>Channel</channel>

            <pronunciation>Pronunciation</pronunciation>

            <custom value=".">CustomSeparator</custom>

            <custom value=":; /()-_[]{},.">defaultPunctuationCharacters</custom>

        </parameterSet>

    </java>

 

Tag

Description

<srcFile>

Defines the JAR file that should be used for this algorithm.

<class>

Defines the class that should be used for this algorithm.

<method>

Defines the (main) method that should be invoked for this algorithm.

<parameterSet>

Within the <parameterSet> it is possible to pass parameters into the Java code and to get back the results. The <parameterSet> has some mandatory parameters, the values of these parameter tags define the required Java methods. See details for each parameter tag below.

   <result>

The <result> parameter is needed to get the XML result back from the Java code. A Java get method matching the value of this tag must be defined.

Example:

<result>XMLresult<result>

needs a Java get method getXMLresult.

   <data>

The <data> parameter is needed to pass the data into the Java code. A Java set method matching the value of this tag must be defined.

Example:

<data>Data<data>

needs a Java set method setData.

   <resourceLocator>

The <resourceLocator> parameter is used to pass the resource locator selected in the Format object to the Java code. A Java set method matching the value of this tag must be defined in this case.

Example:

<resourceLocator>ResourceLocator<resourceLocator>

needs a Java set method setResourceLocator.

   <language>

The <language> parameter is used to pass the currently active language to the Java code. A Java set method matching the value of this tag must be defined in this case.

Example:

<language>Language<language>

needs a Java set method setLanguage.

   <encoding>

The <encoding> parameter is used to pass the currently active encoding to the Java code. A Java set method matching the value of this tag must be defined in this case.

Example:

<encoding>Encoding<encoding>

needs a Java set method setEncoding.

   <options>

The <options> parameter is used to pass the value of the Options field of the Format object to the Java code to distinguish different format types, e.g. a standard dmy (Day, Month, Year) format or a special my (Month, Year) format. A Java set method matching the value of this tag must be defined in this case.

Example:

<options>Options</options>

needs a Java set method setOptions.

   <channel>

The <channel> parameter is used to pass the current channel value of the VoiceObjects session to the Java code. This value can be used to return different formatting based on channel. A Java set method matching the value of this tag must be defined in this case.

Example:

<channel>Channel</channel>

needs a Java set method setChannel.

   <pronunciation>

The <pronunciation> parameter is used to pass the pronunciation value of a Variable object to the Java code. This value can be used to instruct the algorithm how to pronounce the value, in order to mimic the style the caller used, e.g. the intonation, rhythm, or synonym. A Java set method matching the value of this tag must be defined in this case.

Example:

<pronunciation>Pronunciation</pronunciation>

needs a Java set method setPronunciation.

   <custom value>

The <custom value> parameter can be defined N times to pass 1 to N special custom values to the Java code. Java set methods matching the values of these tags must be defined in this case.

Example:

<custom value=".">CustomSeparator</custom>
<custom value=":; /()-_[]{},.">PunctuationCharacters</custom>

needs the Java set methods setCustomSeparator and setPunctuationCharacters.

 

FilePostProcessing

This section defines how VoiceObjects should handle the audio filenames, which are listed in the XML result that is returned by the Java code.


    <filePostProcessing>

        <resourceLocator>true</resourceLocator>

        <filePrefix>false</filePrefix>

        <fileSuffix>false</fileSuffix>

        <fileRandomSuffix>false</fileRandomSuffix>

        <fileExtension>true</fileExtension>

    </filePostProcessing>

</function>


i8    Note: All parameters of this section require Boolean (true or false) arguments.


Tag

Description

<resourceLocator>

If this parameter is set to true VoiceObjects will attach the resource locator information referenced in the Format object to the returned audio filenames.

If this value is set to false the resource locator information won’t be added to the audio filenames.

<filePrefix>

If this parameter is set to true VoiceObjects will attach the file prefix, which is defined in the Format object to the returned audio filenames.

If this parameter is set to false the file prefix information won’t be added to the audio filenames.

<fileSuffix>

If this parameter is set to true VoiceObjects will attach the file suffix, which is defined in the Format object to the returned audio filenames.

If it is set to false the file suffix information won’t be added to the audio filenames.

<fileRandomSuffix>

If this parameter is set to true VoiceObjects will attach the file random suffix, which is defined in the Format object to the returned audio filenames.

If this parameter is set to false the file random suffix information won’t be added to the audio filenames.

<fileExtension>

If this parameter is set to true VoiceObjects will attach the file extension, which is defined in the Format object to the returned audio filenames.

If it is set to false the file prefix information won’t be added to the audio filenames.

Custom TTA Example

This paragraph contains a common example for all files that are needed for a TTA algorithm. They can be used as a reference when building custom TTA algorithms. The listed examples are written for a date TTA algorithm. The different examples for the result XML show the possible influence via the Options field in the Format editor.

i8    Note: The TTA.xml file must be located in the folder
…plugins/com.voiceobjects.eclipseDesktop_9.1.0/config (only when using Desktop for Eclipse) and in …/VoiceObjects/Platform/WEB-INF/config.

The corresponding JAR file must be located in the directory defined as srcPath.

Example for the TTA.xml file

This example includes a custom TTA algorithm for date:

<?xml version="1.0" encoding="UTF-8"?>

<ttaLibrary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../schemas/TTALibrary.xsd">

    <header>

        <defaultLocaleID>en</defaultLocaleID>

        <namePrefix>

            <locale id="en">Custom - </locale>

        </namePrefix>

        <docPath>http://localhost:8099/VoiceObjects/Platform/Desktop/help/</docPath>

        <srcPath>./lib/ttaLibrary/</srcPath>

    </header>

 

    <function>

        <properties>

            <id>custom_date</id>

            <name>

                <locale id="en">TTA_Date</locale>

            </name>

            <description>

                <locale id="en">Speak as a VO Date (input is YYYYMMDD). Example: "20040628" is spoken as "Friday June twenty eighth, two thousand four".</locale>

            </description>

            <docFile>

                <locale id="en">en/speakas_date.html</locale>

            </docFile>

        </properties>

       

        <java>

            <srcFile>TTAExample.jar</srcFile>

            <class>TTAExample</class>

            <method>process</method>

            <parameterSet>

                <result>XMLResult</result>

                <data>data</data>

                <resourceLocator>resourceLocator</resourceLocator>

                <language>language</language>

                <encoding>encoding</encoding>

                <options>options</options>

                <pronunciation>Pronunciation</pronunciation>

                <custom value=".">customSeparator</custom>

            </parameterSet>

        </java>

       

        <filePostProcessing>

            <resourceLocator>true</resourceLocator>

            <filePrefix>false</filePrefix>

            <fileSuffix>false</fileSuffix>

            <fileRandomSuffix>false</fileRandomSuffix>

            <fileExtension>true</fileExtension>

        </filePostProcessing>

    </function>

</ttaLibrary>

Example for the Java class TTAExample

import java.util.ArrayList;

import java.util.List;

import java.util.Locale;

import java.util.Properties;

import java.util.StringTokenizer;

 

public class TTAExample

{

    protected String _data;

    protected String _language;

    protected String _resourceLocator;

    protected String _encoding;

    protected String _customSeparator;

    protected String _options;

    protected String _pronunciation;

    protected String _errorMsg;

    protected List _values;

    static Class class$com$sap$vaf$speakas$SpeakAs;

 

    public final void process()

    {

        if ( has( _data))

        {

           _values = new ArrayList( 5);

           if ( !has( _options))

           {

               _options = "dmy";

           }

           else

           {

               if (!validateOptionString(_options))

               {

                   _errorMsg = "Option string has invalid format!";

               }

           }

           if ( !has( _customSeparator)) _customSeparator = "/";

           final StringTokenizer tokens = new StringTokenizer( _data,_customSeparator);

           while ( tokens.hasMoreTokens())

           {

               _values.add( tokens.nextToken());

           }

        }

        else

        {

            _errorMsg = "Custom TTA Data requires non empty data!";

        }

    }

 

    private boolean validateOptionString( String optionsStr )

    {

        if ( "my".equals( _options))

        {

            _options = optionsStr;

        }

        else if ( "dmy".equals( _options))

        {

            _options = optionsStr;

        }

        else

        {

              return false;

        }

        return true;

    }

   

    public String getXMLResult()

    {

        if ( ( _values == null || _values.isEmpty() )

                && ( _errorMsg == null || "".equals( _errorMsg ) ) )

            setErrorMsg

                    ( "The output was empty. Check the parameter." );

        if ( _errorMsg != null && !"".equals( _errorMsg ) )

        {

            final String message

                    = "<root>" +

                      "<row>" +

                      "<col name=\"status\"><![CDATA[" +

                        this.getClass().getName() +

                      "--> Input:'" + _data +

                      "' Language:'" + _language +

                      "' Options:'" + _options + 

                      " Error: " + _errorMsg + "]]>" +

                      "</col>" +

                      "</row>" +

                      "</root>";

            return message;

        }

        final StringBuffer result = new StringBuffer( "<root>" );

        int counter = 0;

        if ( "my".equals( _options) ) counter = 1;

        for ( int i = counter; i < _values.size(); i++ )

        {

            result.append( "<row><col name=\"file\">" );

            if ( i==0 )

            {

                result.append( "day");

                result.append( _values.get( i) );

                result.append( "</col><col name=\"text\"><![CDATA[" );

                result.append( "day");

                result.append( _values.get( i) );

                result.append( "]]></col>" );

                result.append( "</row>" );               

            }

            else if ( i==1 )

            {

                result.append( "month");

                result.append( _values.get( i) );

                result.append( "</col><col name=\"text\"><![CDATA[" );

                result.append( "month");

                result.append( _values.get( i) );

                result.append( "]]></col>" );

                result.append( "</row>" );               

            }

            else

            {

                result.append( "year");

                result.append( _values.get( i) );

                result.append( "</col><col name=\"text\"><![CDATA[" );

                result.append( "year");

                result.append( _values.get( i) );

                result.append( "]]></col>" );

                result.append( "</row>" );

            }

        }

        result.append( "</root>" );

        clear();

        return result.toString();       

    }   

   

    public void setData( String string )

    {

        if ( string != null )

            _data = string.trim();

    }

 

    public void setResourceLocator( String string )

    {

        if ( string != null )

            _resourceLocator = string.trim();

    }

 

    public void setLanguage( String string )

    {

        if ( string != null )

            _language = string.trim();

    }

   

    public void setEncoding( String string )

    {

        if ( string != null )

            _encoding = string.trim();

    }

 

    public void setOptions( String string )

    {

        if ( string != null )

            _options = string.trim();

    }

 

    public void setPronunciation( String string )

    {

        if ( string != null )

            _pronunciation = string.trim();

    }

 

    public void setCustomSeparator( final String string )

    {

        if ( string != null )

            _customSeparator = string.trim();

    }

 

    public void setErrorMsg( String string )

    {

        if ( string != null )

            _errorMsg = string.trim();

    }

   

    public void clear()

    {

        _data = null;

        _resourceLocator = null;

        _language = null;

        _encoding = null;

        _customSeparator = null;

        _errorMsg = null;

        _options = null;

        _pronunciation = null;

    }

   

    public static boolean has ( Object o )

    {

        return null != o && !"".equals( o );

    }

}

Result XML

The returned XML has to follow this structure:

<root>
  <row>
    <col name="file"></col>
    <col name="text"></col>
  </row>
</root>

Examples:

If the Options field isn’t filled or contains dmy this is the returned XML:

<root>
  <row>
    <col name="file">day19.wav</col>
    <col name="text">day19</col>
  </row>
  <row>
    <col name="file">month12.wav</col>
    <col name="text">month12</col>
  </row>
  <row>
    <col name="file">year2004.wav</col>
    <col name="text">year2004</col>
  </row>
</root>

If the Options field contains my this XML is returned:

<root>
  <row>
    <col name="file">month12.wav</col>
    <col name="text">month12</col>
  </row>
  <row>
    <col name="file">year2004.wav</col>
    <col name="text">year2004</col>
  </row>
</root>

Desktop appearance

The following screenshot shows how a Variable object Date is referenced in an Output object:


 

In the next two screenshots possible object definitions for Date are shown including the formatting. In both examples the type SpeakAs – Date is selected; in the first screenshot dmy is defined as the option, instructing the formatting algorithm to read the date back including the day, whereas the second example defines my as the option, so that only month and year are played back: