Javascript类无法识别新的实例属性

时间:2020-05-18 01:06:24

标签: javascript reactjs class web-audio-api

我正在尝试使用soundtouch.js保留浏览器中音频的音调,但是在事件期间音频上下文无法识别实例属性时遇到了一些麻烦。

这是我的基本React UI:

React UI

App.js

这些是我的React组件中的函数,用于处理输入范围并获取Playback实例:

const playbackEngine = new PlaybackEngine({
            emitter: emitter,
            pitch: pitch,
            tempo: tempo,
    });

const handlePitchChange = (event) => {
        pitch = event.target.value;
        document.getElementById('pitch-value').innerHTML = `Pitch (${pitch}x)`;
        emitter.emit('pitch');
        playbackEngine.pitch = event.target.value;
}

const handleTempoChange = (event) => {
    tempo = event.target.value;
    document.getElementById('tempo-value').innerHTML = `Tempo (${tempo}x)`;
    emitter.emit('tempo');
    playbackEngine.tempo = event.target.value;
}

const handleSeek = (event) => {
    percent = parseFloat(event.target.value);
    document.getElementById('seek-value').value = percent;
    playbackEngine.seekPercent(percent);
    playbackEngine.play();
}

PlaybackEngine.js

const {SimpleFilter, SoundTouch} = require('./soundtouch');

const BUFFER_SIZE = 4096;

export default class PlaybackEngine {
    constructor({emitter, pitch, tempo}) {
        this.emitter = emitter;
        this.context = new AudioContext();
        this.scriptProcessor = this.context.createScriptProcessor(BUFFER_SIZE, 2, 2);

        this.scriptProcessor.onaudioprocess = e => {
            const l = e.outputBuffer.getChannelData(0);
            const r = e.outputBuffer.getChannelData(1);
            const framesExtracted = this.simpleFilter.extract(this.samples, BUFFER_SIZE);
            if (framesExtracted === 0) {
                this.emitter.emit('stop');
            }
            for (let i = 0; i < framesExtracted; i++) {
                l[i] = this.samples[i * 2];
                r[i] = this.samples[i * 2 + 1];
            }
        };

        this.soundTouch = new SoundTouch();
        this.soundTouch.pitch = pitch;
        this.soundTouch.tempo = tempo;

        this.duration = undefined;
    }

    get pitch() {
        return this.soundTouch.pitch;
    }
    set pitch(pitch) {
        this.soundTouch.pitch = pitch;
    }

    get tempo() {
        return this.soundTouch.tempo;
    }
    set tempo(tempo) {
        this.soundTouch.tempo = tempo;
    }

    decodeAudioData(data) {
        return this.context.decodeAudioData(data);
    }

    setBuffer(buffer) {
        const bufferSource = this.context.createBufferSource();
        bufferSource.buffer = buffer;

        this.samples = new Float32Array(BUFFER_SIZE * 2);
        this.source = {
            extract: (target, numFrames, position) => {
                this.emitter.emit('time', (position / this.context.sampleRate));
                const l = buffer.getChannelData(0);
                const r = buffer.getChannelData(1);
                for (let i = 0; i < numFrames; i++) {
                    target[i * 2] = l[i + position];
                    target[i * 2 + 1] = r[i + position];
                }
                return Math.min(numFrames, l.length - position);
            },
        };
        this.simpleFilter = new SimpleFilter(this.source, this.soundTouch);
        this.emitter.emit('buffered', this.simpleFilter);

        this.duration = buffer.duration;
        this.emitter.emit('duration', buffer.duration);
    }

    play() {
        this.scriptProcessor.connect(this.context.destination);
    }

    pause() {
        this.scriptProcessor.disconnect(this.context.destination);
    }

    seekPercent(percent) {
        if (this.simpleFilter !== undefined) {
            this.simpleFilter.sourcePosition = Math.round(
                percent / 100 * this.duration * this.context.sampleRate
            );
        }
    }
}

音频文件就绪后,

App.js调用playbackEngine.setBuffer(),这会将this.simpleFilter添加为实例属性。音频可以正确播放,但是当我在handleSeek函数中调用seekPercent()时,它是未定义的。因此,onaudioprocess由于此而崩溃,并显示以下错误:

Uncaught TypeError: Cannot read property 'extract' of undefined
    at ScriptProcessorNode.PitchEngine.scriptProcessor.onaudioprocess

预先感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

解决了。

问题在于,在组件的setBuffer()期间,音频的arrayBuffer被放置在useEffect中,并且具有空的依赖项数组(componentDidMount样式)。因此,当实例化的播放引擎上发生事件时,它们就像仅存在useEffect之前的实例属性一样。通过在我的输入控件的useEffect内定义事件侦听器来解决此问题。