@minto-ai/xunfei-tts
Version:
借助“讯飞在线语音合成API”实现浏览器端“文本转语音
65 lines (56 loc) • 2.32 kB
text/typescript
import type { TranscodeWorkerReplyWorkerMessage, TranscodeWorkerSendWorkerData, TranscodeWorkerSendWorkerMessage } from './index'
function base64ToS16(base64AudioData: string): Int16Array {
base64AudioData = atob(base64AudioData)
const outputArray = new Uint8Array(base64AudioData.length)
for (let i = 0; i < base64AudioData.length; i += 1) {
outputArray[i] = base64AudioData.charCodeAt(i)
}
return new Int16Array(new DataView(outputArray.buffer).buffer)
}
function transS16ToF32(input: Int16Array): Float32Array {
const tmpData = []
for (let i = 0; i < input.length; i += 1) {
const d = input[i] < 0 ? input[i] / 0x8000 : input[i] / 0x7FFF
tmpData.push(d)
}
return new Float32Array(tmpData)
}
function transSamplingRate(data: Float32Array, fromRate: number = 44100, toRate: number = 16000): Float32Array {
const fitCount = Math.round(data.length * (toRate / fromRate))
const newData = new Float32Array(fitCount)
const springFactor = (data.length - 1) / (fitCount - 1)
newData[0] = data[0]
for (let i = 1; i < fitCount - 1; i += 1) {
const tmp = i * springFactor
const before = Math.floor(tmp).toFixed() as unknown as number
const after = Math.ceil(tmp).toFixed() as unknown as number
const atPoint = tmp - Number(before)
newData[i] = data[before] + (data[after] - data[before]) * atPoint
}
newData[fitCount - 1] = data[data.length - 1]
return newData
}
function transToAudioData(data: TranscodeWorkerSendWorkerData, fromRate: number = 16000, toRate: number = 22505): void {
const audioDataStr = data.original as string
const outputS16 = base64ToS16(audioDataStr)
let output: any = transS16ToF32(outputS16)
output = transSamplingRate(output, fromRate, toRate)
output = Array.from(output)
globalThis.postMessage({
type: 'success',
data: {
audioData: output,
rawAudioData: Array.from(outputS16),
uuid: data.uuid,
},
} as TranscodeWorkerReplyWorkerMessage)
}
function handleRun(data: TranscodeWorkerSendWorkerData): void {
transToAudioData(data)
}
globalThis.addEventListener('message', (event: MessageEvent<TranscodeWorkerSendWorkerMessage>) => {
const { type, data } = event.data
if (type === 'send') {
handleRun(data!)
}
})