@javascriptcommon/react-native-track-player 4.1.23 → 4.1.25

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.
@@ -62,7 +62,7 @@ class Track
62
62
  headers!![header] = httpHeaders.getString(header)!!
63
63
  }
64
64
  }
65
- notPlayable = bundle.getBoolean("notPlayable", false)
65
+ notPlayable = bundle.getBoolean("notPlayable", false) || uri == null || uri.toString().isBlank()
66
66
  setMetadata(context, bundle, ratingType)
67
67
  queueId = System.currentTimeMillis()
68
68
  originalItem = bundle
@@ -389,6 +389,49 @@ class MusicModule(reactContext: ReactApplicationContext) : NativeTrackPlayerSpec
389
389
  callback.resolve(null)
390
390
  }
391
391
 
392
+ // Audio Effects (BassBoost, Loudness, Virtualizer)
393
+
394
+ override fun setBassBoostEnabled(enabled: Boolean, callback: Promise) = launchInScope {
395
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
396
+ musicService.setBassBoostEnabled(enabled)
397
+ callback.resolve(null)
398
+ }
399
+
400
+ override fun setBassBoostLevel(level: Double, callback: Promise) = launchInScope {
401
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
402
+ musicService.setBassBoostLevel(level.toFloat())
403
+ callback.resolve(null)
404
+ }
405
+
406
+ override fun setLoudnessEnabled(enabled: Boolean, callback: Promise) = launchInScope {
407
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
408
+ musicService.setLoudnessEnabled(enabled)
409
+ callback.resolve(null)
410
+ }
411
+
412
+ override fun setLoudnessLevel(level: Double, callback: Promise) = launchInScope {
413
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
414
+ musicService.setLoudnessLevel(level.toFloat())
415
+ callback.resolve(null)
416
+ }
417
+
418
+ override fun setVirtualizerEnabled(enabled: Boolean, callback: Promise) = launchInScope {
419
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
420
+ musicService.setVirtualizerEnabled(enabled)
421
+ callback.resolve(null)
422
+ }
423
+
424
+ override fun setVirtualizerLevel(level: Double, callback: Promise) = launchInScope {
425
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
426
+ musicService.setVirtualizerLevel(level.toFloat())
427
+ callback.resolve(null)
428
+ }
429
+
430
+ override fun setBalance(balance: Double, callback: Promise) = launchInScope {
431
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
432
+ musicService.setBalance(balance.toFloat())
433
+ callback.resolve(null)
434
+ }
392
435
 
