playback: add replaygain setting
Add a settings option for ReplayGain.
This commit is contained in:
parent
dc43edd6cb
commit
9a7571a59a
14 changed files with 167 additions and 66 deletions
|
@ -81,7 +81,6 @@ class PlaybackFragment : Fragment() {
|
|||
setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == R.id.action_queue) {
|
||||
findNavController().navigate(MainFragmentDirections.actionShowQueue())
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -42,25 +42,25 @@ enum class LoopMode {
|
|||
*/
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
NONE -> CONST_NONE
|
||||
ALL -> CONST_ALL
|
||||
TRACK -> CONST_TRACK
|
||||
NONE -> INT_NONE
|
||||
ALL -> INT_ALL
|
||||
TRACK -> INT_TRACK
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CONST_NONE = 0xA100
|
||||
private const val CONST_ALL = 0xA101
|
||||
private const val CONST_TRACK = 0xA102
|
||||
private const val INT_NONE = 0xA100
|
||||
private const val INT_ALL = 0xA101
|
||||
private const val INT_TRACK = 0xA102
|
||||
|
||||
/**
|
||||
* Convert an int [constant] into a LoopMode, or null if it isnt valid.
|
||||
*/
|
||||
fun fromInt(constant: Int): LoopMode? {
|
||||
return when (constant) {
|
||||
CONST_NONE -> NONE
|
||||
CONST_ALL -> ALL
|
||||
CONST_TRACK -> TRACK
|
||||
INT_NONE -> NONE
|
||||
INT_ALL -> ALL
|
||||
INT_TRACK -> TRACK
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
|
|
@ -38,30 +38,30 @@ enum class PlaybackMode {
|
|||
*/
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
ALL_SONGS -> CONST_ALL_SONGS
|
||||
IN_ALBUM -> CONST_IN_ALBUM
|
||||
IN_ARTIST -> CONST_IN_ARTIST
|
||||
IN_GENRE -> CONST_IN_GENRE
|
||||
ALL_SONGS -> INT_ALL_SONGS
|
||||
IN_ALBUM -> INT_IN_ALBUM
|
||||
IN_ARTIST -> INT_IN_ARTIST
|
||||
IN_GENRE -> INT_IN_GENRE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Kept in reverse order because of backwards compat, do not re-order these
|
||||
private const val CONST_ALL_SONGS = 0xA106
|
||||
private const val CONST_IN_ALBUM = 0xA105
|
||||
private const val CONST_IN_ARTIST = 0xA104
|
||||
private const val CONST_IN_GENRE = 0xA103
|
||||
private const val INT_ALL_SONGS = 0xA106
|
||||
private const val INT_IN_ALBUM = 0xA105
|
||||
private const val INT_IN_ARTIST = 0xA104
|
||||
private const val INT_IN_GENRE = 0xA103
|
||||
|
||||
/**
|
||||
* Get a [PlaybackMode] for an int [constant]
|
||||
* @return The mode, null if there isnt one for this.
|
||||
* @return The mode, null if there isn't one for this.
|
||||
*/
|
||||
fun fromInt(constant: Int): PlaybackMode? {
|
||||
return when (constant) {
|
||||
CONST_ALL_SONGS -> ALL_SONGS
|
||||
CONST_IN_ALBUM -> IN_ALBUM
|
||||
CONST_IN_ARTIST -> IN_ARTIST
|
||||
CONST_IN_GENRE -> IN_GENRE
|
||||
INT_ALL_SONGS -> ALL_SONGS
|
||||
INT_IN_ALBUM -> IN_ALBUM
|
||||
INT_IN_ARTIST -> IN_ARTIST
|
||||
INT_IN_GENRE -> IN_GENRE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import kotlin.math.pow
|
|||
* Manages the current volume and playback state across ReplayGain and AudioFocus events.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener {
|
||||
class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener, SettingsManager.Callback {
|
||||
private data class Gain(val track: Float, val album: Float)
|
||||
|
||||
private val playbackManager = PlaybackStateManager.maybeGetInstance()
|
||||
|
@ -62,13 +62,24 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener {
|
|||
get() = field * multiplier
|
||||
private set
|
||||
|
||||
init {
|
||||
settingsManager.addCallback(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the android system for audio focus
|
||||
*/
|
||||
fun requestFocus() {
|
||||
AudioManagerCompat.requestAudioFocus(audioManager, request)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the rough volume adjustment for [Metadata] with ReplayGain tags.
|
||||
* This is based off Vanilla Music's implementation.
|
||||
*/
|
||||
fun applyReplayGain(metadata: Metadata?) {
|
||||
if (metadata == null) {
|
||||
logD("No parsable ReplayGain tags, returning volume to 1.")
|
||||
if (settingsManager.replayGainMode == ReplayGainMode.OFF || metadata == null) {
|
||||
logD("ReplayGain is disabled or cannot be determined for this track, resetting volume.")
|
||||
volume = 1f
|
||||
return
|
||||
}
|
||||
|
@ -76,14 +87,14 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener {
|
|||
val gain = parseReplayGain(metadata)
|
||||
|
||||
// Currently we consider both the album and the track gain.
|
||||
// TODO: Add configuration here
|
||||
var adjust = 0f
|
||||
|
||||
if (gain != null) {
|
||||
adjust = if (gain.album != 0f) {
|
||||
gain.album
|
||||
// Allow the user to configure a preferred mode for ReplayGain.
|
||||
adjust = if (settingsManager.replayGainMode == ReplayGainMode.TRACK) {
|
||||
if (gain.track != 0f) gain.track else gain.album
|
||||
} else {
|
||||
gain.track
|
||||
if (gain.album != 0f) gain.album else gain.track
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,20 +166,16 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the android system for audio focus
|
||||
*/
|
||||
fun requestFocus() {
|
||||
AudioManagerCompat.requestAudioFocus(audioManager, request)
|
||||
}
|
||||
|
||||
/**
|
||||
* Abandon the current focus request, functionally "Destroying it".
|
||||
*/
|
||||
fun release() {
|
||||
AudioManagerCompat.abandonAudioFocusRequest(audioManager, request)
|
||||
settingsManager.removeCallback(this)
|
||||
}
|
||||
|
||||
// --- INTERNAL AUDIO FOCUS ---
|
||||
|
||||
override fun onAudioFocusChange(focusChange: Int) {
|
||||
if (!settingsManager.doAudioFocus) {
|
||||
// Don't do audio focus if its not enabled
|
||||
|
@ -219,6 +226,14 @@ class AudioReactor(context: Context) : AudioManager.OnAudioFocusChangeListener {
|
|||
logD("Unducked volume, now $volume")
|
||||
}
|
||||
|
||||
// --- SETTINGS MANAGEMENT ---
|
||||
|
||||
override fun onAudioFocusUpdate(focus: Boolean) {
|
||||
if (!focus) {
|
||||
onGain()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MULTIPLIER_DUCK = 0.2f
|
||||
|
||||
|
|
|
@ -353,6 +353,10 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
|
|||
}
|
||||
}
|
||||
|
||||
override fun onReplayGainUpdate(mode: ReplayGainMode) {
|
||||
onTracksInfoChanged(player.currentTracksInfo)
|
||||
}
|
||||
|
||||
// --- OTHER FUNCTIONS ---
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package org.oxycblt.auxio.playback.system
|
||||
|
||||
enum class ReplayGainMode {
|
||||
OFF,
|
||||
TRACK,
|
||||
ALBUM;
|
||||
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
OFF -> INT_OFF
|
||||
TRACK -> INT_TRACK
|
||||
ALBUM -> INT_ALBUM
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INT_OFF = 0xA110
|
||||
private const val INT_TRACK = 0xA111
|
||||
private const val INT_ALBUM = 0xA112
|
||||
|
||||
fun fromInt(value: Int): ReplayGainMode? {
|
||||
return when (value) {
|
||||
INT_OFF -> OFF
|
||||
INT_TRACK -> TRACK
|
||||
INT_ALBUM -> ALBUM
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import androidx.preference.PreferenceManager
|
|||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.home.tabs.Tab
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.playback.system.ReplayGainMode
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
|
||||
|
@ -102,6 +103,11 @@ class SettingsManager private constructor(context: Context) :
|
|||
val doPlugMgt: Boolean
|
||||
get() = sharedPrefs.getBoolean(KEY_PLUG_MANAGEMENT, true)
|
||||
|
||||
/** The current ReplayGain configuration */
|
||||
val replayGainMode: ReplayGainMode
|
||||
get() = ReplayGainMode.fromInt(sharedPrefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE))
|
||||
?: ReplayGainMode.OFF
|
||||
|
||||
/** What queue to create when a song is selected (ex. From All Songs or Search) */
|
||||
val songPlaybackMode: PlaybackMode
|
||||
get() = handleSongPlayModeCompat(sharedPrefs)
|
||||
|
@ -236,6 +242,14 @@ class SettingsManager private constructor(context: Context) :
|
|||
KEY_LIB_TABS -> callbacks.forEach {
|
||||
it.onLibTabsUpdate(libTabs)
|
||||
}
|
||||
|
||||
KEY_AUDIO_FOCUS -> callbacks.forEach {
|
||||
it.onAudioFocusUpdate(doAudioFocus)
|
||||
}
|
||||
|
||||
KEY_REPLAY_GAIN -> callbacks.forEach {
|
||||
it.onReplayGainUpdate(replayGainMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,6 +264,8 @@ class SettingsManager private constructor(context: Context) :
|
|||
fun onNotifActionUpdate(useAltAction: Boolean) {}
|
||||
fun onShowCoverUpdate(showCovers: Boolean) {}
|
||||
fun onQualityCoverUpdate(doQualityCovers: Boolean) {}
|
||||
fun onAudioFocusUpdate(focus: Boolean) {}
|
||||
fun onReplayGainUpdate(mode: ReplayGainMode) {}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -267,6 +283,7 @@ class SettingsManager private constructor(context: Context) :
|
|||
|
||||
const val KEY_AUDIO_FOCUS = "KEY_AUDIO_FOCUS"
|
||||
const val KEY_PLUG_MANAGEMENT = "KEY_PLUG_MGT"
|
||||
const val KEY_REPLAY_GAIN = "auxio_replay_gain"
|
||||
|
||||
const val KEY_SONG_PLAYBACK_MODE = "KEY_SONG_PLAY_MODE2"
|
||||
const val KEY_KEEP_SHUFFLE = "KEY_KEEP_SHUFFLE"
|
||||
|
|
|
@ -47,11 +47,11 @@ enum class DisplayMode {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val CONST_NULL = 0xA107
|
||||
private const val CONST_SHOW_GENRES = 0xA108
|
||||
private const val CONST_SHOW_ARTISTS = 0xA109
|
||||
private const val CONST_SHOW_ALBUMS = 0xA10A
|
||||
private const val CONST_SHOW_SONGS = 0xA10B
|
||||
private const val INT_NULL = 0xA107
|
||||
private const val INT_SHOW_GENRES = 0xA108
|
||||
private const val INT_SHOW_ARTISTS = 0xA109
|
||||
private const val INT_SHOW_ALBUMS = 0xA10A
|
||||
private const val INT_SHOW_SONGS = 0xA10B
|
||||
|
||||
/**
|
||||
* Convert this enum into an integer for filtering.
|
||||
|
@ -60,11 +60,11 @@ enum class DisplayMode {
|
|||
*/
|
||||
fun toFilterInt(value: DisplayMode?): Int {
|
||||
return when (value) {
|
||||
SHOW_SONGS -> CONST_SHOW_SONGS
|
||||
SHOW_ALBUMS -> CONST_SHOW_ALBUMS
|
||||
SHOW_ARTISTS -> CONST_SHOW_ARTISTS
|
||||
SHOW_GENRES -> CONST_SHOW_GENRES
|
||||
null -> CONST_NULL
|
||||
SHOW_SONGS -> INT_SHOW_SONGS
|
||||
SHOW_ALBUMS -> INT_SHOW_ALBUMS
|
||||
SHOW_ARTISTS -> INT_SHOW_ARTISTS
|
||||
SHOW_GENRES -> INT_SHOW_GENRES
|
||||
null -> INT_NULL
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,10 +75,10 @@ enum class DisplayMode {
|
|||
*/
|
||||
fun fromFilterInt(value: Int): DisplayMode? {
|
||||
return when (value) {
|
||||
CONST_SHOW_SONGS -> SHOW_SONGS
|
||||
CONST_SHOW_ALBUMS -> SHOW_ALBUMS
|
||||
CONST_SHOW_ARTISTS -> SHOW_ARTISTS
|
||||
CONST_SHOW_GENRES -> SHOW_GENRES
|
||||
INT_SHOW_SONGS -> SHOW_SONGS
|
||||
INT_SHOW_ALBUMS -> SHOW_ALBUMS
|
||||
INT_SHOW_ARTISTS -> SHOW_ARTISTS
|
||||
INT_SHOW_GENRES -> SHOW_GENRES
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,10 +163,10 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
*/
|
||||
fun toInt(): Int {
|
||||
return when (this) {
|
||||
is ByName -> CONST_NAME
|
||||
is ByArtist -> CONST_ARTIST
|
||||
is ByAlbum -> CONST_ALBUM
|
||||
is ByYear -> CONST_YEAR
|
||||
is ByName -> INT_NAME
|
||||
is ByArtist -> INT_ARTIST
|
||||
is ByAlbum -> INT_ALBUM
|
||||
is ByYear -> INT_YEAR
|
||||
}.shl(1) or if (isAscending) 1 else 0
|
||||
}
|
||||
|
||||
|
@ -202,10 +202,10 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val CONST_NAME = 0xA10C
|
||||
private const val CONST_ARTIST = 0xA10D
|
||||
private const val CONST_ALBUM = 0xA10E
|
||||
private const val CONST_YEAR = 0xA10F
|
||||
private const val INT_NAME = 0xA10C
|
||||
private const val INT_ARTIST = 0xA10D
|
||||
private const val INT_ALBUM = 0xA10E
|
||||
private const val INT_YEAR = 0xA10F
|
||||
|
||||
/**
|
||||
* Convert a sort's integer representation into a [Sort] instance.
|
||||
|
@ -216,10 +216,10 @@ sealed class Sort(open val isAscending: Boolean) {
|
|||
val ascending = (value and 1) == 1
|
||||
|
||||
return when (value.shr(1)) {
|
||||
CONST_NAME -> ByName(ascending)
|
||||
CONST_ARTIST -> ByArtist(ascending)
|
||||
CONST_ALBUM -> ByAlbum(ascending)
|
||||
CONST_YEAR -> ByYear(ascending)
|
||||
INT_NAME -> ByName(ascending)
|
||||
INT_ARTIST -> ByArtist(ascending)
|
||||
INT_ALBUM -> ByAlbum(ascending)
|
||||
INT_YEAR -> ByYear(ascending)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@
|
|||
<string name="set_focus_desc">Pausieren wenn andere Töne abspielt wird [Bsp. Anrufe]</string>
|
||||
<string name="set_plug_mgt">Kopfhörerfokus</string>
|
||||
<string name="set_plug_mgt_desc">Abspielen/Pausieren wenn sich die Kopfhörerverbindung ändert</string>
|
||||
<string name="set_replay_gain">ReplayGain (Nur MP3/FLAC)</string>
|
||||
<string name="set_replay_gain_off">Aus</string>
|
||||
<string name="set_replay_gain_track">Titel bevorzugen</string>
|
||||
<string name="set_replay_gain_album">Album bevorzugen</string>
|
||||
|
||||
<string name="set_behavior">Verhalten</string>
|
||||
<string name="set_song_mode">Wenn ein Lied ausgewählt wird</string>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<attr name="entryValues" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<string-array name="entires_theme">
|
||||
<string-array name="entries_theme">
|
||||
<item>@string/set_theme_auto</item>
|
||||
<item>@string/set_theme_day</item>
|
||||
<item>@string/set_theme_night</item>
|
||||
|
@ -32,6 +32,18 @@
|
|||
<item>@integer/play_mode_genre</item>
|
||||
</string-array>
|
||||
|
||||
<array name="entries_replay_gain">
|
||||
<item>@string/set_replay_gain_off</item>
|
||||
<item>@string/set_replay_gain_track</item>
|
||||
<item>@string/set_replay_gain_album</item>
|
||||
</array>
|
||||
|
||||
<string-array name="values_replay_gain">
|
||||
<item>@integer/replay_gain_off</item>
|
||||
<item>@integer/replay_gain_track</item>
|
||||
<item>@integer/replay_gain_album</item>
|
||||
</string-array>
|
||||
|
||||
<integer name="theme_auto">-1</integer>
|
||||
<integer name="theme_light">1</integer>
|
||||
<integer name="theme_dark">2</integer>
|
||||
|
@ -40,4 +52,8 @@
|
|||
<integer name="play_mode_artist">0xA104</integer>
|
||||
<integer name="play_mode_album">0xA105</integer>
|
||||
<integer name="play_mode_songs">0xA106</integer>
|
||||
|
||||
<integer name="replay_gain_off">0xA110</integer>
|
||||
<integer name="replay_gain_track">0xA111</integer>
|
||||
<integer name="replay_gain_album">0xA112</integer>
|
||||
</resources>
|
|
@ -85,6 +85,10 @@
|
|||
<string name="set_focus_desc">Pause when other audio plays [ex. Calls]</string>
|
||||
<string name="set_plug_mgt">Headset focus</string>
|
||||
<string name="set_plug_mgt_desc">Play/Pause when the headset connection changes</string>
|
||||
<string name="set_replay_gain">ReplayGain (MP3/FLAC Only)</string>
|
||||
<string name="set_replay_gain_off">Off</string>
|
||||
<string name="set_replay_gain_track">Prefer track</string>
|
||||
<string name="set_replay_gain_album">Prefer album</string>
|
||||
|
||||
<string name="set_behavior">Behavior</string>
|
||||
<string name="set_song_mode">When a song is selected</string>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<org.oxycblt.auxio.settings.pref.IntListPreference
|
||||
app:defaultValue="@integer/theme_auto"
|
||||
app:isPreferenceVisible="@bool/enable_theme_settings"
|
||||
app:entries="@array/entires_theme"
|
||||
app:entries="@array/entries_theme"
|
||||
app:entryValues="@array/values_theme"
|
||||
app:icon="@drawable/ic_day"
|
||||
app:iconSpaceReserved="false"
|
||||
|
@ -86,13 +86,21 @@
|
|||
app:title="@string/set_focus" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:allowDividerBelow="false"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="KEY_PLUG_MGT"
|
||||
app:summary="@string/set_plug_mgt_desc"
|
||||
app:title="@string/set_plug_mgt" />
|
||||
|
||||
<org.oxycblt.auxio.settings.pref.IntListPreference
|
||||
app:defaultValue="@integer/replay_gain_off"
|
||||
app:key="auxio_replay_gain"
|
||||
app:allowDividerBelow="false"
|
||||
app:iconSpaceReserved="false"
|
||||
app:entries="@array/entries_replay_gain"
|
||||
app:entryValues="@array/values_replay_gain"
|
||||
app:title="@string/set_replay_gain" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
|
|
@ -198,6 +198,10 @@ To prevent any strange bugs, all integer representations must be unique. A table
|
|||
0xA10D | Sort.Artist
|
||||
0xA10E | Sort.Album
|
||||
0xA10F | Sort.Year
|
||||
|
||||
0xA110 | ReplayGainMode.OFF
|
||||
0xA111 | ReplayGainMode.TRACK
|
||||
0xA112 | ReplayGainMode.ALBUM
|
||||
```
|
||||
|
||||
Some datatypes [like `Tab` and `Sort`] have even more fine-grained integer representations for other data. More information can be found in
|
||||
|
|
Loading…
Reference in a new issue