mirror of https://github.com/vector-im/riot-web
Merge pull request #6034 from matrix-org/travis/voicemessages/waveform-noise
Improve visible waveform for voice messagespull/21833/head
commit
8066e5402c
|
@ -75,7 +75,8 @@ export function arraySmoothingResample(input: number[], points: number): number[
|
||||||
for (let i = 1; i < input.length - 1; i += 2) {
|
for (let i = 1; i < input.length - 1; i += 2) {
|
||||||
const prevPoint = input[i - 1];
|
const prevPoint = input[i - 1];
|
||||||
const nextPoint = input[i + 1];
|
const nextPoint = input[i + 1];
|
||||||
const average = (prevPoint + nextPoint) / 2;
|
const currPoint = input[i];
|
||||||
|
const average = (prevPoint + nextPoint + currPoint) / 3;
|
||||||
samples.push(average);
|
samples.push(average);
|
||||||
}
|
}
|
||||||
input = samples;
|
input = samples;
|
||||||
|
|
|
@ -15,12 +15,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
import {UPDATE_EVENT} from "../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../stores/AsyncStore";
|
||||||
import {arrayFastResample, arrayRescale, arraySeed, arraySmoothingResample} from "../utils/arrays";
|
import { arrayFastResample, arrayRescale, arraySeed, arraySmoothingResample } from "../utils/arrays";
|
||||||
import {SimpleObservable} from "matrix-widget-api";
|
import { SimpleObservable } from "matrix-widget-api";
|
||||||
import {IDestroyable} from "../utils/IDestroyable";
|
import { IDestroyable } from "../utils/IDestroyable";
|
||||||
import {PlaybackClock} from "./PlaybackClock";
|
import { PlaybackClock } from "./PlaybackClock";
|
||||||
import {createAudioContext, decodeOgg} from "./compat";
|
import { createAudioContext, decodeOgg } from "./compat";
|
||||||
|
import { clamp } from "../utils/numbers";
|
||||||
|
|
||||||
export enum PlaybackState {
|
export enum PlaybackState {
|
||||||
Decoding = "decoding",
|
Decoding = "decoding",
|
||||||
|
@ -33,9 +34,20 @@ export const PLAYBACK_WAVEFORM_SAMPLES = 39;
|
||||||
const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
|
const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
|
||||||
|
|
||||||
function makePlaybackWaveform(input: number[]): number[] {
|
function makePlaybackWaveform(input: number[]): number[] {
|
||||||
// We use a smoothing resample to keep the rough shape of the waveform the user will be seeing. We
|
// First, convert negative amplitudes to positive so we don't detect zero as "noisy".
|
||||||
// then rescale so the user can see the waveform properly (loud noises == 100%).
|
const noiseWaveform = input.map(v => Math.abs(v));
|
||||||
return arrayRescale(arraySmoothingResample(input, PLAYBACK_WAVEFORM_SAMPLES), 0, 1);
|
|
||||||
|
// Next, 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 for the remaining function logic.
|
||||||
|
const resampled = arrayRescale(arraySmoothingResample(noiseWaveform, PLAYBACK_WAVEFORM_SAMPLES), 0, 1);
|
||||||
|
|
||||||
|
// Then, we'll do a high and low pass filter to isolate actual speaking volumes within the rescaled
|
||||||
|
// waveform. Most speech happens below the 0.5 mark.
|
||||||
|
const filtered = resampled.map(v => clamp(v, 0.1, 0.5));
|
||||||
|
|
||||||
|
// Finally, we'll rescale the filtered waveform (0.1-0.5 becomes 0-1 again) so the user sees something
|
||||||
|
// sensible. This is what we return to keep our contract of "values between zero and one".
|
||||||
|
return arrayRescale(filtered, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Playback extends EventEmitter implements IDestroyable {
|
export class Playback extends EventEmitter implements IDestroyable {
|
||||||
|
|
|
@ -73,10 +73,10 @@ describe('arrays', () => {
|
||||||
// we'd be feeding a thousand values in and seeing what a curve of 250 values looks like,
|
// we'd be feeding a thousand values in and seeing what a curve of 250 values looks like,
|
||||||
// but that's not really feasible to manually verify accuracy.
|
// but that's not really feasible to manually verify accuracy.
|
||||||
[
|
[
|
||||||
{input: [2, 2, 0, 2, 2, 0, 2, 2, 0], output: [1, 1, 2, 1]}, // Odd -> Even
|
{input: [4, 4, 1, 4, 4, 1, 4, 4, 1], output: [3, 3, 3, 3]}, // Odd -> Even
|
||||||
{input: [2, 2, 0, 2, 2, 0, 2, 2, 0], output: [1, 1, 2]}, // Odd -> Odd
|
{input: [4, 4, 1, 4, 4, 1, 4, 4, 1], output: [3, 3, 3]}, // Odd -> Odd
|
||||||
{input: [2, 2, 0, 2, 2, 0, 2, 2], output: [1, 1, 2]}, // Even -> Odd
|
{input: [4, 4, 1, 4, 4, 1, 4, 4], output: [3, 3, 3]}, // Even -> Odd
|
||||||
{input: [2, 2, 0, 2, 2, 0, 2, 2], output: [1, 2]}, // Even -> Even
|
{input: [4, 4, 1, 4, 4, 1, 4, 4], output: [3, 3]}, // Even -> Even
|
||||||
].forEach((c, i) => expectSample(i, c.input, c.output, true));
|
].forEach((c, i) => expectSample(i, c.input, c.output, true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue