Skip to content

Instantly share code, notes, and snippets.

@maltoze
Last active April 19, 2023 01:44
Show Gist options
  • Save maltoze/5dffcdce0c7f0b5fb699f05153fbbcfb to your computer and use it in GitHub Desktop.
Save maltoze/5dffcdce0c7f0b5fb699f05153fbbcfb to your computer and use it in GitHub Desktop.
function mkssml(text, voice, rate, volume) {
return (
"<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-US'>" +
`<voice name='${voice}'><prosody pitch='+0Hz' rate='${rate}' volume='${volume}'>` +
`${text}</prosody></voice></speak>`
)
}
function ssmlHeadersPlusData(request_id, timestamp, ssml) {
return (
`X-RequestId:${request_id}\r\n` +
'Content-Type:application/ssml+xml\r\n' +
`X-Timestamp:${timestamp}Z\r\n` + // This is not a mistake, Microsoft Edge bug.
`Path:ssml\r\n\r\n` +
`${ssml}`
)
}
const text = 'who are you'
const TRUSTED_CLIENT_TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4'
const WSS_URL =
'wss://speech.platform.bing.com/consumer/speech/synthesize/' +
'readaloud/edge/v1?TrustedClientToken=' +
TRUSTED_CLIENT_TOKEN
const connectId = crypto.randomUUID().replaceAll('-', '')
const ws = new WebSocket(`${WSS_URL}&ConnectionId=${connectId}`)
const date = new Date()
ws.onopen = () => {
ws.send(
`X-Timestamp:${date}\r\n` +
'Content-Type:application/json; charset=utf-8\r\n' +
'Path:speech.config\r\n\r\n' +
'{"context":{"synthesis":{"audio":{"metadataoptions":{' +
'"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},' +
'"outputFormat":"audio-24khz-48kbitrate-mono-mp3"' +
'}}}}\r\n'
)
ws.send(
ssmlHeadersPlusData(
connectId,
date,
mkssml(text, 'Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)', '+0%', '+0%')
)
)
}
const audioContext = new AudioContext()
let when = audioContext.currentTime + 1
ws.addEventListener('message', async (event) => {
console.log(event.data)
if (event.data instanceof Blob) {
const str = await event.data.text()
const data = await event.data.slice(str.indexOf('Path:audio\r\n') + 12).arrayBuffer()
const buffer = await audioContext.decodeAudioData(data)
const source = audioContext.createBufferSource()
source.buffer = buffer
source.connect(audioContext.destination)
source.start(when)
when += buffer.duration
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment