Nagrywanie dźwięku przez użytkownika

Wiele przeglądarek ma teraz dostęp do danych wejściowych wideo i dźwięku od użytkownika. Jednak w zależności od przeglądarki może to być pełne, dynamiczne wrażenia w ramach strony lub funkcja delegowana do innej aplikacji na urządzeniu użytkownika.

Zacznij od prostych i stopniowo przejdź do bardziej zaawansowanych

Najłatwiej jest poprosić użytkownika o wcześniej nagrany plik. Aby to zrobić, utwórz prosty element wejścia pliku i dodaj filtr accept, który wskazuje, że akceptujemy tylko pliki audio, oraz atrybut capture, który wskazuje, że chcemy pobrać dane bezpośrednio z mikrofonu.

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

Ta metoda działa na wszystkich platformach. Na komputerze użytkownik zostanie poproszony o przesłanie pliku z systemu plików (ignorując atrybut capture). W Safari na iOS otworzy aplikację mikrofonu, która umożliwi Ci nagrywanie dźwięku i wysłanie go z powrotem na stronę internetową. W Androidzie użytkownik będzie mógł wybrać aplikację, której mikrofonem chce użyć do nagrania dźwięku, zanim wyśle go z powrotem na stronę internetową.

Gdy użytkownik zakończy nagrywanie i wróci do witryny, musisz w jakiś sposób uzyskać dane pliku. Aby uzyskać szybki dostęp, do elementu wejściowego dołącz zdarzenie onchange, 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>

Gdy masz dostęp do pliku, możesz z nim zrobić wszystko, co chcesz. Możesz na przykład:

  • Dołącz go bezpośrednio do elementu <audio>, aby móc go odtworzyć.
  • Pobierz na urządzenie użytkownika.
  • Prześlij go na serwer, dołączając do XMLHttpRequest
  • Prześlij je przez Web Audio API i zastosuj do niego filtry.

Chociaż metoda dostępu do danych audio za pomocą elementu wejściowego jest powszechna, jest to najmniej atrakcyjna opcja. Chcemy uzyskać dostęp do mikrofonu, aby zapewnić użytkownikom wygodę bezpośrednio na stronie.

Dostęp do mikrofonu w interaktywny sposób

Nowoczesne przeglądarki mogą mieć bezpośredni dostęp do mikrofonu, co pozwala nam tworzyć wrażenia, które są w pełni zintegrowane ze stroną internetową, a użytkownik nie musi opuszczać przeglądarki.

Uzyskiwanie dostępu do mikrofonu

Możemy uzyskać bezpośredni dostęp do mikrofonu, korzystając z interfejsu API w specyfikacji WebRTC o nazwie getUserMedia(). getUserMedia() poprosi użytkownika o dostęp do podłączonych mikrofonów i kamer.

Jeśli wszystko pójdzie dobrze, interfejs API zwróci obiekt Stream zawierający dane z kamery lub mikrofonu. Następnie możesz go dołączyć do elementu <audio>, strumienia WebRTC, pliku audio Web AudioContext lub zapisać za pomocą interfejsu API MediaRecorder.

Aby uzyskać dane z mikrofonu, wystarczy ustawić wartość audio: true w obiekcie ograniczeń przekazywanym 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 musisz wymienić 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żyć, gdy dzwonisz na numer getUserMedia.

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

Samo w sobie nie jest zbyt przydatne. Możemy tylko pobrać dane audio i je odtworzyć.

Dostęp do nieprzetworzonych danych z mikrofonu

Aby uzyskać dostęp do nieprzetworzonych danych z mikrofonu, musimy wziąć strumień utworzony przez getUserMedia(), a następnie przetworzyć dane za pomocą interfejsu Web Audio API. Interfejs Web Audio API to prosty interfejs API, który pobiera źródła danych wejściowych i łączy je z węzłami, które mogą przetwarzać dane audio (np. dostosowywać wzmocnienie), a ostatecznie z głośnikiem, aby użytkownik mógł je usłyszeć.

Jednym z węzłów, które możesz połączyć, jest AudioWorkletNode. Ten węzeł zapewnia funkcje niskiego poziomu do niestandardowego przetwarzania dźwięku. Rzeczywiste przetwarzanie dźwięku odbywa się w metodzie wywołania process() w komponencie AudioWorkletProcessor. Wywołaj tę funkcję, aby podać dane wejściowe i parametry oraz pobrać dane wyjściowe.

Aby dowiedzieć się więcej, zapoznaj się z workletem Enter Audio.

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

Dane przechowywane w buforach to dane nieprzetworzone z mikrofonu. Możesz je wykorzystać na kilka sposobów:

  • Prześlij je bezpośrednio na serwer
  • przechowywać lokalnie,
  • Konwertuj je na dedykowany format pliku, np. WAV, a następnie zapisz na serwerach lub lokalnie.

Zapisz dane z mikrofonu

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

Interfejs API MediaRecorder przejmuje strumień utworzony przez getUserMedia, a następnie stopniowo zapisuje dane z tego strumienia w wybranej lokalizacji docelowej.

<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 dane są zapisywane bezpośrednio w tablicy, którą później możemy przekształcić w Blob. Można go następnie użyć do zapisania danych na serwerze WWW lub bezpośrednio na urządzeniu użytkownika.

Prośba o dostęp do mikrofonu

Jeśli użytkownik nie zezwolił wcześniej Twojej witrynie na dostęp do mikrofonu, gdy wywołasz funkcję getUserMedia, przeglądarka poprosi użytkownika o przyznanie Twojej witrynie uprawnień do korzystania z mikrofonu.

Użytkownicy nie lubią otrzymywać próśb o dostęp do potężnych urządzeń na swoich komputerach. Często blokują one prośby lub ignorują je, jeśli nie rozumieją kontekstu, w którym zostały utworzone. Najlepiej prosić o dostęp do mikrofonu tylko wtedy, gdy jest to konieczne. Gdy użytkownik przyzna dostęp, nie będziesz musiał ponownie o to prosić. Jeśli jednak odmówi, nie będziesz mógł/mogła prosić o pozwolenie ponownie.

Sprawdzanie, czy masz już dostęp, za pomocą interfejsu API uprawnień

Interfejs API getUserMedia nie informuje, czy masz już dostęp do mikrofonu. To stwarza problem, ponieważ aby zapewnić wygodę korzystania z aplikacji, musisz poprosić użytkownika o dostęp do mikrofonu.

W niektórych przeglądarkach można to rozwiązać, korzystając z interfejsu Permission API. Interfejs API navigator.permission umożliwia wysyłanie zapytań o stan możliwości dostępu do określonych interfejsów API bez konieczności ponownego wyświetlania prośby.

Aby sprawdzić, czy masz dostęp do mikrofonu użytkownika, możesz przekazać parametr {name: 'microphone'} do metody zapytania. W zależności od tego, co zwróci:

  • granted – użytkownik wcześniej przyznał Ci dostęp do mikrofonu;
  • prompt – użytkownik nie przyznał Ci dostępu i zostanie o to poproszony podczas rozmowy telefonicznej z getUserMedia;
  • denied – system lub użytkownik zablokował dostęp do mikrofonu i nie będziesz mieć do niego dostępu.

Możesz też szybko sprawdzić, czy musisz zmienić interfejs, aby uwzględnić działania, które musi wykonać użytkownik.

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ę