- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有从服务器到客户端的音频数据流。它以 Node.js 缓冲区(Uint8Array)开始,然后通过 port.postMessage() 发送到 AudioWorkletProcessor,在那里它被转换为 Float32Array 并存储在 this.data 中。我花了几个小时试图将输出设置为包含在 Float32Array 中的音频数据。记录 Float32Array 预处理显示准确的数据,但在处理过程中记录它表明它在发布新消息时没有变化。这可能是我低级音频编程知识的差距。
当数据到达客户端时,调用以下函数:
process = (data) => {
this.node.port.postMessage(data)
}
顺便说一句,(你可以告诉我)也许我应该使用参数描述符而不是 postMessage?无论如何,这是我的 AudioWorkletProcessor:
class BypassProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.isPlaying = true;
this.port.onmessage = this.onmessage.bind(this)
}
static get parameterDescriptors() {
return [{ // Maybe we should use parameters. This is not utilized at present.
name: 'stream',
defaultValue: 0.707
}];
}
convertBlock = (incomingData) => { // incoming data is a UInt8Array
let i, l = incomingData.length;
let outputData = new Float32Array(incomingData.length);
for (i = 0; i < l; i++) {
outputData[i] = (incomingData[i] - 128) / 128.0;
}
return outputData;
}
onmessage(event) {
const { data } = event;
let ui8 = new Uint8Array(data);
this.data = this.convertBlock(ui8)
}
process(inputs, outputs) {
const input = inputs[0];
const output = outputs[0];
if (this.data) {
for (let channel = 0; channel < output.length; ++channel) {
const inputChannel = input[channel]
const outputChannel = output[channel]
for (let i = 0; i < inputChannel.length; ++i) {
outputChannel[i] = this.data[i]
}
}
}
return true;
}
}
registerProcessor('bypass-processor', BypassProcessor);
我怎样才能简单地将 AudioWorkletProcessor 的输出设置为通过的数据?
最佳答案
AudioWorkletProcessor 只处理每 128 个字节,因此您需要管理自己的缓冲区以确保 AudioWorklet
也是如此,可能通过添加 FIFO 来实现。我使用在 WebAssembly 中实现的 RingBuffer(FIFO) 解决了类似的问题,在我的例子中,我收到了一个 160 字节的缓冲区。
看看我的 AudioWorkletProcessor 实现
import Module from './buffer-kernel.wasmodule.js';
import { HeapAudioBuffer, RingBuffer, ALAW_TO_LINEAR } from './audio-helper.js';
class SpeakerWorkletProcessor extends AudioWorkletProcessor {
constructor(options) {
super();
this.payload = null;
this.bufferSize = options.processorOptions.bufferSize; // Getting buffer size from options
this.channelCount = options.processorOptions.channelCount;
this.inputRingBuffer = new RingBuffer(this.bufferSize, this.channelCount);
this.outputRingBuffer = new RingBuffer(this.bufferSize, this.channelCount);
this.heapInputBuffer = new HeapAudioBuffer(Module, this.bufferSize, this.channelCount);
this.heapOutputBuffer = new HeapAudioBuffer(Module, this.bufferSize, this.channelCount);
this.kernel = new Module.VariableBufferKernel(this.bufferSize);
this.port.onmessage = this.onmessage.bind(this);
}
alawToLinear(incomingData) {
const outputData = new Float32Array(incomingData.length);
for (let i = 0; i < incomingData.length; i++) {
outputData[i] = (ALAW_TO_LINEAR[incomingData[i]] * 1.0) / 32768;
}
return outputData;
}
onmessage(event) {
const { data } = event;
if (data) {
this.payload = this.alawToLinear(new Uint8Array(data)); //Receiving data from my Socket listener and in my case converting PCM alaw to linear
} else {
this.payload = null;
}
}
process(inputs, outputs) {
const output = outputs[0];
if (this.payload) {
this.inputRingBuffer.push([this.payload]); // Pushing data from my Socket
if (this.inputRingBuffer.framesAvailable >= this.bufferSize) { // if the input data size hits the buffer size, so I can "outputted"
this.inputRingBuffer.pull(this.heapInputBuffer.getChannelData());
this.kernel.process(
this.heapInputBuffer.getHeapAddress(),
this.heapOutputBuffer.getHeapAddress(),
this.channelCount,
);
this.outputRingBuffer.push(this.heapOutputBuffer.getChannelData());
}
this.outputRingBuffer.pull(output); // Retriving data from FIFO and putting our output
}
return true;
}
}
registerProcessor(`speaker-worklet-processor`, SpeakerWorkletProcessor);
查看 AudioContext 和 AudioWorklet 实例
this.audioContext = new AudioContext({
latencyHint: 'interactive',
sampleRate: this.sampleRate,
sinkId: audioinput || "default"
});
this.audioBuffer = this.audioContext.createBuffer(1, this.audioSize, this.sampleRate);
this.audioSource = this.audioContext.createBufferSource();
this.audioSource.buffer = this.audioBuffer;
this.audioSource.loop = true;
this.audioContext.audioWorklet
.addModule('workers/speaker-worklet-processor.js')
.then(() => {
this.speakerWorklet = new AudioWorkletNode(
this.audioContext,
'speaker-worklet-processor',
{
channelCount: 1,
processorOptions: {
bufferSize: 160, //Here I'm passing the size of my output, I'm just saying to RingBuffer what size I need
channelCount: 1,
},
},
);
this.audioSource.connect(this.speakerWorklet).connect(this.audioContext.destination);
}).catch((err)=>{
console.log("Receiver ", err);
})
看看我如何从 Socket 接收数据并将数据发送到 audioWorklet
protected onMessage(e: any): void { //My Socket message listener
const { data:serverData } = e;
const socketId = e.socketId;
if (this.audioWalking && this.ws && !this.ws.isPaused() && this.ws.info.socketId === socketId) {
const buffer = arrayBufferToBuffer(serverData);
const rtp = RTPParser.parseRtpPacket(buffer);
const sharedPayload = new Uint8Array(new SharedArrayBuffer(rtp.payload.length)); //sharing javascript buffer memory between main thread and worklet thread
sharedPayload.set(rtp.payload, 0);
this.speakerWorklet.port.postMessage(sharedPayload); //Sending data to worklet
}
}
为了帮助人们,我把这个解决方案的重要部分放在了 Github 上
我按照这个例子,它解释了 RingBuffer 是如何工作的
关于javascript - AudioWorklet - 将输出设置为 Float32Array 以流式传输实时音频?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57583266/
由于旧的 Webaudio 脚本处理器自 2014 年以来已被弃用,而 Audioworklets 出现在 Chrome 64 中,我决定尝试一下。但是我在移植我的应用程序时遇到了困难。我将从一个不错
我有兴趣在 Javscript 中连续读取麦克风音量。 StackOverflow 上的许多现有解决方案(参见 here 、 here 和 here )利用 BaseAudioContext.crea
我有从服务器到客户端的音频数据流。它以 Node.js 缓冲区(Uint8Array)开始,然后通过 port.postMessage() 发送到 AudioWorkletProcessor,在那里它
我正在开发一个 WebAudio 应用程序,它需要 AudioWorklets 并且需要来自许多不同脚本的函数以用于 process() 函数。因此,我尝试使用 import 命令在 processo
我正在记录来自用户的麦克风输入并进行处理。问题是我使用scriptProcessorNode来处理数据,但是here它说它已被弃用并替换为 AudioWorklet问题是没有明确的方法可以用 Audi
我正在尝试使用 new AudioContext(). audioWorklet.addModule(URL.createObjectURL(blob))加载,但是控制台提示错误:跨源,如果是file
我正在尝试利用 FFMPEG 拥有的大量音频过滤器,看看是否可以直接在自定义 AudioWorklet 中使用它们,这样我就不必为每个过滤器重新发明轮子。我遇到的一个选择是将 AVFilter 库转换
我正在尝试在我的 Electron 应用程序中使用 AudioWorklet 进行计量等,当在开发模式下执行时工作正常,其中工作集由快速开发服务器(如 http://localhost:3000/pr
在开发模式下启动应用程序时没有问题,在构建时出现错误... private record(mediaStream: MediaStream): void { this.audioContext
我正在尝试使用 vue-cli 创建一个使用 AudioWorklet 的 Web 应用程序,但是在尝试访问我的 AudioWorkletNode 的任何属性(如端口或 channel 计数等)时我遇
我是一名优秀的程序员,十分优秀!