Запись звука JS c микрофона или голосовые комментарии

в 11:36, , рубрики: javascript, php, голосовые сообщения, запись звука

Запись звука JS c микрофона или голосовые комментарии

Не давно, при разработке одного корпоративного веб-приложения, заказчик пожелал иметь возможность оставлять голосовые комментарии. Ранее мне не приходил сталкиваться с созданием медиаконтента и я с интересом приступил к изучению данной темы.

В сети предоставлено достаточно справочной информации по теме создания и обработки такого рода контента, однако я не нашел простого, полноценно работающего примера. После реализации поставленной задачи заказчиком, решил опубликовать максимально упрощенный пример записи и сохранения голосового комментария и написать статью. Возможно, этот материал будет кому-то полезен и поможет в изучении.

Постановка задачи

Поставим себе задачу разработать мини приложение, работающее в браузере, которое позволит записать голосовой комментарий, отправить запись на сервер, сервер сохранит запись, в случае успеха вернет ответ с именем созданного файла и отобразит объект на странице для того чтоб запись можно было прослушать.

Запись звука в браузере

Запись звука было решено реализовать с помощью интерфейса веб API MediaStream Recording. Для записи воспользуемся интерфейсом MediaRecorder(). Но прежде создадим интерфейс. Пусть у нас будет index.html содержащий лишь самые основные теги, а в теле тега подключим файл с нашим будущим JavaScript-ом voice.js:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>Voice comments</title>
</head>
<body>
    <script src="voice.js"></script>
</body>
</html>

Создадим файл voice.js, определим в нем константу URL которая будет содержать ссылку на скрипт принимающий записанный звук. Далее создадим кнопки Start и Stop для начала и остановки записи звука, а так же блок div в котором будут отображаться сохраненные записи. На этом наш интерфейс готов, можно приступить непосредственно к записи звука.

Как уже говорилось для записи воспользуемся интерфейсом MediaRecorder()(подробнее об интерфейсе можно узнать из документации), для его работы необходимо определить медиа поток с которого будем брать звук, инициализируем его лишь то что нам нужна только audio дорожка.

navigator.mediaDevices.getUserMedia({ audio: true})
    .then(stream => {
        const mediaRecorder = new MediaRecorder(stream)});

Теперь у нас есть константа mediaRecorder, которая содержит экземпляр интерфейса, с ней и будем далее работать.

Для начала записи нам нужно вызвать метод MediaRecorder.start(), для остановки записи метод MediaRecorder.stop(). При этом MediaRecorder.stop() генерирует событие dataavailable через которое мы получим доступ оцифрованной звукозаписи в виде бинарного массива.

И так опишем выше указанные события, объявим массив voice[] и запишем в него полученные данные:

navigator.mediaDevices.getUserMedia({ audio: true})
    .then(stream => {
        const mediaRecorder = new MediaRecorder(stream);
        let voice = [];
        document.querySelector('#start').addEventListener('click', function(){
            mediaRecorder.start();
        });

        mediaRecorder.addEventListener("dataavailable",function(event) {
            voice.push(event.data);
        });

        document.querySelector('#stop').addEventListener('click', function(){
            mediaRecorder.stop();
        });
    });

Теперь подготовим полученные данные к отправке. Для этого, по событию stop, создадим экземпляр BLOB, поместим в него полученные данные и укажем MIME тип данных. В нашем случае это будет audio/wav.

mediaRecorder.addEventListener("stop", function() {
    const voiceBlob = new Blob(voice, {
        type: 'audio/wav'
    });

В результате мы имеем константу voiceBlob в которой, находится содежримое нашего будущего фала wav с записью голосового сообщения.

Отправка записи на сервер

Для отправки записи на сервер я решил использовать метод fetch(). Так как данный метод является наиболее современным и предоставляет улучшенный интерфейс для осуществления запросов к серверу. В рамках нашей задачи нам нужно инициировать POST запрос в теле которого отправить содержимое нашего будущего файла для сохранения на сервере (как работает и какими возможностями обладает метод fetch(), можно подробно ознакомиться в документации). Создадим новую форму с полем voice и поместим в него содержимое нашей записи.

let fd = new FormData();
fd.append('voice', voiceBlob);

Создадим асинхронную функцию отправки сообщения на сервер получения ответа и отображения объекта audio для проигрывания уже сохраненного файла. В качестве аргумента функция будет принимать созданную выше форму.

Инициируем запрос к серверу:

let promise = await fetch(URL, {
    method: 'POST',
    body: form});

Если HTTP ответ от сервера не содержит кода ошибки (код ответа в диапазоне от 200-299), то нам остается декодировать ответ, создать на странице новый объект audio, определить его свойства и отобразить. Как формируется ответ, будет рассмотрено ниже.

Сохранение файла на сервере

Создадим на сервере скрипт, который будет принимать наш POST запрос с голосовым сообщением. Так как отправленное нами звукозапись по сути уже является файлом в форме, мы его будем получать на сервере соответствующим образом:

$uploadDir = 'voice/';
$typeFile = explode('/', $_FILES['voice']['type']);
$uploadFile = $uploadDir . basename(md5($_FILES['voice']['tmp_name'].time()).'.'.$typeFile[1]);
if (move_uploaded_file($_FILES['voice']['tmp_name'], $uploadFile)) {
    $response = ['result'=>'OK', 'data'=>'../'.$uploadFile];
} else {
    $response = ['result'=>'ERROR', 'data'=>''];
}
echo json_encode($response);

Подобных примеров PHP кода, обработки полученных файлов, в сети можно найти очень много. Для начала инициализируем переменные, $uploadDir – каталог в котором будет сохранен полученный файл, тип файла &typeFile в нашем случае будет равно wav и полное имя файла, включая директорию. Имя файла в данном случае формируется путем объединения «временного» имени файла и строчного значения текущего времени зашифрованного методом md5. В случае удачного сохранения файла с голосовым сообщением в указанном каталоге формируем ответ в виде массива содержащего поле result равное «OK» или «ERROR» в зависимости от результата и поле «data» которое, в случае успешной обработки содержит ссылку на сохраненный файл.

Для удобства массив преобразуем в объект JSON и отправляем в качестве ответа.

Полный код примера размещен на GitHub.

P.S. Браузер позволяет записывать медиаконтент только при безопасном HTTPS подключении.

Автор: Krivodanov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js