diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/client/helper.ts | 29 | ||||
-rw-r--r-- | source/client/local_user.ts | 45 | ||||
-rw-r--r-- | source/client/logger.ts | 5 | ||||
-rw-r--r-- | source/client/rnnoise.ts | 45 | ||||
-rw-r--r-- | source/client/room.ts | 4 |
5 files changed, 117 insertions, 11 deletions
diff --git a/source/client/helper.ts b/source/client/helper.ts index 19c4f46..9fb4052 100644 --- a/source/client/helper.ts +++ b/source/client/helper.ts @@ -1,3 +1,5 @@ +import { parameters } from "." + export function get_query_params(): { [key: string]: string } { const q: { [key: string]: string } = {} for (const kv of window.location.search.substr(1).split("&")) { @@ -12,3 +14,30 @@ export function hex_id(len: number = 8): string { return Math.floor(Math.random() * 16 ** len).toString(16).padStart(len, "0") } + +export function parameter_bool(name: string, def: boolean): boolean { + const v = parameters[name] + if (!v) return def + if (v == "0" || v == "false" || v == "no") return false + if (v == "1" || v == "true" || v == "yes") return true + alert(`parameter ${name} is invalid`) + return def +} + +export function parameter_number(name: string, def: number): number { + const v = parameters[name] + if (!v) return def + const n = parseFloat(v) + if (Number.isNaN(n)) { + alert(`parameter ${name} is invalid`) + return def + } + return n +} + +export function parameter_string(name: string, def: string): string { + const v = parameters[name] + if (!v) return def + return v +} + diff --git a/source/client/local_user.ts b/source/client/local_user.ts index 3fe6216..cda9b53 100644 --- a/source/client/local_user.ts +++ b/source/client/local_user.ts @@ -1,5 +1,7 @@ +import { parameter_bool, parameter_string } from "./helper"; import { log } from "./logger"; import { RemoteUser } from "./remote_user"; +import { rnnoise_track } from "./rnnoise"; import { Room } from "./room"; import { User } from "./user"; @@ -9,12 +11,14 @@ export class LocalUser extends User { private audio_track?: MediaStreamTrack private video_track?: MediaStreamTrack - controls: { audio?: HTMLElement, video?: HTMLElement } = {} + controls?: { audio: HTMLElement, video: HTMLElement } constructor(room: Room, name: string) { super(room, name) this.el.classList.add("local") this.create_controls() + if (parameter_bool("audio_enabled", false)) this.enable_audio() + if (parameter_bool("video_enabled", false)) this.enable_video() } create_controls() { @@ -39,9 +43,21 @@ export class LocalUser extends User { const el = document.createElement("div") el.classList.add("local-controls") el.append(audio_toggle, video_toggle) + this.controls = { video: video_toggle, audio: audio_toggle } document.body.append(el) } + update_view_w() { + this.update_view() + if (this.stream.getAudioTracks().length > 0) + this.controls?.audio.classList.add("enabled") + else this.controls?.audio.classList.remove("enabled") + + if (this.stream.getVideoTracks().length > 0) + this.controls?.video.classList.add("enabled") + else this.controls?.video.classList.remove("enabled") + } + async add_initial_to_remote(ru: RemoteUser) { if (this.audio_track) ru.peer.addTrack(this.audio_track) if (this.video_track) ru.peer.addTrack(this.video_track) @@ -54,16 +70,31 @@ export class LocalUser extends User { const t = this.video_track = user_media.getVideoTracks()[0] this.room.remote_users.forEach(u => u.peer.addTrack(t)) this.stream.addTrack(t) - this.update_view() + this.update_view_w() } async enable_audio() { if (this.audio_track) return log("media", "requesting user media (audio)") - const user_media = await window.navigator.mediaDevices.getUserMedia({ audio: true }) - const t = this.audio_track = user_media.getAudioTracks()[0] + + const use_rnnoise = parameter_bool("rnnoise", true) + const audio_contraints = use_rnnoise ? { + channelCount: { ideal: 1 }, + noiseSuppression: { ideal: false }, + echoCancellation: { ideal: true }, + autoGainControl: { ideal: false }, + } : true; + + const user_media = await window.navigator.mediaDevices.getUserMedia({ audio: audio_contraints }) + let t = user_media.getAudioTracks()[0] + + if (use_rnnoise) { + t = await rnnoise_track(t) + } + + this.audio_track = t this.room.remote_users.forEach(u => u.peer.addTrack(t)) this.stream.addTrack(t) - this.update_view() + this.update_view_w() } async disable_video() { if (!this.video_track) return @@ -73,7 +104,7 @@ export class LocalUser extends User { }) }) this.stream.removeTrack(this.video_track) - this.update_view() + this.update_view_w() this.video_track = undefined } async disable_audio() { @@ -84,7 +115,7 @@ export class LocalUser extends User { }) }) this.stream.removeTrack(this.audio_track) - this.update_view() + this.update_view_w() this.audio_track = undefined } diff --git a/source/client/logger.ts b/source/client/logger.ts index ea319b1..9960c70 100644 --- a/source/client/logger.ts +++ b/source/client/logger.ts @@ -1,10 +1,11 @@ -export type LogTag = "webrtc" | "ws" | "media" | "*" +export type LogTag = "webrtc" | "ws" | "media" | "*" | "misc" const log_tag_color: { [key in LogTag]: string } = { "*": "#FF0000", webrtc: "#990099", media: "#999900", - ws: "#009999" + ws: "#009999", + misc: "#000099", } // TODO maybe log time aswell diff --git a/source/client/rnnoise.ts b/source/client/rnnoise.ts new file mode 100644 index 0000000..6bd632e --- /dev/null +++ b/source/client/rnnoise.ts @@ -0,0 +1,45 @@ +import { log } from "./logger" + +declare global { + class RNNoiseNode extends AudioWorkletNode { + static register(context: AudioContext): Promise<void> + constructor(context: AudioContext) + onstatus: (data: any) => void + update(something: boolean): void + } +} + + +export async function rnnoise_track(track: MediaStreamTrack): Promise<MediaStreamTrack> { + log("misc", "rnnoise enabled") + const context = new AudioContext({ sampleRate: 48000 }) + //@ts-ignore + let RNNoiseNode: typeof RNNoiseNode = window.RNNoiseNode; + + let script: HTMLScriptElement; + if (!RNNoiseNode) { + log("misc", "loading rnnoise...") + script = document.createElement("script") + script.src = "/static/rnnoise/rnnoise-runtime.js" + script.defer = true + document.head.appendChild(script) + //@ts-ignore + while (!window.RNNoiseNode) await new Promise<void>(r => setTimeout(() => r(), 100)) + //@ts-ignore + RNNoiseNode = window.RNNoiseNode; + log("misc", "rnnoise loaded") + } + + await RNNoiseNode.register(context) + const source_stream = new MediaStream() + source_stream.addTrack(track) + const source = context.createMediaStreamSource(source_stream) + const destination = context.createMediaStreamDestination() + const rnnoise = new RNNoiseNode(context) + source.connect(rnnoise) + rnnoise.connect(destination) + // rnnoise.onstatus = console.log + // rnnoise.update(true) + + return destination.stream.getAudioTracks()[0] +}
\ No newline at end of file diff --git a/source/client/room.ts b/source/client/room.ts index fd48eff..3e3ed19 100644 --- a/source/client/room.ts +++ b/source/client/room.ts @@ -4,7 +4,7 @@ import { RemoteUser } from "./remote_user"; import { User } from "./user"; import { LocalUser } from "./local_user"; import { parameters } from "."; -import { hex_id } from "./helper"; +import { hex_id, parameter_string } from "./helper"; export class Room { @@ -25,7 +25,7 @@ export class Room { this.websocket.onmessage = (ev) => { this.websocket_message(JSON.parse(ev.data)) } - this.local_user = new LocalUser(this, parameters.username ?? `guest-${hex_id()}`) + this.local_user = new LocalUser(this, parameter_string("username", `guest-${hex_id()}`)) } websocket_send(data: CSPacket) { |