new: implement desktop audio player with Ogg Vorbis support using VorbisSPI

This commit is contained in:
2026-06-17 17:18:33 +02:00
parent 8fb8ea24cf
commit 1fc8fbbc0d
5 changed files with 73 additions and 10 deletions
+4
View File
@@ -43,6 +43,10 @@ kotlin {
implementation(libs.media3.exoplayer)
implementation(libs.media3.common)
}
jvmMain.dependencies {
// Desktop audio: Ogg Vorbis SPI so javax.sound.sampled can decode .ogg
implementation(libs.vorbisspi)
}
commonMain.dependencies {
// DI
implementation(project.dependencies.platform(libs.koin.bom))
@@ -1,23 +1,61 @@
package fr.ajaury.gwenedeg.player
import java.io.BufferedInputStream
import java.net.URI
import javax.sound.sampled.AudioFormat
import javax.sound.sampled.AudioSystem
import javax.sound.sampled.Clip
/**
* Desktop [AudioPlayer] backed by [javax.sound.sampled].
*
* The Ogg Vorbis recordings are decoded to PCM by the VorbisSPI providers on the classpath, which
* the JDK [AudioSystem] discovers automatically, so loading works the same as any natively
* supported format.
*/
class JvmAudioPlayer : AudioPlayer {
private var clip: Clip? = null
override fun load(uri: String) {
release()
val encodedInput = BufferedInputStream(URI(uri).toURL().openStream())
.let { AudioSystem.getAudioInputStream(it) }
val baseFormat = encodedInput.format
val pcmFormat = AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
baseFormat.sampleRate,
16,
baseFormat.channels,
baseFormat.channels * 2,
baseFormat.sampleRate,
false,
)
val decodedInput = AudioSystem.getAudioInputStream(pcmFormat, encodedInput)
clip = AudioSystem.getClip().apply { open(decodedInput) }
}
override fun play() {
TODO("Not yet implemented")
clip?.start()
}
override fun pause() {
TODO("Not yet implemented")
clip?.stop()
}
override fun stop() {
TODO("Not yet implemented")
clip?.apply {
stop()
framePosition = 0
}
}
override fun release() {
TODO("Not yet implemented")
}
override fun load(uri: String) {
TODO("Not yet implemented")
clip?.apply {
stop()
close()
}
clip = null
}
}
@@ -0,0 +1,12 @@
package fr.ajaury.gwenedeg.player
/**
* Desktop has no system audio focus model to honour, so activation is a no-op.
*/
class JvmAudioSessionManager : AudioSessionManager {
override fun activate() {
}
override fun deactivate() {
}
}
@@ -1,6 +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.JvmAudioPlayer
import fr.ajaury.gwenedeg.player.JvmAudioSessionManager
import org.koin.core.module.Module
import org.koin.dsl.module
actual val audioPlayerModule: Module
get() = TODO("Not yet implemented")
actual val audioPlayerModule: Module = module {
single<AudioPlayer> { JvmAudioPlayer() }
single<AudioSessionManager> { JvmAudioSessionManager() }
}