Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

@ -45,7 +45,7 @@ export const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
function makePlaybackWaveform(input: number[]): number[] {
// First, convert negative amplitudes to positive so we don't detect zero as "noisy".
const noiseWaveform = input.map(v => Math.abs(v));
const noiseWaveform = input.map((v) => Math.abs(v));
// Then, we'll resample the waveform using a smoothing approach so we can keep the same rough shape.
// We also rescale the waveform to be 0-1 so we end up with a clamped waveform to rely upon.
@ -174,7 +174,8 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
// Overall, the point of this is to avoid memory-related issues due to storing a massive
// audio buffer in memory, as that can balloon to far greater than the input buffer's
// byte length.
if (this.buf.byteLength > 5 * 1024 * 1024) { // 5mb
if (this.buf.byteLength > 5 * 1024 * 1024) {
// 5mb
logger.log("Audio file too large: processing through <audio /> element");
this.element = document.createElement("AUDIO") as HTMLAudioElement;
const prom = new Promise((resolve, reject) => {
@ -186,25 +187,33 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
} else {
// Safari compat: promise API not supported on this function
this.audioBuf = await new Promise((resolve, reject) => {
this.context.decodeAudioData(this.buf, b => resolve(b), async e => {
try {
// This error handler is largely for Safari as well, which doesn't support Opus/Ogg
// very well.
logger.error("Error decoding recording: ", e);
logger.warn("Trying to re-encode to WAV instead...");
this.context.decodeAudioData(
this.buf,
(b) => resolve(b),
async (e) => {
try {
// This error handler is largely for Safari as well, which doesn't support Opus/Ogg
// very well.
logger.error("Error decoding recording: ", e);
logger.warn("Trying to re-encode to WAV instead...");
const wav = await decodeOgg(this.buf);
const wav = await decodeOgg(this.buf);
// noinspection ES6MissingAwait - not needed when using callbacks
this.context.decodeAudioData(wav, b => resolve(b), e => {
logger.error("Still failed to decode recording: ", e);
// noinspection ES6MissingAwait - not needed when using callbacks
this.context.decodeAudioData(
wav,
(b) => resolve(b),
(e) => {
logger.error("Still failed to decode recording: ", e);
reject(e);
},
);
} catch (e) {
logger.error("Caught decoding error:", e);
reject(e);
});
} catch (e) {
logger.error("Caught decoding error:", e);
reject(e);
}
});
}
},
);
});
// Update the waveform to the real waveform once we have channel data to use. We don't

View file

@ -64,8 +64,7 @@ export class PlaybackClock implements IDestroyable {
private clipDuration = 0;
private placeholderDuration = 0;
public constructor(private context: AudioContext) {
}
public constructor(private context: AudioContext) {}
public get durationSeconds(): number {
return this.clipDuration || this.placeholderDuration;
@ -104,7 +103,7 @@ export class PlaybackClock implements IDestroyable {
* @param {MatrixEvent} event The event to use for placeholders.
*/
public populatePlaceholdersFrom(event: MatrixEvent) {
const durationMs = Number(event.getContent()['info']?.['duration']);
const durationMs = Number(event.getContent()["info"]?.["duration"]);
if (Number.isFinite(durationMs)) this.placeholderDuration = durationMs / 1000;
}

View file

@ -40,12 +40,12 @@ export class PlaybackManager {
*/
public pauseAllExcept(playback?: Playback) {
this.instances
.filter(p => p !== playback && p.currentState === PlaybackState.Playing)
.forEach(p => p.pause());
.filter((p) => p !== playback && p.currentState === PlaybackState.Playing)
.forEach((p) => p.pause());
}
public destroyPlaybackInstance(playback: ManagedPlayback) {
this.instances = this.instances.filter(p => p !== playback);
this.instances = this.instances.filter((p) => p !== playback);
}
public createPlaybackInstance(buf: ArrayBuffer, waveform = DEFAULT_WAVEFORM): Playback {

View file

@ -116,8 +116,8 @@ export class PlaybackQueue {
const instance = this.playbacks.get(next);
if (!instance) {
logger.warn(
"Voice message queue desync: Missing playback for next message: "
+ `Current=${this.currentPlaybackId} Last=${last} Next=${next}`,
"Voice message queue desync: Missing playback for next message: " +
`Current=${this.currentPlaybackId} Last=${last} Next=${next}`,
);
} else {
this.playbackIdOrder = orderClone;
@ -175,8 +175,8 @@ export class PlaybackQueue {
}
} else {
logger.warn(
"Voice message queue desync: Expected playback stop to be last in order. "
+ `Current=${this.currentPlaybackId} Last=${last} EventID=${mxEvent.getId()}`,
"Voice message queue desync: Expected playback stop to be last in order. " +
`Current=${this.currentPlaybackId} Last=${last} EventID=${mxEvent.getId()}`,
);
}
}
@ -188,8 +188,8 @@ export class PlaybackQueue {
if (order.length === 0 || order[order.length - 1] !== this.currentPlaybackId) {
const lastInstance = this.playbacks.get(this.currentPlaybackId);
if (
lastInstance.currentState === PlaybackState.Playing
|| lastInstance.currentState === PlaybackState.Paused
lastInstance.currentState === PlaybackState.Playing ||
lastInstance.currentState === PlaybackState.Paused
) {
order.push(this.currentPlaybackId);
}

View file

@ -36,7 +36,7 @@ function roundTimeToTargetFreq(seconds: number): number {
function nextTimeForTargetFreq(roundedSeconds: number): number {
// The extra round is just to make sure we cut off any floating point issues
return roundTimeToTargetFreq(roundedSeconds + (1 / TARGET_AMPLITUDE_FREQUENCY));
return roundTimeToTargetFreq(roundedSeconds + 1 / TARGET_AMPLITUDE_FREQUENCY);
}
class MxVoiceWorklet extends AudioWorkletProcessor {

View file

@ -37,10 +37,7 @@ export class VoiceMessageRecording implements IDestroyable {
private buffer = new Uint8Array(0); // use this.audioBuffer to access
private playback: Playback;
public constructor(
private matrixClient: MatrixClient,
private voiceRecording: VoiceRecording,
) {
public constructor(private matrixClient: MatrixClient, private voiceRecording: VoiceRecording) {
this.voiceRecording.onDataAvailable = this.onDataAvailable;
}
@ -106,12 +103,9 @@ export class VoiceMessageRecording implements IDestroyable {
const { url: mxc, file: encrypted } = await uploadFile(
this.matrixClient,
inRoomId,
new Blob(
[this.audioBuffer],
{
type: this.contentType,
},
),
new Blob([this.audioBuffer], {
type: this.contentType,
}),
);
this.lastUpload = { mxc, encrypted };
this.emit(RecordingState.Uploaded);

View file

@ -15,8 +15,8 @@ limitations under the License.
*/
// @ts-ignore
import Recorder from 'opus-recorder/dist/recorder.min.js';
import encoderPath from 'opus-recorder/dist/encoderWorker.min.js';
import Recorder from "opus-recorder/dist/recorder.min.js";
import encoderPath from "opus-recorder/dist/encoderWorker.min.js";
import { SimpleObservable } from "matrix-widget-api";
import EventEmitter from "events";
import { logger } from "matrix-js-sdk/src/logger";
@ -137,15 +137,15 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
// Dev note: we can't use `addEventListener` for some reason. It just doesn't work.
this.recorderWorklet.port.onmessage = (ev) => {
switch (ev.data['ev']) {
switch (ev.data["ev"]) {
case PayloadEvent.Timekeep:
this.processAudioUpdate(ev.data['timeSeconds']);
this.processAudioUpdate(ev.data["timeSeconds"]);
break;
case PayloadEvent.AmplitudeMark:
// Sanity check to make sure we're adding about one sample per second
if (ev.data['forIndex'] === this.amplitudes.length) {
this.amplitudes.push(ev.data['amplitude']);
this.liveWaveform.pushValue(ev.data['amplitude']);
if (ev.data["forIndex"] === this.amplitudes.length) {
this.amplitudes.push(ev.data["amplitude"]);
this.liveWaveform.pushValue(ev.data["amplitude"]);
}
break;
}
@ -159,8 +159,9 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
this.recorderProcessor.addEventListener("audioprocess", this.onAudioProcess);
}
const recorderOptions = this.shouldRecordInHighQuality() ?
highQualityRecorderOptions : voiceRecorderOptions;
const recorderOptions = this.shouldRecordInHighQuality()
? highQualityRecorderOptions
: voiceRecorderOptions;
const { encoderApplication, bitrate } = recorderOptions;
this.recorder = new Recorder({
@ -184,12 +185,13 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
this.recorder.ondataavailable = (data: ArrayBuffer) => this?.onDataAvailable(data);
} catch (e) {
logger.error("Error starting recording: ", e);
if (e instanceof DOMException) { // Unhelpful DOMExceptions are common - parse them sanely
if (e instanceof DOMException) {
// Unhelpful DOMExceptions are common - parse them sanely
logger.error(`${e.name} (${e.code}): ${e.message}`);
}
// Clean up as best as possible
if (this.recorderStream) this.recorderStream.getTracks().forEach(t => t.stop());
if (this.recorderStream) this.recorderStream.getTracks().forEach((t) => t.stop());
if (this.recorderSource) this.recorderSource.disconnect();
if (this.recorder) this.recorder.close();
if (this.recorderContext) {
@ -221,7 +223,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
if (!this.recording) return;
this.observable.update({
waveform: this.liveWaveform.value.map(v => clamp(v, 0, 1)),
waveform: this.liveWaveform.value.map((v) => clamp(v, 0, 1)),
timeSeconds: timeSeconds,
});
@ -243,7 +245,8 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
}
const secondsLeft = TARGET_MAX_LENGTH - this.recorderSeconds;
if (secondsLeft < 0) { // go over to make sure we definitely capture that last frame
if (secondsLeft < 0) {
// go over to make sure we definitely capture that last frame
// noinspection JSIgnoredPromiseFromCall - we aren't concerned with it overlapping
this.stop();
} else if (secondsLeft <= TARGET_WARN_TIME_LEFT) {
@ -256,7 +259,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
/**
* {@link https://github.com/chris-rudmin/opus-recorder#instance-fields ref for recorderSeconds}
*/
*/
public get recorderSeconds() {
return this.recorder.encodedSamplePosition / 48000;
}
@ -295,7 +298,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
await this.recorderContext.close();
// Now stop all the media tracks so we can release them back to the user/OS
this.recorderStream.getTracks().forEach(t => t.stop());
this.recorderStream.getTracks().forEach((t) => t.stop());
// Finally do our post-processing and clean up
this.recording = false;

View file

@ -15,9 +15,9 @@ limitations under the License.
*/
// @ts-ignore - we know that this is not a module. We're looking for a path.
import decoderWasmPath from 'opus-recorder/dist/decoderWorker.min.wasm';
import wavEncoderPath from 'opus-recorder/dist/waveWorker.min.js';
import decoderPath from 'opus-recorder/dist/decoderWorker.min.js';
import decoderWasmPath from "opus-recorder/dist/decoderWorker.min.wasm";
import wavEncoderPath from "opus-recorder/dist/waveWorker.min.js";
import decoderPath from "opus-recorder/dist/decoderWorker.min.js";
import { logger } from "matrix-js-sdk/src/logger";
import { SAMPLE_RATE } from "./VoiceRecording";
@ -38,46 +38,54 @@ export function createAudioContext(opts?: AudioContextOptions): AudioContext {
export function decodeOgg(audioBuffer: ArrayBuffer): Promise<ArrayBuffer> {
// Condensed version of decoder example, using a promise:
// https://github.com/chris-rudmin/opus-recorder/blob/master/example/decoder.html
return new Promise((resolve) => { // no reject because the workers don't seem to have a fail path
return new Promise((resolve) => {
// no reject because the workers don't seem to have a fail path
logger.log("Decoder WASM path: " + decoderWasmPath); // so we use the variable (avoid tree shake)
const typedArray = new Uint8Array(audioBuffer);
const decoderWorker = new Worker(decoderPath);
const wavWorker = new Worker(wavEncoderPath);
decoderWorker.postMessage({
command: 'init',
command: "init",
decoderSampleRate: SAMPLE_RATE,
outputBufferSampleRate: SAMPLE_RATE,
});
wavWorker.postMessage({
command: 'init',
command: "init",
wavBitDepth: 24, // standard for 48khz (SAMPLE_RATE)
wavSampleRate: SAMPLE_RATE,
});
decoderWorker.onmessage = (ev) => {
if (ev.data === null) { // null == done
wavWorker.postMessage({ command: 'done' });
if (ev.data === null) {
// null == done
wavWorker.postMessage({ command: "done" });
return;
}
wavWorker.postMessage({
command: 'encode',
buffers: ev.data,
}, ev.data.map(b => b.buffer));
wavWorker.postMessage(
{
command: "encode",
buffers: ev.data,
},
ev.data.map((b) => b.buffer),
);
};
wavWorker.onmessage = (ev) => {
if (ev.data.message === 'page') {
if (ev.data.message === "page") {
// The encoding comes through as a single page
resolve(new Blob([ev.data.page], { type: "audio/wav" }).arrayBuffer());
}
};
decoderWorker.postMessage({
command: 'decode',
pages: typedArray,
}, [typedArray.buffer]);
decoderWorker.postMessage(
{
command: "decode",
pages: typedArray,
},
[typedArray.buffer],
);
});
}