393
436
  override fun add(data: ReadableArray?, insertBeforeIndex: Double, callback: Promise) = launchInScope {
394
437
  if (verifyServiceBoundOrReject(callback)) return@launchInScope
@@ -201,6 +201,36 @@ class MusicService : HeadlessJsMediaService() {
201
201
  player.resetEqualizer()
202
202
  }
203
203
 
204
+ // Audio Effects (BassBoost, Loudness, Virtualizer)
205
+
206
+ fun setBassBoostEnabled(enabled: Boolean) {
207
+ player.setBassBoostEnabled(enabled)
208
+ }
209
+
210
+ fun setBassBoostLevel(level: Float) {
211
+ player.setBassBoostLevel(level)
212
+ }
213
+
214
+ fun setLoudnessEnabled(enabled: Boolean) {
215
+ player.setLoudnessEnabled(enabled)
216
+ }
217
+
218
+ fun setLoudnessLevel(level: Float) {
219
+ player.setLoudnessLevel(level)
220
+ }
221
+
222
+ fun setVirtualizerEnabled(enabled: Boolean) {
223
+ player.setVirtualizerEnabled(enabled)
224
+ }
225
+
226
+ fun setVirtualizerLevel(level: Float) {
227
+ player.setVirtualizerLevel(level)
228
+ }
229
+
230
+ fun setBalance(balance: Float) {
231
+ player.setBalance(balance)
232
+ }
233
+
204
234
  fun crossFadePrepare(previous: Boolean = false, seekTo: Double = 0.0) {
205
235
  player.crossFadePrepare(previous, seekTo)
206
236
  }
@@ -8,6 +8,8 @@ import androidx.media3.common.MediaItem
8
8
  import androidx.media3.common.MediaMetadata
9
9
  import com.lovegaoshi.kotlinaudio.utils.getEmbeddedBitmapArray
10
10
  import com.lovegaoshi.kotlinaudio.utils.saveMediaCoverToPng
11
+ import androidx.media3.datasource.RawResourceDataSource
12
+ import com.doublesymmetry.trackplayer.R
11
13
  import java.util.UUID
12
14
 
13
15
 
@@ -83,8 +85,7 @@ fun audioItem2MediaItem(audioItem: AudioItem, context: Context? = null): MediaIt
83
85
 
84
86
  return MediaItem.Builder()
85
87
  .setMediaId(audioItem.mediaId ?: UUID.randomUUID().toString())
86
- // Always set URI (even empty string) so ExoPlayer adds item to timeline
87
- .setUri(audioItem.audioUrl)
88
+ .setUri(if (hasValidUrl && !isNotPlayable) audioItem.audioUrl else RawResourceDataSource.buildRawResourceUri(R.raw.silent_5_seconds).toString())
88
89
  .setMediaMetadata(
89
90
  MediaMetadata.Builder()
90
91
  .setTitle(audioItem.title)
@@ -38,6 +38,8 @@ import com.lovegaoshi.kotlinaudio.player.components.Cache
38
38
  import com.lovegaoshi.kotlinaudio.player.components.FocusManager
39
39
  import com.lovegaoshi.kotlinaudio.player.components.MediaFactory
40
40
  import com.lovegaoshi.kotlinaudio.player.components.setupBuffer
41
+ import com.lovegaoshi.kotlinaudio.processors.BalanceAudioProcessor
42
+ import com.lovegaoshi.kotlinaudio.processors.EqualizerAudioProcessor
41
43
  import com.lovegaoshi.kotlinaudio.processors.FFTEmitter
42
44
  import kotlinx.coroutines.Deferred
43
45
  import kotlinx.coroutines.MainScope
@@ -70,6 +72,8 @@ abstract class AudioPlayer internal constructor(
70
72
  private val focusListener = APMFocusListener()
71
73
  private val focusManager = FocusManager(context, listener=focusListener, options=options)
72
74
  var fftEmitter: (DoubleArray) -> Unit = { v -> Timber.tag("APMFFT").d("FFT emitted $v") }
75
+ private val balanceProcessor = BalanceAudioProcessor()
76
+ private val equalizerProcessor = EqualizerAudioProcessor()
73
77
 
74
78
  var alwaysPauseOnInterruption: Boolean
75
79
  get() = focusManager.alwaysPauseOnInterruption
@@ -209,7 +213,9 @@ abstract class AudioPlayer internal constructor(
209
213
  }
210
214
  }
211
215
 
212
- }) else DefaultRenderersFactory(context)
216
+ }, arrayOf(equalizerProcessor, balanceProcessor)) else APMRenderersFactory(
217
+ context, 0, null, arrayOf(equalizerProcessor, balanceProcessor)
218
+ )
213
219
  renderer.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
214
220
  val mPlayer = ExoPlayer
215
221
  .Builder(context)
@@ -295,152 +301,122 @@ abstract class AudioPlayer internal constructor(
295
301
  .map { i -> equalizers[0].getPresetName(i.toShort()) }
296
302
  }
297
303
 
298
- // 10-band Equalizer API (cross-platform compatible)
304
+ // 8-band Software Equalizer API (cross-platform, biquad with coefficient smoothing)
299
305
 
300
- /**
301
- * Enable or disable the equalizer
302
- */
303
306
  fun setEqualizerEnabled(enabled: Boolean) {
304
- equalizers.forEach { equalizer ->
305
- equalizer.enabled = enabled
306
- }
307
+ equalizerProcessor.isEnabled = enabled
307
308
  }
308
309
 
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
- }
310
+ fun getEqualizerEnabled(): Boolean = equalizerProcessor.isEnabled
316
311
 
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
- }
312
+ fun getEqualizerBandCount(): Int = EqualizerAudioProcessor.BAND_COUNT
324
313
 
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
314
  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
- }
315
+ equalizerProcessor.setGain(band, gainDB)
341
316
  }
342
317
 
343
- /**
344
- * Set gains for all equalizer bands at once
345
- * @param gainsDB Array of gain values in dB
346
- */
347
318
  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
- }
319
+ equalizerProcessor.setAllGains(gainsDB)
358
320
  }
359
321
 
360
- /**
361
- * Get all current equalizer band gains
362
- * @return Array of gain values in dB
363
- */
364
322
  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
