@javascriptcommon/react-native-track-player 4.1.10 → 4.1.13
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/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt +57 -0
- package/android/src/main/java/com/doublesymmetry/trackplayer/service/MusicService.kt +42 -0
- package/android/src/main/java/com/lovegaoshi/kotlinaudio/player/AudioPlayer.kt +148 -0
- package/ios/RNTrackPlayer/RNTrackPlayer.swift +79 -1
- package/ios/RNTrackPlayer/TrackPlayer.mm +39 -1
- package/ios/SwiftAudioEx/Sources/SwiftAudioEx/AVPlayerWrapper/AVPlayerWrapper.swift +6 -12
- package/ios/SwiftAudioEx/Sources/SwiftAudioEx/EqualizerAudioTap.swift +346 -0
- package/lib/specs/NativeTrackPlayer.d.ts +9 -0
- package/lib/src/trackPlayer.d.ts +48 -0
- package/lib/src/trackPlayer.js +67 -0
- package/package.json +1 -1
- package/specs/NativeTrackPlayer.ts +11 -0
- package/src/trackPlayer.ts +80 -0
|
@@ -325,6 +325,63 @@ class MusicModule(reactContext: ReactApplicationContext) : NativeTrackPlayerSpec
|
|
|
325
325
|
callback.resolve(Arguments.fromList(musicService.getEqualizerPresets()))
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
+
// Cross-platform Equalizer Band API
|
|
329
|
+
|
|
330
|
+
override fun setEqualizerEnabled(enabled: Boolean, callback: Promise) = launchInScope {
|
|
331
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
332
|
+
musicService.setEqualizerEnabled(enabled)
|
|
333
|
+
callback.resolve(null)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
override fun getEqualizerEnabled(callback: Promise) = launchInScope {
|
|
337
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
338
|
+
callback.resolve(musicService.getEqualizerEnabled())
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
override fun setEqualizerBand(band: Double, gain: Double, callback: Promise) = launchInScope {
|
|
342
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
343
|
+
musicService.setEqualizerBand(band.toInt(), gain.toFloat())
|
|
344
|
+
callback.resolve(null)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
override fun setEqualizerBands(gains: ReadableArray?, callback: Promise) = launchInScope {
|
|
348
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
349
|
+
val gainsList = mutableListOf<Float>()
|
|
350
|
+
gains?.let {
|
|
351
|
+
for (i in 0 until it.size()) {
|
|
352
|
+
gainsList.add(it.getDouble(i).toFloat())
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
musicService.setEqualizerBands(gainsList)
|
|
356
|
+
callback.resolve(null)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
override fun getEqualizerBands(callback: Promise) = launchInScope {
|
|
360
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
361
|
+
callback.resolve(Arguments.fromList(musicService.getEqualizerBands()))
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
override fun getEqualizerFrequencies(callback: Promise) = launchInScope {
|
|
365
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
366
|
+
callback.resolve(Arguments.fromList(musicService.getEqualizerFrequencies()))
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
override fun applyEqualizerPreset(presetIndex: Double, callback: Promise) = launchInScope {
|
|
370
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
371
|
+
musicService.applyEqualizerPreset(presetIndex.toInt())
|
|
372
|
+
callback.resolve(null)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
override fun getEqualizerPresetNames(callback: Promise) = launchInScope {
|
|
376
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
377
|
+
callback.resolve(Arguments.fromList(musicService.getEqualizerPresetNames()))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
override fun resetEqualizer(callback: Promise) = launchInScope {
|
|
381
|
+
if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
|
382
|
+
musicService.resetEqualizer()
|
|
383
|
+
callback.resolve(null)
|
|
384
|
+
}
|
|
328
385
|
|
|
329
386
|
override fun setLoudnessEnhance(gain: Double, callback: Promise) = launchInScope {
|
|
330
387
|
musicService.setLoudnessEnhance(gain.toInt())
|
|
@@ -156,6 +156,48 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
156
156
|
player.setLoudnessEnhance(gain)
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
// Cross-platform Equalizer Band API
|
|
160
|
+
|
|
161
|
+
fun setEqualizerEnabled(enabled: Boolean) {
|
|
162
|
+
player.setEqualizerEnabled(enabled)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fun getEqualizerEnabled(): Boolean {
|
|
166
|
+
return player.getEqualizerEnabled()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fun setEqualizerBand(band: Int, gain: Float) {
|
|
170
|
+
player.setEqualizerBand(band, gain)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fun setEqualizerBands(gains: List<Float>) {
|
|
174
|
+
player.setEqualizerBands(gains)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fun getEqualizerBands(): List<Float> {
|
|
178
|
+
return player.getEqualizerBands()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
fun getEqualizerFrequencies(): List<Int> {
|
|
182
|
+
return player.getEqualizerFrequencies()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
fun getEqualizerBandLevelRange(): List<Float> {
|
|
186
|
+
return player.getEqualizerBandLevelRange()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fun applyEqualizerPreset(presetIndex: Int) {
|
|
190
|
+
player.applyEqualizerPreset(presetIndex)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
fun getEqualizerPresetNames(): List<String> {
|
|
194
|
+
return player.getEqualizerPresetNames()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fun resetEqualizer() {
|
|
198
|
+
player.resetEqualizer()
|
|
199
|
+
}
|
|
200
|
+
|
|
159
201
|
fun crossFadePrepare(previous: Boolean = false, seekTo: Double = 0.0) {
|
|
160
202
|
player.crossFadePrepare(previous, seekTo)
|
|
161
203
|
}
|
|
@@ -295,6 +295,154 @@ abstract class AudioPlayer internal constructor(
|
|
|
295
295
|
.map { i -> equalizers[0].getPresetName(i.toShort()) }
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
// 10-band Equalizer API (cross-platform compatible)
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Enable or disable the equalizer
|
|
302
|
+
*/
|
|
303
|
+
fun setEqualizerEnabled(enabled: Boolean) {
|
|
304
|
+
equalizers.forEach { equalizer ->
|
|
305
|
+
equalizer.enabled = enabled
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Check if the equalizer is enabled
|
|
311
|
+
*/
|
|
312
|
+
fun getEqualizerEnabled(): Boolean {
|
|
313
|
+
if (equalizers.isEmpty()) return false
|
|
314
|
+
return equalizers[0].enabled
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Get the number of equalizer bands available
|
|
319
|
+
*/
|
|
320
|
+
fun getEqualizerBandCount(): Int {
|
|
321
|
+
if (equalizers.isEmpty()) return 0
|
|
322
|
+
return equalizers[0].numberOfBands.toInt()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Set the gain for a specific equalizer band
|
|
327
|
+
* @param band Band index (0 to bandCount-1)
|
|
328
|
+
* @param gainDB Gain in millibels (mB). 1 dB = 100 mB
|
|
329
|
+
*/
|
|
330
|
+
fun setEqualizerBand(band: Int, gainDB: Float) {
|
|
331
|
+
equalizers.forEach { equalizer ->
|
|
332
|
+
if (band >= 0 && band < equalizer.numberOfBands) {
|
|
333
|
+
// Convert dB to millibels (mB)
|
|
334
|
+
val millibels = (gainDB * 100).toInt().toShort()
|
|
335
|
+
val range = equalizer.bandLevelRange
|
|
336
|
+
val clampedLevel = millibels.coerceIn(range[0], range[1])
|
|
337
|
+
equalizer.setBandLevel(band.toShort(), clampedLevel)
|
|
338
|
+
equalizer.enabled = true
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Set gains for all equalizer bands at once
|
|
345
|
+
* @param gainsDB Array of gain values in dB
|
|
346
|
+
*/
|
|
347
|
+
fun setEqualizerBands(gainsDB: List<Float>) {
|
|
348
|
+
equalizers.forEach { equalizer ->
|
|
349
|
+
val bandCount = equalizer.numberOfBands.toInt()
|
|
350
|
+
val range = equalizer.bandLevelRange
|
|
351
|
+
for (i in 0 until minOf(gainsDB.size, bandCount)) {
|
|
352
|
+
val millibels = (gainsDB[i] * 100).toInt().toShort()
|
|
353
|
+
val clampedLevel = millibels.coerceIn(range[0], range[1])
|
|
354
|
+
equalizer.setBandLevel(i.toShort(), clampedLevel)
|
|
355
|
+
}
|
|
356
|
+
equalizer.enabled = true
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Get all current equalizer band gains
|
|
362
|
+
* @return Array of gain values in dB
|
|
363
|
+
*/
|
|
364
|
+
fun getEqualizerBands(): List<Float> {
|
|
365
|
+
if (equalizers.isEmpty()) return emptyList()
|
|
366
|
+
val equalizer = equalizers[0]
|
|
367
|
+
val bandCount = equalizer.numberOfBands.toInt()
|
|
368
|
+
return (0 until bandCount).map { band ->
|
|
369
|
+
// Convert millibels to dB
|
|
370
|
+
equalizer.getBandLevel(band.toShort()).toFloat() / 100f
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get the center frequencies for each equalizer band
|
|
376
|
+
* @return Array of frequency values in Hz
|
|
377
|
+
*/
|
|
378
|
+
fun getEqualizerFrequencies(): List<Int> {
|
|
379
|
+
if (equalizers.isEmpty()) return emptyList()
|
|
380
|
+
val equalizer = equalizers[0]
|
|
381
|
+
val bandCount = equalizer.numberOfBands.toInt()
|
|
382
|
+
return (0 until bandCount).map { band ->
|
|
383
|
+
// getCenterFreq returns milliHz, convert to Hz
|
|
384
|
+
(equalizer.getCenterFreq(band.toShort()) / 1000)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get the band level range in dB [min, max]
|
|
390
|
+
*/
|
|
391
|
+
fun getEqualizerBandLevelRange(): List<Float> {
|
|
392
|
+
if (equalizers.isEmpty()) return listOf(-12f, 12f)
|
|
393
|
+
val range = equalizers[0].bandLevelRange
|
|
394
|
+
// Convert millibels to dB
|
|
395
|
+
return listOf(range[0].toFloat() / 100f, range[1].toFloat() / 100f)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Reset all equalizer bands to 0 (flat response)
|
|
400
|
+
*/
|
|
401
|
+
fun resetEqualizer() {
|
|
402
|
+
equalizers.forEach { equalizer ->
|
|
403
|
+
val bandCount = equalizer.numberOfBands.toInt()
|
|
404
|
+
for (i in 0 until bandCount) {
|
|
405
|
+
equalizer.setBandLevel(i.toShort(), 0)
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Get preset names for iOS compatibility (custom presets mapped to Android system presets)
|
|
412
|
+
*/
|
|
413
|
+
fun getEqualizerPresetNames(): List<String> {
|
|
414
|
+
// Return iOS-compatible preset names
|
|
415
|
+
return listOf(
|
|
416
|
+
"Flat", "Rock", "Pop", "Jazz", "Classical",
|
|
417
|
+
"Hip Hop", "Electronic", "Acoustic", "Bass Boost",
|
|
418
|
+
"Treble Boost", "Vocal", "Loudness"
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Apply a preset by index (iOS-compatible)
|
|
424
|
+
* Maps iOS preset index to gain values
|
|
425
|
+
*/
|
|
426
|
+
fun applyEqualizerPreset(presetIndex: Int) {
|
|
427
|
+
val presets = listOf(
|
|
428
|
+
listOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f), // Flat
|
|
429
|
+
listOf(5f, 4f, 3f, 1f, -1f, 0f, 2f, 3f, 4f, 4f), // Rock
|
|
430
|
+
listOf(-1f, 1f, 3f, 4f, 3f, 1f, 0f, 1f, 2f, 2f), // Pop
|
|
431
|
+
listOf(3f, 2f, 1f, 2f, -1f, -1f, 0f, 1f, 2f, 3f), // Jazz
|
|
432
|
+
listOf(4f, 3f, 2f, 1f, -1f, -1f, 0f, 2f, 3f, 4f), // Classical
|
|
433
|
+
listOf(5f, 5f, 3f, 1f, -1f, 0f, 1f, 0f, 2f, 3f), // Hip Hop
|
|
434
|
+
listOf(4f, 4f, 2f, 0f, -2f, -1f, 0f, 2f, 4f, 4f), // Electronic
|
|
435
|
+
listOf(3f, 2f, 1f, 1f, 0f, 0f, 1f, 2f, 2f, 2f), // Acoustic
|
|
436
|
+
listOf(6f, 5f, 4f, 2f, 0f, 0f, 0f, 0f, 0f, 0f), // Bass Boost
|
|
437
|
+
listOf(0f, 0f, 0f, 0f, 0f, 1f, 2f, 4f, 5f, 6f), // Treble Boost
|
|
438
|
+
listOf(-2f, -1f, 0f, 2f, 4f, 4f, 3f, 1f, 0f, -1f), // Vocal
|
|
439
|
+
listOf(5f, 4f, 2f, 0f, -2f, -2f, 0f, 2f, 4f, 5f) // Loudness
|
|
440
|
+
)
|
|
441
|
+
if (presetIndex >= 0 && presetIndex < presets.size) {
|
|
442
|
+
setEqualizerBands(presets[presetIndex])
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
298
446
|
fun togglePlaying() {
|
|
299
447
|
if (exoPlayer.isPlaying) {
|
|
300
448
|
pause()
|
|
@@ -35,6 +35,7 @@ public class RNTrackPlayer: NSObject, AudioSessionControllerDelegate {
|
|
|
35
35
|
private var hasInitialized = false
|
|
36
36
|
private let player = QueuedAudioPlayer()
|
|
37
37
|
private let audioSessionController = AudioSessionController.shared
|
|
38
|
+
private let equalizerTap = EqualizerAudioTap() // Always created, attached at setup
|
|
38
39
|
private var shouldEmitProgressEvent: Bool = false
|
|
39
40
|
private var shouldResumePlaybackAfterInterruptionEnds: Bool = false
|
|
40
41
|
private var forwardJumpInterval: NSNumber? = nil;
|
|
@@ -134,11 +135,12 @@ public class RNTrackPlayer: NSObject, AudioSessionControllerDelegate {
|
|
|
134
135
|
return
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
// configure the FFT audio tap
|
|
138
|
+
// configure the FFT audio tap if specified
|
|
138
139
|
if let fftLength = config["useFFTProcessor"] as? Int {
|
|
139
140
|
player.audioTap = WaveformAudioTap(mFFTLength: fftLength, mEmit: {data in
|
|
140
141
|
self.emit(event:EventType.FFTUpdated, body:data)})
|
|
141
142
|
}
|
|
143
|
+
// Note: Equalizer tap is NOT attached by default. Call setEqualizerEnabled(true) to enable.
|
|
142
144
|
|
|
143
145
|
// configure buffer size
|
|
144
146
|
if let bufferDuration = config["minBuffer"] as? TimeInterval {
|
|
@@ -895,6 +897,82 @@ public class RNTrackPlayer: NSObject, AudioSessionControllerDelegate {
|
|
|
895
897
|
]
|
|
896
898
|
)
|
|
897
899
|
}
|
|
900
|
+
|
|
901
|
+
// MARK: - iOS Equalizer Methods
|
|
902
|
+
|
|
903
|
+
@objc(setEqualizerEnabled:resolver:rejecter:)
|
|
904
|
+
public func setEqualizerEnabled(enabled: Bool, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
905
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
906
|
+
|
|
907
|
+
// Toggle the enabled flag
|
|
908
|
+
// Note: For the equalizer to work, the tap must be attached to the player first
|
|
909
|
+
equalizerTap.isEnabled = enabled
|
|
910
|
+
resolve(NSNull())
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
@objc(getEqualizerEnabled:rejecter:)
|
|
914
|
+
public func getEqualizerEnabled(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
915
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
916
|
+
|
|
917
|
+
resolve(equalizerTap.isEnabled)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
@objc(setEqualizerBand:gain:resolver:rejecter:)
|
|
921
|
+
public func setEqualizerBand(band: Int, gain: Float, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
922
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
923
|
+
|
|
924
|
+
equalizerTap.setGain(band: band, gainDB: gain)
|
|
925
|
+
resolve(NSNull())
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
@objc(setEqualizerBands:resolver:rejecter:)
|
|
929
|
+
public func setEqualizerBands(gains: [NSNumber], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
930
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
931
|
+
|
|
932
|
+
let floatGains = gains.map { $0.floatValue }
|
|
933
|
+
equalizerTap.setAllGains(floatGains)
|
|
934
|
+
resolve(NSNull())
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
@objc(getEqualizerBands:rejecter:)
|
|
938
|
+
public func getEqualizerBands(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
939
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
940
|
+
|
|
941
|
+
resolve(equalizerTap.getAllGains())
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
@objc(getEqualizerFrequencies:rejecter:)
|
|
945
|
+
public func getEqualizerFrequencies(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
946
|
+
resolve(EqualizerAudioTap.frequencies)
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
@objc(applyEqualizerPreset:resolver:rejecter:)
|
|
950
|
+
public func applyEqualizerPreset(presetIndex: Int, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
951
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
952
|
+
|
|
953
|
+
let presets = EqualizerAudioTap.Preset.allCases
|
|
954
|
+
guard presetIndex >= 0 && presetIndex < presets.count else {
|
|
955
|
+
reject("invalid_preset", "Preset index out of bounds", nil)
|
|
956
|
+
return
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
let preset = presets[presetIndex]
|
|
960
|
+
equalizerTap.applyPreset(preset)
|
|
961
|
+
resolve(NSNull())
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
@objc(getEqualizerPresetNames:rejecter:)
|
|
965
|
+
public func getEqualizerPresetNames(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
966
|
+
resolve(EqualizerAudioTap.presetNames)
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
@objc(resetEqualizer:rejecter:)
|
|
970
|
+
public func resetEqualizer(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
|
|
971
|
+
if (rejectWhenNotInitialized(reject: reject)) { return }
|
|
972
|
+
|
|
973
|
+
equalizerTap.resetGains()
|
|
974
|
+
resolve(NSNull())
|
|
975
|
+
}
|
|
898
976
|
}
|
|
899
977
|
|
|
900
978
|
extension RNTrackPlayer {
|
|
@@ -243,7 +243,45 @@ RCT_EXPORT_MODULE()
|
|
|
243
243
|
- (void)setPlaybackState:(nonnull NSString *)mediaID resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
- (void)switchExoPlayer:(double)fadeDuration fadeInterval:(double)fadeInterval fadeToVolume:(double)fadeToVolume waitUntil:(nonnull NSNumber *)waitUntil resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
246
|
+
- (void)switchExoPlayer:(double)fadeDuration fadeInterval:(double)fadeInterval fadeToVolume:(double)fadeToVolume waitUntil:(nonnull NSNumber *)waitUntil resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// iOS Equalizer methods
|
|
250
|
+
|
|
251
|
+
- (void)setEqualizerEnabled:(BOOL)enabled resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
252
|
+
[trackPlayer setEqualizerEnabled:enabled resolver:resolve rejecter:reject];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
- (void)getEqualizerEnabled:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
256
|
+
[trackPlayer getEqualizerEnabled:resolve rejecter:reject];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
- (void)setEqualizerBand:(double)band gain:(double)gain resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
260
|
+
[trackPlayer setEqualizerBand:(NSInteger)band gain:(float)gain resolver:resolve rejecter:reject];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
- (void)setEqualizerBands:(nonnull NSArray *)gains resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
264
|
+
[trackPlayer setEqualizerBands:gains resolver:resolve rejecter:reject];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
- (void)getEqualizerBands:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
268
|
+
[trackPlayer getEqualizerBands:resolve rejecter:reject];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
- (void)getEqualizerFrequencies:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
272
|
+
[trackPlayer getEqualizerFrequencies:resolve rejecter:reject];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
- (void)applyEqualizerPreset:(double)presetIndex resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
276
|
+
[trackPlayer applyEqualizerPreset:(NSInteger)presetIndex resolver:resolve rejecter:reject];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
- (void)getEqualizerPresetNames:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
280
|
+
[trackPlayer getEqualizerPresetNames:resolve rejecter:reject];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
- (void)resetEqualizer:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
284
|
+
[trackPlayer resetEqualizer:resolve rejecter:reject];
|
|
247
285
|
}
|
|
248
286
|
|
|
249
287
|
@end
|
|
@@ -40,6 +40,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
|
|
40
40
|
label: "AVPlayerWrapper.stateQueue",
|
|
41
41
|
attributes: .concurrent
|
|
42
42
|
)
|
|
43
|
+
private var cachedDuration: TimeInterval = 0.0
|
|
43
44
|
|
|
44
45
|
public init() {
|
|
45
46
|
playerTimeObserver = AVPlayerTimeObserver(periodicObserverTimeInterval: timeEventFrequency.getTime())
|
|
@@ -115,17 +116,8 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
var duration: TimeInterval {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
else if let seconds = currentItem?.duration.seconds, !seconds.isNaN {
|
|
122
|
-
return seconds
|
|
123
|
-
}
|
|
124
|
-
else if let seconds = currentItem?.seekableTimeRanges.last?.timeRangeValue.duration.seconds,
|
|
125
|
-
!seconds.isNaN {
|
|
126
|
-
return seconds
|
|
127
|
-
}
|
|
128
|
-
return 0.0
|
|
119
|
+
// Return cached duration to avoid synchronous access to AVAsset properties
|
|
120
|
+
return cachedDuration
|
|
129
121
|
}
|
|
130
122
|
|
|
131
123
|
var bufferedPosition: TimeInterval {
|
|
@@ -242,7 +234,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
|
|
242
234
|
state = .loading
|
|
243
235
|
|
|
244
236
|
// Load metadata keys asynchronously and separate from playable, to allow that to execute as quickly as it can
|
|
245
|
-
let metdataKeys = ["commonMetadata", "availableChapterLocales", "availableMetadataFormats"]
|
|
237
|
+
let metdataKeys = ["commonMetadata", "availableChapterLocales", "availableMetadataFormats", "duration"]
|
|
246
238
|
pendingAsset.loadValuesAsynchronously(forKeys: metdataKeys, completionHandler: { [weak self] in
|
|
247
239
|
guard let self = self else { return }
|
|
248
240
|
if (pendingAsset != self.asset) { return; }
|
|
@@ -381,6 +373,7 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
|
|
381
373
|
|
|
382
374
|
asset.cancelLoading()
|
|
383
375
|
self.asset = nil
|
|
376
|
+
cachedDuration = 0.0
|
|
384
377
|
|
|
385
378
|
avPlayer.replaceCurrentItem(with: nil)
|
|
386
379
|
}
|
|
@@ -518,6 +511,7 @@ extension AVPlayerWrapper: AVPlayerItemObserverDelegate {
|
|
|
518
511
|
}
|
|
519
512
|
|
|
520
513
|
func item(didUpdateDuration duration: Double) {
|
|
514
|
+
cachedDuration = duration
|
|
521
515
|
delegate?.AVWrapper(didUpdateDuration: duration)
|
|
522
516
|
}
|
|
523
517
|
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
//
|
|
2
|
+
// EqualizerAudioTap.swift
|
|
3
|
+
// SwiftAudioEx
|
|
4
|
+
//
|
|
5
|
+
// iOS Equalizer implementation using biquad peaking filters
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import AVFoundation
|
|
10
|
+
import Accelerate
|
|
11
|
+
|
|
12
|
+
/// 10-band parametric equalizer using cascaded biquad filters
|
|
13
|
+
/// Processes audio in real-time via MTAudioProcessingTap
|
|
14
|
+
public class EqualizerAudioTap: AudioTap {
|
|
15
|
+
|
|
16
|
+
// MARK: - Constants
|
|
17
|
+
|
|
18
|
+
/// Standard 10-band equalizer frequencies (Hz)
|
|
19
|
+
public static let frequencies: [Float] = [
|
|
20
|
+
32, // Sub-bass
|
|
21
|
+
64, // Bass
|
|
22
|
+
125, // Low-bass
|
|
23
|
+
250, // Low-mid
|
|
24
|
+
500, // Mid
|
|
25
|
+
1000, // Mid
|
|
26
|
+
2000, // Upper-mid
|
|
27
|
+
4000, // Presence
|
|
28
|
+
8000, // Brilliance
|
|
29
|
+
16000 // Air
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
/// Number of equalizer bands
|
|
33
|
+
public static let bandCount = 10
|
|
34
|
+
|
|
35
|
+
/// Default Q factor for peaking filters (standard for graphic EQ)
|
|
36
|
+
private static let defaultQ: Float = 1.41
|
|
37
|
+
|
|
38
|
+
// MARK: - Properties
|
|
39
|
+
|
|
40
|
+
/// Whether the equalizer is enabled
|
|
41
|
+
public var isEnabled: Bool = true
|
|
42
|
+
|
|
43
|
+
/// Gain values for each band in dB (-12 to +12)
|
|
44
|
+
private var _gains: [Float] = Array(repeating: 0, count: bandCount)
|
|
45
|
+
|
|
46
|
+
/// Thread-safe access to gains
|
|
47
|
+
private let gainsLock = NSLock()
|
|
48
|
+
|
|
49
|
+
/// Current sample rate (set during prepare)
|
|
50
|
+
private var sampleRate: Float = 44100
|
|
51
|
+
|
|
52
|
+
/// Biquad filter coefficients for each band [b0, b1, b2, a1, a2]
|
|
53
|
+
private var coefficients: [[Float]] = []
|
|
54
|
+
|
|
55
|
+
/// Filter state (delay elements) for each band, per channel
|
|
56
|
+
/// Structure: [band][channel][z1, z2]
|
|
57
|
+
private var filterStates: [[[Float]]] = []
|
|
58
|
+
|
|
59
|
+
/// Number of audio channels
|
|
60
|
+
private var channelCount: Int = 2
|
|
61
|
+
|
|
62
|
+
/// Flag to indicate coefficients need recalculation
|
|
63
|
+
private var needsUpdate: Bool = true
|
|
64
|
+
|
|
65
|
+
// MARK: - Public API
|
|
66
|
+
|
|
67
|
+
public override init() {
|
|
68
|
+
super.init()
|
|
69
|
+
// Initialize with flat response (all gains at 0)
|
|
70
|
+
resetGains()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Set gain for a specific band
|
|
74
|
+
/// - Parameters:
|
|
75
|
+
/// - band: Band index (0-9)
|
|
76
|
+
/// - gainDB: Gain in decibels (-12 to +12)
|
|
77
|
+
public func setGain(band: Int, gainDB: Float) {
|
|
78
|
+
guard band >= 0 && band < Self.bandCount else { return }
|
|
79
|
+
|
|
80
|
+
let clampedGain = max(-12, min(12, gainDB))
|
|
81
|
+
|
|
82
|
+
gainsLock.lock()
|
|
83
|
+
_gains[band] = clampedGain
|
|
84
|
+
needsUpdate = true
|
|
85
|
+
gainsLock.unlock()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Set gains for all bands at once
|
|
89
|
+
/// - Parameter gains: Array of 10 gain values in dB
|
|
90
|
+
public func setAllGains(_ gains: [Float]) {
|
|
91
|
+
guard gains.count == Self.bandCount else { return }
|
|
92
|
+
|
|
93
|
+
gainsLock.lock()
|
|
94
|
+
for i in 0..<Self.bandCount {
|
|
95
|
+
_gains[i] = max(-12, min(12, gains[i]))
|
|
96
|
+
}
|
|
97
|
+
needsUpdate = true
|
|
98
|
+
gainsLock.unlock()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Get current gain for a specific band
|
|
102
|
+
public func getGain(band: Int) -> Float {
|
|
103
|
+
guard band >= 0 && band < Self.bandCount else { return 0 }
|
|
104
|
+
|
|
105
|
+
gainsLock.lock()
|
|
106
|
+
let gain = _gains[band]
|
|
107
|
+
gainsLock.unlock()
|
|
108
|
+
return gain
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Get all current gains
|
|
112
|
+
public func getAllGains() -> [Float] {
|
|
113
|
+
gainsLock.lock()
|
|
114
|
+
let gains = _gains
|
|
115
|
+
gainsLock.unlock()
|
|
116
|
+
return gains
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Reset all gains to 0 (flat response)
|
|
120
|
+
public func resetGains() {
|
|
121
|
+
gainsLock.lock()
|
|
122
|
+
_gains = Array(repeating: 0, count: Self.bandCount)
|
|
123
|
+
needsUpdate = true
|
|
124
|
+
gainsLock.unlock()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// MARK: - AudioTap Overrides
|
|
128
|
+
|
|
129
|
+
public override func initialize() {
|
|
130
|
+
// Called when tap is attached
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public override func finalize() {
|
|
134
|
+
// Called when tap is detached
|
|
135
|
+
filterStates = []
|
|
136
|
+
coefficients = []
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public override func prepare(description: AudioStreamBasicDescription) {
|
|
140
|
+
sampleRate = Float(description.mSampleRate)
|
|
141
|
+
channelCount = Int(description.mChannelsPerFrame)
|
|
142
|
+
|
|
143
|
+
// Initialize filter states for each band and channel
|
|
144
|
+
filterStates = Array(
|
|
145
|
+
repeating: Array(
|
|
146
|
+
repeating: [0, 0], // z1, z2 delay elements
|
|
147
|
+
count: channelCount
|
|
148
|
+
),
|
|
149
|
+
count: Self.bandCount
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// Calculate initial coefficients
|
|
153
|
+
updateCoefficients()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public override func unprepare() {
|
|
157
|
+
// Reset filter states
|
|
158
|
+
for band in 0..<filterStates.count {
|
|
159
|
+
for channel in 0..<filterStates[band].count {
|
|
160
|
+
filterStates[band][channel] = [0, 0]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public override func process(numberOfFrames: Int, buffer: UnsafeMutableAudioBufferListPointer) {
|
|
166
|
+
// Skip processing if disabled or no frames
|
|
167
|
+
guard isEnabled && numberOfFrames > 0 else { return }
|
|
168
|
+
|
|
169
|
+
// Check if coefficients need update
|
|
170
|
+
gainsLock.lock()
|
|
171
|
+
if needsUpdate {
|
|
172
|
+
updateCoefficientsLocked()
|
|
173
|
+
needsUpdate = false
|
|
174
|
+
}
|
|
175
|
+
let currentCoefficients = coefficients
|
|
176
|
+
gainsLock.unlock()
|
|
177
|
+
|
|
178
|
+
// Process each channel
|
|
179
|
+
for channelIndex in 0..<min(buffer.count, channelCount) {
|
|
180
|
+
guard let channelData = buffer[channelIndex].mData else { continue }
|
|
181
|
+
|
|
182
|
+
let samples = channelData.assumingMemoryBound(to: Float.self)
|
|
183
|
+
|
|
184
|
+
// Apply each band's filter in cascade
|
|
185
|
+
for bandIndex in 0..<Self.bandCount {
|
|
186
|
+
guard bandIndex < currentCoefficients.count else { continue }
|
|
187
|
+
|
|
188
|
+
let coeff = currentCoefficients[bandIndex]
|
|
189
|
+
guard coeff.count >= 5 else { continue }
|
|
190
|
+
|
|
191
|
+
let b0 = coeff[0]
|
|
192
|
+
let b1 = coeff[1]
|
|
193
|
+
let b2 = coeff[2]
|
|
194
|
+
let a1 = coeff[3]
|
|
195
|
+
let a2 = coeff[4]
|
|
196
|
+
|
|
197
|
+
// Get filter state for this band and channel
|
|
198
|
+
var z1 = filterStates[bandIndex][channelIndex][0]
|
|
199
|
+
var z2 = filterStates[bandIndex][channelIndex][1]
|
|
200
|
+
|
|
201
|
+
// Process samples using Direct Form II Transposed
|
|
202
|
+
for i in 0..<numberOfFrames {
|
|
203
|
+
let input = samples[i]
|
|
204
|
+
let output = b0 * input + z1
|
|
205
|
+
z1 = b1 * input - a1 * output + z2
|
|
206
|
+
z2 = b2 * input - a2 * output
|
|
207
|
+
samples[i] = output
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Save filter state
|
|
211
|
+
filterStates[bandIndex][channelIndex][0] = z1
|
|
212
|
+
filterStates[bandIndex][channelIndex][1] = z2
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// MARK: - Private Methods
|
|
218
|
+
|
|
219
|
+
private func updateCoefficients() {
|
|
220
|
+
gainsLock.lock()
|
|
221
|
+
updateCoefficientsLocked()
|
|
222
|
+
gainsLock.unlock()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// Calculate biquad coefficients for all bands (must be called with lock held)
|
|
226
|
+
private func updateCoefficientsLocked() {
|
|
227
|
+
coefficients = []
|
|
228
|
+
|
|
229
|
+
for i in 0..<Self.bandCount {
|
|
230
|
+
let freq = Self.frequencies[i]
|
|
231
|
+
let gainDB = _gains[i]
|
|
232
|
+
|
|
233
|
+
let coeff = calculatePeakingEQCoefficients(
|
|
234
|
+
frequency: freq,
|
|
235
|
+
gainDB: gainDB,
|
|
236
|
+
q: Self.defaultQ,
|
|
237
|
+
sampleRate: sampleRate
|
|
238
|
+
)
|
|
239
|
+
coefficients.append(coeff)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Calculate biquad coefficients for a peaking EQ filter
|
|
244
|
+
/// Based on Audio EQ Cookbook by Robert Bristow-Johnson
|
|
245
|
+
/// - Parameters:
|
|
246
|
+
/// - frequency: Center frequency in Hz
|
|
247
|
+
/// - gainDB: Gain in decibels
|
|
248
|
+
/// - q: Q factor (bandwidth)
|
|
249
|
+
/// - sampleRate: Sample rate in Hz
|
|
250
|
+
/// - Returns: Coefficients [b0, b1, b2, a1, a2] (normalized by a0)
|
|
251
|
+
private func calculatePeakingEQCoefficients(
|
|
252
|
+
frequency: Float,
|
|
253
|
+
gainDB: Float,
|
|
254
|
+
q: Float,
|
|
255
|
+
sampleRate: Float
|
|
256
|
+
) -> [Float] {
|
|
257
|
+
// If gain is essentially 0, return unity (pass-through)
|
|
258
|
+
if abs(gainDB) < 0.01 {
|
|
259
|
+
return [1, 0, 0, 0, 0] // b0=1, rest=0 means y[n] = x[n]
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
let A = pow(10, gainDB / 40) // sqrt(10^(dB/20))
|
|
263
|
+
let w0 = 2 * Float.pi * frequency / sampleRate
|
|
264
|
+
let cosW0 = cos(w0)
|
|
265
|
+
let sinW0 = sin(w0)
|
|
266
|
+
let alpha = sinW0 / (2 * q)
|
|
267
|
+
|
|
268
|
+
// Peaking EQ coefficients
|
|
269
|
+
let b0 = 1 + alpha * A
|
|
270
|
+
let b1 = -2 * cosW0
|
|
271
|
+
let b2 = 1 - alpha * A
|
|
272
|
+
let a0 = 1 + alpha / A
|
|
273
|
+
let a1 = -2 * cosW0
|
|
274
|
+
let a2 = 1 - alpha / A
|
|
275
|
+
|
|
276
|
+
// Normalize by a0
|
|
277
|
+
return [
|
|
278
|
+
b0 / a0,
|
|
279
|
+
b1 / a0,
|
|
280
|
+
b2 / a0,
|
|
281
|
+
a1 / a0,
|
|
282
|
+
a2 / a0
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// MARK: - Preset Support
|
|
288
|
+
|
|
289
|
+
extension EqualizerAudioTap {
|
|
290
|
+
|
|
291
|
+
/// Predefined equalizer presets
|
|
292
|
+
public enum Preset: String, CaseIterable {
|
|
293
|
+
case flat = "Flat"
|
|
294
|
+
case rock = "Rock"
|
|
295
|
+
case pop = "Pop"
|
|
296
|
+
case jazz = "Jazz"
|
|
297
|
+
case classical = "Classical"
|
|
298
|
+
case hiphop = "Hip Hop"
|
|
299
|
+
case electronic = "Electronic"
|
|
300
|
+
case acoustic = "Acoustic"
|
|
301
|
+
case bass = "Bass Boost"
|
|
302
|
+
case treble = "Treble Boost"
|
|
303
|
+
case vocal = "Vocal"
|
|
304
|
+
case loudness = "Loudness"
|
|
305
|
+
|
|
306
|
+
/// Gain values for each preset (10 bands)
|
|
307
|
+
public var gains: [Float] {
|
|
308
|
+
switch self {
|
|
309
|
+
case .flat:
|
|
310
|
+
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
311
|
+
case .rock:
|
|
312
|
+
return [5, 4, 3, 1, -1, 0, 2, 3, 4, 4]
|
|
313
|
+
case .pop:
|
|
314
|
+
return [-1, 1, 3, 4, 3, 1, 0, 1, 2, 2]
|
|
315
|
+
case .jazz:
|
|
316
|
+
return [3, 2, 1, 2, -1, -1, 0, 1, 2, 3]
|
|
317
|
+
case .classical:
|
|
318
|
+
return [4, 3, 2, 1, -1, -1, 0, 2, 3, 4]
|
|
319
|
+
case .hiphop:
|
|
320
|
+
return [5, 5, 3, 1, -1, 0, 1, 0, 2, 3]
|
|
321
|
+
case .electronic:
|
|
322
|
+
return [4, 4, 2, 0, -2, -1, 0, 2, 4, 4]
|
|
323
|
+
case .acoustic:
|
|
324
|
+
return [3, 2, 1, 1, 0, 0, 1, 2, 2, 2]
|
|
325
|
+
case .bass:
|
|
326
|
+
return [6, 5, 4, 2, 0, 0, 0, 0, 0, 0]
|
|
327
|
+
case .treble:
|
|
328
|
+
return [0, 0, 0, 0, 0, 1, 2, 4, 5, 6]
|
|
329
|
+
case .vocal:
|
|
330
|
+
return [-2, -1, 0, 2, 4, 4, 3, 1, 0, -1]
|
|
331
|
+
case .loudness:
|
|
332
|
+
return [5, 4, 2, 0, -2, -2, 0, 2, 4, 5]
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/// Apply a preset
|
|
338
|
+
public func applyPreset(_ preset: Preset) {
|
|
339
|
+
setAllGains(preset.gains)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/// Get all available preset names
|
|
343
|
+
public static var presetNames: [String] {
|
|
344
|
+
return Preset.allCases.map { $0.rawValue }
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -46,6 +46,15 @@ export interface Spec extends TurboModule {
|
|
|
46
46
|
setEqualizerPreset(preset: number): Promise<void>;
|
|
47
47
|
getCurrentEqualizerPreset(): Promise<number>;
|
|
48
48
|
getEqualizerPresets(): Promise<string[]>;
|
|
49
|
+
setEqualizerEnabled(enabled: boolean): Promise<void>;
|
|
50
|
+
getEqualizerEnabled(): Promise<boolean>;
|
|
51
|
+
setEqualizerBand(band: number, gain: number): Promise<void>;
|
|
52
|
+
setEqualizerBands(gains: number[]): Promise<void>;
|
|
53
|
+
getEqualizerBands(): Promise<number[]>;
|
|
54
|
+
getEqualizerFrequencies(): Promise<number[]>;
|
|
55
|
+
applyEqualizerPreset(presetIndex: number): Promise<void>;
|
|
56
|
+
getEqualizerPresetNames(): Promise<string[]>;
|
|
57
|
+
resetEqualizer(): Promise<void>;
|
|
49
58
|
addListener(eventName: string): void;
|
|
50
59
|
removeListeners(count: number): void;
|
|
51
60
|
getConstants: () => {
|
package/lib/src/trackPlayer.d.ts
CHANGED
|
@@ -369,3 +369,51 @@ export declare function getCurrentEqualizerPreset(): Promise<number>;
|
|
|
369
369
|
* android only. get the current eq preset names.
|
|
370
370
|
*/
|
|
371
371
|
export declare function getEqualizerPresets(): Promise<string[]>;
|
|
372
|
+
/**
|
|
373
|
+
* Enable or disable the parametric equalizer.
|
|
374
|
+
* - iOS: 10-band biquad peaking filters
|
|
375
|
+
* - Android: System equalizer (band count varies by device, typically 5)
|
|
376
|
+
*/
|
|
377
|
+
export declare function setEqualizerEnabled(enabled: boolean): Promise<void>;
|
|
378
|
+
/**
|
|
379
|
+
* Check if the equalizer is enabled.
|
|
380
|
+
*/
|
|
381
|
+
export declare function getEqualizerEnabled(): Promise<boolean>;
|
|
382
|
+
/**
|
|
383
|
+
* Set the gain for a specific equalizer band.
|
|
384
|
+
* @param band Band index (0 to bandCount-1)
|
|
385
|
+
* @param gain Gain in dB (range varies by platform, typically -12 to +12)
|
|
386
|
+
*/
|
|
387
|
+
export declare function setEqualizerBand(band: number, gain: number): Promise<void>;
|
|
388
|
+
/**
|
|
389
|
+
* Set all equalizer band gains at once.
|
|
390
|
+
* @param gains Array of gain values in dB
|
|
391
|
+
* Note: iOS has 10 bands, Android typically has 5 (device dependent)
|
|
392
|
+
*/
|
|
393
|
+
export declare function setEqualizerBands(gains: number[]): Promise<void>;
|
|
394
|
+
/**
|
|
395
|
+
* Get all current equalizer band gains.
|
|
396
|
+
* @returns Array of gain values in dB
|
|
397
|
+
*/
|
|
398
|
+
export declare function getEqualizerBands(): Promise<number[]>;
|
|
399
|
+
/**
|
|
400
|
+
* Get the center frequencies for each equalizer band.
|
|
401
|
+
* @returns Array of frequency values in Hz
|
|
402
|
+
* - iOS: [32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]
|
|
403
|
+
* - Android: varies by device (typically 5 bands)
|
|
404
|
+
*/
|
|
405
|
+
export declare function getEqualizerFrequencies(): Promise<number[]>;
|
|
406
|
+
/**
|
|
407
|
+
* Apply a preset to the equalizer.
|
|
408
|
+
* @param presetIndex Index of the preset (use getEqualizerPresetNames to get available presets)
|
|
409
|
+
*/
|
|
410
|
+
export declare function applyEqualizerPreset(presetIndex: number): Promise<void>;
|
|
411
|
+
/**
|
|
412
|
+
* Get available equalizer preset names.
|
|
413
|
+
* @returns Array of preset names ["Flat", "Rock", "Pop", "Jazz", ...]
|
|
414
|
+
*/
|
|
415
|
+
export declare function getEqualizerPresetNames(): Promise<string[]>;
|
|
416
|
+
/**
|
|
417
|
+
* Reset all equalizer bands to 0 (flat response).
|
|
418
|
+
*/
|
|
419
|
+
export declare function resetEqualizer(): Promise<void>;
|
package/lib/src/trackPlayer.js
CHANGED
|
@@ -625,3 +625,70 @@ export async function getEqualizerPresets() {
|
|
|
625
625
|
return [];
|
|
626
626
|
return TrackPlayer.getEqualizerPresets();
|
|
627
627
|
}
|
|
628
|
+
// MARK: - Cross-Platform Equalizer API (Band Control)
|
|
629
|
+
/**
|
|
630
|
+
* Enable or disable the parametric equalizer.
|
|
631
|
+
* - iOS: 10-band biquad peaking filters
|
|
632
|
+
* - Android: System equalizer (band count varies by device, typically 5)
|
|
633
|
+
*/
|
|
634
|
+
export async function setEqualizerEnabled(enabled) {
|
|
635
|
+
return TrackPlayer.setEqualizerEnabled(enabled);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Check if the equalizer is enabled.
|
|
639
|
+
*/
|
|
640
|
+
export async function getEqualizerEnabled() {
|
|
641
|
+
return TrackPlayer.getEqualizerEnabled();
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Set the gain for a specific equalizer band.
|
|
645
|
+
* @param band Band index (0 to bandCount-1)
|
|
646
|
+
* @param gain Gain in dB (range varies by platform, typically -12 to +12)
|
|
647
|
+
*/
|
|
648
|
+
export async function setEqualizerBand(band, gain) {
|
|
649
|
+
return TrackPlayer.setEqualizerBand(band, gain);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Set all equalizer band gains at once.
|
|
653
|
+
* @param gains Array of gain values in dB
|
|
654
|
+
* Note: iOS has 10 bands, Android typically has 5 (device dependent)
|
|
655
|
+
*/
|
|
656
|
+
export async function setEqualizerBands(gains) {
|
|
657
|
+
return TrackPlayer.setEqualizerBands(gains);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Get all current equalizer band gains.
|
|
661
|
+
* @returns Array of gain values in dB
|
|
662
|
+
*/
|
|
663
|
+
export async function getEqualizerBands() {
|
|
664
|
+
return TrackPlayer.getEqualizerBands();
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Get the center frequencies for each equalizer band.
|
|
668
|
+
* @returns Array of frequency values in Hz
|
|
669
|
+
* - iOS: [32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]
|
|
670
|
+
* - Android: varies by device (typically 5 bands)
|
|
671
|
+
*/
|
|
672
|
+
export async function getEqualizerFrequencies() {
|
|
673
|
+
return TrackPlayer.getEqualizerFrequencies();
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Apply a preset to the equalizer.
|
|
677
|
+
* @param presetIndex Index of the preset (use getEqualizerPresetNames to get available presets)
|
|
678
|
+
*/
|
|
679
|
+
export async function applyEqualizerPreset(presetIndex) {
|
|
680
|
+
return TrackPlayer.applyEqualizerPreset(presetIndex);
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Get available equalizer preset names.
|
|
684
|
+
* @returns Array of preset names ["Flat", "Rock", "Pop", "Jazz", ...]
|
|
685
|
+
*/
|
|
686
|
+
export async function getEqualizerPresetNames() {
|
|
687
|
+
return TrackPlayer.getEqualizerPresetNames();
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Reset all equalizer bands to 0 (flat response).
|
|
691
|
+
*/
|
|
692
|
+
export async function resetEqualizer() {
|
|
693
|
+
return TrackPlayer.resetEqualizer();
|
|
694
|
+
}
|
package/package.json
CHANGED
|
@@ -68,6 +68,17 @@ export interface Spec extends TurboModule {
|
|
|
68
68
|
getCurrentEqualizerPreset(): Promise<number>;
|
|
69
69
|
getEqualizerPresets(): Promise<string[]>;
|
|
70
70
|
|
|
71
|
+
// iOS Equalizer methods (10-band parametric EQ)
|
|
72
|
+
setEqualizerEnabled(enabled: boolean): Promise<void>;
|
|
73
|
+
getEqualizerEnabled(): Promise<boolean>;
|
|
74
|
+
setEqualizerBand(band: number, gain: number): Promise<void>;
|
|
75
|
+
setEqualizerBands(gains: number[]): Promise<void>;
|
|
76
|
+
getEqualizerBands(): Promise<number[]>;
|
|
77
|
+
getEqualizerFrequencies(): Promise<number[]>;
|
|
78
|
+
applyEqualizerPreset(presetIndex: number): Promise<void>;
|
|
79
|
+
getEqualizerPresetNames(): Promise<string[]>;
|
|
80
|
+
resetEqualizer(): Promise<void>;
|
|
81
|
+
|
|
71
82
|
// event listeners
|
|
72
83
|
addListener(eventName: string): void;
|
|
73
84
|
removeListeners(count: number): void;
|
package/src/trackPlayer.ts
CHANGED
|
@@ -804,3 +804,83 @@ export async function getEqualizerPresets(): Promise<string[]> {
|
|
|
804
804
|
if (!isAndroid) return [];
|
|
805
805
|
return TrackPlayer.getEqualizerPresets();
|
|
806
806
|
}
|
|
807
|
+
|
|
808
|
+
// MARK: - Cross-Platform Equalizer API (Band Control)
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Enable or disable the parametric equalizer.
|
|
812
|
+
* - iOS: 10-band biquad peaking filters
|
|
813
|
+
* - Android: System equalizer (band count varies by device, typically 5)
|
|
814
|
+
*/
|
|
815
|
+
export async function setEqualizerEnabled(enabled: boolean): Promise<void> {
|
|
816
|
+
return TrackPlayer.setEqualizerEnabled(enabled);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Check if the equalizer is enabled.
|
|
821
|
+
*/
|
|
822
|
+
export async function getEqualizerEnabled(): Promise<boolean> {
|
|
823
|
+
return TrackPlayer.getEqualizerEnabled();
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Set the gain for a specific equalizer band.
|
|
828
|
+
* @param band Band index (0 to bandCount-1)
|
|
829
|
+
* @param gain Gain in dB (range varies by platform, typically -12 to +12)
|
|
830
|
+
*/
|
|
831
|
+
export async function setEqualizerBand(
|
|
832
|
+
band: number,
|
|
833
|
+
gain: number,
|
|
834
|
+
): Promise<void> {
|
|
835
|
+
return TrackPlayer.setEqualizerBand(band, gain);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Set all equalizer band gains at once.
|
|
840
|
+
* @param gains Array of gain values in dB
|
|
841
|
+
* Note: iOS has 10 bands, Android typically has 5 (device dependent)
|
|
842
|
+
*/
|
|
843
|
+
export async function setEqualizerBands(gains: number[]): Promise<void> {
|
|
844
|
+
return TrackPlayer.setEqualizerBands(gains);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Get all current equalizer band gains.
|
|
849
|
+
* @returns Array of gain values in dB
|
|
850
|
+
*/
|
|
851
|
+
export async function getEqualizerBands(): Promise<number[]> {
|
|
852
|
+
return TrackPlayer.getEqualizerBands();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Get the center frequencies for each equalizer band.
|
|
857
|
+
* @returns Array of frequency values in Hz
|
|
858
|
+
* - iOS: [32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]
|
|
859
|
+
* - Android: varies by device (typically 5 bands)
|
|
860
|
+
*/
|
|
861
|
+
export async function getEqualizerFrequencies(): Promise<number[]> {
|
|
862
|
+
return TrackPlayer.getEqualizerFrequencies();
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Apply a preset to the equalizer.
|
|
867
|
+
* @param presetIndex Index of the preset (use getEqualizerPresetNames to get available presets)
|
|
868
|
+
*/
|
|
869
|
+
export async function applyEqualizerPreset(presetIndex: number): Promise<void> {
|
|
870
|
+
return TrackPlayer.applyEqualizerPreset(presetIndex);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Get available equalizer preset names.
|
|
875
|
+
* @returns Array of preset names ["Flat", "Rock", "Pop", "Jazz", ...]
|
|
876
|
+
*/
|
|
877
|
+
export async function getEqualizerPresetNames(): Promise<string[]> {
|
|
878
|
+
return TrackPlayer.getEqualizerPresetNames();
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Reset all equalizer bands to 0 (flat response).
|
|
883
|
+
*/
|
|
884
|
+
export async function resetEqualizer(): Promise<void> {
|
|
885
|
+
return TrackPlayer.resetEqualizer();
|
|
886
|
+
}
|