Skip to content

audio-compressor plugin: add Auto Track Gain to boost quiet tracks#4440

Open
emilichu wants to merge 2 commits into
pear-devs:masterfrom
emilichu:feat/audio-compressor-auto-track-gain
Open

audio-compressor plugin: add Auto Track Gain to boost quiet tracks#4440
emilichu wants to merge 2 commits into
pear-devs:masterfrom
emilichu:feat/audio-compressor-auto-track-gain

Conversation

@emilichu
Copy link
Copy Markdown

@emilichu emilichu commented Apr 26, 2026

Summary

The Audio Compressor plugin tames loud peaks but never raises quiet tracks, so playlists with mixed loudness still
need constant manual volume adjustment. This adds an opt-in Auto Track Gain mode that reads YouTube's
playerConfig.audioConfig.loudnessDb per track and applies compensating gain (capped, configurable) to anything below
the reference level.

Closes #3032.

Changes

  • New Chain class wires source → DynamicsCompressor → trackGain → destination once on peard:audio-can-play and
    adjusts trackGain per track instead of swapping nodes.
  • onConfigChange updates the live GainNode instead of rebuilding the chain.
  • Compressor settings are byte-identical to before; the new code path only activates when Auto track gain is
    enabled.
  • Two new menu entries:
    • Auto track gain: checkbox (off by default)
    • Maximum gain: 6 / 9 / 12 / 15 / 18 / 24 dB
  • New i18n keys under plugins.audio-compressor.menu.

Test plan

  • Existing users with Audio Compressor enabled and Auto track gain off see no change.
  • Enabling Auto track gain mid-track lifts quiet tracks within ~100 ms.
  • Disabling it mid-track returns to baseline within ~100 ms.
  • Tracks at or above YouTube reference loudness receive 0 dB extra gain.
  • DevTools shows [audio-compressor] track loudness X.X dB → track gain Y.Y dB on each track change.
  • Switching tracks updates the gain to match the new track's loudness.

Summary by CodeRabbit

  • New Features

    • Enhanced audio compressor with configurable track-gain control, auto-track-gain toggle, and maximum gain setting.
    • Automatic loudness-based gain adjustment with clamping to the chosen max; applies quickly when playback starts.
  • Bug Fixes

    • More robust handling when loudness info is delayed and when media elements swap, keeping audio adjustments active.
  • Localization

    • Added English UI labels for the new audio-compressor menu entries.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d539548-e034-48d3-8ec0-bf48f9b5f370

📥 Commits

Reviewing files that changed from the base of the PR and between 2a45d7c and 4a1be43.

📒 Files selected for processing (2)
  • src/i18n/resources/en.json
  • src/plugins/audio-compressor.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/i18n/resources/en.json

📝 Walkthrough

Walkthrough

The audio-compressor plugin now uses a dedicated Chain to rebuild WebAudio routing and applies configurable loudness-derived track-gain (with clamping). It adds config/menu options (autoTrackGain, maxTrackGainDb), watches for YouTube video element swaps, and expands renderer lifecycle and teardown. i18n menu labels were added.

Changes

Audio compressor plugin

Layer / File(s) Summary
Imports, types, helpers
src/plugins/audio-compressor.ts
Adds menu typing import, exports AudioCompressorPluginConfig, MAX_TRACK_GAIN_CHOICES, and dbToLinear helper.
Audio Chain implementation
src/plugins/audio-compressor.ts
Introduces Chain class to rebuild routing: source → dynamics compressor → trackGain → destination, with build/applyTrackGain/teardown and a singleton instance.
Loudness reading and gain application
src/plugins/audio-compressor.ts
Reads YouTube loudnessDb/perceptualLoudnessDb via DOM with cancellable retries, computes signed compensation, clamps to maxTrackGainDb, and applies via chain.applyTrackGain with a linear ramp.
Video element swap handling
src/plugins/audio-compressor.ts
Adds MutationObserver to detect <video> swaps, rebinds source to new media element and rebuilds the chain when necessary.
Audio-can-play handler and context guard
src/plugins/audio-compressor.ts
Updates peard:audio-can-play handler to build the chain and recompute/apply track gain immediately; audio-context guard now uses chain.context.
Config shape and menu UI
src/plugins/audio-compressor.ts, src/i18n/resources/en.json
Exports AudioCompressorPluginConfig, defines menu options for autoTrackGain and a maxTrackGainDb radio submenu; adds i18n labels under plugins.audio-compressor.menu.
Renderer lifecycle and teardown
src/plugins/audio-compressor.ts
Reworks start, onConfigChange, and stop to load config, register/remove listeners, cancel pending loudness retries, stop video-swap watching, and teardown the audio chain.