- }
323
+ return equalizerProcessor.getAllGains().toList()
372
324
  }
373
325
 
374
- /**
375
- * Get the center frequencies for each equalizer band
376
- * @return Array of frequency values in Hz
377
- */
378
326
  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
- }
327
+ return EqualizerAudioProcessor.FREQUENCIES.map { it.toInt() }
386
328
  }
387
329
 
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
- }
330
+ fun getEqualizerBandLevelRange(): List<Float> = listOf(-12f, 12f)
397
331
 
398
- /**
399
- * Reset all equalizer bands to 0 (flat response)
400
- */
401
332
  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
- }
333
+ equalizerProcessor.resetGains()
334
+ }
335
+
336
+ // Bass Boost API (software DSP — matches iOS low shelf filter)
337
+
338
+ fun setBassBoostEnabled(enabled: Boolean) {
339
+ equalizerProcessor.isBassBoostEnabled = enabled
340
+ }
341
+
342
+ fun setBassBoostLevel(level: Float) {
343
+ equalizerProcessor.updateBassBoostLevel(level)
344
+ }
345
+
346
+ // Loudness Enhancer API (software DSP — matches iOS low+high shelf)
347
+
348
+ fun setLoudnessEnabled(enabled: Boolean) {
349
+ equalizerProcessor.isLoudnessEnabled = enabled
408
350
  }
409
351
 
352
+ fun setLoudnessLevel(level: Float) {
353
+ equalizerProcessor.updateLoudnessLevel(level)
354
+ }
355
+
356
+ // Virtualizer API (software DSP — matches iOS all-pass stereo widener)
357
+
358
+ fun setVirtualizerEnabled(enabled: Boolean) {
359
+ equalizerProcessor.isVirtualizerEnabled = enabled
360
+ }
361
+
362
+ fun setVirtualizerLevel(level: Float) {
363
+ equalizerProcessor.updateVirtualizerLevel(level)
364
+ }
365
+
366
+ // Balance API
367
+
368
+ fun setBalance(balance: Float) {
369
+ balanceProcessor.setBalance(balance)
370
+ }
371
+
372
+ fun getBalance(): Float = balanceProcessor.getBalance()
373
+
410
374
  /**
411
375
  * Get preset names for iOS compatibility (custom presets mapped to Android system presets)
412
376
  */
413
377
  fun getEqualizerPresetNames(): List<String> {
414
- // Return iOS-compatible preset names
378
+ // Must match iOS EqualizerAudioTap.Preset order
415
379
  return listOf(
416
- "Flat", "Rock", "Pop", "Jazz", "Classical",
417
- "Hip Hop", "Electronic", "Acoustic", "Bass Boost",
418
- "Treble Boost", "Vocal", "Loudness"
380
+ "eqAcoustic", "eqBassBooster", "eqBassReducer", "eqClassical",
381
+ "eqDance", "eqDeep", "eqElectronic", "eqFlat",
382
+ "eqHipHop", "eqJazz", "eqLatin", "eqLoudness",
383
+ "eqLounge", "eqPiano", "eqPop", "eqRnb",
384
+ "eqRock", "eqSmallSpeakers", "eqSpokenWord",
385
+ "eqTrebleBooster", "eqTrebleReducer", "eqVocalBooster"
419
386
  )
420
387
  }
421
388
 
422
389
  /**
423
- * Apply a preset by index (iOS-compatible)
424
- * Maps iOS preset index to gain values
390
+ * Apply a preset by index (matches iOS preset order).
391
+ * 8 bands: 60, 150, 400, 1K, 2.5K, 6K, 12K, 16K — same as software EQ.
425
392
  */
426
393
  fun applyEqualizerPreset(presetIndex: Int) {
427
394
  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
395
+ listOf( 3f, 2f, 1f, 0f, 1f, 2f, 2f, 2f), // Acoustic
396
+ listOf( 6f, 5f, 3f, 1f, 0f, 0f, 0f, 0f), // Bass Booster
397
+ listOf(-6f, -5f, -3f, -1f, 0f, 0f, 0f, 0f), // Bass Reducer
398
+ listOf( 4f, 2f, 0f, -1f, 0f, 2f, 3f, 3f), // Classical
399
+ listOf( 5f, 4f, 1f, 0f, 2f, 4f, 3f, 2f), // Dance
400
+ listOf( 5f, 4f, 2f, 1f, 0f, -1f, -2f, -3f), // Deep
401
+ listOf( 4f, 3f, 0f, -1f, 0f, 3f, 4f, 4f), // Electronic
402
+ listOf( 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f), // Flat
403
+ listOf( 5f, 4f, 2f, 0f, -1f, 1f, 2f, 3f), // Hip-Hop
404
+ listOf( 3f, 2f, 1f, 0f, -1f, 1f, 2f, 2f), // Jazz
405
+ listOf( 4f, 3f, 0f, -1f, 0f, 2f, 4f, 3f), // Latin
406
+ listOf( 5f, 3f, 0f, -2f, 0f, 2f, 4f, 4f), // Loudness
407
+ listOf(-1f, 1f, 2f, 1f, 0f, -1f, 1f, 1f), // Lounge
408
+ listOf( 1f, 0f, 1f, 2f, 3f, 2f, 2f, 1f), // Piano
409
+ listOf(-1f, 1f, 3f, 3f, 2f, 1f, 1f, 1f), // Pop
410
+ listOf( 5f, 4f, 2f, 0f, -1f, 1f, 2f, 2f), // R&B
411
+ listOf( 4f, 3f, 0f, -1f, 1f, 3f, 4f, 3f), // Rock
412
+ listOf( 5f, 4f, 3f, 1f, 0f, -1f, 2f, 3f), // Small Speakers
413
+ listOf(-2f, 0f, 1f, 3f, 4f, 3f, 1f, -1f), // Spoken Word
414
+ listOf( 0f, 0f, 0f, 0f, 1f, 3f, 5f, 6f), // Treble Booster
415
+ listOf( 0f, 0f, 0f, 0f, -1f, -3f, -5f, -6f), // Treble Reducer
416
+ listOf(-2f, -1f, 1f, 3f, 4f, 3f, 1f, 0f) // Vocal Booster
440
417
  )
441
- if (presetIndex >= 0 && presetIndex < presets.size) {
442
- setEqualizerBands(presets[presetIndex])
443
- }
418
+ if (presetIndex < 0 || presetIndex >= presets.size) return
419
+ setEqualizerBands(presets[presetIndex])
444
420
  }
445
421
 
