Многие браузеры теперь имеют возможность доступа к видео- и аудиовходу пользователя. Однако, в зависимости от браузера, это может быть полностью динамический и встроенный интерфейс или его можно делегировать другому приложению на устройстве пользователя.
Начните с простого и постепенно
Самый простой способ — просто попросить пользователя предоставить заранее записанный файл. Сделайте это, создав простой элемент ввода файла и добавив фильтр accept
, который указывает, что мы можем принимать только аудиофайлы, и атрибут capture
, который указывает, что мы хотим получить его непосредственно с микрофона.
<input type="file" accept="audio/*" capture />
Этот метод работает на всех платформах. На рабочем столе пользователю будет предложено загрузить файл из файловой системы (игнорируя атрибут capture
). В Safari на iOS откроется приложение микрофона, позволяющее записать звук, а затем отправить его обратно на веб-страницу; на Android пользователю предоставляется выбор, какое приложение использовать для записи звука перед отправкой его обратно на веб-страницу.
Как только пользователь завершит запись и вернется на веб-сайт, вам нужно каким-то образом получить данные файла. Вы можете получить быстрый доступ, прикрепив событие onchange
к элементу ввода, а затем прочитав свойство files
объекта события.
<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, присоединить его к Web Audio 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()
, а затем использовать API веб-аудио для обработки данных. API веб-аудио — это простой API, который принимает источники входного сигнала и подключает эти источники к узлам, которые могут обрабатывать аудиоданные (регулировать усиление и т. д.), и, в конечном итоге, к динамику, чтобы пользователь мог его слышать.
Одним из узлов, который вы можете подключить, является AudioWorkletNode
. Этот узел предоставляет вам низкоуровневую возможность пользовательской обработки звука. Фактическая обработка звука происходит в методе обратного вызоваprocess 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 () {};
});