@@ -2,14 +2,19 @@ import { AUDIO_EVENTS, BaseAudioPlayer, type AudioEventType } from "./BaseAudioP
22
33/**
44 * 基于 HTMLAudioElement 的播放器实现
5+ *
6+ * 使用原生 HTML5 Audio 元素进行音频播放,支持大多数常见格式
7+ * 通过 MediaElementAudioSourceNode 连接到 Web Audio API 音频图谱
58 */
69export class AudioElementPlayer extends BaseAudioPlayer {
10+ /** 内部 Audio 元素 */
711 private audioElement : HTMLAudioElement ;
12+ /** MediaElementAudioSourceNode 用于连接 Web Audio API */
813 private sourceNode : MediaElementAudioSourceNode | null = null ;
914
10- /** Seek 锁 */
15+ /** Seek 锁,用于在 seek 过程中返回稳定的 currentTime */
1116 private isInternalSeeking = false ;
12- /** 目标时间缓存 */
17+ /** 目标时间缓存,用于在 seek 过程中返回稳定的 currentTime */
1318 private targetSeekTime = 0 ;
1419
1520 constructor ( ) {
@@ -23,6 +28,10 @@ export class AudioElementPlayer extends BaseAudioPlayer {
2328 } ) ;
2429 }
2530
31+ /**
32+ * 当音频图谱初始化完成时调用
33+ * 创建 MediaElementAudioSourceNode 并连接到输入节点
34+ */
2635 protected onGraphInitialized ( ) : void {
2736 if ( ! this . audioCtx || ! this . inputNode ) return ;
2837
@@ -35,66 +44,107 @@ export class AudioElementPlayer extends BaseAudioPlayer {
3544 }
3645 }
3746
47+ /**
48+ * 加载音频资源
49+ * @param url 音频地址
50+ */
3851 public async load ( url : string ) : Promise < void > {
3952 this . audioElement . src = url ;
4053 this . audioElement . load ( ) ;
4154 }
4255
56+ /**
57+ * 执行底层播放
58+ * @returns 播放 Promise
59+ */
4360 protected async doPlay ( ) : Promise < void > {
4461 return this . audioElement . play ( ) ;
4562 }
4663
64+ /**
65+ * 执行底层暂停
66+ */
4767 protected doPause ( ) : void {
4868 this . audioElement . pause ( ) ;
4969 }
5070
71+ /**
72+ * 跳转到指定时间
73+ * @param time 目标时间(秒)
74+ */
5175 public async seek ( time : number ) : Promise < void > {
5276 this . isInternalSeeking = true ;
5377 this . targetSeekTime = time ;
5478
5579 await super . seek ( time ) ;
5680 }
5781
82+ /**
83+ * 执行底层 Seek
84+ * @param time 目标时间(秒)
85+ */
5886 protected doSeek ( time : number ) : void {
5987 if ( Number . isFinite ( time ) ) {
6088 this . audioElement . currentTime = time ;
6189 }
6290 }
6391
92+ /**
93+ * 设置播放速率
94+ * @param value 速率值 (0.5 - 2.0)
95+ */
6496 public setRate ( value : number ) : void {
6597 this . audioElement . playbackRate = value ;
6698 }
6799
100+ /**
101+ * 获取当前播放速率
102+ * @returns 当前速率值
103+ */
68104 public getRate ( ) : number {
69105 return this . audioElement . playbackRate ;
70106 }
71107
108+ /**
109+ * 设置音频输出设备
110+ * @param deviceId 设备 ID
111+ */
72112 protected async doSetSinkId ( deviceId : string ) : Promise < void > {
73113 if ( typeof this . audioElement . setSinkId === "function" ) {
74114 await this . audioElement . setSinkId ( deviceId ) ;
75115 }
76116 }
77117
118+ /** 获取当前音频源地址 */
78119 public get src ( ) : string {
79120 return this . audioElement . src || "" ;
80121 }
81122
123+ /** 获取音频总时长(秒) */
82124 public get duration ( ) : number {
83125 return this . audioElement . duration || 0 ;
84126 }
85127
128+ /**
129+ * 获取当前播放时间(秒)
130+ * 如果正在 Seek,返回目标时间以避免进度跳回
131+ */
86132 public get currentTime ( ) : number {
87- // 如果正在 Seek (无论是等待淡出,还是正在缓冲),强制返回目标时间以避免进度跳回
88133 if ( this . isInternalSeeking ) {
89134 return this . targetSeekTime ;
90135 }
91136 return this . audioElement . currentTime || 0 ;
92137 }
93138
139+ /** 获取是否暂停状态 */
94140 public get paused ( ) : boolean {
95141 return this . audioElement . paused ;
96142 }
97143
144+ /**
145+ * 获取错误码
146+ * @returns 错误码 (0: 无错误, 1: ABORTED, 2: NETWORK, 3: DECODE, 4: SRC_NOT_SUPPORTED)
147+ */
98148 public getErrorCode ( ) : number {
99149 if ( ! this . audioElement . error ) return 0 ;
100150 switch ( this . audioElement . error . code ) {
@@ -113,6 +163,7 @@ export class AudioElementPlayer extends BaseAudioPlayer {
113163
114164 /**
115165 * 监听原生 DOM 事件并转发为标准事件
166+ * 将 HTMLAudioElement 的事件转换为 BaseAudioPlayer 的统一事件格式
116167 */
117168 private bindInternalEvents ( ) {
118169 const events : AudioEventType [ ] = Object . values ( AUDIO_EVENTS ) ;
0 commit comments