react-native-nitro-player 0.7.0 → 0.7.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -46
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +9 -13
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +45 -90
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +48 -182
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +21 -77
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +55 -104
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +113 -123
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +82 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ListenerRegistry.kt +48 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +62 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +153 -1887
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +122 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +44 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +162 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +179 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +170 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +28 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerTempQueue.kt +121 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +98 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +27 -18
- package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +150 -135
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +13 -30
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +102 -162
- package/ios/HybridDownloadManager.swift +32 -26
- package/ios/HybridEqualizer.swift +48 -35
- package/ios/HybridTrackPlayer.swift +127 -102
- package/ios/core/ListenerRegistry.swift +60 -0
- package/ios/core/TrackPlayerCore.swift +130 -2356
- package/ios/core/TrackPlayerListener.swift +395 -0
- package/ios/core/TrackPlayerNotify.swift +52 -0
- package/ios/core/TrackPlayerPlayback.swift +274 -0
- package/ios/core/TrackPlayerQueue.swift +221 -0
- package/ios/core/TrackPlayerQueueBuild.swift +493 -0
- package/ios/core/TrackPlayerTempQueue.swift +167 -0
- package/ios/core/TrackPlayerUrlLoader.swift +169 -0
- package/ios/equalizer/EqualizerCore.swift +63 -123
- package/ios/media/MediaSessionManager.swift +32 -49
- package/ios/playlist/PlaylistManager.swift +2 -9
- package/ios/queue/HybridPlayerQueue.swift +69 -66
- package/lib/hooks/useDownloadedTracks.js +16 -13
- package/lib/hooks/useEqualizer.d.ts +4 -4
- package/lib/hooks/useEqualizer.js +22 -17
- package/lib/hooks/useEqualizerPresets.d.ts +3 -3
- package/lib/hooks/useEqualizerPresets.js +12 -18
- package/lib/specs/AndroidAutoMediaLibrary.nitro.d.ts +2 -2
- package/lib/specs/AudioDevices.nitro.d.ts +2 -2
- package/lib/specs/DownloadManager.nitro.d.ts +10 -10
- package/lib/specs/Equalizer.nitro.d.ts +10 -10
- package/lib/specs/TrackPlayer.nitro.d.ts +38 -16
- package/lib/types/EqualizerTypes.d.ts +3 -3
- package/nitrogen/generated/android/NitroPlayerOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__std__vector_TrackItem_.hpp +122 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.cpp +31 -6
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.hpp +2 -2
- package/nitrogen/generated/android/c++/JHybridAudioDevicesSpec.cpp +16 -3
- package/nitrogen/generated/android/c++/JHybridAudioDevicesSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridDownloadManagerSpec.cpp +154 -44
- package/nitrogen/generated/android/c++/JHybridDownloadManagerSpec.hpp +10 -10
- package/nitrogen/generated/android/c++/JHybridEqualizerSpec.cpp +130 -34
- package/nitrogen/generated/android/c++/JHybridEqualizerSpec.hpp +9 -9
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.cpp +115 -24
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.hpp +8 -8
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +243 -24
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +16 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__vector_TrackItem__std__vector_TrackItem_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrarySpec.kt +3 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAudioDevicesSpec.kt +2 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridDownloadManagerSpec.kt +10 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridEqualizerSpec.kt +10 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridPlayerQueueSpec.kt +9 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +45 -8
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.cpp +74 -18
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +380 -151
- package/nitrogen/generated/ios/c++/HybridDownloadManagerSpecSwift.hpp +10 -10
- package/nitrogen/generated/ios/c++/HybridEqualizerSpecSwift.hpp +12 -9
- package/nitrogen/generated/ios/c++/HybridPlayerQueueSpecSwift.hpp +23 -8
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +82 -8
- package/nitrogen/generated/ios/swift/Func_void_EqualizerState.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__DownloadedPlaylist_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__DownloadedTrack_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__std__string_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_DownloadedPlaylist_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_DownloadedTrack_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_EqualizerBand_.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__std__vector_TrackItem_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridDownloadManagerSpec.swift +10 -10
- package/nitrogen/generated/ios/swift/HybridDownloadManagerSpec_cxx.swift +141 -71
- package/nitrogen/generated/ios/swift/HybridEqualizerSpec.swift +9 -9
- package/nitrogen/generated/ios/swift/HybridEqualizerSpec_cxx.swift +105 -41
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec.swift +8 -8
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec_cxx.swift +95 -32
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +16 -8
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +267 -32
- package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.hpp +3 -2
- package/nitrogen/generated/shared/c++/HybridAudioDevicesSpec.hpp +2 -1
- package/nitrogen/generated/shared/c++/HybridDownloadManagerSpec.hpp +10 -10
- package/nitrogen/generated/shared/c++/HybridEqualizerSpec.hpp +10 -9
- package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.hpp +9 -8
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +8 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +16 -8
- package/package.json +5 -5
- package/src/hooks/useDownloadedTracks.ts +17 -13
- package/src/hooks/useEqualizer.ts +26 -21
- package/src/hooks/useEqualizerPresets.ts +15 -21
- package/src/specs/AndroidAutoMediaLibrary.nitro.ts +2 -2
- package/src/specs/AudioDevices.nitro.ts +2 -2
- package/src/specs/DownloadManager.nitro.ts +10 -10
- package/src/specs/Equalizer.nitro.ts +10 -10
- package/src/specs/TrackPlayer.nitro.ts +52 -16
- package/src/types/EqualizerTypes.ts +17 -13
|
@@ -2,8 +2,10 @@ package com.margelo.nitro.nitroplayer.equalizer
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.content.SharedPreferences
|
|
5
|
+
import android.media.audiofx.DynamicsProcessing
|
|
5
6
|
import android.media.audiofx.Equalizer
|
|
6
|
-
import android.
|
|
7
|
+
import android.os.Build
|
|
8
|
+
import androidx.annotation.RequiresApi
|
|
7
9
|
import com.margelo.nitro.core.NullType
|
|
8
10
|
import com.margelo.nitro.nitroplayer.EqualizerBand
|
|
9
11
|
import com.margelo.nitro.nitroplayer.EqualizerPreset
|
|
@@ -14,42 +16,33 @@ import com.margelo.nitro.nitroplayer.Variant_NullType_String
|
|
|
14
16
|
import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
|
|
15
17
|
import org.json.JSONArray
|
|
16
18
|
import org.json.JSONObject
|
|
17
|
-
import
|
|
18
|
-
import java.util.Collections
|
|
19
|
+
import com.margelo.nitro.nitroplayer.core.ListenerRegistry
|
|
19
20
|
|
|
20
21
|
class EqualizerCore private constructor(
|
|
21
22
|
private val context: Context,
|
|
22
23
|
) {
|
|
23
24
|
private var equalizer: Equalizer? = null
|
|
25
|
+
private var dynamicsProcessing: DynamicsProcessing? = null
|
|
26
|
+
private var usingDynamicsProcessing: Boolean = false
|
|
24
27
|
private var audioSessionId: Int = 0
|
|
25
28
|
private var isUsingFallbackSession: Boolean = false // Track if using fallback session 0
|
|
26
29
|
private var isEqualizerEnabled: Boolean = false
|
|
27
30
|
private var currentPresetName: String? = null
|
|
28
31
|
|
|
29
|
-
// Standard
|
|
30
|
-
private val targetFrequencies = intArrayOf(
|
|
31
|
-
private val frequencyLabels = arrayOf("
|
|
32
|
-
private val frequencies = intArrayOf(
|
|
33
|
-
private var bandMapping = IntArray(
|
|
32
|
+
// Standard 10-band frequencies: 31Hz, 63Hz, 125Hz, 250Hz, 500Hz, 1kHz, 2kHz, 4kHz, 8kHz, 16kHz
|
|
33
|
+
private val targetFrequencies = intArrayOf(31000, 63000, 125000, 250000, 500000, 1000000, 2000000, 4000000, 8000000, 16000000) // milliHz
|
|
34
|
+
private val frequencyLabels = arrayOf("31 Hz", "63 Hz", "125 Hz", "250 Hz", "500 Hz", "1 kHz", "2 kHz", "4 kHz", "8 kHz", "16 kHz")
|
|
35
|
+
private val frequencies = intArrayOf(31, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000)
|
|
36
|
+
private var bandMapping = IntArray(10) // Maps our 10 bands to actual EQ bands (fallback only)
|
|
37
|
+
private val currentGainsArray = DoubleArray(10) { 0.0 } // Local gain cache (both DP and fallback paths)
|
|
34
38
|
|
|
35
39
|
private val prefs: SharedPreferences =
|
|
36
40
|
context.getSharedPreferences("equalizer_settings", Context.MODE_PRIVATE)
|
|
37
41
|
|
|
38
|
-
// Weak callback wrapper for auto-cleanup
|
|
39
|
-
private data class WeakCallbackBox<T>(
|
|
40
|
-
private val ownerRef: WeakReference<Any>,
|
|
41
|
-
val callback: T,
|
|
42
|
-
) {
|
|
43
|
-
val isAlive: Boolean get() = ownerRef.get() != null
|
|
44
|
-
}
|
|
45
|
-
|
|
46
42
|
// Event listeners
|
|
47
|
-
private val onEnabledChangeListeners =
|
|
48
|
-
|
|
49
|
-
private val
|
|
50
|
-
Collections.synchronizedList(mutableListOf<WeakCallbackBox<(Array<EqualizerBand>) -> Unit>>())
|
|
51
|
-
private val onPresetChangeListeners =
|
|
52
|
-
Collections.synchronizedList(mutableListOf<WeakCallbackBox<(Variant_NullType_String?) -> Unit>>())
|
|
43
|
+
private val onEnabledChangeListeners = ListenerRegistry<(Boolean) -> Unit>()
|
|
44
|
+
private val onBandChangeListeners = ListenerRegistry<(Array<EqualizerBand>) -> Unit>()
|
|
45
|
+
private val onPresetChangeListeners = ListenerRegistry<(Variant_NullType_String?) -> Unit>()
|
|
53
46
|
|
|
54
47
|
companion object {
|
|
55
48
|
private const val TAG = "EqualizerCore"
|
|
@@ -62,24 +55,28 @@ class EqualizerCore private constructor(
|
|
|
62
55
|
INSTANCE ?: EqualizerCore(context).also { INSTANCE = it }
|
|
63
56
|
}
|
|
64
57
|
|
|
65
|
-
// Built-in presets: name -> [
|
|
58
|
+
// Built-in presets: name -> [31Hz, 63Hz, 125Hz, 250Hz, 500Hz, 1kHz, 2kHz, 4kHz, 8kHz, 16kHz] in dB
|
|
66
59
|
private val BUILT_IN_PRESETS =
|
|
67
60
|
mapOf(
|
|
68
|
-
"Flat" to doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0),
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
61
|
+
"Flat" to doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
|
62
|
+
"Rock" to doubleArrayOf(4.8, 2.88, -3.36, -4.8, -1.92, 2.4, 5.28, 6.72, 6.72, 6.72),
|
|
63
|
+
"Pop" to doubleArrayOf(0.96, 2.88, 4.32, 4.8, 3.36, 0.0, -1.44, -1.44, 0.96, 0.96),
|
|
64
|
+
"Classical" to doubleArrayOf(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.32, -4.32, -4.32, -5.76),
|
|
65
|
+
"Dance" to doubleArrayOf(5.76, 4.32, 1.44, 0.0, 0.0, -3.36, -4.32, -4.32, 0.0, 0.0),
|
|
66
|
+
"Techno" to doubleArrayOf(4.8, 3.36, 0.0, -3.36, -2.88, 0.0, 4.8, 5.76, 5.76, 5.28),
|
|
67
|
+
"Club" to doubleArrayOf(0.0, 0.0, 4.8, 3.36, 3.36, 3.36, 1.92, 0.0, 0.0, 0.0),
|
|
68
|
+
"Live" to doubleArrayOf(-2.88, 0.0, 2.4, 3.36, 3.36, 3.36, 2.4, 1.44, 1.44, 1.44),
|
|
69
|
+
"Reggae" to doubleArrayOf(0.0, 0.0, 0.0, -3.36, 0.0, 3.84, 3.84, 0.0, 0.0, 0.0),
|
|
70
|
+
"Full Bass" to doubleArrayOf(4.8, 5.76, 5.76, 3.36, 0.96, -2.4, -4.8, -6.24, -6.72, -6.72),
|
|
71
|
+
"Full Treble" to doubleArrayOf(-5.76, -5.76, -5.76, -2.4, 1.44, 6.72, 9.6, 9.6, 9.6, 10.08),
|
|
72
|
+
"Full Bass & Treble" to doubleArrayOf(4.32, 3.36, 0.0, -4.32, -2.88, 0.96, 4.8, 6.72, 7.2, 7.2),
|
|
73
|
+
"Large Hall" to doubleArrayOf(6.24, 6.24, 3.36, 3.36, 0.0, -2.88, -2.88, -2.88, 0.0, 0.0),
|
|
74
|
+
"Party" to doubleArrayOf(4.32, 4.32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.32, 4.32),
|
|
75
|
+
"Ska" to doubleArrayOf(-1.44, -2.88, -2.4, 0.0, 2.4, 3.36, 5.28, 5.76, 6.72, 5.76),
|
|
76
|
+
"Soft" to doubleArrayOf(2.88, 0.96, 0.0, -1.44, 0.0, 2.4, 4.8, 5.76, 6.72, 7.2),
|
|
77
|
+
"Soft Rock" to doubleArrayOf(2.4, 2.4, 1.44, 0.0, -2.4, -3.36, -1.92, 0.0, 1.44, 5.28),
|
|
78
|
+
"Headphones" to doubleArrayOf(2.88, 6.72, 3.36, -1.92, -1.44, 0.96, 2.88, 5.76, 7.68, 8.64),
|
|
79
|
+
"Laptop Speakers" to doubleArrayOf(2.88, 6.72, 3.36, -1.92, -1.44, 0.96, 2.88, 5.76, 7.68, 8.64),
|
|
83
80
|
)
|
|
84
81
|
}
|
|
85
82
|
|
|
@@ -92,24 +89,47 @@ class EqualizerCore private constructor(
|
|
|
92
89
|
this.isUsingFallbackSession = (audioSessionId == 0)
|
|
93
90
|
|
|
94
91
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
releaseAudioEffects()
|
|
93
|
+
|
|
94
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
95
|
+
initDynamicsProcessing(audioSessionId)
|
|
96
|
+
usingDynamicsProcessing = true
|
|
97
|
+
} else {
|
|
98
|
+
equalizer = Equalizer(0, audioSessionId).apply {
|
|
98
99
|
enabled = false
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
+
usingDynamicsProcessing = false
|
|
102
|
+
setupBandMapping()
|
|
103
|
+
}
|
|
101
104
|
restoreSettings()
|
|
102
105
|
} catch (e: Exception) {
|
|
103
106
|
NitroPlayerLogger.log("EqualizerCore", "Failed to initialize equalizer: ${e.message}")
|
|
104
107
|
}
|
|
105
108
|
}
|
|
106
109
|
|
|
110
|
+
@RequiresApi(Build.VERSION_CODES.P)
|
|
111
|
+
private fun initDynamicsProcessing(sessionId: Int) {
|
|
112
|
+
val config = DynamicsProcessing.Config.Builder(
|
|
113
|
+
DynamicsProcessing.VARIANT_FAVOR_FREQUENCY_RESOLUTION,
|
|
114
|
+
1, // channelCount (stereo handled internally)
|
|
115
|
+
true, 10, // Pre-EQ enabled, 10 bands
|
|
116
|
+
false, 0, // MBC disabled
|
|
117
|
+
false, 0, // Post-EQ disabled
|
|
118
|
+
false // Limiter disabled
|
|
119
|
+
).build()
|
|
120
|
+
dynamicsProcessing = DynamicsProcessing(0, sessionId, config).apply { enabled = false }
|
|
121
|
+
for (i in 0 until 10) {
|
|
122
|
+
val band = DynamicsProcessing.EqBand(true, frequencies[i].toFloat(), 0f)
|
|
123
|
+
dynamicsProcessing!!.setPreEqBandAllChannelsTo(i, band)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
107
127
|
/**
|
|
108
128
|
* Ensure equalizer is initialized, using audio session 0 (global output mix) if needed
|
|
109
129
|
* This allows the equalizer to work even before TrackPlayer is used
|
|
110
130
|
*/
|
|
111
131
|
fun ensureInitialized() {
|
|
112
|
-
if (equalizer == null) {
|
|
132
|
+
if (equalizer == null && dynamicsProcessing == null) {
|
|
113
133
|
initialize(0)
|
|
114
134
|
}
|
|
115
135
|
}
|
|
@@ -136,10 +156,12 @@ class EqualizerCore private constructor(
|
|
|
136
156
|
}
|
|
137
157
|
|
|
138
158
|
fun setEnabled(enabled: Boolean): Boolean {
|
|
139
|
-
val eq = equalizer ?: return false
|
|
140
|
-
|
|
141
159
|
return try {
|
|
142
|
-
|
|
160
|
+
if (usingDynamicsProcessing && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
161
|
+
dynamicsProcessing?.enabled = enabled
|
|
162
|
+
} else {
|
|
163
|
+
equalizer?.enabled = enabled
|
|
164
|
+
}
|
|
143
165
|
isEqualizerEnabled = enabled
|
|
144
166
|
notifyEnabledChange(enabled)
|
|
145
167
|
saveEnabled(enabled)
|
|
@@ -153,19 +175,9 @@ class EqualizerCore private constructor(
|
|
|
153
175
|
fun isEnabled(): Boolean = isEqualizerEnabled
|
|
154
176
|
|
|
155
177
|
fun getBands(): Array<EqualizerBand> {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return (0 until 5)
|
|
178
|
+
return (0 until 10)
|
|
159
179
|
.map { i ->
|
|
160
|
-
val
|
|
161
|
-
val gainMb =
|
|
162
|
-
try {
|
|
163
|
-
eq.getBandLevel(actualBand)
|
|
164
|
-
} catch (e: Exception) {
|
|
165
|
-
0.toShort()
|
|
166
|
-
}
|
|
167
|
-
val gainDb = gainMb / 100.0 // convert millibels to dB
|
|
168
|
-
|
|
180
|
+
val gainDb = getCurrentBandGain(i)
|
|
169
181
|
EqualizerBand(
|
|
170
182
|
index = i.toDouble(),
|
|
171
183
|
centerFrequency = frequencies[i].toDouble(),
|
|
@@ -175,19 +187,26 @@ class EqualizerCore private constructor(
|
|
|
175
187
|
}.toTypedArray()
|
|
176
188
|
}
|
|
177
189
|
|
|
190
|
+
private fun getCurrentBandGain(bandIndex: Int): Double = currentGainsArray[bandIndex]
|
|
191
|
+
|
|
178
192
|
fun setBandGain(
|
|
179
193
|
bandIndex: Int,
|
|
180
194
|
gainDb: Double,
|
|
181
195
|
): Boolean {
|
|
182
|
-
if (bandIndex !in 0..
|
|
196
|
+
if (bandIndex !in 0..9) return false
|
|
183
197
|
|
|
184
|
-
val eq = equalizer ?: return false
|
|
185
198
|
val clampedGain = gainDb.coerceIn(-12.0, 12.0)
|
|
186
|
-
val gainMb = (clampedGain * 100).toInt().toShort() // convert dB to millibels
|
|
187
199
|
|
|
188
200
|
return try {
|
|
189
|
-
|
|
190
|
-
|
|
201
|
+
currentGainsArray[bandIndex] = clampedGain
|
|
202
|
+
if (usingDynamicsProcessing && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
203
|
+
setDPBandGain(bandIndex, clampedGain.toFloat())
|
|
204
|
+
} else {
|
|
205
|
+
val eq = equalizer ?: return false
|
|
206
|
+
val gainMb = (clampedGain * 100).toInt().toShort()
|
|
207
|
+
eq.setBandLevel(bandMapping[bandIndex].toShort(), gainMb)
|
|
208
|
+
}
|
|
209
|
+
currentPresetName = null
|
|
191
210
|
notifyBandChange(getBands())
|
|
192
211
|
notifyPresetChange(null)
|
|
193
212
|
saveBandGainsAndPreset(getAllGains(), null)
|
|
@@ -197,16 +216,30 @@ class EqualizerCore private constructor(
|
|
|
197
216
|
}
|
|
198
217
|
}
|
|
199
218
|
|
|
200
|
-
|
|
201
|
-
|
|
219
|
+
@RequiresApi(Build.VERSION_CODES.P)
|
|
220
|
+
private fun setDPBandGain(bandIndex: Int, gainDb: Float) {
|
|
221
|
+
val band = DynamicsProcessing.EqBand(true, frequencies[bandIndex].toFloat(), gainDb)
|
|
222
|
+
dynamicsProcessing?.setPreEqBandAllChannelsTo(bandIndex, band)
|
|
223
|
+
}
|
|
202
224
|
|
|
203
|
-
|
|
225
|
+
fun setAllBandGains(gains: DoubleArray): Boolean {
|
|
226
|
+
if (gains.size != 10) return false
|
|
204
227
|
|
|
205
228
|
return try {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
229
|
+
if (usingDynamicsProcessing && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
230
|
+
gains.forEachIndexed { i, gain ->
|
|
231
|
+
val clampedGain = gain.coerceIn(-12.0, 12.0)
|
|
232
|
+
currentGainsArray[i] = clampedGain
|
|
233
|
+
setDPBandGain(i, clampedGain.toFloat())
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
val eq = equalizer ?: return false
|
|
237
|
+
gains.forEachIndexed { i, gain ->
|
|
238
|
+
val clampedGain = gain.coerceIn(-12.0, 12.0)
|
|
239
|
+
currentGainsArray[i] = clampedGain
|
|
240
|
+
val gainMb = (clampedGain * 100).toInt().toShort()
|
|
241
|
+
eq.setBandLevel(bandMapping[i].toShort(), gainMb)
|
|
242
|
+
}
|
|
210
243
|
}
|
|
211
244
|
notifyBandChange(getBands())
|
|
212
245
|
saveBandGains(gains.toList())
|
|
@@ -217,26 +250,23 @@ class EqualizerCore private constructor(
|
|
|
217
250
|
}
|
|
218
251
|
|
|
219
252
|
private fun getAllGains(): List<Double> {
|
|
220
|
-
|
|
221
|
-
return (0 until 5).map { i ->
|
|
222
|
-
try {
|
|
223
|
-
eq.getBandLevel(bandMapping[i].toShort()) / 100.0
|
|
224
|
-
} catch (e: Exception) {
|
|
225
|
-
0.0
|
|
226
|
-
}
|
|
227
|
-
}
|
|
253
|
+
return (0 until 10).map { i -> getCurrentBandGain(i) }
|
|
228
254
|
}
|
|
229
255
|
|
|
230
256
|
fun getBandRange(): GainRange {
|
|
231
|
-
|
|
232
|
-
return if (eq != null) {
|
|
233
|
-
val range = eq.bandLevelRange
|
|
234
|
-
GainRange(
|
|
235
|
-
min = (range[0] / 100.0).coerceAtLeast(-12.0),
|
|
236
|
-
max = (range[1] / 100.0).coerceAtMost(12.0),
|
|
237
|
-
)
|
|
238
|
-
} else {
|
|
257
|
+
return if (usingDynamicsProcessing) {
|
|
239
258
|
GainRange(min = -12.0, max = 12.0)
|
|
259
|
+
} else {
|
|
260
|
+
val eq = equalizer
|
|
261
|
+
if (eq != null) {
|
|
262
|
+
val range = eq.bandLevelRange
|
|
263
|
+
GainRange(
|
|
264
|
+
min = (range[0] / 100.0).coerceAtLeast(-12.0),
|
|
265
|
+
max = (range[1] / 100.0).coerceAtMost(12.0),
|
|
266
|
+
)
|
|
267
|
+
} else {
|
|
268
|
+
GainRange(min = -12.0, max = 12.0)
|
|
269
|
+
}
|
|
240
270
|
}
|
|
241
271
|
}
|
|
242
272
|
|
|
@@ -265,7 +295,7 @@ class EqualizerCore private constructor(
|
|
|
265
295
|
.asSequence()
|
|
266
296
|
.map { name ->
|
|
267
297
|
val gainsArray = json.getJSONArray(name)
|
|
268
|
-
val gains = DoubleArray(
|
|
298
|
+
val gains = DoubleArray(gainsArray.length()) { gainsArray.getDouble(it) }
|
|
269
299
|
EqualizerPreset(
|
|
270
300
|
name = name,
|
|
271
301
|
gains = gains,
|
|
@@ -300,7 +330,7 @@ class EqualizerCore private constructor(
|
|
|
300
330
|
val json = JSONObject(customPresetsJson)
|
|
301
331
|
if (json.has(name)) {
|
|
302
332
|
val gainsArray = json.getJSONArray(name)
|
|
303
|
-
DoubleArray(
|
|
333
|
+
DoubleArray(gainsArray.length()) { gainsArray.getDouble(it) }
|
|
304
334
|
} else {
|
|
305
335
|
null
|
|
306
336
|
}
|
|
@@ -362,7 +392,7 @@ class EqualizerCore private constructor(
|
|
|
362
392
|
)
|
|
363
393
|
|
|
364
394
|
fun reset() {
|
|
365
|
-
setAllBandGains(
|
|
395
|
+
setAllBandGains(DoubleArray(10) { 0.0 })
|
|
366
396
|
currentPresetName = "Flat"
|
|
367
397
|
notifyPresetChange("Flat")
|
|
368
398
|
saveCurrentPreset("Flat")
|
|
@@ -409,8 +439,12 @@ class EqualizerCore private constructor(
|
|
|
409
439
|
if (gainsJson != null) {
|
|
410
440
|
try {
|
|
411
441
|
val arr = JSONArray(gainsJson)
|
|
412
|
-
|
|
413
|
-
|
|
442
|
+
// Migration: if saved array length != 10, skip (5-band data incompatible)
|
|
443
|
+
if (arr.length() == 10) {
|
|
444
|
+
val gains = DoubleArray(10) { arr.getDouble(it) }
|
|
445
|
+
setAllBandGains(gains)
|
|
446
|
+
}
|
|
447
|
+
// else: start at flat (migration from 5-band)
|
|
414
448
|
} catch (e: Exception) {
|
|
415
449
|
// Ignore
|
|
416
450
|
}
|
|
@@ -420,7 +454,11 @@ class EqualizerCore private constructor(
|
|
|
420
454
|
isEqualizerEnabled = enabled
|
|
421
455
|
|
|
422
456
|
try {
|
|
423
|
-
|
|
457
|
+
if (usingDynamicsProcessing && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
458
|
+
dynamicsProcessing?.enabled = enabled
|
|
459
|
+
} else {
|
|
460
|
+
equalizer?.enabled = enabled
|
|
461
|
+
}
|
|
424
462
|
} catch (e: Exception) {
|
|
425
463
|
// Ignore
|
|
426
464
|
}
|
|
@@ -429,71 +467,48 @@ class EqualizerCore private constructor(
|
|
|
429
467
|
// === Callback management ===
|
|
430
468
|
|
|
431
469
|
fun addOnEnabledChangeListener(callback: (Boolean) -> Unit) {
|
|
432
|
-
|
|
433
|
-
onEnabledChangeListeners.add(box)
|
|
470
|
+
onEnabledChangeListeners.add(callback)
|
|
434
471
|
}
|
|
435
472
|
|
|
436
473
|
fun addOnBandChangeListener(callback: (Array<EqualizerBand>) -> Unit) {
|
|
437
|
-
|
|
438
|
-
synchronized(onBandChangeListeners) {
|
|
439
|
-
@Suppress("UNCHECKED_CAST")
|
|
440
|
-
(onBandChangeListeners as MutableList<WeakCallbackBox<(Array<EqualizerBand>) -> Unit>>).add(box)
|
|
441
|
-
}
|
|
474
|
+
onBandChangeListeners.add(callback)
|
|
442
475
|
}
|
|
443
476
|
|
|
444
477
|
fun addOnPresetChangeListener(callback: (Variant_NullType_String?) -> Unit) {
|
|
445
|
-
|
|
446
|
-
onPresetChangeListeners.add(box)
|
|
478
|
+
onPresetChangeListeners.add(callback)
|
|
447
479
|
}
|
|
448
480
|
|
|
449
481
|
private fun notifyEnabledChange(enabled: Boolean) {
|
|
450
|
-
|
|
451
|
-
onEnabledChangeListeners.removeAll { !it.isAlive }
|
|
452
|
-
onEnabledChangeListeners.forEach { box ->
|
|
453
|
-
try {
|
|
454
|
-
box.callback(enabled)
|
|
455
|
-
} catch (e: Exception) {
|
|
456
|
-
// Ignore callback errors
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
482
|
+
onEnabledChangeListeners.forEach { it(enabled) }
|
|
460
483
|
}
|
|
461
484
|
|
|
462
485
|
private fun notifyBandChange(bands: Array<EqualizerBand>) {
|
|
463
|
-
|
|
464
|
-
@Suppress("UNCHECKED_CAST")
|
|
465
|
-
val listeners = onBandChangeListeners as MutableList<WeakCallbackBox<(Array<EqualizerBand>) -> Unit>>
|
|
466
|
-
listeners.removeAll { !it.isAlive }
|
|
467
|
-
listeners.forEach { box ->
|
|
468
|
-
try {
|
|
469
|
-
box.callback(bands)
|
|
470
|
-
} catch (e: Exception) {
|
|
471
|
-
// Ignore callback errors
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
486
|
+
onBandChangeListeners.forEach { it(bands) }
|
|
475
487
|
}
|
|
476
488
|
|
|
477
489
|
private fun notifyPresetChange(presetName: String?) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
onPresetChangeListeners.forEach { box ->
|
|
481
|
-
try {
|
|
482
|
-
val variant = presetName?.let { Variant_NullType_String.create(it) }
|
|
483
|
-
box.callback(variant)
|
|
484
|
-
} catch (e: Exception) {
|
|
485
|
-
// Ignore callback errors
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
490
|
+
val variant = presetName?.let { Variant_NullType_String.create(it) }
|
|
491
|
+
onPresetChangeListeners.forEach { it(variant) }
|
|
489
492
|
}
|
|
490
493
|
|
|
491
|
-
fun
|
|
494
|
+
private fun releaseAudioEffects() {
|
|
492
495
|
try {
|
|
493
496
|
equalizer?.release()
|
|
494
497
|
equalizer = null
|
|
495
498
|
} catch (e: Exception) {
|
|
496
499
|
// Ignore
|
|
497
500
|
}
|
|
501
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
502
|
+
try {
|
|
503
|
+
dynamicsProcessing?.release()
|
|
504
|
+
dynamicsProcessing = null
|
|
505
|
+
} catch (e: Exception) {
|
|
506
|
+
// Ignore
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
fun release() {
|
|
512
|
+
releaseAudioEffects()
|
|
498
513
|
}
|
|
499
514
|
}
|
|
@@ -26,6 +26,7 @@ import com.google.common.util.concurrent.ListenableFuture
|
|
|
26
26
|
import com.margelo.nitro.nitroplayer.TrackItem
|
|
27
27
|
import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
|
|
28
28
|
import com.margelo.nitro.nitroplayer.core.TrackPlayerCore
|
|
29
|
+
import com.margelo.nitro.nitroplayer.core.loadPlaylist
|
|
29
30
|
import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
|
|
30
31
|
import com.margelo.nitro.nitroplayer.playlist.PlaylistManager
|
|
31
32
|
import kotlinx.coroutines.CoroutineScope
|
|
@@ -50,6 +51,8 @@ class MediaSessionManager(
|
|
|
50
51
|
private set
|
|
51
52
|
private var notificationManager: NotificationManager? = null
|
|
52
53
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
|
54
|
+
@Volatile private var currentTrack: TrackItem? = null
|
|
55
|
+
@Volatile private var isPlaying: Boolean = false
|
|
53
56
|
private val artworkCache = object : LruCache<String, Bitmap>(20) {
|
|
54
57
|
override fun sizeOf(key: String, value: Bitmap): Int = 1
|
|
55
58
|
}
|
|
@@ -216,7 +219,7 @@ class MediaSessionManager(
|
|
|
216
219
|
|
|
217
220
|
if (trackIndex >= 0) {
|
|
218
221
|
// Load the entire playlist into TrackPlayerCore
|
|
219
|
-
trackPlayerCore?.loadPlaylist(playlistId)
|
|
222
|
+
trackPlayerCore?.let { core -> scope.launch { core.loadPlaylist(playlistId) } }
|
|
220
223
|
|
|
221
224
|
// Create MediaItems for the entire playlist
|
|
222
225
|
val playlistMediaItems =
|
|
@@ -324,28 +327,7 @@ class MediaSessionManager(
|
|
|
324
327
|
}
|
|
325
328
|
}
|
|
326
329
|
|
|
327
|
-
private fun getCurrentTrack(): TrackItem?
|
|
328
|
-
val currentMediaItem = player.currentMediaItem ?: return null
|
|
329
|
-
val mediaId = currentMediaItem.mediaId
|
|
330
|
-
|
|
331
|
-
// Parse mediaId format: "playlistId:trackId" or just "trackId"
|
|
332
|
-
val trackId =
|
|
333
|
-
if (mediaId.contains(':')) {
|
|
334
|
-
mediaId.substring(mediaId.indexOf(':') + 1)
|
|
335
|
-
} else {
|
|
336
|
-
mediaId
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Find track in current playlist or all playlists
|
|
340
|
-
return trackPlayerCore?.getCurrentPlaylistId()?.let { playlistId ->
|
|
341
|
-
playlistManager.getPlaylist(playlistId)?.tracks?.find { it.id == trackId }
|
|
342
|
-
} ?: run {
|
|
343
|
-
for (playlist in playlistManager.getAllPlaylists()) {
|
|
344
|
-
playlist.tracks.find { it.id == trackId }?.let { return it }
|
|
345
|
-
}
|
|
346
|
-
null
|
|
347
|
-
}
|
|
348
|
-
}
|
|
330
|
+
private fun getCurrentTrack(): TrackItem? = currentTrack
|
|
349
331
|
|
|
350
332
|
private fun updateNotification() {
|
|
351
333
|
if (!showInNotification) return
|
|
@@ -376,7 +358,7 @@ class MediaSessionManager(
|
|
|
376
358
|
.setSmallIcon(android.R.drawable.ic_media_play)
|
|
377
359
|
.setContentIntent(contentIntent)
|
|
378
360
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
379
|
-
.setOngoing(
|
|
361
|
+
.setOngoing(isPlaying)
|
|
380
362
|
.setShowWhen(false)
|
|
381
363
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
382
364
|
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
|
@@ -401,7 +383,7 @@ class MediaSessionManager(
|
|
|
401
383
|
createMediaAction(ACTION_PREVIOUS),
|
|
402
384
|
)
|
|
403
385
|
|
|
404
|
-
if (
|
|
386
|
+
if (isPlaying) {
|
|
405
387
|
builder.addAction(
|
|
406
388
|
android.R.drawable.ic_media_pause,
|
|
407
389
|
"Pause",
|
|
@@ -459,12 +441,12 @@ class MediaSessionManager(
|
|
|
459
441
|
notificationManager?.cancel(NOTIFICATION_ID)
|
|
460
442
|
}
|
|
461
443
|
|
|
462
|
-
fun onTrackChanged() {
|
|
444
|
+
fun onTrackChanged(track: TrackItem?) {
|
|
445
|
+
currentTrack = track
|
|
463
446
|
// Preload artwork for better notification display
|
|
464
|
-
|
|
465
|
-
if (currentTrack != null) {
|
|
447
|
+
if (track != null) {
|
|
466
448
|
scope.launch {
|
|
467
|
-
|
|
449
|
+
track.artwork?.asSecondOrNull()?.let { artworkUrl ->
|
|
468
450
|
loadArtworkBitmap(artworkUrl)
|
|
469
451
|
}
|
|
470
452
|
updateNotification()
|
|
@@ -474,7 +456,8 @@ class MediaSessionManager(
|
|
|
474
456
|
}
|
|
475
457
|
}
|
|
476
458
|
|
|
477
|
-
fun onPlaybackStateChanged() {
|
|
459
|
+
fun onPlaybackStateChanged(playing: Boolean) {
|
|
460
|
+
isPlaying = playing
|
|
478
461
|
updateNotification()
|
|
479
462
|
}
|
|
480
463
|
|