new: display current playback position and implement cross-platform position tracking

This commit is contained in:
2026-06-19 12:23:51 +02:00
parent bc848ebb62
commit 090305dbde
8 changed files with 76 additions and 10 deletions
@@ -7,6 +7,8 @@ import androidx.media3.exoplayer.ExoPlayer
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
class AndroidAudioPlayer(
context: Context,
@@ -36,6 +38,8 @@ class AndroidAudioPlayer(
)
}
override fun currentPosition(): Duration = player.currentPosition.coerceAtLeast(0).milliseconds
override fun load(uri: String) {
val mediaItem = MediaItem.fromUri(uri)
player.setMediaItem(mediaItem)
@@ -1,10 +1,13 @@
package fr.ajaury.gwenedeg.player
import kotlinx.coroutines.flow.StateFlow
import kotlin.time.Duration
interface AudioPlayer {
val playbackState: StateFlow<PlaybackState>
fun currentPosition(): Duration
fun play()
fun pause()
@@ -7,13 +7,18 @@ import kotlinx.coroutines.flow.asStateFlow
import platform.AVFoundation.AVPlayer
import platform.AVFoundation.AVPlayerItem
import platform.AVFoundation.AVPlayerItemDidPlayToEndTimeNotification
import platform.AVFoundation.currentTime
import platform.AVFoundation.pause
import platform.AVFoundation.play
import platform.AVFoundation.replaceCurrentItemWithPlayerItem
import platform.CoreMedia.CMTimeGetSeconds
import platform.Foundation.NSNotificationCenter
import platform.Foundation.NSOperationQueue
import platform.Foundation.NSURL
import platform.darwin.NSObjectProtocol
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
class IosAudioPlayer : AudioPlayer {
private var player: AVPlayer? = null
@@ -22,6 +27,13 @@ class IosAudioPlayer : AudioPlayer {
private val _playbackState = MutableStateFlow(PlaybackState.IDLE)
override val playbackState: StateFlow<PlaybackState> = _playbackState.asStateFlow()
@OptIn(ExperimentalForeignApi::class)
override fun currentPosition(): Duration {
val player = player ?: return 0.milliseconds
val seconds = CMTimeGetSeconds(player.currentTime()).takeIf { !it.isNaN() }
return seconds?.seconds ?: 0.milliseconds
}
@OptIn(ExperimentalForeignApi::class)
override fun load(uri: String) {
release()
@@ -9,6 +9,8 @@ import javax.sound.sampled.AudioFormat
import javax.sound.sampled.AudioSystem
import javax.sound.sampled.Clip
import javax.sound.sampled.LineEvent
import kotlin.time.Duration
import kotlin.time.Duration.Companion.microseconds
/**
* Desktop [AudioPlayer] backed by [javax.sound.sampled].
@@ -25,6 +27,8 @@ class JvmAudioPlayer : AudioPlayer {
private val _playbackState = MutableStateFlow(PlaybackState.IDLE)
override val playbackState: StateFlow<PlaybackState> = _playbackState.asStateFlow()
override fun currentPosition(): Duration = (clip?.microsecondPosition ?: 0L).microseconds
override fun load(uri: String) {
release()
@@ -5,6 +5,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import org.w3c.dom.HTMLAudioElement
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
class WebAudioPlayer : AudioPlayer {
private var audio: HTMLAudioElement? = null
@@ -12,6 +14,8 @@ class WebAudioPlayer : AudioPlayer {
private val _playbackState = MutableStateFlow(PlaybackState.IDLE)
override val playbackState: StateFlow<PlaybackState> = _playbackState.asStateFlow()
override fun currentPosition(): Duration = (audio?.currentTime ?: 0.0).seconds
override fun load(uri: String) {
release()