| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 | 
							- import Toast from '@/app/components/base/toast'
 
- import { textToAudioStream } from '@/service/share'
 
- declare global {
 
-   // eslint-disable-next-line ts/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
 
-   }
 
- }
 
 
  |