re text to speech

Page principale

Répondre à ce message
Auteur: Yves Gufflet
Date:  
À: guilde
Sujet: re text to speech
Bonjour,

Le fichier joint répond à la demande (j'ai du mal à expédier le fichier
en html non supporté par la liste, il faut recopier le texte et le
mettre dans un fichier html)

Il s'agit d'un simple fichier que l'on peut ouvrir avec son navigateur
et sans besoin d'un serveur web.

Il utilise la fonctionnalité SpeechSynthetizer du navigateur.

Cette fonctionnalité est expérimentale et certaines choses bugs en
fonction du navigateur : ici le curseur ne suit pas le texte lu et la
pause et le résume ne marche pas.

Les voix sous chrome sont naturelles. Sous FF elles sont très robotisés.

Yves

<!DOCTYPE html>
<html lang="fr-FR">
   <head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, 
initial-scale=1.0" />
     <title>Text to Speech</title>
     <style>


html,
body
{
    width : 100%;
    height : 100%;
    margin : 0px;
    padding : 0px;
}


#texttospeak,
#textbeingspoken
{
    margin:0px;
    width : 50%;
    height : 50%;
}


#controllers
{
    position:absolute;
    top:0px;
    right:0px;    
    width : 50%;
    height : 100%;    
    display:flex;
    flex-direction:column;
    row-gap:20px;
    align-items : center;
    padding : 20px;
}


#controllers > *
{
    display:flex;
}


.speecharg label
{
    margin-right:10px;
    font-weight:bold;
    width : 100px;
    text-align:right;
}


.speecharg input,
.speecharg select
{
    width : 200px;
}


#start
{
    margin:10px;
    font-weight:bold;
}


#start.started        
{
    background: red;
}


#marker
{
    position:absolute;
    top:0px;
    left:0px;
    z-index:10;    
    width:10px;
    height:10px;
    background:black;
}


     </style>


     <script>


var texttospeak;
var textbeingspoken;
var marker;
var range;
var firstBoundary;
var voices = [];
var utterance;

function populateVoiceList()
{
    voices = window.speechSynthesis.getVoices();
    var selectElm = document.querySelector('#voice');
    selectElm.innerHTML = '';


    voices.sort ((a, b) => { return a.name > b.name; })


    var selected = false;


    for (var i=0;i < voices.length;i++)
    {
        var option = document.createElement('option');
        option.innerHTML = voices[i].name + ' (' + voices[i].lang + ')';
        option.setAttribute('value', voices[i].voiceURI);
        option.voice = voices[i];
        if (!selected && (voices[i].lang == "fr-FR"))
        {
            selected = true;
            option.selected = true;
        }
        selectElm.appendChild(option);
    }
}


function stop()
{
    speechSynthesis.cancel();
}


function pauseresume()
{
    if (speechSynthesis.paused)
        speechSynthesis.resume();
    else
        speechSynthesis.pause();
}


function start()
{
    firstBoundary = true;

    
    utterance = new SpeechSynthesisUtterance(texttospeak.value);

    
    textbeingspoken.textContent = texttospeak.value;

    
    utterance.voice = voices[document.getElementById('voice').selectedIndex];
    utterance.volume = document.getElementById('volume').value;
    utterance.pitch = document.getElementById('pitch').value;    
    var rate = document.getElementById('rate').value;
    utterance.rate = Math.pow(Math.abs(rate) + 1, rate < 0 ? -1 : 1);

    
    utterance.addEventListener('start', function ()
    {
        marker.classList.remove('animate');
        document.body.classList.add('speaking');
    });

    
    utterance.addEventListener('start', handleSpeechEvent);
    utterance.addEventListener('end', handleSpeechEvent);
    utterance.addEventListener('error', handleSpeechEvent);
    utterance.addEventListener('boundary', handleSpeechEvent);
    utterance.addEventListener('pause', handleSpeechEvent);
    utterance.addEventListener('resume', handleSpeechEvent);


    speechSynthesis.speak(utterance);
}

    
function handleSpeechEvent(e)
{
    console.log('Speech Event:', e);


    switch (e.type)
    {
        case 'start':
            marker.classList.remove('animate');
            document.body.classList.add('speaking');
            break;
        case 'end':
        case 'endEvent':
        case 'error':
            document.body.classList.remove('speaking');
            marker.classList.remove('moved');
        break;
        case 'boundary':
        {
            if (e.name != 'word')
            break;

            
            var substr = speechtext.slice(e.charIndex);
            var rex = /\S+/g;
            var res = rex.exec(substr);

            
            if (!res) return;

            
            var startOffset = res.index + e.charIndex;
            var endOffset = rex.lastIndex + e.charIndex;

            
            range.setStart(textbeingspoken.firstChild, startOffset);
            range.setEnd(textbeingspoken.firstChild, endOffset);

            
            var rect = range.getBoundingClientRect();
            var delta = 0;

            
            var parentRect = textbeingspoken.getBoundingClientRect();


            if (rect.bottom > parentRect.bottom) delta = rect.bottom - 
parentRect.bottom;
            if (rect.top < parentRect.top) delta = rect.top - parentRect.top;


            textbeingspoken.scrollTop += delta;

            
            texttospeak.scrollTop = textbeingspoken.scrollTop;


            marker.style.top = rect.top - delta - 1;
            marker.style.left = rect.left - 1;
            marker.style.width = rect.width + 1;
            marker.style.height = rect.height + 1;
            marker.classList.add('moved');


            if (firstBoundary)
            {
                firstBoundary = false;
                marker.classList.add('animate');
            }

            
            break;
        }
        default:
          break;
    }
}


window.onload = () =>
{
    texttospeak = document.getElementById('texttospeak');
    textbeingspoken = document.getElementById('textbeingspoken');
    marker = document.getElementById('marker');
    range = document.createRange();


    populateVoiceList();

    
    if (speechSynthesis.onvoiceschanged !== undefined) 
speechSynthesis.onvoiceschanged = populateVoiceList;
};


     </script>


    </head>
    <body>
        <textarea id="texttospeak">Pour lire ce texte appuyez sur PLAY.
Pour mettre en pause et résumer la lecture, cliquez sur PAUSE/RESUME.
Cette fonctionnalité buggue en fonction des plateformes.
Pour arrêter la lecture, cliquez sur STOP.
Vous pouvez modifier les paramètres de lecture en ajustant les paramètres.
Vous devez arrêter et reprendre la lecture pour qu'ils soient pris en 
compte.</textarea>
        <textarea id="textbeingspoken"></textarea>
        <div id="marker"></div>
        <div id="controllers">
            <div class="speecharg">
                <label for="voice">Voice</label><select id="voice"></select>
            </div>
            <div class="speecharg">
                <label for="pitch">Pitch</label><input id="pitch" type="range" 
value="0.5" min="0" max="1" step="0.05">
            </div>
            <div class="speecharg">
                <label for="rate">Rate</label><input id="rate" type="range" 
value="0" min="-3" max="3" step="0.25">
            </div>
            <div class="speecharg">
                <label for="volue">Volume</label><input id="volume" type="range" 
value="1" min="0" max="1" step="0.05">
            </div>
            <button id="start"type="button" 
onmousedown="start();"><strong>PLAY</strong></button>
            <button type="button" aria-label="Pause/Resume" title="Pause/Resume" 
onmousedown="pauseresume();"><strong>RESUME/PAUSE</strong></button>
            <button type="button" aria-label="Cancel" title="Cancel" 
onmousedown="stop();"><strong>STOP</strong></button>
            <div class="bottom"></div>
        </div>
    </body>
</html>