Sequence Diagram

sequenceDiagram
    participant YouTubePlayer as YouTube Player
    participant Plugin as Audio-Compressor Plugin
    participant Chain as Chain
    participant AudioContext as AudioContext

    YouTubePlayer->>Plugin: peard:audio-can-play
    Plugin->>YouTubePlayer: read loudnessDb / perceptualLoudnessDb (with retries)
    YouTubePlayer-->>Plugin: loudness metadata (or null)
    Plugin->>Plugin: compute signed gain (clamp to maxTrackGainDb)
    Plugin->>Chain: build(source, destination)
    Chain->>AudioContext: connect source -> compressor -> trackGain -> destination
    Plugin->>Chain: applyTrackGain(linear ramp to new gain)
    Chain-->>AudioContext: adjusted output level
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰
I nudged the nodes with careful hop,
Measured quiet, turned knobs non-stop,
A gentle ramp, a soft constrain,
Now every song is close to same,
Hooray — no more surprise drop! 🎶

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding an Auto Track Gain feature to the audio-compressor plugin to boost quiet tracks.
Linked Issues check ✅ Passed The implementation addresses the core requirement from #3032 by reading YouTube's loudnessDb metadata and applying compensating gain per track for volume normalization.
Out of Scope Changes check ✅ Passed All changes are directly related to the Auto Track Gain feature: audio-compressor.ts implements the new functionality and en.json adds required i18n translations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@emilichu emilichu changed the title feat(audio-compressor): add Auto Track Gain to boost quiet tracks audio-compressor plugin: add Auto Track Gain to boost quiet tracks Apr 26, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/plugins/audio-compressor.ts (3)

14-14: Consider exporting/aligning MAX_TRACK_GAIN_CHOICES with the config default.

maxTrackGainDb defaults to 12 (line 103, 182), which is a member of MAX_TRACK_GAIN_CHOICES. If a user (or future config migration) sets maxTrackGainDb to a value not in this list, no radio item will be checked in the menu — the user has no visual indication of the current setting. Either narrow the type ((typeof MAX_TRACK_GAIN_CHOICES)[number]) or add a defensive fallback that highlights the closest choice.

-const MAX_TRACK_GAIN_CHOICES = [6, 9, 12, 15, 18, 24] as const;
+const MAX_TRACK_GAIN_CHOICES = [6, 9, 12, 15, 18, 24] as const;
+type MaxTrackGainDb = (typeof MAX_TRACK_GAIN_CHOICES)[number];

