From 8fb8ea24cfdee9dd32e60c921e00a9cc3d244cae Mon Sep 17 00:00:00 2001 From: Antoine Jaury Date: Tue, 16 Jun 2026 17:55:49 +0200 Subject: [PATCH] new: implement web audio player with support for js and wasmJs platforms --- core/audioplayer/build.gradle.kts | 9 ++++++ .../ajaury/gwenedeg/player/JsAudioPlayer.kt | 23 ------------- .../player/di/AudioPlayerModule.js.kt | 6 ---- .../ajaury/gwenedeg/player/WasmAudioPlayer.kt | 23 ------------- .../player/di/AudioPlayerModule.wasmJs.kt | 6 ---- .../ajaury/gwenedeg/player/WebAudioPlayer.kt | 32 +++++++++++++++++++ .../gwenedeg/player/WebAudioSessionManager.kt | 9 ++++++ .../player/di/AudioPlayerModule.web.kt | 13 ++++++++ gradle/libs.versions.toml | 2 ++ 9 files changed, 65 insertions(+), 58 deletions(-) delete mode 100644 core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/JsAudioPlayer.kt delete mode 100644 core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.js.kt delete mode 100644 core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/WasmAudioPlayer.kt delete mode 100644 core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.wasmJs.kt create mode 100644 core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioPlayer.kt create mode 100644 core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioSessionManager.kt create mode 100644 core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.web.kt diff --git a/core/audioplayer/build.gradle.kts b/core/audioplayer/build.gradle.kts index 0c42b9c..5fe38ea 100644 --- a/core/audioplayer/build.gradle.kts +++ b/core/audioplayer/build.gradle.kts @@ -48,6 +48,15 @@ kotlin { implementation(project.dependencies.platform(libs.koin.bom)) implementation(libs.koin.core) } + // webMain is the js + wasmJs intermediate source set from the default hierarchy template. + webMain.dependencies { + // Browser audio (HTMLAudioElement, document) for js + wasmJs + implementation(libs.kotlinx.browser) + } + wasmJsMain { + // HTMLAudioElement interop on Wasm requires this opt-in. + languageSettings.optIn("kotlin.js.ExperimentalWasmJsInterop") + } commonTest.dependencies { implementation(libs.kotlin.test) } diff --git a/core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/JsAudioPlayer.kt b/core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/JsAudioPlayer.kt deleted file mode 100644 index 6b18ed1..0000000 --- a/core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/JsAudioPlayer.kt +++ /dev/null @@ -1,23 +0,0 @@ -package fr.ajaury.gwenedeg.player - -class JsAudioPlayer : AudioPlayer { - override fun play() { - TODO("Not yet implemented") - } - - override fun pause() { - TODO("Not yet implemented") - } - - override fun stop() { - TODO("Not yet implemented") - } - - override fun release() { - TODO("Not yet implemented") - } - - override fun load(uri: String) { - TODO("Not yet implemented") - } -} diff --git a/core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.js.kt b/core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.js.kt deleted file mode 100644 index 785aa27..0000000 --- a/core/audioplayer/src/jsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.js.kt +++ /dev/null @@ -1,6 +0,0 @@ -package fr.ajaury.gwenedeg.player.di - -import org.koin.core.module.Module - -actual val audioPlayerModule: Module - get() = TODO("Not yet implemented") diff --git a/core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/WasmAudioPlayer.kt b/core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/WasmAudioPlayer.kt deleted file mode 100644 index a218379..0000000 --- a/core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/WasmAudioPlayer.kt +++ /dev/null @@ -1,23 +0,0 @@ -package fr.ajaury.gwenedeg.player - -class WasmAudioPlayer : AudioPlayer { - override fun play() { - TODO("Not yet implemented") - } - - override fun pause() { - TODO("Not yet implemented") - } - - override fun stop() { - TODO("Not yet implemented") - } - - override fun release() { - TODO("Not yet implemented") - } - - override fun load(uri: String) { - TODO("Not yet implemented") - } -} diff --git a/core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.wasmJs.kt b/core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.wasmJs.kt deleted file mode 100644 index 785aa27..0000000 --- a/core/audioplayer/src/wasmJsMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.wasmJs.kt +++ /dev/null @@ -1,6 +0,0 @@ -package fr.ajaury.gwenedeg.player.di - -import org.koin.core.module.Module - -actual val audioPlayerModule: Module - get() = TODO("Not yet implemented") diff --git a/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioPlayer.kt b/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioPlayer.kt new file mode 100644 index 0000000..0294128 --- /dev/null +++ b/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioPlayer.kt @@ -0,0 +1,32 @@ +package fr.ajaury.gwenedeg.player + +import kotlinx.browser.document +import org.w3c.dom.HTMLAudioElement + +class WebAudioPlayer : AudioPlayer { + private var audio: HTMLAudioElement? = null + + override fun load(uri: String) { + audio = (document.createElement("audio") as HTMLAudioElement).apply { + src = uri + } + } + + override fun play() { + audio?.play() + } + + override fun pause() { + audio?.pause() + } + + override fun stop() { + audio?.pause() + audio?.currentTime = 0.0 + } + + override fun release() { + audio?.pause() + audio = null + } +} diff --git a/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioSessionManager.kt b/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioSessionManager.kt new file mode 100644 index 0000000..6ffaf8a --- /dev/null +++ b/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/WebAudioSessionManager.kt @@ -0,0 +1,9 @@ +package fr.ajaury.gwenedeg.player + +class WebAudioSessionManager : AudioSessionManager { + override fun activate() { + } + + override fun deactivate() { + } +} diff --git a/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.web.kt b/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.web.kt new file mode 100644 index 0000000..4720fe6 --- /dev/null +++ b/core/audioplayer/src/webMain/kotlin/fr/ajaury/gwenedeg/player/di/AudioPlayerModule.web.kt @@ -0,0 +1,13 @@ +package fr.ajaury.gwenedeg.player.di + +import fr.ajaury.gwenedeg.player.AudioPlayer +import fr.ajaury.gwenedeg.player.AudioSessionManager +import fr.ajaury.gwenedeg.player.WebAudioPlayer +import fr.ajaury.gwenedeg.player.WebAudioSessionManager +import org.koin.core.module.Module +import org.koin.dsl.module + +actual val audioPlayerModule: Module = module { + single { WebAudioPlayer() } + single { WebAudioSessionManager() } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c4510ae..1d357ba 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ navigation3 = "1.1.1" composeMultiplatform = "1.11.1" kotlin = "2.4.0" kotlinWrappers = "2026.6.3" +kotlinxBrowser = "0.5.0" kotlinxCoroutines = "1.11.0" kotlinSerialization = "2.4.0" material3 = "1.11.0-alpha07" @@ -31,6 +32,7 @@ koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" } koin-core = { module = "io.insert-koin:koin-core" } koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinxBrowser" } kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutines" } lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" } lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }