mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
playback: make Auto the default ReplayGain mode
Use Auto for the preference fallback, the settings initial state and the controller's pre-preference default. Existing stored modes are unchanged.
This commit is contained in:
@@ -154,7 +154,7 @@ class UserPreferences(
|
||||
|
||||
fun replayGainMode(): Flow<ReplayGainMode> =
|
||||
store.data.map { prefs ->
|
||||
prefs[replayGainModeKey]?.let { stored -> ReplayGainMode.entries.firstOrNull { it.name == stored } } ?: ReplayGainMode.Track
|
||||
prefs[replayGainModeKey]?.let { stored -> ReplayGainMode.entries.firstOrNull { it.name == stored } } ?: ReplayGainMode.Auto
|
||||
}
|
||||
|
||||
suspend fun setReplayGainMode(mode: ReplayGainMode) {
|
||||
|
||||
@@ -8,7 +8,7 @@ internal class ReplayGainController(
|
||||
) {
|
||||
private var track = ReplayGain()
|
||||
private var albumContext = false
|
||||
private var settings = ReplayGainSettings(ReplayGainMode.Track, 0f)
|
||||
private var settings = ReplayGainSettings(ReplayGainMode.Auto, 0f)
|
||||
|
||||
@Synchronized
|
||||
fun applyItem(item: MediaItem) {
|
||||
|
||||
@@ -203,7 +203,7 @@ fun SettingsScreen(
|
||||
verboseLogging: Boolean = false,
|
||||
syncMode: SyncMode = SyncMode.WifiOnly,
|
||||
syncInterval: Int = DEFAULT_SYNC_INTERVAL_MINUTES,
|
||||
replayGainMode: ReplayGainMode = ReplayGainMode.Track,
|
||||
replayGainMode: ReplayGainMode = ReplayGainMode.Auto,
|
||||
replayGainPreamp: Float = 0f,
|
||||
onSelectSource: (Long) -> Unit,
|
||||
onEditServer: (Long) -> Unit,
|
||||
|
||||
@@ -69,7 +69,7 @@ class SettingsViewModel(
|
||||
val replayGainMode: StateFlow<ReplayGainMode> =
|
||||
userPreferences
|
||||
.replayGainMode()
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), ReplayGainMode.Track)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), ReplayGainMode.Auto)
|
||||
|
||||
val replayGainPreamp: StateFlow<Float> =
|
||||
userPreferences
|
||||
|
||||
@@ -210,13 +210,13 @@ class UserPreferencesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun replayGainDefaultsToTrackModeAndRoundTrips() =
|
||||
fun replayGainDefaultsToAutoModeAndRoundTrips() =
|
||||
runTest {
|
||||
val file = File.createTempFile("user-prefs", ".preferences_pb").apply { delete() }
|
||||
val store = PreferenceDataStoreFactory.create(scope = backgroundScope) { file }
|
||||
val prefs = UserPreferences(store)
|
||||
|
||||
assertEquals(ReplayGainMode.Track, prefs.replayGainMode().first())
|
||||
assertEquals(ReplayGainMode.Auto, prefs.replayGainMode().first())
|
||||
assertEquals(0f, prefs.replayGainPreampDb().first(), 0f)
|
||||
|
||||
prefs.setReplayGainMode(ReplayGainMode.Album)
|
||||
|
||||
@@ -98,7 +98,7 @@ Server writes — now-playing, scrobbles and stars — go through a `ServerActio
|
||||
|
||||
The media notification carries a star button for the playing track. `PlaybackService` adds a `com.subtracks.STAR` session command and a `CommandButton` to the `MediaSession`, and drives its filled/unfilled icon from the playing song's Room row. That path — `MediaSession.Callback`, `SessionCommand`, `CommandButton`, `SessionResult`/`SessionError` and `MediaSession.setMediaButtonPreferences` — is annotated `@UnstableApi` in Media3 1.11.1 and is therefore opted into explicitly: when bumping Media3, re-check those signatures and the notification's button resolution. The stable `setCustomLayout` is not a substitute here, because the notification manager only refreshes on `onMediaButtonPreferencesChanged`, so the icon would not update mid-playback.
|
||||
|
||||
ReplayGain is applied at the audio sink. The gain the server reports per song (`trackGain`/`albumGain` and their peaks; `baseGain` and `fallbackGain` are ignored) is synced into the song row, carried onto the media item, and applied by a `ReplayGainProcessor` in `DefaultAudioSink`'s processor chain, installed through a `RenderersFactory`. The gain is set from `AudioProcessor.flush(StreamMetadata)`, which Media3 1.11 populates with the playback `Timeline` and the current period id (`androidx/media#418`) and calls on every sink configuration — including a media-item transition at an unchanged format — so a new item's gain is in place before any of its buffers are processed. That timing is the point: `Player.Listener.onMediaItemTransition` arrives on the app thread after the playback thread has already rendered, which is too late. When bumping Media3, re-check that `flush` still carries the timeline and period, and that the chain is reached at all: float output and audio offload both bypass it, which is why float output stays off. The mode (track/album/auto/off) and a preamp are DataStore preferences read by `PlaybackService`, and the anti-clip cap uses the peak when the server sends one: without a peak there is no headroom to check, so a peakless track is only ever attenuated. Auto mode selects album gain for items whose queue entry references an album and track gain for playlists, singles and other mixed contexts, including shuffled album queues; the choice is carried on each media item because the audio thread cannot query the queue.
|
||||
ReplayGain is applied at the audio sink. The gain the server reports per song (`trackGain`/`albumGain` and their peaks; `baseGain` and `fallbackGain` are ignored) is synced into the song row, carried onto the media item, and applied by a `ReplayGainProcessor` in `DefaultAudioSink`'s processor chain, installed through a `RenderersFactory`. The gain is set from `AudioProcessor.flush(StreamMetadata)`, which Media3 1.11 populates with the playback `Timeline` and the current period id (`androidx/media#418`) and calls on every sink configuration — including a media-item transition at an unchanged format — so a new item's gain is in place before any of its buffers are processed. That timing is the point: `Player.Listener.onMediaItemTransition` arrives on the app thread after the playback thread has already rendered, which is too late. When bumping Media3, re-check that `flush` still carries the timeline and period, and that the chain is reached at all: float output and audio offload both bypass it, which is why float output stays off. The mode (track/album/auto/off) and a preamp are DataStore preferences read by `PlaybackService`, and the anti-clip cap uses the peak when the server sends one: without a peak there is no headroom to check, so a peakless track is only ever attenuated. Auto is the default and selects album gain for items whose queue entry references an album and track gain for playlists, singles and other mixed contexts, including shuffled album queues; the choice is carried on each media item because the audio thread cannot query the queue.
|
||||
|
||||
## Data model
|
||||
|
||||
|
||||
Reference in New Issue
Block a user