Skip to content

Commit

Permalink
Various changes on media3-based navigators (#363)
Browse files Browse the repository at this point in the history
  • Loading branch information
qnga authored Jul 8, 2023
1 parent c868166 commit 57bc32b
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import kotlinx.coroutines.flow.StateFlow
import org.readium.r2.shared.ExperimentalReadiumApi

/**
* A [MediaNavigator] which can play audio files.
* A [MediaNavigator] whose locations provide time offsets.
*/
@ExperimentalReadiumApi
interface AudioNavigator<L : AudioNavigator.Location, P : AudioNavigator.Playback,
R : AudioNavigator.ReadingOrder> : MediaNavigator<L, P, R> {
interface TimeBasedMediaNavigator<L : TimeBasedMediaNavigator.Location, P : TimeBasedMediaNavigator.Playback,
R : TimeBasedMediaNavigator.ReadingOrder> : MediaNavigator<L, P, R> {

/**
* Location of the navigator.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import org.readium.r2.shared.publication.Metadata
import org.readium.r2.shared.publication.Publication

/**
* To be implemented by adapters for third-party audio engines which can be used with [AudiobookNavigator].
* To be implemented by adapters for third-party audio engines which can be used with [AudioNavigator].
*/
@ExperimentalReadiumApi
interface AudioEngineProvider<S : Configurable.Settings, P : Configurable.Preferences<P>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.StateFlow
import org.readium.r2.navigator.extensions.sum
import org.readium.r2.navigator.extensions.time
import org.readium.r2.navigator.media3.api.AudioNavigator
import org.readium.r2.navigator.media3.api.Media3Adapter
import org.readium.r2.navigator.media3.api.MediaNavigator
import org.readium.r2.navigator.media3.api.TimeBasedMediaNavigator
import org.readium.r2.navigator.preferences.Configurable
import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.extensions.mapStateIn
Expand All @@ -30,13 +30,13 @@ import timber.log.Timber

@ExperimentalReadiumApi
@OptIn(ExperimentalTime::class)
class AudiobookNavigator<S : Configurable.Settings, P : Configurable.Preferences<P>> private constructor(
class AudioNavigator<S : Configurable.Settings, P : Configurable.Preferences<P>> private constructor(
override val publication: Publication,
private val audioEngine: AudioEngine<S, P>,
override val readingOrder: ReadingOrder,
) :
MediaNavigator<AudiobookNavigator.Location, AudiobookNavigator.Playback, AudiobookNavigator.ReadingOrder>,
AudioNavigator<AudiobookNavigator.Location, AudiobookNavigator.Playback, AudiobookNavigator.ReadingOrder>,
MediaNavigator<AudioNavigator.Location, AudioNavigator.Playback, AudioNavigator.ReadingOrder>,
TimeBasedMediaNavigator<AudioNavigator.Location, AudioNavigator.Playback, AudioNavigator.ReadingOrder>,
Media3Adapter,
Configurable<S, P> by audioEngine {

Expand All @@ -48,7 +48,7 @@ class AudiobookNavigator<S : Configurable.Settings, P : Configurable.Preferences
readingOrder: List<Link> = publication.readingOrder,
initialPreferences: P? = null,
initialLocator: Locator? = null,
): AudiobookNavigator<S, P>? {
): AudioNavigator<S, P>? {
if (readingOrder.isEmpty()) {
return null
}
Expand All @@ -71,7 +71,7 @@ class AudiobookNavigator<S : Configurable.Settings, P : Configurable.Preferences
initialPreferences ?: audioEngineProvider.createEmptyPreferences()
) ?: return null

return AudiobookNavigator(publication, audioEngine, actualReadingOrder)
return AudioNavigator(publication, audioEngine, actualReadingOrder)
}

private fun duration(link: Link, publication: Publication): Duration? {
Expand All @@ -90,17 +90,17 @@ class AudiobookNavigator<S : Configurable.Settings, P : Configurable.Preferences
data class Location(
override val href: Href,
override val offset: Duration,
) : AudioNavigator.Location
) : TimeBasedMediaNavigator.Location

data class ReadingOrder(
override val duration: Duration?,
override val items: List<Item>
) : AudioNavigator.ReadingOrder {
) : TimeBasedMediaNavigator.ReadingOrder {

data class Item(
val href: Href,
override val duration: Duration?
) : AudioNavigator.ReadingOrder.Item
) : TimeBasedMediaNavigator.ReadingOrder.Item
}

data class Playback(
Expand All @@ -109,7 +109,7 @@ class AudiobookNavigator<S : Configurable.Settings, P : Configurable.Preferences
override val index: Int,
override val offset: Duration,
override val buffered: Duration?,
) : AudioNavigator.Playback
) : TimeBasedMediaNavigator.Playback

sealed class State {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ class AudioNavigatorFactory<S : Configurable.Settings, P : Configurable.Preferen
suspend fun createNavigator(
initialPreferences: P? = null,
initialLocator: Locator? = null
): AudiobookNavigator<S, P>? {
return AudiobookNavigator(
): AudioNavigator<S, P>? {
return AudioNavigator(
publication = publication,
audioEngineProvider = audioEngineProvider,
initialPreferences = initialPreferences,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

package org.readium.r2.navigator.media3.exoplayer

import org.readium.r2.navigator.media3.audio.AudioNavigator
import org.readium.r2.navigator.media3.audio.AudioNavigatorFactory
import org.readium.r2.navigator.media3.audio.AudiobookNavigator
import org.readium.r2.shared.ExperimentalReadiumApi

@OptIn(ExperimentalReadiumApi::class)
typealias ExoPlayerNavigatorFactory = AudioNavigatorFactory<ExoPlayerSettings, ExoPlayerPreferences, ExoPlayerPreferencesEditor>

@OptIn(ExperimentalReadiumApi::class)
typealias ExoPlayerNavigator = AudiobookNavigator<ExoPlayerSettings, ExoPlayerPreferences>
typealias ExoPlayerNavigator = AudioNavigator<ExoPlayerSettings, ExoPlayerPreferences>
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ package org.readium.r2.navigator.media3.syncmedia
import androidx.media3.common.Player
import kotlin.time.Duration
import kotlinx.coroutines.flow.StateFlow
import org.readium.r2.navigator.media3.api.AudioNavigator
import org.readium.r2.navigator.media3.api.Media3Adapter
import org.readium.r2.navigator.media3.api.MediaNavigator
import org.readium.r2.navigator.media3.api.TextAwareMediaNavigator
import org.readium.r2.navigator.media3.audio.AudiobookNavigator
import org.readium.r2.navigator.media3.api.TimeBasedMediaNavigator
import org.readium.r2.navigator.media3.audio.AudioNavigator
import org.readium.r2.navigator.preferences.Configurable
import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.publication.Link
Expand All @@ -22,12 +22,12 @@ import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.util.Href

@ExperimentalReadiumApi
class GuidedAudioNavigator<S : Configurable.Settings, P : Configurable.Preferences<P>>(
private val audioNavigator: AudiobookNavigator<S, P>,
class GuidedMediaNavigator<S : Configurable.Settings, P : Configurable.Preferences<P>>(
private val audioNavigator: AudioNavigator<S, P>,
) :
MediaNavigator<GuidedAudioNavigator.Location, GuidedAudioNavigator.Playback, GuidedAudioNavigator.ReadingOrder>,
AudioNavigator<GuidedAudioNavigator.Location, GuidedAudioNavigator.Playback, GuidedAudioNavigator.ReadingOrder>,
TextAwareMediaNavigator<GuidedAudioNavigator.Location, GuidedAudioNavigator.Playback, GuidedAudioNavigator.ReadingOrder>,
MediaNavigator<GuidedMediaNavigator.Location, GuidedMediaNavigator.Playback, GuidedMediaNavigator.ReadingOrder>,
TimeBasedMediaNavigator<GuidedMediaNavigator.Location, GuidedMediaNavigator.Playback, GuidedMediaNavigator.ReadingOrder>,
TextAwareMediaNavigator<GuidedMediaNavigator.Location, GuidedMediaNavigator.Playback, GuidedMediaNavigator.ReadingOrder>,
Media3Adapter,
Configurable<S, P> {

Expand All @@ -41,7 +41,7 @@ class GuidedAudioNavigator<S : Configurable.Settings, P : Configurable.Preferenc
override val range: IntRange?,
override val utteranceLocator: Locator,
override val tokenLocator: Locator?,
) : AudioNavigator.Location,
) : TimeBasedMediaNavigator.Location,
TextAwareMediaNavigator.Location

data class Playback(
Expand All @@ -52,17 +52,17 @@ class GuidedAudioNavigator<S : Configurable.Settings, P : Configurable.Preferenc
override val buffered: Duration?,
override val utterance: String,
override val range: IntRange?,
) : AudioNavigator.Playback, TextAwareMediaNavigator.Playback
) : TimeBasedMediaNavigator.Playback, TextAwareMediaNavigator.Playback

data class ReadingOrder(
override val duration: Duration?,
override val items: List<Item>
) : AudioNavigator.ReadingOrder, TextAwareMediaNavigator.ReadingOrder {
) : TimeBasedMediaNavigator.ReadingOrder, TextAwareMediaNavigator.ReadingOrder {

data class Item(
val href: Href,
override val duration: Duration?
) : AudioNavigator.ReadingOrder.Item, TextAwareMediaNavigator.ReadingOrder.Item
) : TimeBasedMediaNavigator.ReadingOrder.Item, TextAwareMediaNavigator.ReadingOrder.Item
}

override val publication: Publication =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class TtsNavigator<S : TtsEngine.Settings, P : TtsEngine.Preferences<P>,
MediaNavigator<TtsNavigator.Location, TtsNavigator.Playback, TtsNavigator.ReadingOrder>,
TextAwareMediaNavigator<TtsNavigator.Location, TtsNavigator.Playback, TtsNavigator.ReadingOrder>,
Media3Adapter,
Configurable<S, P> by player {
Configurable<S, P> {

companion object {

Expand Down Expand Up @@ -247,6 +247,14 @@ class TtsNavigator<S : TtsEngine.Settings, P : TtsEngine.Preferences<P>,
return true
}

override val settings: StateFlow<S> =
player.settings

override fun submitPreferences(preferences: P) {
player.submitPreferences(preferences)
player.restartUtterance()
}

private fun navigatorPlayback(playback: TtsPlayer.Playback, utterance: TtsPlayer.Utterance) =
Playback(
state = playback.state.toState(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,19 +400,23 @@ class AndroidTtsEngine private constructor(
utteranceLanguage: Language?,
voices: Set<Voice>
): Boolean {
var language = utteranceLanguage
val language = utteranceLanguage
.takeUnless { settings.overrideContentLanguage }
// We take utterance language if data are missing but not if the language is not supported
?.takeIf { isLanguageAvailable(it.locale) != LANG_NOT_SUPPORTED }
?: settings.language
.takeIf { isLanguageAvailable(it.locale) != LANG_NOT_SUPPORTED }
?: defaultVoice?.locale?.let { Language(it) }

utteranceListener?.onError(id, Error.LanguageMissingData(language))
return false
if (language == null) {
// We don't know what to do.
utteranceListener?.onError(id, Error.Unknown)
return false
}

when (isLanguageAvailable(language.locale)) {
LANG_MISSING_DATA -> {
utteranceListener?.onError(id, Error.LanguageMissingData(language))
return false
}
LANG_NOT_SUPPORTED -> language = Language(defaultVoice.locale)
if (isLanguageAvailable(language.locale) < LANG_AVAILABLE) {
utteranceListener?.onError(id, Error.LanguageMissingData(language))
return false
}

val preferredVoiceWithRegion =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import kotlin.time.DurationUnit
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.readium.r2.navigator.media3.api.AudioNavigator
import org.readium.r2.navigator.media3.api.MediaNavigator
import org.readium.r2.navigator.media3.api.TimeBasedMediaNavigator
import org.readium.r2.navigator.media3.exoplayer.ExoPlayerPreferences
import org.readium.r2.navigator.media3.exoplayer.ExoPlayerSettings
import org.readium.r2.navigator.preferences.Configurable
Expand All @@ -40,7 +40,7 @@ import timber.log.Timber
@OptIn(ExperimentalReadiumApi::class)
class AudioReaderFragment : BaseReaderFragment(), SeekBar.OnSeekBarChangeListener {

override lateinit var navigator: AudioNavigator<*, *, *>
override lateinit var navigator: TimeBasedMediaNavigator<*, *, *>

private var binding: FragmentAudiobookBinding by viewLifecycle()
private var seekingItem: Int? = null
Expand Down Expand Up @@ -88,7 +88,7 @@ class AudioReaderFragment : BaseReaderFragment(), SeekBar.OnSeekBarChangeListene
.launchIn(viewLifecycleOwner.lifecycleScope)
}

private fun onPlaybackChanged(playback: AudioNavigator.Playback) {
private fun onPlaybackChanged(playback: TimeBasedMediaNavigator.Playback) {
Timber.v("onPlaybackChanged $playback")
if (playback.state is MediaNavigator.State.Error) {
onPlayerError()
Expand All @@ -111,7 +111,7 @@ class AudioReaderFragment : BaseReaderFragment(), SeekBar.OnSeekBarChangeListene
}
}

private fun updateTimeline(playback: AudioNavigator.Playback) {
private fun updateTimeline(playback: TimeBasedMediaNavigator.Playback) {
val currentItem = navigator.readingOrder.items[playback.index]
binding.timelineBar.max = currentItem.duration?.inWholeSeconds?.toInt() ?: 0
binding.timelineDuration.text = currentItem.duration?.formatElapsedTime()
Expand Down

0 comments on commit 57bc32b

Please sign in to comment.