Play Watson Text-to-Speech via XHR

Updated: 03 September 2023

Play the Audio

In order to play audio from the result of an XHR request we can make use of the following code (in this case using the IBM Watson Text to Speech Service), this will make the request, store the response audio as a Blob, assign that to an Audio Element, and then play the audio.

We will need to have the following in our HTML

1
<audio id="myAudioElement" />

And then the necessary Javascript to get the audio source set on the element

1
var data = null
2
var xhr = new XMLHttpRequest()
3
xhr.withCredentials = true
4
xhr.responseType = 'blob'
5
xhr.addEventListener('readystatechange', function () {
6
if (this.readyState === 4) {
7
//console.log(this.responseText);
8
}
9
})
10
xhr.onload = function (evt) {
11
var binaryData = []
12
binaryData.push(xhr.response)
13
var blob = new Blob(binaryData, { type: 'audio/mp3' })
14
var objectUrl = window.URL.createObjectURL(
15
new Blob(binaryData, { type: 'application/zip' })
16
)
17
console.log(objectUrl)
18
var audioElement = document.getElementById('myAudioElement')
19
audioElement.src = objectUrl
20
audioElement.play()
21
}
22
xhr.open(
23
'GET',
24
`speech/text-to-speech/api/v1/voices/en-GB_KateVoice/synthesize?text=${text}`
25
)
26
xhr.setRequestHeader('Accept', 'audio/mp3')
27
xhr.setRequestHeader('Authorization', 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXX')
28
xhr.setRequestHeader('cache-control', 'no-cache')
29
xhr.send(data)

Note that we may get blocked by CORS, in order to prevent this we can make use of an Express Proxy on the server side (or really any kind of proxy) which will serve our site files from a public folder, and proxy requests to /speech to https://stream.watsonplatform.net/

1
const express = require('express')
2
var proxy = require('express-http-proxy')
3
4
const app = express()
5
6
app.use(express.static('public'))
7
app.use('/speech', proxy('https://stream.watsonplatform.net/'))

It may be possible to make the request on the server as well and simply pass the binary forward, however I have not gotten that to work as yet