Wiele przeglądarek ma teraz możliwość dostępu 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 działań i stopniowo je rozwijaj
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 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 zostanie on wysłany 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 go przez interfejs 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ę korzystania z aplikacji 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.
W przypadku powodzenia interfejs API zwróci obiekt Stream
, który będzie zawierać dane z kamery lub mikrofonu. Następnie możemy go dołączyć do elementu <audio>
, strumienia WebRTC, dźwięku internetowego 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, możesz najpierw 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 możliwość przetwarzania dźwięku na niskim poziomie. 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,
- przekonwertować go do odpowiedniego formatu pliku, np. WAV, a następnie zapisać na serwerach lub lokalnie.
Zapisz dane z mikrofonu
Najprostszym sposobem zapisania danych z mikrofonu jest użycie interfejsu API MediaRecorder
.
Interfejs API MediaRecorder
przejmuje strumień utworzony przez getUserMedia
, a potem stopniowo zapisuje dane z tego strumienia w wybranej przez Ciebie lokalizacji.
<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
, a następnie użyć do zapisania danych na naszym 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, w momencie wywołania funkcji getUserMedia
przeglądarka poprosi go o przyznanie Twojej witrynie dostępu do mikrofonu.
Użytkownicy nie lubią otrzymywać próśb o dostęp do zaawansowanych urządzeń na swoim komputerze. Często blokują one lub ignorują je, jeśli nie rozumieją kontekstu, w którym została wyświetlona prośba. Sprawdzoną metodą jest proszenie 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ł ponownie poprosić o pozwolenie.
Używanie interfejsu API uprawnień do sprawdzenia, czy masz już dostęp
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 poproszony o to podczas rozmowy telefonicznejgetUserMedia
;denied
– system lub użytkownik zablokował dostęp do mikrofonu, więc 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 () {};
});