Overview
In some applications it is very convenient if the user can call up information as voice output, for example, packaging instructions in outgoing goods. The speech output is available in good quality in the common browsers without having to install anything extra.

We can freely compile the text to be spoken at ABAP level from application data. Quite similar to what is explained in the article Charts and diagrams, we use a hidden field to transport data "ABAP -> HTML" and to call a JavaScript function that performs the speech output.


Example
In the display of customer visits, we offer an "Audio" button that reads out the essential data about the customer visit:

deepl


Implementation
We display the audio symbol in HTML using an image "audio.png". Clicking on the audio symbol starts the ABAP method "audio":

<img src="../../../icons/audio.png" 
     style="width:28px; border-radius:6px;"
     onclick="S10Apply('audio');" />

In the ABAP method we compose the desired text. In the example, the method "audio" of the class "customer" calls another method "speech_output" of the class "visit" and sets the text to be spoken in a class attribute "speech_output":


* class "customer" 
data:
   speech_output type string.
 
method audio.
  speech_output =  myvisit->speech_output( ).
endmethod.
 
 
* class "visit"
 method speech_output.

* date and time
    datadate_out(20),
          time_out type string.
    
    write vdate to date_out.
    time_out vtime(2&& |:| && vtime+2(2&&  |h 
                    && etime(2&& |:| && etime+2(2&& |h .|.

    text s10getuservalue'weekday_long' 
                   && | | && date_out && || && time_out.
* topic and participants 
    datatopic        type string,
          participants type string.

    case sy-langu.
      when 'D'.
        topic 'Thema: '.
        participants 'Teilnehmer: '.

      when 'F'.
        topic 'Sujet: '.
        participants 'Participants: '.

      when others.
        topic 'Topic: '.
        participants 'Participants: '.

    endcase.

    text text && topic && ktext && |.|.
    text text && participants.
    loop at visit_ap_table into mycontact.
      text text && mycontact->fullname && |,
                && mycontact->parau && ||.
    endloop.

* long texts
    if text01 is not initial.
      text text && title01 && |.|.
      text text && text01.
    endif.

    if text02 is not initial.
      text text && title02 && |.|.
      text text && text02.
    endif.

    if text03 is not initial.
      text text && title03 && |.|.
      text text && text03.
    endif.

  endmethod.

The string "speech_output" is not to be displayed in HTML, but passed to a JavaScript routine for speech output. For this purpose, we define an HTML element of the type "hidden", which is bound to the ABAP class attribute "speech_output" via name=:

 <!--speech output -->
<input type="hidden" 
        class="input" 
        name="speech_output" 
        onchange="speak(this);" />

                    

When the content is changed, the JavaScript function "speak" is called by the "onchange=" specification, which forwards the content to the speech output of the browser:

// speech output
var speaker = new SpeechSynthesisUtterance();

function speak(f) {

    var mytext = f.value;
    f.value = '';

    if (speechSynthesis.speaking) {
        speechSynthesis.pause();
        speechSynthesis.cancel();
        return;
    };

    speaker.text = mytext;
    speaker.lang = S10Language();
    speaker.volume = 1;  
    speaker.rate = 1; 
    speaker.pitch = 1;  
    speechSynthesis.speak(speaker);
}
In the JavaScript function "speak" we first put the text to be spoken into a variable "mytext" and then delete the content of the "hidden" element. If we do not do this, the "onchange" function will not be executed again when the audio symbol is clicked on again later for the same visit, because the content has not changed. Next, we ask whether a voice output is currently running. If so, it will be stopped, so that the user can always cancel a running voice output by clicking on the audio symbol again. Then we set some parameters (text language, volume,...) and start the voice output.


Explanation of the HTML elements of the type "hidden
An element

<input type='hidden' ...>

is not displayed, but can be used to transfer the value of an ABAP class attribute to HTML and vice versa.

If you only need the "download" direction of ABAP->HTML, use class='output':

<input type='hidden' class='output' name='attrname'> 

Here, "attrname" is the name of the ABAP class attribute. The content of the class attribute is transferred by the S10 Framework to the <input> element and can be read there as the attribute "value". Class attributes of the type "string" are usually used, but all other data types are possible. The content is always made available at HTML level in external format according to the user settings (date, decimal point). If you also need the "upload" direction, HTML->ABAP, use class="input":

<input type='hidden' class='input' name='attrname'>

In most cases, after the data transport, you want to call a JavaScript function that locally does something specific with the data, for example, creates a diagram or, as in the example above, performs the language output of a text. We achieve this by specifying onchange=, which calls the specified JavaScript function when the content changes: "input type='hidden' class='output' name='attrname' onchange="...""

<input type='hidden' class='output' name='attrname' onchange="...">

For testing, you can output the content as a message:

<input type='hidden' class='output' name='attrname' onchange="alert(this.value)">

The specified JavaScript function is only called if the content has changed. Initial value is the empty string. As long as the named class attribute is empty, nothing is called. If you need the content of several attributes, it is best to work with several "input" elements and start the JavaScript function with another <input type='hidden'> by incrementing a counter in ABAP and thus triggering "onchange".
Example:

<input type='hidden' class='output' name='charttitle' id='charttitle'>
<input type='hidden' class='output' name='charttype' id='charttype'>
<input type='hidden' class='output' name='chartfigures' id='chartfigures'>

<input type='hidden' class='output' name='chartstep' onchange="generate_chart()">

In ABAP you have defined "chartstep" by

data: chartstep type i.

You increment "chartstep" by 1 as soon as you want to generate a new chart. In the called JavaScript function "generate_chart()" you can access the contents as follows:

var mytitle = document.getElementById('charttitle').value;
var mytype = document.getElementById('charttype').value;
var myfigures = document.getElementById('chartfigures').value;

Components: S10 Framework