446
422
  fun togglePlaying() {
@@ -605,7 +581,13 @@ abstract class AudioPlayer internal constructor(
605
581
  inner class AudioFxInitListener: AnalyticsListener {
606
582
  @OptIn(UnstableApi::class)
607
583
  override fun onAudioSessionIdChanged(eventTime: AnalyticsListener.EventTime, audioSessionId: Int) {
608
- // Try to add LoudnessEnhancer
584
+ // Release old native effects before creating new ones
585
+ loudnessEnhancers.forEach { try { it.release() } catch (_: Exception) {} }
586
+ loudnessEnhancers.clear()
587
+ equalizers.forEach { try { it.release() } catch (_: Exception) {} }
588
+ equalizers.clear()
589
+
590
+ // Native LoudnessEnhancer (only for setLoudnessEnhance legacy API)
609
591
  try {
610
592
  val enhancer = LoudnessEnhancer(audioSessionId)
611
593
  loudnessEnhancers.add(enhancer)
@@ -613,13 +595,16 @@ abstract class AudioPlayer internal constructor(
613
595
  Timber.tag("APMAudioFx").e("[AudioFx] failed to load loudnessEnhancer. it's fine if in dev!")
614
596
  }
615
597
 
616
- // Try to add Equalizer
598
+ // Native Equalizer (only for setEqualizerPreset legacy API)
617
599
  try {
618
600
  val equalizer = Equalizer(0, audioSessionId)
619
601
  equalizers.add(equalizer)
620
602
  } catch (e: RuntimeException) {
621
603
  Timber.tag("APMAudioFx").e("[AudioFx] failed to load equalizer. it's fine if in dev!")
622
604
  }
605
+
606
+ // Bass boost, loudness, virtualizer, EQ bands — all handled by
607
+ // software EqualizerAudioProcessor (no native effects needed)
623
608
  }
624
609
  }
625
610
 
@@ -1,6 +1,7 @@
1
1
  package com.lovegaoshi.kotlinaudio.player.components
2
2
 
3
3
  import android.content.Context
4
+ import androidx.media3.common.audio.AudioProcessor
4
5
  import androidx.media3.common.util.UnstableApi
5
6
  import androidx.media3.exoplayer.DefaultRenderersFactory
6
7
  import androidx.media3.exoplayer.audio.AudioSink
@@ -13,21 +14,24 @@ import com.lovegaoshi.kotlinaudio.processors.TeeListener
13
14
  class APMRenderersFactory(
14
15
  context: Context,
15
16
  sampleRate: Int = 4096,
16
- emitter: FFTEmitter?
17
+ emitter: FFTEmitter?,
18
+ private val extraProcessors: Array<AudioProcessor> = emptyArray()
17
19
  ) : DefaultRenderersFactory(context) {
18
- val teeProcessor = TeeAudioProcessor(TeeListener(sampleRate, emitter))
19
-
20
+ val teeProcessor = if (sampleRate > 0 && emitter != null)
21
+ TeeAudioProcessor(TeeListener(sampleRate, emitter)) else null
20
22
 
21
23
  override fun buildAudioSink(
22
24
  context: Context,
23
25
  enableFloatOutput: Boolean,
24
26
  enableAudioTrackPlaybackParams: Boolean
25
27
  ): AudioSink? {
28
+ val processors = if (teeProcessor != null)
29
+ arrayOf(*extraProcessors, teeProcessor) else arrayOf(*extraProcessors)
26
30
  return DefaultAudioSink.Builder(context)
27
31
  .setEnableFloatOutput(enableFloatOutput)
28
32
  .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
29
- .setAudioProcessors(arrayOf(teeProcessor))
33
+ .setAudioProcessors(processors)
30
34
  .build()
31
35
  }
32
36
 
33
- }
37
+ }
@@ -0,0 +1,66 @@
1
+ package com.lovegaoshi.kotlinaudio.processors
2
+
3
+ import androidx.media3.common.C
4
+ import androidx.media3.common.audio.AudioProcessor
5
+ import androidx.media3.common.audio.BaseAudioProcessor
6
+ import androidx.media3.common.util.UnstableApi
7
+ import java.nio.ByteBuffer
8
+
9
+ @UnstableApi
10
+ class BalanceAudioProcessor : BaseAudioProcessor() {
11
+ @Volatile
12
+ private var balance: Float = 0f
13
+
14
+ fun setBalance(value: Float) {
15
+ balance = value.coerceIn(-1f, 1f)
16
+ }
17
+
18
+ fun getBalance(): Float = balance
19
+
20
+ override fun onConfigure(inputAudioFormat: AudioProcessor.AudioFormat): AudioProcessor.AudioFormat {
21
+ if (inputAudioFormat.channelCount == 2 &&
22
+ (inputAudioFormat.encoding == C.ENCODING_PCM_16BIT || inputAudioFormat.encoding == C.ENCODING_PCM_FLOAT)
23
+ ) {
24
+ return inputAudioFormat
25
+ }
26
+ return AudioProcessor.AudioFormat.NOT_SET
27
+ }
28
+
29
+ override fun queueInput(inputBuffer: ByteBuffer) {
30
+ val bal = balance
31
+ if (bal == 0f) {
32
+ val output = replaceOutputBuffer(inputBuffer.remaining())
33
+ if (output !== inputBuffer) {
34
+ output.put(inputBuffer)
35
+ }
36
+ output.flip()
37
+ return
38
+ }
39
+
40
+ val leftGain = if (bal <= 0f) 1f else 1f - bal
41
+ val rightGain = if (bal >= 0f) 1f else 1f + bal
42
+
43
+ val size = inputBuffer.remaining()
44
+ val output = replaceOutputBuffer(size)
45
+
46
+ when (inputAudioFormat.encoding) {
47
+ C.ENCODING_PCM_16BIT -> {
48
+ while (inputBuffer.hasRemaining()) {
49
+ val left = inputBuffer.short
50
+ val right = inputBuffer.short
51
+ output.putShort((left * leftGain).toInt().coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt()).toShort())
52
+ output.putShort((right * rightGain).toInt().coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt()).toShort())
53
+ }
54
+ }
55
+ C.ENCODING_PCM_FLOAT -> {
56
+ while (inputBuffer.hasRemaining()) {
57
+ val left = inputBuffer.float
58
+ val right = inputBuffer.float
59
+ output.putFloat(left * leftGain)
60
+ output.putFloat(right * rightGain)
61
+ }
62
+ }
63
+ }
64
+ output.flip()
65
+ }
66
+ }