@alexandr-web
Программист

Почему при повторном прослушивании песни, качество звука ухудшается?

У меня есть несколько аудио, которые могу слушать с помощью аудиоплеера. Для этого есть отдельный класс Audioplayer, в котором присутствуют 2 метода (на самом деле их больше, попытался сократить для большего понимания. Кому нужен весь код, то он находится здесь). Метод play начинает запускать проигрывание песни или ставить ее на паузу, в нем присутствует работа с AudioContext, а метод _setAudioBarsAnim просто отображает анимацию на холсте.

Проблема состоит в том, что в первый раз, когда слушаешь песню, ее звучание обычное, а потом, когда ставишь ее на паузу и опять проигрываешь, то она становится громче и хуже по звучанию.

Только-только разбираюсь с этим инструментом, поэтому не сильно разбираюсь во всех его аспектах. В чем может быть проблема?

class Audioplayer {
    constructor() {
        this.elAudio = document.createElement("audio");
        this.elCanvasAnim = document.querySelector(".audio-player__anim-canvas");

        this.play = false;
        this.animIsActive = false;
        this.audioAnalyser = null;
    }

    playAudio(audioSrc) {
        if (audioSrc !== this.elAudio.src) {
            this.elAudio.src = audioSrc;
            this.play = true;
        }

        const promise = fetch(audioSrc)
            .then((res) => res.blob())
            .then(() => this.elAudio.play());

        if (promise !== undefined) {
            promise
                .then(() => {
                    if (this.play) {
                        this.elAudio.play();

                        const ctx = new (window.AudioContext || window.webkitAudioContext)();
                        const source = ctx.createMediaElementSource(this.elAudio);

                        this.audioAnalyser = ctx.createAnalyser();

                        this.audioAnalyser.connect(ctx.destination);

                        source.connect(ctx.destination);
                        source.connect(this.audioAnalyser);

                        if (!this.animIsActive) {
                            this.animIsActive = true;

                            this._setAudioBarsAnim();
                        }
                    } else {
                        this.elAudio.pause();
                    }
                }).catch((err) => {
                    throw err;
                });
        }
    }

    _setAudioBarsAnim() {
        const canvas = this.elCanvasAnim;
        const ctx = canvas.getContext("2d");
        const countLinesOnArea = 250;
        const widthLine = canvas.offsetWidth / countLinesOnArea;

        canvas.width = canvas.offsetWidth;
        canvas.height = canvas.offsetHeight;

        const drawBars = () => {
            const fbcArray = new Uint8Array(this.audioAnalyser.frequencyBinCount);

            this.audioAnalyser.getByteFrequencyData(fbcArray);

            ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);

            fbcArray.slice(0, countLinesOnArea).forEach((n, i) => {
                const x = i * widthLine;
                const percent = Math.ceil((n / 255) * 100);
                const height = (percent * canvas.offsetHeight) / 100;

                ctx.fillStyle = "white";
                ctx.fillRect(x, canvas.offsetHeight - height, widthLine, height);
            });

            (window.requestAnimationFrame || window.webkitRequestAnimationFrame)(drawBars);
        };

        drawBars();
    }
}
  • Вопрос задан
  • 157 просмотров
Решения вопроса 1
@alexandr-web Автор вопроса
Программист
Как я понял проблема была в том, что я несколько раз инициализировал AudioContext, поэтому в этот раз я сделал проверку на то, что он уже инициализирован.

Поместил всю работу с AudioContext в отдельный метод для разделения логики.

Сделал проверку состояния AudioContext (кому интересно, то вам сюда)

Также добавил gainNode, который потом изменяю, если меняется громкость аудио (эта работа происходит уже в другом методе, здесь его нет, кому интересно, то вам сюда)

Метод _setAudioBarsAnim не изменял.

В конечном итоге получилось это:

class Audioplayer {
    constructor() {
        this.elAudio = document.createElement("audio");
        this.elCanvasAnim = document.querySelector(".audio-player__anim-canvas");

        this.play = false;
        this.animIsActive = false;
        this.audioContext = null;
        this.gainNode = null;
        this.audioAnalyser = null;
    }

    playAudio(audioSrc) {
        if (audioSrc !== this.elAudio.src) {
            this.elAudio.src = audioSrc;
            this.play = true;
        }

        const promise = this.elAudio.play();

        if (promise instanceof Promise) {
            promise
                .then(() => {
                    if (this.audioContext instanceof AudioContext && this.audioContext.state === "suspended") {
                        this.audioContext.resume();
                    }

                    if (this.play) {
                        this.elAudio.play();

                        this._initAudioContext();

                        if (!this.animIsActive) {
                            this.animIsActive = true;

                            this._setAudioBarsAnim();
                        }
                    } else {
                        this.elAudio.pause();
                    }
                }).catch((err) => {
                    throw err;
                });
        }
    }

    _initAudioContext() {
        if (this.audioContext) {
            return;
        }

        this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
        this.gainNode = this.audioContext.createGain();
        this.audioAnalyser = this.audioContext.createAnalyser();

        const source = this.audioContext.createMediaElementSource(this.elAudio);

        this.audioAnalyser.fftSize = 2048;

        source
            .connect(this.gainNode)
            .connect(this.audioAnalyser)
            .connect(this.audioContext.destination);
    }

    _setAudioBarsAnim() {
        const canvas = this.elCanvasAnim;
        const ctx = canvas.getContext("2d");
        const countLinesOnArea = 250;
        const widthLine = canvas.offsetWidth / countLinesOnArea;

        canvas.width = canvas.offsetWidth;
        canvas.height = canvas.offsetHeight;

        const drawBars = () => {
            const fbcArray = new Uint8Array(this.audioAnalyser.frequencyBinCount);

            this.audioAnalyser.getByteFrequencyData(fbcArray);

            ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);

            fbcArray.slice(0, countLinesOnArea).forEach((n, i) => {
                const x = i * widthLine;
                const percent = Math.ceil((n / 255) * 100);
                const height = (percent * canvas.offsetHeight) / 100;

                ctx.fillStyle = "white";
                ctx.fillRect(x, canvas.offsetHeight - height, widthLine, height);
            });

            (window.requestAnimationFrame || window.webkitRequestAnimationFrame)(drawBars);
        };

        drawBars();
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы