Многие браузеры теперь имеют возможность доступа к видео- и аудиовходу пользователя. Однако, в зависимости от браузера, это может быть полностью динамический и встроенный опыт или он может быть делегирован другому приложению на устройстве пользователя.
Начните с простого и постепенного
Самый простой способ — просто попросить пользователя предоставить предварительно записанный файл. Сделайте это, создав простой элемент ввода файла и добавив фильтр accept
, который указывает, что мы можем принимать только аудиофайлы, и атрибут capture
, который указывает, что мы хотим получить его напрямую с микрофона.
<input type="file" accept="audio/*" capture />
Этот метод работает на всех платформах. На рабочем столе он предложит пользователю загрузить файл из файловой системы (игнорируя атрибут capture
). В Safari на iOS он откроет приложение микрофона, позволяя вам записывать звук, а затем отправлять его обратно на веб-страницу; на Android он предоставит пользователю выбор, в каком приложении записывать звук, прежде чем отправлять его обратно на веб-страницу.
После того, как пользователь закончил запись и вернулся на сайт, вам нужно как-то получить доступ к данным файла. Вы можете получить быстрый доступ, прикрепив событие onchange
к элементу input, а затем прочитав свойство files
объекта event.
<input type="file" accept="audio/*" capture id="recorder" />
<audio id="player" controls></audio>
<script>
const recorder = document.getElementById('recorder');
const player = document.getElementById('player');
recorder.addEventListener('change', function (e) {
const file = e.target.files[0];
const url = URL.createObjectURL(file);
// Do something with the audio file.
player.src = url;
});
</script>
</audio>
Получив доступ к файлу, вы можете делать с ним все, что захотите. Например, вы можете:
- Прикрепите его непосредственно к элементу
<audio>
, чтобы вы могли его воспроизвести. - Загрузите его на устройство пользователя.
- Загрузите его на сервер, прикрепив к
XMLHttpRequest
- Пропустите его через API веб-аудио и примените к нему фильтры.
Хотя использование метода элемента ввода для получения доступа к аудиоданным повсеместно, это наименее привлекательный вариант. Мы действительно хотим получить доступ к микрофону и предоставить приятный опыт непосредственно на странице.
Доступ к микрофону в интерактивном режиме
Современные браузеры могут иметь прямую линию к микрофону, что позволяет нам создавать интерфейсы, полностью интегрированные с веб-страницей, и пользователю никогда не придется покидать браузер.
Получить доступ к микрофону
Мы можем напрямую получить доступ к микрофону, используя API в спецификации WebRTC, называемый getUserMedia()
. getUserMedia()
предложит пользователю доступ к подключенным микрофонам и камерам.
В случае успеха API вернет Stream
, содержащий данные с камеры или микрофона, и мы затем сможем прикрепить его к элементу <audio>
, прикрепить к потоку WebRTC, прикрепить к веб-аудио AudioContext
или сохранить с помощью API MediaRecorder
.
Чтобы получить данные с микрофона, мы просто устанавливаем audio: true
в объекте ограничений, который передается в API getUserMedia()
.
<audio id="player" controls></audio>
<script>
const player = document.getElementById('player');
const handleSuccess = function (stream) {
if (window.URL) {
player.srcObject = stream;
} else {
player.src = stream;
}
};
navigator.mediaDevices
.getUserMedia({audio: true, video: false})
.then(handleSuccess);
</script>
Если вы хотите выбрать определенный микрофон, вы можете сначала перечислить доступные микрофоны.
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices = devices.filter((d) => d.kind === 'audioinput');
});
Затем вы можете передать deviceId
, который хотите использовать, при вызове getUserMedia
.
navigator.mediaDevices.getUserMedia({
audio: {
deviceId: devices[0].deviceId,
},
});
Само по себе это не так уж и полезно. Все, что мы можем сделать, это взять аудиоданные и воспроизвести их.
Доступ к необработанным данным с микрофона
Чтобы получить доступ к необработанным данным с микрофона, нам нужно взять поток, созданный getUserMedia()
, а затем использовать Web Audio API для обработки данных. Web Audio API — это простой API, который берет входные источники и подключает эти источники к узлам, которые могут обрабатывать аудиоданные (настраивать усиление и т. д.) и, в конечном итоге, к динамику, чтобы пользователь мог его слышать.
Один из узлов, который вы можете подключить, — это AudioWorkletNode
. Этот узел предоставляет вам низкоуровневые возможности для пользовательской обработки звука. Фактическая обработка звука происходит в методе обратного вызова process()
в AudioWorkletProcessor
. Вызовите эту функцию для подачи входов и параметров и извлечения выходов.
Чтобы узнать больше, ознакомьтесь с Enter Audio Worklet .
<script>
const handleSuccess = async function(stream) {
const context = new AudioContext();
const source = context.createMediaStreamSource(stream);
await context.audioWorklet.addModule("processor.js");
const worklet = new AudioWorkletNode(context, "worklet-processor");
source.connect(worklet);
worklet.connect(context.destination);
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(handleSuccess);
</script>
// processor.js
class WorkletProcessor extends AudioWorkletProcessor {
process(inputs, outputs, parameters) {
// Do something with the data, e.g. convert it to WAV
console.log(inputs);
return true;
}
}
registerProcessor("worklet-processor", WorkletProcessor);
Данные, хранящиеся в буферах, представляют собой необработанные данные с микрофона, и у вас есть ряд вариантов того, что вы можете сделать с этими данными:
- Загрузите его прямо на сервер
- Хранить локально
- Конвертируйте его в специальный формат файла, например WAV, а затем сохраните на своих серверах или локально.
Сохраните данные с микрофона
Самый простой способ сохранить данные с микрофона — использовать API MediaRecorder
.
API MediaRecorder
принимает поток, созданный getUserMedia
, а затем постепенно сохраняет данные, находящиеся в потоке, в выбранном вами месте назначения.
<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
const downloadLink = document.getElementById('download');
const stopButton = document.getElementById('stop');
const handleSuccess = function(stream) {
const options = {mimeType: 'audio/webm'};
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.addEventListener('dataavailable', function(e) {
if (e.data.size > 0) recordedChunks.push(e.data);
});
mediaRecorder.addEventListener('stop', function() {
downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
downloadLink.download = 'acetest.wav';
});
stopButton.addEventListener('click', function() {
mediaRecorder.stop();
});
mediaRecorder.start();
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(handleSuccess);
</script>
В нашем случае мы сохраняем данные непосредственно в массив, который впоследствии можно преобразовать в Blob
, который затем можно использовать для сохранения данных на нашем веб-сервере или непосредственно в хранилище на устройстве пользователя.
Попросите разрешения использовать микрофон ответственно.
Если пользователь ранее не предоставил вашему сайту доступ к микрофону, то в тот момент, когда вы вызовете getUserMedia
, браузер предложит пользователю предоставить вашему сайту разрешение на использование микрофона.
Пользователи ненавидят, когда им предлагают доступ к мощным устройствам на их компьютере, и они часто блокируют запрос или игнорируют его, если не понимают контекста, в котором был создан запрос. Лучше всего запрашивать доступ к микрофону только тогда, когда это необходимо в первый раз. После того, как пользователь предоставил доступ, его больше не будут спрашивать, однако, если он отклонит доступ, вы не сможете снова попросить пользователя предоставить разрешение.
Используйте API разрешений, чтобы проверить, есть ли у вас уже доступ
API getUserMedia
не предоставляет вам никаких сведений о том, есть ли у вас уже доступ к микрофону. Это создает для вас проблему: чтобы предоставить хороший пользовательский интерфейс, чтобы заставить пользователя предоставить вам доступ к микрофону, вам нужно запросить доступ к микрофону.
Это можно решить в некоторых браузерах с помощью API разрешений. API navigator.permission
позволяет вам запрашивать состояние возможности доступа к определенным API без необходимости повторного запроса.
Чтобы узнать, есть ли у вас доступ к микрофону пользователя, вы можете передать {name: 'microphone'}
в метод запроса, и он вернет либо:
-
granted
— пользователь ранее предоставил вам доступ к микрофону; -
prompt
— пользователь не предоставил вам доступ и получит запрос при вызовеgetUserMedia
; -
denied
— система или пользователь явно заблокировали доступ к микрофону, и вы не сможете получить к нему доступ.
И теперь вы можете быстро проверить, нужно ли вам изменить пользовательский интерфейс, чтобы приспособить его к действиям, которые необходимо выполнить пользователю.
navigator.permissions.query({name: 'microphone'}).then(function (result) {
if (result.state == 'granted') {
} else if (result.state == 'prompt') {
} else if (result.state == 'denied') {
}
result.onchange = function () {};
});