…and tighten maxTrackGainDb: MaxTrackGainDb in AudioCompressorPluginConfig.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/audio-compressor.ts` at line 14, Export and narrow
MAX_TRACK_GAIN_CHOICES and align the config type so maxTrackGainDb is
constrained to those choices: change MAX_TRACK_GAIN_CHOICES to an exported const
and use a union type like (typeof MAX_TRACK_GAIN_CHOICES)[number] for
AudioCompressorPluginConfig.maxTrackGainDb (i.e., tighten the type on
maxTrackGainDb in AudioCompressorPluginConfig). Also add a defensive runtime
fallback wherever the UI builds the radio items (or reads maxTrackGainDb) to map
any unexpected numeric value to the closest entry in MAX_TRACK_GAIN_CHOICES so
at least one radio item is checked and the UI reflects the effective setting.

106-128: Add a clarifying comment explaining the perceptualLoudnessDb fallback.

The fallback from loudnessDb to perceptualLoudnessDb is semantically different: loudnessDb represents standard loudness normalization (LUFS-based), while perceptualLoudnessDb uses a psychoacoustic model weighted for human perception. Both serve loudness normalization but measure different aspects. A brief inline comment explaining why this fallback is acceptable (e.g., "fallback for content without explicit loudness metadata, perceptual loudness provides reasonable estimate") would clarify the choice. No change required if behavior matches PR testing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/audio-compressor.ts` around lines 106 - 128, Add a brief inline
comment inside getContentLoudnessDb explaining why perceptualLoudnessDb is used
as a fallback for loudnessDb: note that loudnessDb is the LUFS/standard loudness
value while perceptualLoudnessDb is a psychoacoustic estimate and is acceptable
as a fallback for content lacking explicit loudness metadata; place this comment
near the assignment of loudnessDb (the expression using
response?.playerConfig?.audioConfig?.loudnessDb ??
response?.playerConfig?.audioConfig?.perceptualLoudnessDb) so readers understand
the semantic difference and rationale.

71-77: Cancel scheduled gain values before scheduling a new ramp.

linearRampToValueAtTime appends a new ramp event to the AudioParam's timeline, starting from the value at the previous event's end time. If applyTrackGain is invoked again before the prior 0.1s ramp completes (e.g., rapid track switching or onConfigChange firing during playback), the new ramp will start from a partially-completed value, creating audible artifacts or incorrect target gain. Mixing direct value writes and ramps without canceling scheduled events is error-prone.

