@minto-ai/huoshan-tts
Version:
借助“火山引擎在线语音合成API”实现浏览器端“文本转语音
340 lines (289 loc) • 10.7 kB
text/typescript
import type { InterfaceHandler } from './handler/core'
import type { BusinessParams, PrivateCustomEventName, PublicCustomEventName, SsmlConfig, SystemConfig } from './types'
import { isIos } from '@minto-ai/tools'
import {
AudioActuator,
ByteBufferNotIos,
CreateSsml,
DecodeDataBaShanIos,
DecodeDataIos,
DecodeDataNotIos,
TextSplit,
TtsRequestBaShanIos,
TtsRequestIos,
TtsRequestNotIos,
} from './process'
import { SystemStatus } from './types'
import { createEventBus } from './utils/event-bus'
class TtsController {
private isEqualBaShan: boolean = false
private isNeedCreateSsml: boolean = false
private textSplitInstance: TextSplit
private createSsmlInstance: CreateSsml
private audioActuatorInstance!: AudioActuator
// iOS 处理器实例
private ttsRequestIosInstance!: TtsRequestIos
private decodeDataIosInstance!: DecodeDataIos
// 巴山 IOS 处理器实例
private ttsRequestBaShanIosInstance!: TtsRequestBaShanIos
private decodeDataShanIosInstance!: DecodeDataBaShanIos
// 非iOS 处理器实例
private ttsRequestNotIosInstance!: TtsRequestNotIos
private byteBufferNotIosInstance!: ByteBufferNotIos
private decodeDataNotIosInstance!: DecodeDataNotIos
public systemStatus: SystemStatus = SystemStatus.OFFLINE
public $bus = createEventBus<PublicCustomEventName | PrivateCustomEventName>()
/**
* TTS控制器构造函数
* @param systemConfig - 系统配置
* @param businessParams - 业务参数
* @param ssmlConfig - SSML 配置
*/
constructor(systemConfig: SystemConfig, businessParams: BusinessParams, ssmlConfig: SsmlConfig) {
this.isEqualBaShan = businessParams.provider === '100'
this.textSplitInstance = new TextSplit()
if (this.isEqualBaShan) {
this.isNeedCreateSsml = false
}
else {
if (businessParams.text_type === 'ssml') {
this.isNeedCreateSsml = true
}
else {
this.isNeedCreateSsml = false
}
}
this.createSsmlInstance = new CreateSsml()
this.createSsmlInstance.initProperty(ssmlConfig)
this.audioActuatorInstance = new AudioActuator()
// 根据设备类型初始化对应的处理器
if (isIos()) {
if (this.isEqualBaShan) {
this.ttsRequestBaShanIosInstance = new TtsRequestBaShanIos()
this.decodeDataShanIosInstance = new DecodeDataBaShanIos()
this.ttsRequestBaShanIosInstance.initProperty(systemConfig, businessParams)
}
else {
this.ttsRequestIosInstance = new TtsRequestIos()
this.decodeDataIosInstance = new DecodeDataIos()
this.ttsRequestIosInstance.initProperty(systemConfig, businessParams)
}
}
else {
this.ttsRequestNotIosInstance = new TtsRequestNotIos()
this.byteBufferNotIosInstance = new ByteBufferNotIos()
this.decodeDataNotIosInstance = new DecodeDataNotIos()
this.ttsRequestNotIosInstance.initProperty(systemConfig, businessParams)
}
this.textSplitInstance.setExecuteController(this)
this.createSsmlInstance.setExecuteController(this)
// 设置处理器控制器
if (isIos()) {
if (this.isEqualBaShan) {
this.ttsRequestBaShanIosInstance.setExecuteController(this)
this.decodeDataShanIosInstance.setExecuteController(this)
this.audioActuatorInstance.setExecuteController(this)
}
else {
this.ttsRequestIosInstance.setExecuteController(this)
this.decodeDataIosInstance.setExecuteController(this)
this.audioActuatorInstance.setExecuteController(this)
}
}
else {
this.ttsRequestNotIosInstance.setExecuteController(this)
this.byteBufferNotIosInstance.setExecuteController(this)
this.decodeDataNotIosInstance.setExecuteController(this)
this.audioActuatorInstance.setExecuteController(this)
}
// 链接处理器
if (isIos()) {
if (this.isEqualBaShan) {
// 巴山iOS设备:TextSplit -> CreateSsml(可选)-> TtsRequestBaShanIos -> DecodeDataBaShanIos -> AudioActuator
if (this.isNeedCreateSsml) {
this.textSplitInstance.linkHandler(this.createSsmlInstance as unknown as InterfaceHandler)
this.createSsmlInstance.linkHandler(this.ttsRequestBaShanIosInstance as unknown as InterfaceHandler)
}
else {
this.textSplitInstance.linkHandler(this.ttsRequestBaShanIosInstance as unknown as InterfaceHandler)
}
this.ttsRequestBaShanIosInstance.linkHandler(this.decodeDataShanIosInstance as unknown as InterfaceHandler)
this.decodeDataShanIosInstance.linkHandler(this.audioActuatorInstance as unknown as InterfaceHandler)
}
else {
// iOS设备:TextSplit -> CreateSsml (可选)-> TtsRequestIos -> DecodeDataIos -> AudioActuator
if (this.isNeedCreateSsml) {
this.textSplitInstance.linkHandler(this.createSsmlInstance as unknown as InterfaceHandler)
this.createSsmlInstance.linkHandler(this.ttsRequestIosInstance as unknown as InterfaceHandler)
}
else {
this.textSplitInstance.linkHandler(this.ttsRequestIosInstance as unknown as InterfaceHandler)
}
this.ttsRequestIosInstance.linkHandler(this.decodeDataIosInstance as unknown as InterfaceHandler)
this.decodeDataIosInstance.linkHandler(this.audioActuatorInstance as unknown as InterfaceHandler)
}
}
else {
// 非iOS设备:TextSplit -> CreateSsml (可选)-> TtsRequestNotIos -> ByteBufferNotIos -> DecodeDataNotIos -> AudioActuator
if (this.isNeedCreateSsml) {
this.textSplitInstance.linkHandler(this.createSsmlInstance as unknown as InterfaceHandler)
this.createSsmlInstance.linkHandler(this.ttsRequestNotIosInstance as unknown as InterfaceHandler)
}
else {
this.textSplitInstance.linkHandler(this.ttsRequestNotIosInstance as unknown as InterfaceHandler)
}
this.ttsRequestNotIosInstance.linkHandler(this.byteBufferNotIosInstance as unknown as InterfaceHandler)
this.byteBufferNotIosInstance.linkHandler(this.decodeDataNotIosInstance as unknown as InterfaceHandler)
this.decodeDataNotIosInstance.linkHandler(this.audioActuatorInstance as unknown as InterfaceHandler)
}
this.bindEvent()
}
/**
* 绑定事件监听器
*/
private bindEvent(): void {
/**
* 音频首次播放
*/
this.$bus.on('_audioActuatorBeforeFirstExecute', () => {
this.emit('audioFirstStart')
})
/**
* 应用出错,触发 appFinish 事件
*/
this.$bus.on('_appError', (error) => {
this.finish()
this.emit('appError', error)
})
/**
* 应用被销毁,触发 appFinish 事件
*/
this.$bus.on('_appFinish', () => {
this.finish()
})
}
/**
* 进入待机状态,等待传入文本数据
*/
public start(): TtsController {
if (this.systemStatus === SystemStatus.EXECUTE) {
return this
}
this.textSplitInstance.triggerHandlerActive()
this.createSsmlInstance.triggerHandlerActive()
if (isIos()) {
if (this.isEqualBaShan) {
this.ttsRequestBaShanIosInstance.triggerHandlerActive()
this.decodeDataShanIosInstance.triggerHandlerActive()
this.audioActuatorInstance.triggerHandlerActive()
}
else {
this.ttsRequestIosInstance.triggerHandlerActive()
this.decodeDataIosInstance.triggerHandlerActive()
this.audioActuatorInstance.triggerHandlerActive()
}
}
else {
this.ttsRequestNotIosInstance.triggerHandlerActive()
this.byteBufferNotIosInstance.triggerHandlerActive()
this.decodeDataNotIosInstance.triggerHandlerActive()
this.audioActuatorInstance.triggerHandlerActive()
}
this.systemStatus = SystemStatus.EXECUTE
return this
}
/**
* 传入文本数据
* @param text - 待转换的文本
*/
public send(text: string): TtsController {
if (this.systemStatus !== SystemStatus.EXECUTE) {
return this
}
this.textSplitInstance.handle(text)
return this
}
/**
* 应用停止处理传入的文本,但是并不会停止音频播放
*/
public end(): TtsController {
if (this.systemStatus !== SystemStatus.EXECUTE) {
return this
}
this.textSplitInstance.handle(null)
this.textSplitInstance.isHandleDataAcceptedComplete = true
return this
}
/**
* 停止所有处理器,并且重置状态,触发 appFinish 事件
*/
public finish(): void {
if (this.systemStatus === SystemStatus.OFFLINE) {
return
}
this.textSplitInstance.triggerHandlerFinish()
this.createSsmlInstance.triggerHandlerFinish()
if (isIos()) {
if (this.isEqualBaShan) {
this.ttsRequestBaShanIosInstance.triggerHandlerFinish()
this.decodeDataShanIosInstance.triggerHandlerFinish()
this.audioActuatorInstance.triggerHandlerFinish()
}
else {
this.ttsRequestIosInstance.triggerHandlerFinish()
this.decodeDataIosInstance.triggerHandlerFinish()
this.audioActuatorInstance.triggerHandlerFinish()
}
}
else {
this.ttsRequestNotIosInstance.triggerHandlerFinish()
this.byteBufferNotIosInstance.triggerHandlerFinish()
this.decodeDataNotIosInstance.triggerHandlerFinish()
this.audioActuatorInstance.triggerHandlerFinish()
}
this.systemStatus = SystemStatus.OFFLINE
this.emit('appFinish')
}
/**
* 发射事件
* @param eventName - 事件名称
* @param data - 事件数据
*/
private emit<T>(eventName: PublicCustomEventName, data?: T): void {
this.$bus.emit(eventName, data)
}
/**
* 监听事件
* @param eventName - 事件名称
* @param callback - 回调函数
*/
public on(eventName: PublicCustomEventName, callback: (data?: any) => void): TtsController {
this.$bus.on(eventName, callback)
return this
}
/**
* 静音
*/
public mute(): void {
this.audioActuatorInstance.mute()
}
/**
* 取消静音
*/
public unmute(): void {
this.audioActuatorInstance.unmute()
}
/**
* 暂停播放
*/
public pause(): void {
this.audioActuatorInstance.pause()
}
/**
* 恢复播放
*/
public resume(): void {
this.audioActuatorInstance.resume()
}
}
export default TtsController