#javascript #audio #buffer #html5-audio #audiocontext
#javascript #Аудио #буфер #html5-аудио #audiocontext
Вопрос:
Я хочу получить аудиобуфер во время разговора, я использовал этот метод для его обнаружения, но я получаю сообщение, что этот метод onaudioprocess устарел и не запускается, есть ли для него какая-либо альтернатива с примером.
audioContext = new AudioContext({ sampleRate: 16000 });
scriptNode = (audioContext.createScriptProcessor || audioContext.createJavaScriptNode).call(audioContext, 1024, 1, 1);
scriptNode.onaudioprocess = function (audioEvent) {
if (recording) {
input = audioEvent.inputBuffer.getChannelData(0);
// convert float audio data to 16-bit PCM
var buffer = new ArrayBuffer(input.length * 2);
var output = new DataView(buffer);
for (var i = 0, offset = 0; i < input.length; i , offset = 2) {
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
}
ws.send(buffer);
}
};
Комментарии:
1. Как вы записываете свой звук? Вы собираетесь записывать звук с вашего микрофона и отправлять его в веб-сокет?
2. @EmielZuurbier да, это с моего микрофона на веб-сокет.
3. Не могли бы вы дать некоторые отзывы об ответе ниже?
Ответ №1:
С помощью API MediaStream Recording и MediaDevices.getUserMedia()
метода вы можете передавать аудио с вашего микрофона и передавать его на диктофон. Затем рекордер может отправлять Blob
объекты через WebSockets всякий ondataavailable
раз, когда событие срабатывает на рекордере.
Приведенная ниже функция создает поток и передает его MediaRecorder
экземпляру. Этот экземпляр запишет ваш звук с микрофона и сможет отправить его на ваш веб-сокет. Экземпляр MediaRecorder
возвращается для управления записью.
async function streamMicrophoneAudioToSocket(ws) {
let stream;
const constraints = { video: false, audio: true };
try {
stream = await navigator.mediaDevices.getUserMedia(constraints);
} catch (error) {
throw new Error(`
MediaDevices.getUserMedia() threw an error.
Stream did not open.
${error.name} -
${error.message}
`);
}
const recorder = new MediaRecorder(stream);
recorder.addEventListener('dataavailable', ({ data }) => {
ws.send(data);
});
recorder.start();
return recorder;
});
Таким образом, вы также можете остановить запись, если захотите, вызвав stop()
метод на диктофоне.
(async () => {
const ws = new WebSocket('ws://yoururl.com');
const recorder = await streamMicrophoneAudioToSocket(ws);
document.addEventListener('click', event => {
recorder.stop();
});
}());
Ответ №2:
Примечание: Хотя мой предыдущий ответ помог некоторым людям, он не предоставил альтернативы устаревшему onaudioprocess
интерфейсу event и ScriptProcessorNode. Этот ответ должен предоставить альтернативу вопросу OP.
Ответом должно быть использование аудиообработок, позволяющих нам создавать пользовательские узлы обработки звука, которые могут быть реализованы как обычные AudioNode
.
Интерфейс AudioWorkletNode API Web Audio представляет базовый класс для определяемого пользователем AudioNode, который может быть подключен к графу маршрутизации звука вместе с другими узлами. Он имеет связанный AudioWorkletProcessor, который выполняет фактическую обработку звука в потоке веб-рендеринга звука.
Он работает путем расширения AudioWorkletProcessor
класса и предоставления обязательного process
метода. process
Метод предоставляет inputs
, outputs
и parameters
устанавливается в получателе статических параметровdescriptors .
Здесь вы можете вставить ту же логику, что и в onaudioprocess
обратном вызове. Но вам нужно внести некоторые изменения, чтобы работать должным образом.
Одним из недостатков использования рабочих столов является то, что вы должны включить этот скрипт в виде файла из интерфейса worklets. Это означает, что любые зависимости, такие как ws
переменная, необходимо вводить на более позднем этапе. Мы можем расширить класс, чтобы добавить любые значения или зависимости к экземпляру worklet.
Примечание: process
необходимо вернуть логическое значение, чтобы браузер знал, следует ли поддерживать аудиоузел в рабочем состоянии или нет.
registerProcessor('buffer-detector', class extends AudioWorkletProcessor {
process (inputs, outputs, parameters) {
if (this.socket === null) {
return false;
}
if (this._isRecording === true) {
const [input] = inputs;
const buffer = new ArrayBuffer(input.length * 2);
const output = new DataView(buffer);
for (let i = 0, offset = 0; i < input.length; i , offset = 2) {
const s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
}
this.socket.send(buffer);
}
return true;
}
static get parameterDescriptors() {
return [{
name: 'Buffer Detector',
}]
}
constructor() {
super();
this._socket = null;
this._isRecording = false;
}
get socket() {
return this._socket;
}
set socket(value) {
if (value instanceof WebSocket) {
this._socket = value;
}
}
get recording() {
return this._isRecording;
}
set recording(value) {
if ('boolean' === typeof value) {
this._isRecording = value;
}
}
});
Теперь все, что нам нужно сделать, это включить рабочий лист в ваш скрипт и создать экземпляр узла. Мы можем сделать это с addModule
помощью метода, который существует в этом BaseAudioContext.audioWorklet
свойстве.
Важно: Добавление модуля работает только в защищенных (HTTPS) контекстах.
Когда модуль будет успешно добавлен, создайте новый узел с AudioWorkletNode
помощью конструктора. Назначьте экземпляр WebSocket, установите флаг записи, и все готово.
const ws = new WebSocket('ws://...');
const audioContext = new AudioContext();
const source = new MediaStreamAudioSourceNode(audioContext, {
mediaStream: stream // Your stream here.
});
// In an async context
await audioContext.audioWorklet.addModule('buffer-detector.js');
// Create our custom node.
const bufferDetectorNode = new AudioWorkletNode(audioContext, 'buffer-detector');
// Assign the socket and the recording state.
bufferDetectorNode.socket = ws;
bufferDetectorNode.recording = true;
// Connect the node.
source.connect(bufferDetectorNode);
Комментарии:
1. Привет, @emiel-zuurbier, как настройка
bufferDetectorNode.recording
передает значениеthis._isRecording
. Я тестировал этот пример, и, похоже, он не работает.2. @EX0MAK3R Это была опечатка с моей стороны.
recording
Установщик не был правильно определен в классе. Попробуйте еще раз с измененным кодом выше.