diff --git a/src/content/CloningController/CloningController.ts b/src/content/CloningController/CloningController.ts index 2299936a..c034bb55 100644 --- a/src/content/CloningController/CloningController.ts +++ b/src/content/CloningController/CloningController.ts @@ -17,6 +17,7 @@ export type ControllerSettings = | 'soundedSpeed' | 'marginBefore' | 'marginAfter' + | 'muteSilence' | 'enableDesyncCorrection' > & { silenceSpeed: number, diff --git a/src/content/StretchingController/StretcherAndPitchCorrectorNode.ts b/src/content/StretchingController/StretcherAndPitchCorrectorNode.ts index ada5313c..c3573405 100644 --- a/src/content/StretchingController/StretcherAndPitchCorrectorNode.ts +++ b/src/content/StretchingController/StretcherAndPitchCorrectorNode.ts @@ -42,6 +42,7 @@ export default class StretcherAndPitchCorrectorNode { private slowDownPitchShift: PitchShift; private originalPitchCompensationDelay: DelayNode; private delayNode: DelayNode; + // private silenceMuter: GainNode; lastScheduledStretch?: StretchInfo & { speedupOrSlowdown: PitchSetting.SPEEDUP | PitchSetting.SLOWDOWN }; private lastElementSpeedChangeAtInputTime?: Time; @@ -89,9 +90,16 @@ export default class StretcherAndPitchCorrectorNode { this.delayNode = context.createDelay(maxDelay); this.delayNode.delayTime.value = initialDelay; + // this.silenceMuter = context.createGain(); + + // this.delayNode.connect(this.silenceMuter); + this.delayNode.connect(this.speedUpGain); this.delayNode.connect(this.slowDownGain); this.delayNode.connect(this.normalSpeedGain); + // this.silenceMuter.connect(this.speedUpGain); + // this.silenceMuter.connect(this.slowDownGain); + // this.silenceMuter.connect(this.normalSpeedGain); ToneConnect(this.speedUpGain, this.speedUpPitchShift); ToneConnect(this.slowDownGain, this.slowDownPitchShift); @@ -119,7 +127,10 @@ export default class StretcherAndPitchCorrectorNode { } connect = this.connectOutputTo; - onSilenceEnd(elementSpeedSwitchedAt: Time): void { + /** + * @returns marginBeforeStartOutputTime + */ + onSilenceEnd(elementSpeedSwitchedAt: Time): Time { // TODO all this does look like it may cause a snowballing floating point error. Mathematically simplify this? // Or just use if-else? @@ -213,8 +224,15 @@ export default class StretcherAndPitchCorrectorNode { // if (isLogging(this)) { // this._log({ type: 'stretch', lastScheduledStretch: this.lastScheduledStretch }); // } + + // this.silenceMuter.gain.setValueAtTime(1, marginBeforeStartOutputTime); + + return marginBeforeStartOutputTime; } - onSilenceStart(elementSpeedSwitchedAt: Time) { + /** + * @returns marginAfterEndOutputTime + */ + onSilenceStart(elementSpeedSwitchedAt: Time): Time { this.lastElementSpeedChangeAtInputTime = elementSpeedSwitchedAt; // See the same assignment in `onSilenceEnd`. const settings = this.getSettings(); @@ -245,6 +263,10 @@ export default class StretcherAndPitchCorrectorNode { // if (isLogging(this)) { // this._log({ type: 'reset', lastScheduledStretch: this.lastScheduledStretch }); // } + + // this.silenceMuter.gain.setValueAtTime(0, marginAfterEndOutputTime); + + return marginAfterEndOutputTime; } private setOutputPitchAt(pitchSetting: PitchSetting, time: Time, oldPitchSetting: PitchSetting) { diff --git a/src/content/StretchingController/StretchingController.ts b/src/content/StretchingController/StretchingController.ts index dcad8733..d1a133f5 100644 --- a/src/content/StretchingController/StretchingController.ts +++ b/src/content/StretchingController/StretchingController.ts @@ -46,6 +46,7 @@ export type ControllerSettings = | 'soundedSpeed' | 'marginBefore' | 'marginAfter' + | 'muteSilence' | 'enableDesyncCorrection' > & { silenceSpeed: number, @@ -255,6 +256,12 @@ export default class Controller { this._stretcherAndPitch.connectInputFrom(this._lookahead); toDestinationChainLastConnectedLink = this._stretcherAndPitch; } + let silenceMuter: undefined | GainNode; + if (this.settings.muteSilence) { + silenceMuter = audioContext.createGain(); + toDestinationChainLastConnectedLink.connect(silenceMuter); + toDestinationChainLastConnectedLink = silenceMuter; + } toAwait.push(volumeFilterP.then(async volumeFilter => { mediaElementSource.connect(volumeFilter); this._onDestroyCallbacks.push(() => { @@ -313,10 +320,20 @@ export default class Controller { const elementSpeedSwitchedAt = audioContext.currentTime; if (silenceStartOrEnd === SilenceDetectorEventType.SILENCE_END) { this._setSpeedAndLog(SpeedName.SOUNDED); - this._stretcherAndPitch?.onSilenceEnd(elementSpeedSwitchedAt); + const marginBeforeStartOutputTime = this._stretcherAndPitch?.onSilenceEnd(elementSpeedSwitchedAt); + // TODO See the problem? When the stretcher is disabled, the change is scheduled to now, + // without any lookahead. + // TODO we either need to take `_stretcherAndPitch`'s pitch shifters' delays into consideration (instead of + // just the `delayNode`) or explain why we don't. + const unmuteAt = marginBeforeStartOutputTime ?? audioContext.currentTime; + // TODO fade to negate clicking. + silenceMuter?.gain.setValueAtTime(1, unmuteAt); } else { this._setSpeedAndLog(SpeedName.SILENCE); - this._stretcherAndPitch?.onSilenceStart(elementSpeedSwitchedAt); + const marginAfterEndOutputTime = this._stretcherAndPitch?.onSilenceStart(elementSpeedSwitchedAt); + // See comments above, in the if true block. + const muteAt = marginAfterEndOutputTime ?? audioContext.currentTime; + silenceMuter?.gain.setValueAtTime(0, muteAt); if (BUILD_DEFINITIONS.BROWSER === 'chromium' && this.settings.enableDesyncCorrection) { // A workaround for https://github.com/vantezzen/skip-silence/issues/28. diff --git a/src/content/extensionSettings2ControllerSettings.ts b/src/content/extensionSettings2ControllerSettings.ts index 26143ea2..ffb023a2 100644 --- a/src/content/extensionSettings2ControllerSettings.ts +++ b/src/content/extensionSettings2ControllerSettings.ts @@ -9,6 +9,7 @@ export default function extensionSettings2ControllerSettings(extensionSettings: volumeThreshold: extensionSettings.volumeThreshold, marginBefore: extensionSettings.marginBefore, marginAfter: extensionSettings.marginAfter, + muteSilence: extensionSettings.muteSilence, enableDesyncCorrection: extensionSettings.enableDesyncCorrection, silenceSpeed: getAbsoluteSilenceSpeed(extensionSettings), diff --git a/src/popup/App.svelte b/src/popup/App.svelte index 324d4d48..9984c6fc 100644 --- a/src/popup/App.svelte +++ b/src/popup/App.svelte @@ -453,6 +453,23 @@ ${wouldHaveLastedIfSpeedWasIntrinsic} – how long playback would take at intrin bind:value={settings.silenceSpeedRaw} disabled={settings.experimentalControllerType === ControllerKind_CLONING} /> + = { marginAfter: 0.100, previousMarginAfter: 0.100, + muteSilence: false, + applyTo: 'videoOnly', enableHotkeys: true, diff --git a/src/settings/index.ts b/src/settings/index.ts index 764ea99e..86174c24 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -20,6 +20,8 @@ export interface Settings { marginAfter: number, previousMarginAfter: number, + muteSilence: boolean, + applyTo: 'videoOnly' | 'audioOnly' | 'both', enableHotkeys: boolean,