| 
                        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 | 
                        - import Toast from '@/app/components/base/toast'
 - import { textToAudioStream } from '@/service/share'
 - 
 - declare global {
 -   // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
 -   interface Window {
 -     ManagedMediaSource: any
 -   }
 - }
 - 
 - export default class AudioPlayer {
 -   mediaSource: MediaSource | null
 -   audio: HTMLAudioElement
 -   audioContext: AudioContext
 -   sourceBuffer?: any
 -   cacheBuffers: ArrayBuffer[] = []
 -   pauseTimer: number | null = null
 -   msgId: string | undefined
 -   msgContent: string | null | undefined = null
 -   voice: string | undefined = undefined
 -   isLoadData = false
 -   url: string
 -   isPublic: boolean
 -   callback: ((event: string) => {}) | null
 - 
 -   constructor(streamUrl: string, isPublic: boolean, msgId: string | undefined, msgContent: string | null | undefined, voice: string | undefined, callback: ((event: string) => {}) | null) {
 -     this.audioContext = new AudioContext()
 -     this.msgId = msgId
 -     this.msgContent = msgContent
 -     this.url = streamUrl
 -     this.isPublic = isPublic
 -     this.voice = voice
 -     this.callback = callback
 - 
 -     // Compatible with iphone ios17 ManagedMediaSource
 -     const MediaSource = window.ManagedMediaSource || window.MediaSource
 -     if (!MediaSource) {
 -       Toast.notify({
 -         message: 'Your browser does not support audio streaming, if you are using an iPhone, please update to iOS 17.1 or later.',
 -         type: 'error',
 -       })
 -     }
 -     this.mediaSource = MediaSource ? new MediaSource() : null
 -     this.audio = new Audio()
 -     this.setCallback(callback)
 -     if (!window.MediaSource) { // if use  ManagedMediaSource
 -       this.audio.disableRemotePlayback = true
 -       this.audio.controls = true
 -     }
 -     this.audio.src = this.mediaSource ? URL.createObjectURL(this.mediaSource) : ''
 -     this.audio.autoplay = true
 - 
 -     const source = this.audioContext.createMediaElementSource(this.audio)
 -     source.connect(this.audioContext.destination)
 -     this.listenMediaSource('audio/mpeg')
 -   }
 - 
 -   public resetMsgId(msgId: string) {
 -     this.msgId = msgId
 -   }
 - 
 -   private listenMediaSource(contentType: string) {
 -     this.mediaSource?.addEventListener('sourceopen', () => {
 -       if (this.sourceBuffer)
 -         return
 - 
 -       this.sourceBuffer = this.mediaSource?.addSourceBuffer(contentType)
 -     })
 -   }
 - 
 -   public setCallback(callback: ((event: string) => {}) | null) {
 -     this.callback = callback
 -     if (callback) {
 -       this.audio.addEventListener('ended', () => {
 -         callback('ended')
 -       }, false)
 -       this.audio.addEventListener('paused', () => {
 -         callback('paused')
 -       }, true)
 -       this.audio.addEventListener('loaded', () => {
 -         callback('loaded')
 -       }, true)
 -       this.audio.addEventListener('play', () => {
 -         callback('play')
 -       }, true)
 -       this.audio.addEventListener('timeupdate', () => {
 -         callback('timeupdate')
 -       }, true)
 -       this.audio.addEventListener('loadeddate', () => {
 -         callback('loadeddate')
 -       }, true)
 -       this.audio.addEventListener('canplay', () => {
 -         callback('canplay')
 -       }, true)
 -       this.audio.addEventListener('error', () => {
 -         callback('error')
 -       }, true)
 -     }
 -   }
 - 
 -   private async loadAudio() {
 -     try {
 -       const audioResponse: any = await textToAudioStream(this.url, this.isPublic, { content_type: 'audio/mpeg' }, {
 -         message_id: this.msgId,
 -         streaming: true,
 -         voice: this.voice,
 -         text: this.msgContent,
 -       })
 - 
 -       if (audioResponse.status !== 200) {
 -         this.isLoadData = false
 -         if (this.callback)
 -           this.callback('error')
 -       }
 - 
 -       const reader = audioResponse.body.getReader()
 -       while (true) {
 -         const { value, done } = await reader.read()
 - 
 -         if (done) {
 -           this.receiveAudioData(value)
 -           break
 -         }
 - 
 -         this.receiveAudioData(value)
 -       }
 -     }
 -     catch (error) {
 -       this.isLoadData = false
 -       this.callback && this.callback('error')
 -     }
 -   }
 - 
 -   // play audio
 -   public playAudio() {
 -     if (this.isLoadData) {
 -       if (this.audioContext.state === 'suspended') {
 -         this.audioContext.resume().then((_) => {
 -           this.audio.play()
 -           this.callback && this.callback('play')
 -         })
 -       }
 -       else if (this.audio.ended) {
 -         this.audio.play()
 -         this.callback && this.callback('play')
 -       }
 -       if (this.callback)
 -         this.callback('play')
 -     }
 -     else {
 -       this.isLoadData = true
 -       this.loadAudio()
 -     }
 -   }
 - 
 -   private theEndOfStream() {
 -     const endTimer = setInterval(() => {
 -       if (!this.sourceBuffer?.updating) {
 -         this.mediaSource?.endOfStream()
 -         clearInterval(endTimer)
 -       }
 -     }, 10)
 -   }
 - 
 -   private finishStream() {
 -     const timer = setInterval(() => {
 -       if (!this.cacheBuffers.length) {
 -         this.theEndOfStream()
 -         clearInterval(timer)
 -       }
 - 
 -       if (this.cacheBuffers.length && !this.sourceBuffer?.updating) {
 -         const arrayBuffer = this.cacheBuffers.shift()!
 -         this.sourceBuffer?.appendBuffer(arrayBuffer)
 -       }
 -     }, 10)
 -   }
 - 
 -   public async playAudioWithAudio(audio: string, play = true) {
 -     if (!audio || !audio.length) {
 -       this.finishStream()
 -       return
 -     }
 - 
 -     const audioContent = Buffer.from(audio, 'base64')
 -     this.receiveAudioData(new Uint8Array(audioContent))
 -     if (play) {
 -       this.isLoadData = true
 -       if (this.audio.paused) {
 -         this.audioContext.resume().then((_) => {
 -           this.audio.play()
 -           this.callback && this.callback('play')
 -         })
 -       }
 -       else if (this.audio.ended) {
 -         this.audio.play()
 -         this.callback && this.callback('play')
 -       }
 -       else if (this.audio.played) { /* empty */ }
 - 
 -       else {
 -         this.audio.play()
 -         this.callback && this.callback('play')
 -       }
 -     }
 -   }
 - 
 -   public pauseAudio() {
 -     this.callback && this.callback('paused')
 -     this.audio.pause()
 -     this.audioContext.suspend()
 -   }
 - 
 -   private cancer() {
 - 
 -   }
 - 
 -   private receiveAudioData(unit8Array: Uint8Array) {
 -     if (!unit8Array) {
 -       this.finishStream()
 -       return
 -     }
 -     const audioData = this.byteArrayToArrayBuffer(unit8Array)
 -     if (!audioData.byteLength) {
 -       if (this.mediaSource?.readyState === 'open')
 -         this.finishStream()
 -       return
 -     }
 - 
 -     if (this.sourceBuffer?.updating) {
 -       this.cacheBuffers.push(audioData)
 -     }
 -     else {
 -       if (this.cacheBuffers.length && !this.sourceBuffer?.updating) {
 -         this.cacheBuffers.push(audioData)
 -         const cacheBuffer = this.cacheBuffers.shift()!
 -         this.sourceBuffer?.appendBuffer(cacheBuffer)
 -       }
 -       else {
 -         this.sourceBuffer?.appendBuffer(audioData)
 -       }
 -     }
 -   }
 - 
 -   private byteArrayToArrayBuffer(byteArray: Uint8Array): ArrayBuffer {
 -     const arrayBuffer = new ArrayBuffer(byteArray.length)
 -     const uint8Array = new Uint8Array(arrayBuffer)
 -     uint8Array.set(byteArray)
 -     return arrayBuffer
 -   }
 - }
 
 
  |