1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
export class musicPlayer {
constructor(audioEl) {
this.audioEl = audioEl
// 音频上下文
this.audioCtx = null
// 音源
this.audioSource = null
// 时长
this.duration = 0
// 当前播放时间
this.currentTime = 0
// 播放进度
this.progress = 0
// 声音
this.volume = 20
// AnalyserNode 接口表示了一个可以提供实时频域和时域分析信息的节点
this.analyser = null
// 音量节点
this.gainNode = null
// 缓冲进度
this.timeRange = null
// 是否播放中
this.playing = false
}
init() {
// 音频上下文
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)()
if (!this.audioCtx) {
throw new Error('audioCtx is null')
}
// 获取音频数据的节点
this.analyser = this.audioCtx.createAnalyser()
// 音量节点
this.gainNode = this.audioCtx.createGain()
this.gainNode.gain.value = 1
this.audioEl.volume = this.volume / 100
// 从<audio>或<video>元素生成的音频源
this.audioSource = this.audioCtx.createMediaElementSource(this.audioEl)
this.audioEl.ontimeupdate = () => {
this.currentTime = this.audioEl.currentTime
if (this.progress >= this.duration) return
this.progress = this.currentTime
}
this.audioEl.onloadedmetadata = () => {
// 时长
this.duration = this.audioEl.duration
console.log('onloadedmetadata', this.duration)
}
this.audioEl.onprogress = () => {
// 当浏览器正在下载音频/视频时
this.timeRange = this.audioEl.buffered
if (this.timeRange && this.timeRange.length > 0) {
console.log(
'buffered',
this.audioEl.buffered.start(0),
this.audioEl.buffered.end(0)
)
}
}
this.audioEl.oncanplay = () => {
console.log('可播放')
setTimeout(() => {
this.getAudioSource()
}, 100)
}
this.audioEl.onerror = (e) => {
console.error('加载出现错误', e)
this.onerror(e)
}
}
onerror() {}
onended() {}
setSrc(src) {
this.progress = 0
this.audioEl.src = src
this.audioEl.load()
}
// 调整进度
progressChange(value) {
this.audioEl.currentTime = value
}
// 调整音量
volumeChange(value) {
this.volume = value
this.audioEl.volume = this.volume / 100
}
// 音频波形处理
getAudioSource() {
// 节点链接到音源
this.audioSource.connect(this.analyser)
// 链接音量节点
this.analyser.connect(this.gainNode)
this.gainNode.connect(this.audioCtx.destination)
return
// 使用快速傅立叶变换(Fast Fourier Transform (FFT) )来捕获音频数据
// this.analyser.fftSize = 2048
this.analyser.fftSize = 256
let bufferLength = this.analyser.frequencyBinCount
let dataArray = new Uint8Array(bufferLength)
let c = this.boardEl
let canvasWidth = c.width
let canvasHeight = c.height
let ctx = c.getContext('2d')
let left = this
ctx.strokeStyle = 'rgba(81,167,255, .5)'
drawBar()
function drawBar() {
// 波形绘制
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
let barWidth = (canvasWidth / bufferLength) * 1
let barHeight = 0
let x = 0
left.analyser.getByteFrequencyData(dataArray)
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i] / 2
let tempColor = barHeight * 3 > 255 ? 255 : barHeight * 3
ctx.fillStyle = 'rgba(' + tempColor + ', 160, 255, .5)'
ctx.fillRect(x, canvasHeight - barHeight / 2, barWidth, barHeight)
x += barWidth + 0.1
}
requestAnimationFrame(drawBar)
}
}
}