Quem trabalha com desenvolvimento de software conhece bem essa sensação. É o seu primeiro contato implementando algo totalmente novo, eu particularmente adoro isso, mas sei que no meio desse caminho algo vai dar errado e até que eu fale “noooooooooossa, mas era tão simples”, vou dizer muitos “quem foi o gênio que resolveu fazer isso dessa maneira?” ou “quem inventou isso não tem mãe não!?”, isso somente para pegar leve.

Boa parte desses problemas ocorrem em função de informações desencontradas. A internet é muito legal, tudo que você quer aprender está nela, mas nem sempre estruturado e organizado como um livro ou um curso, as vezes aprendemos algo juntando pedaços de código, tutoriais e documentações que encontramos em foruns, stackoverflow, google, udemy e por ai vai, o que dificulta e leva a gente as vezes para caminhos errados até encontrar a saída correta.
Por isso esse POST de hoje, pois tive muita dificuldade de juntar e encontrar essas informações na internet. Desenvolver uma solução para um chatbot que convertesse no backend um texto em áudio, para ser obtido via streaming por javascript, foi cansativo e testei diversas soluções sem exito.
O que eu precisava era o seguinte, desenvolver um novo recurso para um chatbot que usando uma ferramenta de text-to-speech (conversão de texto para áudio) fosse permitido ao usuário ouvir a resposta do chatbot.
A solução foi construída em java utilizando o framework springboot que já servia como backend do chatbot.
BACKEND – Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @PostMapping("/text-to-speech") @Consumes(value = MediaType.APPLICATION_JSON_VALUE) public void textToSpeech(@RequestBody ConversationBean bean, HttpServletRequest request, HttpServletResponse response) { <pre><code> try { if (manager.isActive(EFeatureFlag.CHATBOT_TEXT_TO_SPEECH)) { SpeechModel speechModel = new SpeechModelFactory().create(EPlataforma.IBM_BLUEMIX.getCodigo()); InputStream audio = speechModel.textToSpeech(bean.getInput()); response.setContentType("audio/wav"); response.setHeader("Accept-Ranges", "bytes"); ServletOutputStream out = response.getOutputStream(); IOUtils.copy(audio, out); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
No código acima obtemos um inputStream do serviço de text-to-Speech do Watson, mas serviria qualquer inputStream retornado por outros players, como AWS, google e azure, que possuem serviços semelhantes.
O código do backend copia para o response os bytes do áudio retornado para o inputStream e avisa ao navegador que precisa tratar o retorno como um audio/wav.
O que me travou bastante nesse ponto foi o retorno do serviço para que o frontend pudesse tratar o retorno via streaming. Na documentação do watson não encontrei este procedimento, pois em todos os exemplos apos receber o inputstream, o backend guardava o arquivo em um .wav dentro do filesystem, o que funcionava perfeitamente, porem era um exemplo pouco usual para o dia a dia.
O grande problema foi obter esses dados apos uma requisição jQuery e executar o áudio. 80% do tempo perdido na construção dessa funcionalidade foi devido ao áudio quando executado, apresentar um conjunto de ruídos indescritíveis e depois de alguns sustos com o barulho do áudio sem sentido, finalmente encontrei uma solução conforme descrevo abaixo:
FRONTEND- Javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | window.onload = loadTextToSpeech; var context; // Audio context var buf; // Audio buffer function loadTextToSpeech() { if (!window.AudioContext) { if (!window.webkitAudioContext) { alert("Your browser does not support any AudioContext and cannot play back this audio."); return; } <pre><code>window.AudioContext = window.webkitAudioContext;</code></pre> } context = new AudioContext(); console.log("saida de audio ligada") } |
chamando serviço de backend para converter o texto em audio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | var urlTextToSpeech = 'http://localhost:8081/chatbot/text-to-speech'; var texto = "ola bom dia"; textToSpeech(texto); function textToSpeech(text) { var data = { input: text }; var request = new XMLHttpRequest(); request.open('POST', urlTextToSpeech, true); request.responseType = 'arraybuffer'; request.setRequestHeader("Authorization", tokenAuth); request.setRequestHeader("Content-Type", 'application/json'); // Decode asynchronously request.onload = function() { context.decodeAudioData(request.response, function(buffer) { var source = context.createBufferSource(); //cria uma fonte de audio source.buffer = buffer; // informa a fonte de audio o que deve ser executado source.connect(context.destination); // conecta no alto falante source.start(0); //play do audio }); } request.send(JSON.stringify(data)); } |
Esta solução hoje funciona muito bem e encerrou uma serie de frustrações e xingamentos que foram jogados ao espaço.

Seja o primeiro a comentar