mp3.js 4.19 KB
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
    }

    // 调整进度
    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)
        }
    }
}