From ac68f06d230fd589edb9b1d13af50836d554f23e Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 25 Dec 2022 13:53:26 +0100 Subject: optimize accessability (screen readers 'n stuff) --- client-web/public/assets/style/master.css | 2 +- client-web/public/start.html | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'client-web/public') diff --git a/client-web/public/assets/style/master.css b/client-web/public/assets/style/master.css index caa8c85..c030f63 100644 --- a/client-web/public/assets/style/master.css +++ b/client-web/public/assets/style/master.css @@ -33,7 +33,7 @@ body { justify-content: left; } -h2 { +h1 { font-weight: 700; margin: 1em; } diff --git a/client-web/public/start.html b/client-web/public/start.html index aa60f35..efb9f22 100644 --- a/client-web/public/start.html +++ b/client-web/public/start.html @@ -8,7 +8,7 @@
-

keks-meet

+

keks-meet

A web conferencing application using webrtc

keks-meet is free software! It is licenced under the terms of @@ -29,6 +29,7 @@ room_input.type = "text"; room_input.id = "room-id-input"; room_input.placeholder = "Room ID"; + room_input.ariaLabel = "Room ID" const submit = document.createElement("input"); submit.type = "button"; -- cgit v1.2.3-70-g09d2 From 028c382c9f5408422832b29a8fa466a6386c86f7 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 25 Dec 2022 14:31:03 +0100 Subject: show voice activity --- client-web/public/assets/style/room.css | 14 +++++++++++++ client-web/source/preferences/decl.ts | 2 ++ client-web/source/resource/track.ts | 35 +++++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) (limited to 'client-web/public') diff --git a/client-web/public/assets/style/room.css b/client-web/public/assets/style/room.css index 3f62685..928ab4e 100644 --- a/client-web/public/assets/style/room.css +++ b/client-web/public/assets/style/room.css @@ -23,9 +23,11 @@ .user .info .name { font-weight: 400; } + .user.local .info .name { text-decoration: underline; } + .user .info { margin-bottom: 1em; } @@ -44,6 +46,7 @@ .local-controls { display: inline; } + .local-controls::before { content: "|"; } @@ -51,6 +54,7 @@ .transfer-status { background-color: #00000040; } + .progress-bar { z-index: -1; padding: 0px; @@ -59,3 +63,13 @@ border-radius: 3px; background-color: var(--ac-light); } + + +.resource-track>div { + border: 2px solid transparent; + border-radius: 4px; +} + +.resource-track>div.audio-active { + border: 2px solid rgb(37, 228, 37); +} diff --git a/client-web/source/preferences/decl.ts b/client-web/source/preferences/decl.ts index effd885..f3f8e84 100644 --- a/client-web/source/preferences/decl.ts +++ b/client-web/source/preferences/decl.ts @@ -32,6 +32,8 @@ export const PREF_DECLS = { camera_facing_mode: { type: optional(string), possible_values: ["environment", "user"], description: "Prefer user-facing or env-facing camera" }, auto_gain_control: { type: bool, description: "Automatically adjust mic gain" }, echo_cancellation: { type: bool, description: "Cancel echo" }, + audio_activity_threshold: { type: number, optional: true, default: 0.003, description: "Audio activity threshold" }, + // TODO differenciate between mic, cam and screen optional_audio_default_enable: { type: bool, default: true, description: "Enable audio tracks by default" }, optional_video_default_enable: { type: bool, default: false, description: "Enable video tracks by default" }, diff --git a/client-web/source/resource/track.ts b/client-web/source/resource/track.ts index 7d53522..58157cf 100644 --- a/client-web/source/resource/track.ts +++ b/client-web/source/resource/track.ts @@ -62,24 +62,55 @@ export function new_local_track(info: ProvideInfo, track: TrackHandle): LocalRes } function create_track_display(track: TrackHandle): HTMLElement { - const el = document.createElement("div") const is_video = track.kind == "video" - const media_el = is_video ? document.createElement("video") : document.createElement("audio") + const is_audio = track.kind == "audio" + const stream = new MediaStream([track.track]) + + const el = document.createElement("div") + + const media_el = is_video + ? document.createElement("video") + : document.createElement("audio") + media_el.srcObject = stream media_el.classList.add("media") media_el.autoplay = true media_el.controls = true media_el.addEventListener("pause", () => media_el.play()) + if (track.local) media_el.muted = true el.append(media_el) track.addEventListener("ended", () => { media_el.srcObject = null // TODO // TODO figure out why i wrote todo here el.remove() }) + + if (is_audio && PREFS.audio_activity_threshold !== undefined) check_volume(stream, vol => { + const active = vol > PREFS.audio_activity_threshold + if (active != el.classList.contains("audio-active")) { + if (active) el.classList.add("audio-active") + else el.classList.remove("audio-active") + } + }) + return el } +function check_volume(track: MediaStream, cb: (vol: number) => void) { + const ctx = new AudioContext(); + const s = ctx.createMediaStreamSource(track) + const a = ctx.createAnalyser() + s.connect(a) + const samples = new Float32Array(a.fftSize); + setInterval(() => { + a.getFloatTimeDomainData(samples); + let sum = 0.0; + for (const amplitude of samples) { sum += amplitude * amplitude; } + cb(Math.sqrt(sum / samples.length)) + }, 1000 / 15) +} + export async function create_camera_res() { log("media", "requesting user media (camera)") const user_media = await window.navigator.mediaDevices.getUserMedia({ -- cgit v1.2.3-70-g09d2 From c2e6a4f002be8a687038e3d2cff836d43275617f Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 25 Dec 2022 18:18:47 +0100 Subject: mute button --- client-web/public/assets/style/master.css | 9 ++++++++- client-web/public/assets/style/room.css | 2 +- client-web/source/resource/track.ts | 20 ++++++++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) (limited to 'client-web/public') diff --git a/client-web/public/assets/style/master.css b/client-web/public/assets/style/master.css index c030f63..7a59d78 100644 --- a/client-web/public/assets/style/master.css +++ b/client-web/public/assets/style/master.css @@ -46,20 +46,24 @@ button { border: 0px solid transparent; border-radius: 3px; } + input[type="button"]:hover, button:hover { filter: brightness(130%); } + input[type="button"].active, button.active { filter: hue-rotate(20deg); } + input[type="text"], select, input[type="number"] { background-color: var(--bg-dark); border: 1px solid var(--ac-light); } + input:disabled, button:disabled { filter: sepia(90%); @@ -71,15 +75,18 @@ button:disabled { left: 50vw; transform: translate(-50%, -50%); } + .start-box p { margin-bottom: 0.5em; } + .start-box input[type="text"] { margin: 0.5em; font-size: xx-large; } + .start-box input[type="button"] { margin: 0.5em; font-size: x-large; width: calc(100% - 1em); -} +} \ No newline at end of file diff --git a/client-web/public/assets/style/room.css b/client-web/public/assets/style/room.css index 928ab4e..ad84325 100644 --- a/client-web/public/assets/style/room.css +++ b/client-web/public/assets/style/room.css @@ -71,5 +71,5 @@ } .resource-track>div.audio-active { - border: 2px solid rgb(37, 228, 37); + border: 2px solid var(--ac-light); } diff --git a/client-web/source/resource/track.ts b/client-web/source/resource/track.ts index 58157cf..22af16f 100644 --- a/client-web/source/resource/track.ts +++ b/client-web/source/resource/track.ts @@ -5,7 +5,7 @@ */ /// import { ProvideInfo } from "../../../common/packets.d.ts"; -import { ebutton, ediv } from "../helper.ts"; +import { ebutton, ediv, elabel } from "../helper.ts"; import { log } from "../logger.ts"; import { on_pref_changed, PREFS } from "../preferences/mod.ts"; import { get_rnnoise_node } from "../rnnoise.ts"; @@ -48,11 +48,12 @@ export const resource_track: ResourceHandlerDecl = { } } -export function new_local_track(info: ProvideInfo, track: TrackHandle): LocalResource { +export function new_local_track(info: ProvideInfo, track: TrackHandle, ...extra_controls: HTMLElement[]): LocalResource { return { info, el: ediv({}, - create_track_display(track) + create_track_display(track), + ...extra_controls ), destroy() { track.end() }, on_request(_user, _create_channel) { @@ -172,5 +173,16 @@ export async function create_mic_res() { clear_gain_cb() destination.disconnect() }) - return new_local_track({ id: t.id, kind: "track", track_kind: "audio", label: "Microphone" }, t) + + const mute = document.createElement("input") + mute.type = "checkbox" + mute.onchange = () => { + log("media", mute.checked ? "muted" : "unmuted") + if (mute.checked) gain.gain.value = Number.MIN_VALUE + else gain.gain.value = PREFS.microphone_gain + } + const mute_label = elabel("Mute", { class: "check-button" }) + mute_label.prepend(mute) + + return new_local_track({ id: t.id, kind: "track", track_kind: "audio", label: "Microphone" }, t, mute_label) } -- cgit v1.2.3-70-g09d2