♻️ Anchor the current value, then ramp
   applyTrackGain(gainDb: number) {
     if (!this.context || !this.trackGain) return;
+    const now = this.context.currentTime;
+    this.trackGain.gain.cancelScheduledValues(now);
+    this.trackGain.gain.setValueAtTime(this.trackGain.gain.value, now);
     this.trackGain.gain.linearRampToValueAtTime(
       dbToLinear(gainDb),
-      this.context.currentTime + 0.1,
+      now + 0.1,
     );
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/audio-compressor.ts` around lines 71 - 77, applyTrackGain
currently appends ramps without clearing prior scheduled events causing ramps to
start from in-flight values; update applyTrackGain to first anchor and cancel
previous schedules on trackGain.gain by calling
cancelScheduledValues(this.context.currentTime) (or cancelAndHoldAtTime where
supported), set the current instantaneous value with
setValueAtTime(dbToLinear(currentDb) or getValueAtTime equivalent) at
this.context.currentTime, then schedule the linearRampToValueAtTime to
dbToLinear(gainDb) at this.context.currentTime + 0.1; use the existing symbols
applyTrackGain, this.trackGain.gain, dbToLinear and this.context.currentTime to
locate and modify the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/plugins/audio-compressor.ts`:
- Around line 130-156: The retry timeouts in updateTrackGain() must be tracked
and cancelled to avoid races and work after teardown: store the timeout id when
calling setTimeout in updateTrackGain (e.g., lastUpdateTrackGainTimer),
clearTimeout(lastUpdateTrackGainTimer) before scheduling a new retry (at the top
of audioCanPlayHandler) and in stop() to cancel pending retries; keep the
retriesLeft logic but ensure the scheduled function uses the stored id
lifecycle. Also gate the console.log behind a debug flag in currentConfig (or
remove it for release) so logs only emit when currentConfig.debug is true.
- Around line 158-163: The chain is always rebuilt with a new GainNode in
audioCanPlayHandler (calling chain.build(audioSource, audioContext)) even when
autoTrackGain is disabled; change the logic so the chain topology only includes
the trackGain node when currentConfig.autoTrackGain is true: either (A) wrap the
chain.build/updateTrackGain calls in audioCanPlayHandler with a conditional on
currentConfig.autoTrackGain so you only create/insert trackGain when enabled, or
(B) modify chain.build(...) to accept a flag or read currentConfig.autoTrackGain
and conditionally create/attach trackGain inside chain.build; ensure
applyTrackGain(0) is only used to set gain when the node exists and
updateTrackGain and any references to trackGain handle the absent node safely.

---

Nitpick comments:
In `@src/plugins/audio-compressor.ts`:
- Line 14: Export and narrow MAX_TRACK_GAIN_CHOICES and align the config type so
maxTrackGainDb is constrained to those choices: change MAX_TRACK_GAIN_CHOICES to
an exported const and use a union type like (typeof
MAX_TRACK_GAIN_CHOICES)[number] for AudioCompressorPluginConfig.maxTrackGainDb
(i.e., tighten the type on maxTrackGainDb in AudioCompressorPluginConfig). Also
add a defensive runtime fallback wherever the UI builds the radio items (or
reads maxTrackGainDb) to map any unexpected numeric value to the closest entry
in MAX_TRACK_GAIN_CHOICES so at least one radio item is checked and the UI
reflects the effective setting.
- Around line 106-128: Add a brief inline comment inside getContentLoudnessDb
explaining why perceptualLoudnessDb is used as a fallback for loudnessDb: note
that loudnessDb is the LUFS/standard loudness value while perceptualLoudnessDb
is a psychoacoustic estimate and is acceptable as a fallback for content lacking
explicit loudness metadata; place this comment near the assignment of loudnessDb
(the expression using response?.playerConfig?.audioConfig?.loudnessDb ??
response?.playerConfig?.audioConfig?.perceptualLoudnessDb) so readers understand
the semantic difference and rationale.
- Around line 71-77: applyTrackGain currently appends ramps without clearing
prior scheduled events causing ramps to start from in-flight values; update
applyTrackGain to first anchor and cancel previous schedules on trackGain.gain
by calling cancelScheduledValues(this.context.currentTime) (or
cancelAndHoldAtTime where supported), set the current instantaneous value with
setValueAtTime(dbToLinear(currentDb) or getValueAtTime equivalent) at
this.context.currentTime, then schedule the linearRampToValueAtTime to
dbToLinear(gainDb) at this.context.currentTime + 0.1; use the existing symbols
applyTrackGain, this.trackGain.gain, dbToLinear and this.context.currentTime to
locate and modify the code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5468164e-7988-4516-962b-b1f3bd1e9ce9

📥 Commits

Reviewing files that changed from the base of the PR and between 7b1c574 and 6e19d66.

📒 Files selected for processing (2)
  • src/i18n/resources/en.json
  • src/plugins/audio-compressor.ts

Comment thread src/plugins/audio-compressor.ts
Comment on lines 158 to 163
const audioCanPlayHandler = ({
detail: { audioSource, audioContext },
}: CustomEvent<Compressor>) => {
storage.connectToCompressor(
audioSource,
audioContext,
createCompressorNode(audioContext),
);
chain.build(audioSource, audioContext);
updateTrackGain();
};
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Does inserting an extra GainNode (gain=1) into a Web Audio graph cause any audible difference or measurable latency?

💡 Result:

No, inserting an extra GainNode with gain=1 into a Web Audio graph does not cause any audible difference and adds no measurable latency.

Citations:


🏁 Script executed:

# First, let's look at the structure of the audio-compressor.ts file around the mentioned lines
cat -n src/plugins/audio-compressor.ts | head -180 | tail -50

Repository: pear-devs/pear-desktop

Length of output: 1911


🏁 Script executed:

# Also get the full context of the handler and related code
wc -l src/plugins/audio-compressor.ts

Repository: pear-devs/pear-desktop

Length of output: 102


🏁 Script executed:

# Search for chain.build implementation and autoTrackGain usage
rg "chain\.build" src/plugins/audio-compressor.ts -B 2 -A 5

Repository: pear-devs/pear-desktop

Length of output: 562


🏁 Script executed:

# Search for autoTrackGain references to understand how it gates behavior
rg "autoTrackGain" src/plugins/audio-compressor.ts -B 2 -A 2

Repository: pear-devs/pear-desktop

Length of output: 788


🏁 Script executed:

# Look for onConfigChange handler to understand config change handling
rg "onConfigChange|currentConfig" src/plugins/audio-compressor.ts -B 1 -A 3

Repository: pear-devs/pear-desktop

Length of output: 886


🏁 Script executed:

# Find where chain is defined
rg "const chain|let chain|var chain|export.*chain" src/plugins/audio-compressor.ts -B 2 -A 5

Repository: pear-devs/pear-desktop

Length of output: 212


🏁 Script executed:

# Look for the chain implementation - it might be in a separate file or defined in this file
rg "chain\s*=" src/plugins/audio-compressor.ts -B 2 -A 20 | head -100

Repository: pear-devs/pear-desktop

Length of output: 637


🏁 Script executed:

# Search for chain object definition - might be createChain or similar
rg "createChain|new.*Chain|function.*chain" src/plugins/audio-compressor.ts -B 2 -A 10

Repository: pear-devs/pear-desktop

Length of output: 338


🏁 Script executed:

# Find the Chain class definition - it's likely imported or in this file
rg "class Chain|import.*Chain" src/plugins/audio-compressor.ts -B 2 -A 20

Repository: pear-devs/pear-desktop

Length of output: 615


🏁 Script executed:

# If Chain is defined in another file, find it
fd "chain" -t f src/

Repository: pear-devs/pear-desktop

Length of output: 48


🏁 Script executed:

# Search for Chain in the entire plugins directory
rg "class Chain" src/plugins/

Repository: pear-devs/pear-desktop

Length of output: 112


🏁 Script executed:

# Get the full Chain class definition including the build method
cat -n src/plugins/audio-compressor.ts | sed -n '1,246p' | grep -A 100 "class Chain"

Repository: pear-devs/pear-desktop

Length of output: 3521


🏁 Script executed:

# Or more specifically, get lines from the Chain class
sed -n '/^class Chain/,/^}/p' src/plugins/audio-compressor.ts

Repository: pear-devs/pear-desktop

Length of output: 1841


Consider whether the extra GainNode should be inserted conditionally based on autoTrackGain.

The chain is rebuilt on every audio-can-play event with a new GainNode inserted between the compressor and destination, regardless of whether autoTrackGain is enabled. When autoTrackGain is disabled, applyTrackGain(0) sets the gain to 1, making the node transparent—but the node remains in the audio graph and routing changes from the original (likely direct compressor-to-destination path).

While a GainNode with gain=1 causes no audible difference or measurable latency, this still represents an unconditional topology change. If the intent is to keep the old path identical when auto-track-gain is off, consider conditionally building the chain only when currentConfig.autoTrackGain === true, or restructure to conditionally insert the trackGain node during chain.build() based on the config.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/audio-compressor.ts` around lines 158 - 163, The chain is always
rebuilt with a new GainNode in audioCanPlayHandler (calling
chain.build(audioSource, audioContext)) even when autoTrackGain is disabled;
change the logic so the chain topology only includes the trackGain node when
currentConfig.autoTrackGain is true: either (A) wrap the
chain.build/updateTrackGain calls in audioCanPlayHandler with a conditional on
currentConfig.autoTrackGain so you only create/insert trackGain when enabled, or
(B) modify chain.build(...) to accept a flag or read currentConfig.autoTrackGain
and conditionally create/attach trackGain inside chain.build; ensure
applyTrackGain(0) is only used to set gain when the node exists and
updateTrackGain and any references to trackGain handle the absent node safely.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The always-present GainNode is intentional, it's what enables autoTrackGain to be toggled live (mid-track) without rebuilding the audio chain. The earlier draft of this PR conditionally rebuilt the chain on config change and I hit a class of bugs where toggles only took effect on the first switch (the second toggle would silently no-op due to disconnect/reconnect ordering issues with the source node). Keeping a transparent gain node always in the graph and only mutating its gain.value sidesteps that entirely.

Since (as the web search confirms) a GainNode with gain=1 has zero audible/perf cost, I'd rather keep the topology stable and the toggle behavior reliable than save one node when the feature is off.

Happy to revisit if a maintainer prefers the conditional-insert path though.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only users with a collaborator, contributor, member, or owner role can interact with CodeRabbit.

@emilichu emilichu force-pushed the feat/audio-compressor-auto-track-gain branch from 6e19d66 to 57f7fc6 Compare April 26, 2026 23:58
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/plugins/audio-compressor.ts (1)

71-77: Anchor current value before scheduling a fresh ramp.

linearRampToValueAtTime appends an automation event without invalidating any previously scheduled ones. Two ramps scheduled within the 0.1 s window (e.g., a fast onConfigChange followed by an updateTrackGain retry resolving) can produce a piecewise trajectory that overshoots or stalls before reaching the new target. The conventional Web Audio idiom is to cancel future automation and anchor the current value first.

♻️ Suggested fix
   applyTrackGain(gainDb: number) {
     if (!this.context || !this.trackGain) return;
-    this.trackGain.gain.linearRampToValueAtTime(
-      dbToLinear(gainDb),
-      this.context.currentTime + 0.1,
-    );
+    const now = this.context.currentTime;
+    const gain = this.trackGain.gain;
+    gain.cancelScheduledValues(now);
+    gain.setValueAtTime(gain.value, now);
+    gain.linearRampToValueAtTime(dbToLinear(gainDb), now + 0.1);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/audio-compressor.ts` around lines 71 - 77, In applyTrackGain,
before calling this.trackGain.gain.linearRampToValueAtTime, cancel any future
automation and anchor the current value so we don't create overlapping ramps:
call this.trackGain.gain.cancelScheduledValues(this.context.currentTime), set
the gain to the current instantaneous value (e.g.,
this.trackGain.gain.setValueAtTime(this.trackGain.gain.value,
this.context.currentTime)) and then schedule
linearRampToValueAtTime(dbToLinear(gainDb), this.context.currentTime + 0.1);
this uses this.context, this.trackGain and dbToLinear to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/plugins/audio-compressor.ts`:
- Around line 71-77: In applyTrackGain, before calling
this.trackGain.gain.linearRampToValueAtTime, cancel any future automation and
anchor the current value so we don't create overlapping ramps: call
this.trackGain.gain.cancelScheduledValues(this.context.currentTime), set the
gain to the current instantaneous value (e.g.,
this.trackGain.gain.setValueAtTime(this.trackGain.gain.value,
this.context.currentTime)) and then schedule
linearRampToValueAtTime(dbToLinear(gainDb), this.context.currentTime + 0.1);
this uses this.context, this.trackGain and dbToLinear to locate the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 323cffb0-19bf-430a-b0a8-b43bfd49a306

📥 Commits

Reviewing files that changed from the base of the PR and between 6e19d66 and 57f7fc6.

📒 Files selected for processing (2)
  • src/i18n/resources/en.json
  • src/plugins/audio-compressor.ts
✅ Files skipped from review due to trivial changes (1)
  • src/i18n/resources/en.json

@emilichu emilichu force-pushed the feat/audio-compressor-auto-track-gain branch from dd92b8c to 2a45d7c Compare May 15, 2026 09:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 276e22b6-5e80-42b6-a0c4-3bbe952db98e

📥 Commits

Reviewing files that changed from the base of the PR and between 57f7fc6 and 2a45d7c.

📒 Files selected for processing (2)
  • src/i18n/resources/en.json
  • src/plugins/audio-compressor.ts
✅ Files skipped from review due to trivial changes (1)
  • src/i18n/resources/en.json

Comment thread src/plugins/audio-compressor.ts
emilichu added 2 commits May 15, 2026 13:10
Reads YouTube's per-track loudnessDb metadata and applies compensating
gain to tracks quieter than the reference level.

Closes pear-devs#3032
@emilichu emilichu force-pushed the feat/audio-compressor-auto-track-gain branch from 2a45d7c to 4a1be43 Compare May 15, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Normalize Song Volume

1 participant