Skip to content

Migrate loudness analyzer to audio analysis provider#3727

Merged
marcelveldt merged 3 commits intodevfrom
add-loudness-analyzer-provider
Apr 18, 2026
Merged

Migrate loudness analyzer to audio analysis provider#3727
marcelveldt merged 3 commits intodevfrom
add-loudness-analyzer-provider

Conversation

@marcelveldt
Copy link
Copy Markdown
Member

Problem

Volume-loudness detection was the last analyzer still hand-rolled in the streams and music controllers, storing measurements in its own loudness_measurements table. Every other analyzer already uses the AudioAnalysisProvider pattern. Keeping loudness separate meant duplicated plumbing, a bespoke DB table, and no reuse of the PCM chunk fan-out the new controller already does.

Changes

  • New builtin loudness_analysis audio analysis provider (always enabled) - ebur128 on live PCM via the shared audio-analysis fan-out
  • Removes attach_loudness_analyzer from the streams controller and set_loudness / get_loudness from the music controller; filesystem_local and opensubsonic now use mass.streams.audio_analysis.set_track_loudness(...)
  • Adds loudness_album field to AudioAnalysisData so album-level loudness from tags survives the unified format
  • DB migration 38→39: copies valid rows (> -50 LUFS) from loudness_measurements into audio_analysis under aa_provider_domain='loudness_analysis'; drops the old table
  • Loudness hydration on streamdetails moved to just-in-time in get_queue_item_stream, so a measurement completed during a previous play is picked up without restart
  • Provider skips analysis when streamdetails.volume_normalization_mode == DISABLED (respects per-player opt-out); the base-class version gating already skips tracks that already have a measurement
  • Optional nightly background task (default on): batches 250 local-filesystem tracks without a loudness row, runs ebur128 directly on the file path, 2s spacing, aborts if the storage provider goes offline mid-run
  • Opt-in write_replaygain_tags config (default off): writes REPLAYGAIN_TRACK_GAIN back to the file (ID3/MP4/Vorbis/APEv2) via a new write_replaygain_track_gain mutagen helper

Copilot AI review requested due to automatic review settings April 17, 2026 23:12
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

🔒 Dependency Security Report

✅ No dependency changes detected in this PR.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates loudness measurement into the existing AudioAnalysisProvider framework, replacing bespoke loudness plumbing with a unified audio-analysis storage and runtime pipeline.

Changes:

  • Introduces a new builtin loudness_analysis audio analysis provider (FFmpeg ebur128) and routes tag/ReplayGain-derived loudness into streams.audio_analysis.
  • Consolidates loudness persistence into the audio_analysis table (schema 38→39 migration + removal of the legacy loudness_measurements table).
  • Adds loudness_album to AudioAnalysisData and adds optional ReplayGain tag writing support via helpers.tags.write_replaygain_track_gain.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
music_assistant/providers/opensubsonic/sonic_provider.py Stores tag/ReplayGain loudness via streams.audio_analysis.set_track_loudness.
music_assistant/providers/filesystem_local/init.py Stores parsed loudness tags via streams.audio_analysis.set_track_loudness for track/audiobook/podcast.
music_assistant/providers/loudness_analysis/provider.py New provider implementing live PCM loudness analysis + nightly background file analysis.
music_assistant/providers/loudness_analysis/init.py Provider setup + config entries for background analysis and ReplayGain tag writing.
music_assistant/providers/loudness_analysis/manifest.json Declares the new builtin audio analysis provider.
music_assistant/models/audio_analysis.py Adds loudness_album to the shared analysis data model.
music_assistant/helpers/tags.py Adds mutagen-based helper to write REPLAYGAIN_TRACK_GAIN back to files.
music_assistant/controllers/streams/audio_buffer.py Switches from the legacy loudness attachment to unified audio analysis startup.
music_assistant/controllers/streams/audio_analysis.py Adds set_track_loudness helper to persist loudness into audio_analysis.
music_assistant/controllers/streams/audio.py Moves loudness hydration to just-in-time lookup from audio_analysis in get_queue_item_stream.
music_assistant/controllers/music.py Bumps DB schema to 39 and migrates loudness rows into audio_analysis, then drops the legacy table.
music_assistant/controllers/media/base.py Updates library removal cleanup to delete audio analysis rows (instead of legacy loudness rows).

Comment thread music_assistant/providers/loudness_analysis/manifest.json Outdated
Comment thread music_assistant/helpers/tags.py
Comment thread music_assistant/providers/loudness_analysis/provider.py Outdated
Comment thread music_assistant/providers/loudness_analysis/provider.py Outdated
Comment thread music_assistant/providers/loudness_analysis/provider.py Outdated
Comment thread music_assistant/providers/loudness_analysis/provider.py
Comment thread music_assistant/providers/filesystem_local/__init__.py
Copilot AI review requested due to automatic review settings April 18, 2026 14:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Comment thread music_assistant/controllers/streams/audio_analysis.py
@marcelveldt marcelveldt changed the title Add loudness analyzer audio analysis provider Migrate loudness analyzer to audio analysis provider Apr 18, 2026
@marcelveldt marcelveldt added the dependencies-reviewed Indication that any added or modified/updated dependencies on a PR have been reviewed label Apr 18, 2026
@marcelveldt marcelveldt requested a review from Copilot April 18, 2026 23:02
@marcelveldt marcelveldt merged commit ac91f3d into dev Apr 18, 2026
19 of 21 checks passed
@marcelveldt marcelveldt deleted the add-loudness-analyzer-provider branch April 18, 2026 23:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comment on lines +326 to +333
providers = self.providers
if not providers:
return

for provider in providers:
candidates = await self._find_tracks_missing_analysis(
provider.domain, BACKGROUND_SCAN_BATCH_SIZE
)
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

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

[PROBLEM] The nightly background scan iterates every available AudioAnalysisProvider, but most providers don’t override analyze_file (default returns None), so this will still fetch streamdetails for up to 250 tracks per provider and do no work, causing unnecessary DB/IO load each night.
Suggested fix: skip providers that don’t support file-based analysis (e.g., only include providers where provider.__class__.analyze_file is not AudioAnalysisProvider.analyze_file, or add an explicit capability flag/property and filter on it) before calling _find_tracks_missing_analysis/get_stream_details.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies-reviewed Indication that any added or modified/updated dependencies on a PR have been reviewed maintenance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants