我正在尝试使用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 {
this.simpleFilter;
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 calls playbackEngine.setBuffer()
once the audio file is ready, which adds this.simpleFilter as an instance property. The audio plays correctly, but when I call seekPercent()
in my handleSeek function, it is undefined. Consequently, the onaudioprocess
crashes because of this, with the following error:
Uncaught TypeError: Cannot read property 'extract' of undefined
at ScriptProcessorNode.PitchEngine.scriptProcessor.onaudioprocess
先谢谢您的帮助。