我正在尝试使用soundtouch.js保留浏览器中音频的音调,但是在事件期间音频上下文无法识别实例属性时遇到了一些麻烦。
这是我的基本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
预先感谢您的帮助。
答案 0 :(得分:0)
解决了。
问题在于,在组件的setBuffer()
期间,音频的arrayBuffer被放置在useEffect
中,并且具有空的依赖项数组(componentDidMount
样式)。因此,当实例化的播放引擎上发生事件时,它们就像仅存在useEffect
之前的实例属性一样。通过在我的输入控件的useEffect
内定义事件侦听器来解决此问题。