Skip to content

Commit

Permalink
Add framework for building dynamic threshold feature
Browse files Browse the repository at this point in the history
  • Loading branch information
vantezzen committed Jul 1, 2021
1 parent ec2daa5 commit 88d7098
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 11 deletions.
47 changes: 47 additions & 0 deletions src/pages/content/lib/DynamicThresholdCalculator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import ConfigProvider from "../../shared/configProvider";

/**
* Dynamic Threshold Calculator:
* Dynamically detect the volume level silence has
*
* Related: https://github.com/vantezzen/skip-silence/issues/36
*/
export default class DynamicThresholdCalculator {
// Current dynamic threshold
threshold = 0;

// Extension configuration
config : ConfigProvider;

// Volume of the previous 500 samples to better calculate the threshold
previousSamples : number[] = [];

/**
* Set up the calculator
*
* @param config Config to use
*/
constructor(config : ConfigProvider) {
this.config = config;
}

/**
* Let the calculator inspect a new sample.
* This will be used to adjust the calculated threshold
*/
calculate() {
if(this.previousSamples.length > 20) {
// Calculate the dynamic silence level
// TODO: Implement a real algorithm
const averageVolume = this.previousSamples.reduce((acc, val) => acc + val) / this.previousSamples.length;

this.threshold = averageVolume * 0.3;
this.config.set('silence_threshold', this.threshold);
}

// Make sure the samples array only contains 500 samples max
while(this.previousSamples.length > 500) {
this.previousSamples.shift();
}
}
}
22 changes: 21 additions & 1 deletion src/pages/content/lib/SilenceSkipper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { browser } from "webextension-polyfill-ts";
import { MediaElement } from '../../shared/types';
import debug from '../../shared/debug';
import ConfigProvider from '../../shared/configProvider';
import DynamicThresholdCalculator from "./DynamicThresholdCalculator";

/**
* Silence Skipper: This class is doing the job of actually inspecting media elements and
Expand All @@ -22,6 +23,7 @@ export default class SilenceSkipper {
_rateChangeListenerAdded = false;
_blockRateChangeEvents = false;
_handlingRateChangeError = false;
_samplePosition = 0; // This will count up to 50, then and reset to 0

// Audio variables
audioContext : AudioContext | undefined;
Expand All @@ -30,6 +32,9 @@ export default class SilenceSkipper {
source: MediaElementAudioSourceNode | undefined;
audioFrequencies : Float32Array | undefined;

// Dependencies
dynamicThresholdCalculator : DynamicThresholdCalculator;

/**
* Add silence skipper to element
*
Expand All @@ -48,6 +53,7 @@ export default class SilenceSkipper {
this._inspectSample();
}
}
this.dynamicThresholdCalculator = new DynamicThresholdCalculator(config);

// Attach our config listener
this.config.onUpdate(() => this._onConfigUpdate());
Expand Down Expand Up @@ -278,8 +284,22 @@ export default class SilenceSkipper {
// Make sure we are attached
if (!this.isAttached) this._attachToElement();

this._samplePosition = (this._samplePosition + 1) % 50;

const volume = this._calculateVolume();
const threshold = this.config.get('silence_threshold');
const useDynamicThreshold = this.config.get('dynamic_silence_threshold');

if (useDynamicThreshold && volume > 0) {
this.dynamicThresholdCalculator.previousSamples.push(volume);

if (this._samplePosition === 0) {
// Let the dynamic threshold calculator re-calculate the threshold
// This is only done every 50 samples to reduce load
this.dynamicThresholdCalculator.calculate();
}
}

const threshold = useDynamicThreshold ? this.dynamicThresholdCalculator.threshold : this.config.get('silence_threshold');
const sampleThreshold = this.config.get('samples_threshold');

if (volume < threshold && !this.element.paused && !this.isSpedUp) {
Expand Down
27 changes: 20 additions & 7 deletions src/pages/popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,31 @@ class Popup extends Component {
/>
</div>

<SliderSetting
label="Volume Threshold"
max={200}
name="silence_threshold"
<Switch
name="dynamic_silence_threshold"
label="Use dynamic threshold"
config={this.config}
unit="%"
half
/>
<p className="small">
If the volume is under the red line, the video will be sped up.
Dynamic threshold will try to dynamically calculate the volume of the silence in your media
</p>

{!this.config.get('dynamic_silence_threshold') && (
<>
<SliderSetting
label="Volume Threshold"
max={200}
name="silence_threshold"
config={this.config}
unit="%"
half
/>
<p className="small">
If the volume is under the red line, the video will be sped up.
</p>
</>
)}

<SliderSetting
label="Sample Threshold"
max={50}
Expand Down
6 changes: 4 additions & 2 deletions src/pages/shared/components/sliderSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ interface SliderSettingProps {
name: "silence_threshold" | "samples_threshold",
config: ConfigProvider,
unit: String,
half: boolean
half: boolean,
disabled?: boolean
}

const SliderSetting = ({ label, max, name, config, unit, half } : SliderSettingProps) => {
const SliderSetting = ({ label, max, name, config, unit, half, disabled } : SliderSettingProps) => {
const value = config.get(name);

return (
Expand All @@ -31,6 +32,7 @@ const SliderSetting = ({ label, max, name, config, unit, half } : SliderSettingP
onChange={(evt) => {
config.set(name, Number(evt.target.value));
}}
disabled={disabled}
/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/shared/components/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "./switch.scss";

interface SwitchProps {
label: string,
name: "enabled" | "mute_silence" | "is_bar_icon_enabled" | "allow_analytics",
name: "enabled" | "mute_silence" | "is_bar_icon_enabled" | "allow_analytics" | "dynamic_silence_threshold",
config: ConfigProvider
}

Expand Down
1 change: 1 addition & 0 deletions src/pages/shared/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const defaultConfig = {

// Thresholds
silence_threshold: 30,
dynamic_silence_threshold: false,
samples_threshold: 10,

// Speeds
Expand Down
1 change: 1 addition & 0 deletions src/pages/shared/configProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Environment = "popup" | "content" | "background";
// List of keys that should be saved to local storage
const storedKeys : (keyof typeof defaultConfig)[] = [
"silence_threshold",
"dynamic_silence_threshold",
"samples_threshold",

"playback_speed",
Expand Down

0 comments on commit 88d7098

Please sign in to comment.