Nagrywanie dźwięku przez użytkownika

Wiele przeglądarek ma teraz możliwość uzyskania dostępu do wejścia wideo i audio od użytkownika. Jednak w zależności od przeglądarki może być ono w pełni dynamiczne i wbudowane lub może zostać przekazane do innej aplikacji na urządzeniu użytkownika.

Zacznij od prostego i stopniowo

Najłatwiej jest poprosić użytkownika o udostępnienie nagranego wcześniej pliku. W tym celu utwórz prosty element wejściowy pliku i dodaj filtr accept, który wskazuje, że akceptujemy tylko pliki audio, oraz atrybut capture, który wskazuje, że dane mają być pobierane bezpośrednio z mikrofonu.

<input type="file" accept="audio/*" capture />

Ta metoda działa na wszystkich platformach. Na komputerze pojawi się prośba o przesłanie pliku z systemu plików (atrybut capture zostanie zignorowany). W Safari w iOS otwiera się aplikacja mikrofonu, dzięki której można nagrać dźwięk, a potem przesłać go z powrotem na stronę internetową. Na Androidzie użytkownik będzie mógł wybrać aplikację, w której ma nagrać dźwięk, zanim prześle go z powrotem na stronę internetową.

Gdy użytkownik skończy nagrywać i wróci do witryny, trzeba będzie jakoś uchwycić dane pliku. Aby uzyskać szybki dostęp, dołącz zdarzenie onchange do elementu wejściowego, a potem odczytaj właściwość files obiektu zdarzenia.

<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>

Kiedy masz już dostęp do pliku, możesz z nim korzystać w dowolny sposób. Możesz na przykład:

  • Dołącz je bezpośrednio do elementu <audio>, aby można było go odtworzyć
  • Pobierz aplikację na urządzenie użytkownika
  • Prześlij go na serwer, podłączając go do folderu XMLHttpRequest.
  • Przekazuj go przez interfejs Web Audio API i stosuj do niego filtry.

Choć metoda uzyskiwania dostępu do danych dźwiękowych za pomocą elementów wejściowych jest wszechstronna, jest to najmniej atrakcyjna opcja. Chcemy mieć dostęp do mikrofonu i umożliwiać wygodne korzystanie bezpośrednio z poziomu strony.

Interaktywny dostęp do mikrofonu

Nowoczesne przeglądarki mogą bezpośrednio połączyć się z mikrofonem, co pozwala nam oferować użytkownikom jej rozwiązania, które są w pełni zintegrowane ze stroną internetową, a użytkownik nigdy nie opuszcza przeglądarki.

Uzyskaj dostęp do mikrofonu

Możemy bezpośrednio uzyskać dostęp do mikrofonu za pomocą interfejsu API getUserMedia() w specyfikacji WebRTC. getUserMedia() poprosi użytkownika o dostęp do podłączonych mikrofonów i kamer.

Jeśli interfejs API się uda, interfejs API zwróci obiekt Stream zawierający dane z kamery lub mikrofonu. Następnie możemy dołączyć go do elementu <audio>, dołączyć do strumienia WebRTC, do interfejsu Web Audio AudioContext albo zapisać za pomocą interfejsu API MediaRecorder.

Aby pobrać dane z mikrofonu, ustawiamy po prostu właściwość audio: true w obiekcie ograniczeń, który jest przekazywany do interfejsu 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>

Jeśli chcesz wybrać konkretny mikrofon, najpierw wylicz wszystkie dostępne mikrofony.

navigator.mediaDevices.enumerateDevices().then((devices) => {
  devices = devices.filter((d) => d.kind === 'audioinput');
});

Następnie możesz przekazać deviceId, którego chcesz używać podczas dzwonienia do firmy getUserMedia.

navigator.mediaDevices.getUserMedia({
  audio: {
    deviceId: devices[0].deviceId,
  },
});

To nie jest zbyt przydatne. Możemy tylko odtworzyć dane dźwiękowe.

Dostęp do nieprzetworzonych danych z mikrofonu

Aby uzyskać dostęp do nieprzetworzonych danych z mikrofonu, musimy pobrać strumień utworzony przez usługę getUserMedia(), a potem przetworzyć je za pomocą interfejsu Web Audio API. Interfejs Web Audio API to prosty interfejs API, który łączy źródła danych wejściowych i łączy je z węzłami, które mogą przetwarzać dane dźwiękowe (korygować wzmocnienie itp.) oraz w ostatecznym rozrachunku bezpośrednio na głośniku, aby użytkownik mógł je usłyszeć.

Jednym z węzłów, które możesz połączyć, jest AudioWorkletNode. Ten węzeł udostępnia niskopoziomowe możliwości niestandardowego przetwarzania dźwięku. Przetwarzanie dźwięku ma miejsce w metodzie wywołania zwrotnego process() w AudioWorkletProcessor. Wywołuj tę funkcję, by przesyłać dane wejściowe i parametry oraz pobierać dane wyjściowe.

Aby dowiedzieć się więcej, zajrzyj do 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);

W buforach są dane nieprzetworzone z mikrofonu. Możesz je wykorzystać na kilka sposobów:

  • Prześlij go bezpośrednio na serwer.
  • Przechowuj lokalnie
  • Przekonwertuj go na specjalny format pliku, np. WAV, a potem zapisz go na swoich serwerach lub lokalnie.

Zapisywanie danych z mikrofonu

Najprostszym sposobem zapisania danych z mikrofonu jest użycie interfejsu API MediaRecorder.

Interfejs MediaRecorder API pobierze strumień utworzony przez użytkownika getUserMedia, a potem będzie stopniowo zapisywać znajdujące się w nim dane w preferowanym miejscu docelowym.

<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>

W naszym przypadku zapisujemy dane bezpośrednio w tablicy, którą możemy później przekształcić w tablicę Blob, której można używać do zapisywania danych na serwerze WWW lub bezpośrednio w pamięci urządzenia użytkownika.

Proszenie o uprawnienia do odpowiedzialnego korzystania z mikrofonu

Jeśli użytkownik nie zezwolił wcześniej witrynie na dostęp do mikrofonu, zaraz po wywołaniu polecenia getUserMedia przeglądarka poprosi go o przyznanie dostępu do mikrofonu.

Użytkownicy nienawidzą próśb o dostęp do zaawansowanych urządzeń na swoim komputerze i często blokują żądanie lub zignorują je, jeśli nie zrozumieją kontekstu, z którego pochodzi prośba. Najlepiej jest prosić o dostęp do mikrofonu tylko wtedy, gdy jest to konieczne. Gdy użytkownik udzieli dostępu, nie będzie on już pytany ponownie. Jeśli jednak odrzuci dostęp, nie będzie można go ponownie poprosić.

Użyj interfejsu IAM API, aby sprawdzić, czy masz już dostęp.

Interfejs getUserMedia API nie informuje Cię o tym, czy masz już dostęp do mikrofonu. Pojawia się problem. Aby użytkownik miał ładny interfejs, który umożliwiłby Ci przyznanie dostępu do mikrofonu, musisz poprosić o dostęp do mikrofonu.

W niektórych przeglądarkach można rozwiązać ten problem przy użyciu interfejsu Permission API. Interfejs navigator.permission API pozwala wysyłać zapytania o stan możliwości dostępu do konkretnych interfejsów API bez konieczności ponownego wysyłania zapytań.

Aby przesłać zapytanie o dostęp do mikrofonu użytkownika, możesz przekazać do metody zapytania parametr {name: 'microphone'}, który zwróci:

  • granted – użytkownik już wcześniej przyznał Ci dostęp do mikrofonu;
  • prompt – użytkownik nie udzielił Ci dostępu i zostanie poproszony, gdy zadzwonisz pod numer getUserMedia;
  • denied – system lub użytkownik wyraźnie zablokował dostęp do mikrofonu i nie będziesz mieć do niego dostępu.

Możesz też szybko sprawdzić, czy trzeba zmienić interfejs użytkownika, aby dostosować go do działań, które musi on wykonać.

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 () {};
});

Prześlij opinię