react-native-queue-player 1.2.0 → 2.0.0
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/QueuePlayer.podspec +14 -9
- package/README.md +3 -3
- package/android/consumer-rules.pro +1 -1
- package/android/src/main/cpp/airplay2_control.c +4 -4
- package/android/src/main/cpp/airplay2_control.h +4 -4
- package/android/src/main/cpp/airplay2_jni.cpp +3 -19
- package/android/src/main/cpp/airplay2_pair.c +7 -7
- package/android/src/main/cpp/airplay2_pair.h +2 -2
- package/android/src/main/cpp/airplay2_rtsp.c +8 -8
- package/android/src/main/cpp/airplay2_session.c +2 -2
- package/android/src/main/cpp/airplay_jni.cpp +8 -69
- package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +7 -16
- package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +2 -14
- package/android/src/main/java/com/margelo/nitro/queueplayer/CellularTransportMonitor.kt +77 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +187 -52
- package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +27 -7
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +33 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +31 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +45 -30
- package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +111 -15
- package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +60 -33
- package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +3 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +103 -29
- package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +77 -29
- package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +6 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +48 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +71 -76
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +68 -46
- package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +6 -5
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +5 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +11 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +26 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +22 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +38 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +17 -13
- package/android/src/main/java/com/margelo/nitro/queueplayer/SuppliedReplayGain.kt +66 -0
- package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +839 -271
- package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +4 -8
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +4 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +5 -4
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +0 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +4 -2
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +16 -9
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +6 -14
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +1 -13
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +11 -35
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +0 -5
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +0 -6
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +32 -33
- package/android/src/test/java/androidx/media3/session/{MediaSessionControllerRequestTestSeam.kt → MediaSessionControllerRequestTestSupport.kt} +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +12 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineFocusLossTest.kt +115 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +45 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EngineEndSignalOrderTest.kt +70 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +17 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +0 -6
- package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +17 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +15 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +135 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +1 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +36 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +24 -18
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterCancelTest.kt +85 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +112 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +5 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +45 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +47 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +53 -29
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +8 -8
- package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +33 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +128 -5
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +209 -14
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +150 -10
- package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainMergeTest.kt +258 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowDynamicsProcessingRejectingEnable.kt +19 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizer.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/ShadowEqualizerRejectingBandWrites.kt +27 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/SuppliedReplayGainTest.kt +61 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +4 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +1 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastCommandRoutingTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerCastStateTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +88 -38
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +205 -27
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +277 -38
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +5 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueChangeTest.kt +85 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +11 -7
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +2 -1
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +90 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +8 -26
- package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +3 -3
- package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +0 -4
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +2 -2
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitorTest.kt +173 -0
- package/android/src/test/java/com/margelo/nitro/queueplayer/cast/net/ShadowConnectivityManagerRecordingRequests.kt +27 -0
- package/app.plugin.js +17 -1
- package/ios/AVPlayerItemQueueItemId.swift +26 -12
- package/ios/AVQueueBuilder.swift +120 -85
- package/ios/ArtworkLoader.swift +0 -7
- package/ios/ArtworkResolver.swift +22 -4
- package/ios/AssetReplayGainReader.swift +118 -0
- package/ios/AudioSession.swift +67 -16
- package/ios/AudioTapProvider.swift +284 -149
- package/ios/CarPlayBridge.swift +0 -12
- package/ios/CarPlayCoordinator.swift +18 -2
- package/ios/CarPlaySceneDelegate.swift +2 -2
- package/ios/Cast/AirPlayRouteState.swift +9 -8
- package/ios/Cast/Core/CastEventBridge.swift +66 -26
- package/ios/Cast/Core/CastNowPlayingController.swift +38 -16
- package/ios/Cast/Core/CastSession.swift +2 -2
- package/ios/Cast/Core/CastTransportRouter.swift +21 -9
- package/ios/Cast/Core/LocalMediaServer.swift +41 -52
- package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +32 -11
- package/ios/Cast/Core/MediaServerHandle.swift +0 -4
- package/ios/Cast/Core/MediaTokenRegistry.swift +0 -6
- package/ios/CastManager.swift +1 -1
- package/ios/CrossfadeEngine.swift +660 -226
- package/ios/EQTap.swift +20 -17
- package/ios/Equalizer.swift +1 -1
- package/ios/FLACStreamInfo.swift +65 -0
- package/ios/GaplessEngine.swift +57 -30
- package/ios/InputGuards.swift +35 -5
- package/ios/LookaheadCache.swift +202 -58
- package/ios/LookaheadCachePrefetcher.swift +48 -36
- package/ios/MediaServer/MediaHTTPConnection.swift +691 -0
- package/ios/NetworkRecoveryPolicy.swift +32 -0
- package/ios/NowPlayingFormatExtractor.swift +25 -18
- package/ios/NowPlayingInfo.swift +84 -48
- package/ios/OutputRouteMonitor.swift +16 -3
- package/ios/PendingBrowseRequests.swift +1 -1
- package/ios/PlaceholderArtwork.swift +9 -5
- package/ios/PlaybackEngine.swift +165 -76
- package/ios/PlaybackErrorMapping.swift +3 -3
- package/ios/PlaybackModeStateMachine.swift +2 -2
- package/ios/PlaybackNetworkMonitor.swift +73 -0
- package/ios/PlayerStateDerivation.swift +6 -6
- package/ios/QueueMutationArithmetic.swift +25 -0
- package/ios/QueueSkipArithmetic.swift +5 -3
- package/ios/QueueWindowArithmetic.swift +152 -0
- package/ios/ReadThroughServer.swift +283 -0
- package/ios/RemoteCommands.swift +44 -4
- package/ios/ReplayGainData.swift +22 -3
- package/ios/ReplayGainExtractor.swift +70 -27
- package/ios/Siri/VoiceDonation.swift +14 -9
- package/ios/StreamingBitrateProbe.swift +22 -68
- package/ios/SuppliedReplayGain.swift +83 -0
- package/ios/Tests/AVQueueBuilderTests.swift +158 -221
- package/ios/Tests/ActiveItemEngineStub.swift +44 -0
- package/ios/Tests/AssetReplayGainReaderTests.swift +228 -0
- package/ios/Tests/AudioTapProviderDispatchTargetTests.swift +158 -0
- package/ios/Tests/AudioTapProviderReplayGainTests.swift +122 -10
- package/ios/Tests/BitrateReresolveBudgetTests.swift +71 -0
- package/ios/Tests/CastMediaItemTranslationTests.swift +72 -0
- package/ios/Tests/CastNowPlayingControllerTests.swift +1 -1
- package/ios/Tests/CrossfadeAdvanceWithoutFadeTests.swift +120 -0
- package/ios/Tests/CrossfadeEngineIncomingMixTests.swift +122 -0
- package/ios/Tests/CrossfadeEngineTests.swift +164 -0
- package/ios/Tests/CrossfadeMixReapplyTests.swift +66 -0
- package/ios/Tests/CrossfadePlaybackIntentTests.swift +81 -0
- package/ios/Tests/EngineSwapTests.swift +91 -0
- package/ios/Tests/EqualizerAudioMixProviderTests.swift +46 -0
- package/ios/Tests/EqualizerHybridTests.swift +5 -2
- package/ios/Tests/FLACStreamInfoTests.swift +88 -0
- package/ios/Tests/GaplessEngineLifecycleTests.swift +4 -4
- package/ios/Tests/InputGuardsTests.swift +41 -0
- package/ios/Tests/InterruptionIntentTests.swift +77 -0
- package/ios/Tests/LookaheadCacheCellularAccessTests.swift +185 -0
- package/ios/Tests/LookaheadCachePrefetcherTests.swift +53 -30
- package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +160 -1
- package/ios/Tests/LookaheadCacheTests.swift +44 -18
- package/ios/Tests/MediaHTTPConnectionTests.swift +79 -0
- package/ios/Tests/MutationDeferralTests.swift +220 -0
- package/ios/Tests/NowPlayingInfoTests.swift +86 -99
- package/ios/Tests/NowPlayingSnapshotTests.swift +127 -0
- package/ios/Tests/OriginRestartStitcherTests.swift +60 -0
- package/ios/Tests/PlaybackNetworkMonitorTests.swift +95 -0
- package/ios/Tests/PlayerFixtures.swift +48 -0
- package/ios/Tests/PlayerStateDerivationTests.swift +10 -10
- package/ios/Tests/QueueMutationGenerationTests.swift +141 -0
- package/ios/Tests/QueueRebuildPrefixTests.swift +317 -0
- package/ios/Tests/QueueStateTests.swift +3 -1
- package/ios/Tests/QueueWindowArithmeticTests.swift +56 -0
- package/ios/Tests/QueueWindowSliceTests.swift +162 -0
- package/ios/Tests/ReadThroughRoutingLifecycleTests.swift +63 -0
- package/ios/Tests/ReadThroughServerTests.swift +345 -0
- package/ios/Tests/RemoteCommandsTests.swift +28 -0
- package/ios/Tests/ReplayGainExtractorTests.swift +216 -6
- package/ios/Tests/ReplayGainMergeTests.swift +230 -0
- package/ios/Tests/RetryRecoveryTests.swift +303 -0
- package/ios/Tests/SkipCapabilityTests.swift +233 -8
- package/ios/Tests/SkipIndexTests.swift +32 -2
- package/ios/Tests/SleepTimerPauseIntentTests.swift +43 -0
- package/ios/Tests/StallRecoveryTests.swift +97 -0
- package/ios/Tests/StreamingBitrateProbeTests.swift +17 -20
- package/ios/Tests/TopUpWindowGateTests.swift +394 -0
- package/ios/Tests/TrackPlayer+TestHops.swift +14 -0
- package/ios/Tests/TrackPlayerCallOrderTests.swift +93 -0
- package/ios/Tests/TrackPlayerConfigureTeardownTests.swift +86 -0
- package/ios/Tests/TrackPlayerEndVerdictTests.swift +165 -0
- package/ios/Tests/TrackPlayerSeekTests.swift +137 -0
- package/ios/Tests/TrackPlayerThreadingTests.swift +127 -0
- package/ios/Tests/TrackSourceClassifierTests.swift +33 -7
- package/ios/Tests/VoiceVocabularyOptInTests.swift +38 -0
- package/ios/TrackPlayer+Automotive.swift +101 -0
- package/ios/TrackPlayer+Cache.swift +232 -0
- package/ios/TrackPlayer+Config.swift +291 -0
- package/ios/TrackPlayer+EngineDelegate.swift +209 -0
- package/ios/TrackPlayer+EventsAPI.swift +111 -0
- package/ios/TrackPlayer+EventsDispatch.swift +968 -0
- package/ios/TrackPlayer+EventsWiring.swift +173 -0
- package/ios/TrackPlayer+Lifecycle.swift +930 -0
- package/ios/TrackPlayer+NowPlayingFormat.swift +258 -0
- package/ios/TrackPlayer+Queue.swift +940 -0
- package/ios/TrackPlayer+Recovery.swift +196 -0
- package/ios/TrackPlayer+Skip.swift +455 -0
- package/ios/TrackPlayer+SleepTimer.swift +175 -0
- package/ios/TrackPlayer+State.swift +108 -0
- package/ios/TrackPlayer+Threading.swift +135 -0
- package/ios/TrackPlayer+Transport.swift +267 -0
- package/ios/TrackPlayer+Window.swift +195 -0
- package/ios/TrackPlayer.swift +305 -4619
- package/ios/Visualizer.swift +6 -5
- package/ios/tests-harness/Podfile +1 -1
- package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +19 -11
- package/ios/tests-harness/scripts/seed-xcodeproj.rb +2 -2
- package/lib/module/hooks/useActiveTrack.js +29 -22
- package/lib/module/hooks/useActiveTrack.js.map +1 -1
- package/lib/module/hooks/useCast.js +8 -26
- package/lib/module/hooks/useCast.js.map +1 -1
- package/lib/module/hooks/useEqualizer.js +19 -12
- package/lib/module/hooks/useEqualizer.js.map +1 -1
- package/lib/module/hooks/useLookaheadCache.js +3 -7
- package/lib/module/hooks/useLookaheadCache.js.map +1 -1
- package/lib/module/hooks/useQueue.js +66 -19
- package/lib/module/hooks/useQueue.js.map +1 -1
- package/lib/module/index.js +6 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/queueDelta.js +41 -0
- package/lib/module/queueDelta.js.map +1 -0
- package/lib/module/types.js +28 -5
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/TrackPlayer.nitro.d.ts +53 -28
- package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -1
- package/lib/typescript/hooks/useActiveTrack.d.ts +5 -7
- package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -1
- package/lib/typescript/hooks/useCast.d.ts +6 -1
- package/lib/typescript/hooks/useCast.d.ts.map +1 -1
- package/lib/typescript/hooks/useEqualizer.d.ts +2 -1
- package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -1
- package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -1
- package/lib/typescript/hooks/useQueue.d.ts +4 -4
- package/lib/typescript/hooks/useQueue.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/queueDelta.d.ts +10 -0
- package/lib/typescript/queueDelta.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +168 -12
- package/lib/typescript/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JFunc_void_QueueChangeDelta_double_QueueChangeReason.hpp +85 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +45 -19
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +3 -3
- package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +8 -4
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +5 -1
- package/nitrogen/generated/android/c++/JQueueChangeDelta.hpp +128 -0
- package/nitrogen/generated/android/c++/JTrackItem.hpp +19 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/{Func_void_std__vector_TrackItem__double_QueueChangeReason.kt → Func_void_QueueChangeDelta_double_QueueChangeReason.kt} +14 -16
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +6 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +9 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +7 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/QueueChangeDelta.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +24 -4
- package/nitrogen/generated/android/queueplayerOnLoad.cpp +2 -2
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +16 -16
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +80 -65
- package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +6 -3
- package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_QueueChangeDelta_double_QueueChangeReason.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +3 -3
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +32 -24
- package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +20 -2
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
- package/nitrogen/generated/ios/swift/QueueChangeDelta.swift +82 -0
- package/nitrogen/generated/ios/swift/TrackItem.swift +73 -1
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +6 -3
- package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +7 -3
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +5 -1
- package/nitrogen/generated/shared/c++/QueueChangeDelta.hpp +113 -0
- package/nitrogen/generated/shared/c++/TrackItem.hpp +18 -2
- package/package.json +8 -8
- package/src/TrackPlayer.nitro.ts +53 -27
- package/src/hooks/useActiveTrack.ts +29 -22
- package/src/hooks/useCast.ts +14 -15
- package/src/hooks/useEqualizer.ts +19 -12
- package/src/hooks/useLookaheadCache.ts +3 -7
- package/src/hooks/useQueue.ts +66 -17
- package/src/index.ts +13 -3
- package/src/queueDelta.ts +41 -0
- package/src/types.ts +169 -12
- package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +0 -82
- package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +0 -15
- package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +0 -192
- package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +0 -135
- package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +0 -27
- package/ios/Cast/Core/LocalAddressMonitor.swift +0 -110
- package/ios/Cast/Core/MediaHTTPConnection.swift +0 -349
- package/ios/MetadataReader.swift +0 -555
- package/ios/Tests/CrossfadeEngineStubTests.swift +0 -43
- package/ios/Tests/MetadataReaderTests.swift +0 -603
- package/ios/Tests/PlaybackEngineProtocolTests.swift +0 -92
- package/ios/Tests/VoiceDonationTests.swift +0 -26
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__double_QueueChangeReason.hpp +0 -101
- package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__double_QueueChangeReason.swift +0 -46
- /package/ios/{Cast/Core → MediaServer}/MimeTypes.swift +0 -0
|
@@ -4,11 +4,12 @@ package com.margelo.nitro.queueplayer
|
|
|
4
4
|
* Pure-Kotlin skip-index arithmetic. Mirrors the iOS sibling
|
|
5
5
|
* `ios/QueueSkipArithmetic.swift` 1-for-1.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the
|
|
11
|
-
*
|
|
7
|
+
* Kept separate from [TrackPlayer] so the logic runs without an
|
|
8
|
+
* ExoPlayer. [TrackPlayer.computeNextIndex] is the thin adapter that
|
|
9
|
+
* translates the Nitrogen-generated [RepeatMode] enum into
|
|
10
|
+
* [QueueSkipRepeatMode] and delegates; the skip-previous path goes
|
|
11
|
+
* through [QueueSkipArithmetic.resolveSkipToPrevious], which wraps
|
|
12
|
+
* [QueueSkipArithmetic.computePrevious].
|
|
12
13
|
*
|
|
13
14
|
* Value order (0/1/2) is held fixed across the iOS enum and this one
|
|
14
15
|
* so a test that asserts the raw integer catches any accidental
|
|
@@ -102,7 +103,9 @@ internal object QueueSkipArithmetic {
|
|
|
102
103
|
* When [restartEnabled] is true (`PlayerConfig.skipToPreviousBehavior
|
|
103
104
|
* == RESTART_OR_PREVIOUS`), Previous is enabled whenever a track is
|
|
104
105
|
* loaded — even at index 0, where past the threshold it restarts the
|
|
105
|
-
* current track — so the control never greys out
|
|
106
|
+
* current track — so the control never greys out while something is
|
|
107
|
+
* playing. With no loaded track (`currentIndex < 0`) there is nothing
|
|
108
|
+
* to restart, matching what resolveSkipToPrevious does for that state.
|
|
106
109
|
*/
|
|
107
110
|
fun canSkipPrevious(
|
|
108
111
|
trackCount: Int,
|
|
@@ -110,7 +113,7 @@ internal object QueueSkipArithmetic {
|
|
|
110
113
|
repeatMode: QueueSkipRepeatMode,
|
|
111
114
|
restartEnabled: Boolean = false
|
|
112
115
|
): Boolean =
|
|
113
|
-
if (restartEnabled) trackCount > 0
|
|
116
|
+
if (restartEnabled) trackCount > 0 && currentIndex >= 0
|
|
114
117
|
else computePrevious(trackCount, currentIndex, repeatMode) != null
|
|
115
118
|
|
|
116
119
|
/**
|
|
@@ -126,7 +129,7 @@ internal object QueueSkipArithmetic {
|
|
|
126
129
|
data class Previous(val index: Int) : SkipToPreviousAction
|
|
127
130
|
/** Restart the current track (seek to 0) — not a track change. */
|
|
128
131
|
object RestartCurrent : SkipToPreviousAction
|
|
129
|
-
/** Nothing to do (empty queue). */
|
|
132
|
+
/** Nothing to do (empty queue, or no loaded track). */
|
|
130
133
|
object NoOp : SkipToPreviousAction
|
|
131
134
|
}
|
|
132
135
|
|
|
@@ -12,8 +12,20 @@ import kotlin.math.roundToInt
|
|
|
12
12
|
* gain to a PCM stream. No knowledge of the engine, the EQ stage, or the
|
|
13
13
|
* visualizer pipeline — purely "given a PCM buffer, multiply by
|
|
14
14
|
* [linearGain]". The owning engine pushes a new gain via [setLinearGain]
|
|
15
|
-
* from its `Player.Listener` callbacks
|
|
16
|
-
* unity until the next track's
|
|
15
|
+
* from its `Player.Listener` callbacks and from the ReplayGain-mode
|
|
16
|
+
* setter; `onMediaItemTransition` resets to unity until the next track's
|
|
17
|
+
* tags arrive.
|
|
18
|
+
*
|
|
19
|
+
* It is **always active** on a supported PCM encoding once configured, NOT
|
|
20
|
+
* gated on the current gain. Media3's
|
|
21
|
+
* [androidx.media3.common.audio.AudioProcessingPipeline] fixes the
|
|
22
|
+
* active-processor set in `configure()` / `flush()` and never re-evaluates
|
|
23
|
+
* it while a stream plays, so gating `isActive` on the (later-pushed) gain
|
|
24
|
+
* would exclude this processor from the pipeline for any queue loaded at
|
|
25
|
+
* unity and no gain could ever reach the audio afterwards. Staying active
|
|
26
|
+
* and passing the block straight through at unity keeps it present and
|
|
27
|
+
* ready, at the cost of one bulk buffer copy per block when no gain is
|
|
28
|
+
* applied.
|
|
17
29
|
*
|
|
18
30
|
* Handles both 16-bit and float PCM. The sink runs at 16-bit (see
|
|
19
31
|
* [AudioPipelineRenderersFactory] — float output would drop the whole
|
|
@@ -30,7 +42,7 @@ internal class ReplayGainAudioProcessor : BaseAudioProcessor() {
|
|
|
30
42
|
* it on every [queueInput] while the engine's main thread writes it
|
|
31
43
|
* from the metadata listener. The value is `pow(10, gainDb / 20)`
|
|
32
44
|
* already clip-capped against the track peak (caller's job); a
|
|
33
|
-
* value of `1.0f`
|
|
45
|
+
* value of `1.0f` passes the stream through untouched.
|
|
34
46
|
*/
|
|
35
47
|
@Volatile
|
|
36
48
|
private var linearGain: Float = 1.0f
|
|
@@ -43,8 +55,8 @@ internal class ReplayGainAudioProcessor : BaseAudioProcessor() {
|
|
|
43
55
|
private var encoding: Int = C.ENCODING_INVALID
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
|
-
* Push a new linear gain
|
|
47
|
-
* multiply
|
|
58
|
+
* Push a new linear gain, observed from the next [queueInput] onwards.
|
|
59
|
+
* `1.0f` skips the per-sample multiply and passes the block through.
|
|
48
60
|
*/
|
|
49
61
|
fun setLinearGain(value: Float) {
|
|
50
62
|
linearGain = value
|
|
@@ -60,7 +72,7 @@ internal class ReplayGainAudioProcessor : BaseAudioProcessor() {
|
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
override fun isActive(): Boolean =
|
|
63
|
-
super.isActive() &&
|
|
75
|
+
super.isActive() &&
|
|
64
76
|
(encoding == C.ENCODING_PCM_FLOAT || encoding == C.ENCODING_PCM_16BIT)
|
|
65
77
|
|
|
66
78
|
override fun queueInput(input: ByteBuffer) {
|
|
@@ -68,6 +80,14 @@ internal class ReplayGainAudioProcessor : BaseAudioProcessor() {
|
|
|
68
80
|
val byteCount = input.remaining()
|
|
69
81
|
if (byteCount == 0) return
|
|
70
82
|
val out = replaceOutputBuffer(byteCount)
|
|
83
|
+
if (gain == 1.0f) {
|
|
84
|
+
// Unity — present in the pipeline but applying nothing. A bulk copy
|
|
85
|
+
// is ~25x cheaper per block than walking the samples for a multiply
|
|
86
|
+
// by one, and both consume the input and produce identical bytes.
|
|
87
|
+
out.put(input)
|
|
88
|
+
out.flip()
|
|
89
|
+
return
|
|
90
|
+
}
|
|
71
91
|
if (encoding == C.ENCODING_PCM_FLOAT) {
|
|
72
92
|
val inF = input.asFloatBuffer()
|
|
73
93
|
val outF = out.asFloatBuffer()
|
|
@@ -31,9 +31,10 @@ data class ReplayGainData(
|
|
|
31
31
|
* both may be null when the album tag is absent. No fallback
|
|
32
32
|
* to track gain.
|
|
33
33
|
*
|
|
34
|
-
* Caller computes `linearGain = 10^(gainDb / 20)` and clip-caps
|
|
35
|
-
* peak when
|
|
36
|
-
*
|
|
34
|
+
* Caller computes `linearGain = 10^(gainDb / 20)` and clip-caps it
|
|
35
|
+
* at `1 / peak` when the peak is usable, at unity when it is not —
|
|
36
|
+
* see [computeLinearGain]. When `gainDb` is null the caller treats
|
|
37
|
+
* RG as inactive for this track (`linearGain = 1.0f`,
|
|
37
38
|
* `replayGainActive = false`).
|
|
38
39
|
*/
|
|
39
40
|
fun selectGain(mode: ReplayGainMode): Pair<Float?, Float?> = when (mode) {
|
|
@@ -41,4 +42,22 @@ data class ReplayGainData(
|
|
|
41
42
|
ReplayGainMode.TRACK -> trackGainDb to trackPeak
|
|
42
43
|
ReplayGainMode.ALBUM -> albumGainDb to albumPeak
|
|
43
44
|
}
|
|
45
|
+
|
|
46
|
+
companion object {
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the ReplayGain a track plays with, all-or-nothing per
|
|
49
|
+
* item. [suppliedClaimed] is `true` when the consumer put any of
|
|
50
|
+
* the four fields on the `TrackItem`, computed before any value
|
|
51
|
+
* is validated: a claim discards detection outright, including
|
|
52
|
+
* for the mode the consumer never mentioned.
|
|
53
|
+
* `suppliedClaimed && supplied == null` is a real state — the
|
|
54
|
+
* consumer owns the track and gave nothing usable, so no gain is
|
|
55
|
+
* applied and the file's own tags stay unused.
|
|
56
|
+
*/
|
|
57
|
+
internal fun merge(
|
|
58
|
+
suppliedClaimed: Boolean,
|
|
59
|
+
supplied: ReplayGainData?,
|
|
60
|
+
detected: ReplayGainData?,
|
|
61
|
+
): ReplayGainData? = if (suppliedClaimed) supplied else detected
|
|
62
|
+
}
|
|
44
63
|
}
|
|
@@ -159,7 +159,9 @@ internal object ReplayGainExtractor {
|
|
|
159
159
|
/**
|
|
160
160
|
* Parse a `REPLAYGAIN_*_GAIN` string (e.g. `"-6.54 dB"`,
|
|
161
161
|
* `"+3.21"`, `"−1.0 dB"` with U+2212 minus) into a Float dB.
|
|
162
|
-
* Returns `null` for unparseable
|
|
162
|
+
* Returns `null` for unparseable input; the parsed number is then
|
|
163
|
+
* put through [acceptGainDb], so text handling is all this adds
|
|
164
|
+
* over the numeric entry point.
|
|
163
165
|
*/
|
|
164
166
|
internal fun parseGainDb(rawInput: String): Float? {
|
|
165
167
|
var trimmed = rawInput.trim()
|
|
@@ -184,25 +186,49 @@ internal object ReplayGainExtractor {
|
|
|
184
186
|
// Reject comma decimals (RG2 spec — period only).
|
|
185
187
|
if (trimmed.contains(',')) return null
|
|
186
188
|
|
|
187
|
-
val value = trimmed.
|
|
189
|
+
val value = trimmed.toDoubleOrNull() ?: return null
|
|
190
|
+
return acceptGainDb(value)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Shared numeric acceptance for a peak value, whatever entry point
|
|
195
|
+
* it arrived through — a tag string or a consumer-supplied number.
|
|
196
|
+
* Accepts `0.001 ... 2.0` inclusive (`1.0` is digital full scale and
|
|
197
|
+
* a legitimate measurement); rejects anything outside that band and
|
|
198
|
+
* anything non-finite. The band is checked on the `Double`, before the
|
|
199
|
+
* narrowing to `Float`, so a value just past it cannot round in.
|
|
200
|
+
*/
|
|
201
|
+
internal fun acceptPeak(value: Double): Float? {
|
|
202
|
+
if (value.isNaN() || value.isInfinite()) return null
|
|
203
|
+
// Band-check the Double: 2.0000001 narrows to 2.0f, so a check
|
|
204
|
+
// applied after the narrowing would let it through.
|
|
205
|
+
if (value < 0.001 || value > 2.0) return null
|
|
206
|
+
return value.toFloat()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Shared numeric acceptance for a gain in dB, whatever entry point
|
|
211
|
+
* it arrived through. There is no plausibility band on gain — only
|
|
212
|
+
* non-finite values are rejected, including a magnitude that
|
|
213
|
+
* narrows to an infinite [Float].
|
|
214
|
+
*/
|
|
215
|
+
internal fun acceptGainDb(value: Double): Float? {
|
|
188
216
|
if (value.isNaN() || value.isInfinite()) return null
|
|
189
|
-
|
|
217
|
+
val narrowed = value.toFloat()
|
|
218
|
+
if (narrowed.isInfinite()) return null
|
|
219
|
+
return narrowed
|
|
190
220
|
}
|
|
191
221
|
|
|
192
222
|
/**
|
|
193
223
|
* Parse a `REPLAYGAIN_*_PEAK` string (linear, period decimal).
|
|
194
|
-
* Returns `null` for unparseable
|
|
195
|
-
*
|
|
196
|
-
*
|
|
224
|
+
* Returns `null` for unparseable input; the parsed number is then
|
|
225
|
+
* put through [acceptPeak], which holds the `0.001 ... 2.0`
|
|
226
|
+
* acceptance band — a full-scale `1.0` included.
|
|
197
227
|
*/
|
|
198
228
|
internal fun parsePeak(rawInput: String): Float? {
|
|
199
229
|
val trimmed = rawInput.trim()
|
|
200
|
-
val value = trimmed.
|
|
201
|
-
|
|
202
|
-
if (value < 0f || value > 2.0f) return null
|
|
203
|
-
if (value < 0.001f) return null
|
|
204
|
-
if (value == 1.0f) return null
|
|
205
|
-
return value
|
|
230
|
+
val value = trimmed.toDoubleOrNull() ?: return null
|
|
231
|
+
return acceptPeak(value)
|
|
206
232
|
}
|
|
207
233
|
|
|
208
234
|
// endregion
|
|
@@ -5,30 +5,34 @@ import kotlin.math.pow
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Pure gain math per NOTES.md §25. Selects the `(gainDb, peak)` pair
|
|
8
|
-
* from [data] under [mode] (strict, no fallback), then
|
|
8
|
+
* from [data] under [mode] (strict, no fallback), then caps the result
|
|
9
|
+
* for clip safety:
|
|
9
10
|
*
|
|
10
11
|
* ```
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* linearGain = min(linearGain, 1.0 / peak)
|
|
12
|
+
* cap = usable peak ? 1.0 / peak : 1.0
|
|
13
|
+
* linearGain = min(pow(10, gainDb / 20), cap)
|
|
14
14
|
* ```
|
|
15
15
|
*
|
|
16
|
+
* A peak is usable inside the `0.001 .. 2.0` band that
|
|
17
|
+
* [ReplayGainExtractor.acceptPeak] holds — full scale (`1.0`) and true
|
|
18
|
+
* peaks above it included. With no usable peak the cap sits at unity,
|
|
19
|
+
* so a boost is suppressed rather than applied unprotected: a peak
|
|
20
|
+
* cannot be derived from a gain. Attenuation is untouched either way,
|
|
21
|
+
* since a negative gain can never clip. The band is re-checked here so
|
|
22
|
+
* the math stays correct for any caller that bypasses the extractor.
|
|
23
|
+
*
|
|
16
24
|
* Returns `1.0f` (unity) whenever the selected mode's gain tag is
|
|
17
25
|
* absent — RG is inactive for that track. Mirrors the iOS
|
|
18
26
|
* `AudioTapProvider.computeLinearGain(data:mode:)` contract so a tag
|
|
19
27
|
* fixture parses to the same linear gain on both platforms.
|
|
20
|
-
*
|
|
21
|
-
* Encoder-default sentinels (`peak == 1.0` and `peak < 0.001`) bypass
|
|
22
|
-
* the peak-based clip cap. The extractor returns `null` for both
|
|
23
|
-
* already, but defending here keeps the math correct against any
|
|
24
|
-
* future caller that bypasses the extractor.
|
|
25
28
|
*/
|
|
26
29
|
internal fun computeLinearGain(data: ReplayGainData?, mode: ReplayGainMode): Float {
|
|
27
30
|
val (gainDb, peak) = data?.selectGain(mode) ?: (null to null)
|
|
28
31
|
if (gainDb == null) return 1.0f
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
val raw = 10.0.pow(gainDb.toDouble() / 20.0).toFloat()
|
|
33
|
+
var cap = 1.0f
|
|
34
|
+
if (peak != null && peak >= 0.001f && peak <= 2.0f) {
|
|
35
|
+
cap = 1.0f / peak
|
|
32
36
|
}
|
|
33
|
-
return raw
|
|
37
|
+
return min(raw, cap)
|
|
34
38
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
package com.margelo.nitro.queueplayer
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.util.UnstableApi
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The consumer's ReplayGain claim over one queue entry, resolved from
|
|
7
|
+
* the `TrackItem` the engine is playing.
|
|
8
|
+
*
|
|
9
|
+
* The claim and the values it survived with are two separate facts, and
|
|
10
|
+
* keeping them separate is the whole point of this type. A `TrackItem`
|
|
11
|
+
* that supplies only an out-of-band peak has claimed its track just as
|
|
12
|
+
* firmly as one that supplies all four values, so collapsing the claim
|
|
13
|
+
* into "did any value survive acceptance" would hand that track back to
|
|
14
|
+
* the file's own tags. Presence claims the track; validity only decides
|
|
15
|
+
* what there is to apply.
|
|
16
|
+
*
|
|
17
|
+
* An instance therefore exists whenever any of the four fields was
|
|
18
|
+
* present, and [data] is `null` when none of those values survived
|
|
19
|
+
* [ReplayGainExtractor]'s acceptance rules — a track that owns its
|
|
20
|
+
* ReplayGain and plays at unity.
|
|
21
|
+
*
|
|
22
|
+
* Mirrors the iOS `SuppliedReplayGain`, which rides on the
|
|
23
|
+
* `AVPlayerItem` rather than being resolved per read; the shapes match
|
|
24
|
+
* so one contract covers both platforms.
|
|
25
|
+
*/
|
|
26
|
+
@UnstableApi
|
|
27
|
+
internal class SuppliedReplayGain private constructor(val data: ReplayGainData?) {
|
|
28
|
+
companion object {
|
|
29
|
+
/**
|
|
30
|
+
* Build the claim for [track], or `null` when the consumer supplied
|
|
31
|
+
* none of the four ReplayGain fields and the track is left on
|
|
32
|
+
* whatever its file carries.
|
|
33
|
+
*
|
|
34
|
+
* Each present value goes through the same
|
|
35
|
+
* [ReplayGainExtractor.acceptGainDb] / [ReplayGainExtractor.acceptPeak]
|
|
36
|
+
* pair the tag strings land on, so a supplied number and a tagged
|
|
37
|
+
* number are held to one rule.
|
|
38
|
+
*/
|
|
39
|
+
fun from(track: TrackItem): SuppliedReplayGain? {
|
|
40
|
+
if (track.replayGainTrackGain == null &&
|
|
41
|
+
track.replayGainTrackPeak == null &&
|
|
42
|
+
track.replayGainAlbumGain == null &&
|
|
43
|
+
track.replayGainAlbumPeak == null
|
|
44
|
+
) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
val trackGain = track.replayGainTrackGain?.let { ReplayGainExtractor.acceptGainDb(it) }
|
|
48
|
+
val trackPeak = track.replayGainTrackPeak?.let { ReplayGainExtractor.acceptPeak(it) }
|
|
49
|
+
val albumGain = track.replayGainAlbumGain?.let { ReplayGainExtractor.acceptGainDb(it) }
|
|
50
|
+
val albumPeak = track.replayGainAlbumPeak?.let { ReplayGainExtractor.acceptPeak(it) }
|
|
51
|
+
if (trackGain == null && trackPeak == null &&
|
|
52
|
+
albumGain == null && albumPeak == null
|
|
53
|
+
) {
|
|
54
|
+
return SuppliedReplayGain(null)
|
|
55
|
+
}
|
|
56
|
+
return SuppliedReplayGain(
|
|
57
|
+
ReplayGainData(
|
|
58
|
+
trackGainDb = trackGain,
|
|
59
|
+
trackPeak = trackPeak,
|
|
60
|
+
albumGainDb = albumGain,
|
|
61
|
+
albumPeak = albumPeak,
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|