new: implement desktop audio player with Ogg Vorbis support using VorbisSPI
This commit is contained in:
@@ -43,6 +43,10 @@ kotlin {
|
|||||||
implementation(libs.media3.exoplayer)
|
implementation(libs.media3.exoplayer)
|
||||||
implementation(libs.media3.common)
|
implementation(libs.media3.common)
|
||||||
}
|
}
|
||||||
|
jvmMain.dependencies {
|
||||||
|
// Desktop audio: Ogg Vorbis SPI so javax.sound.sampled can decode .ogg
|
||||||
|
implementation(libs.vorbisspi)
|
||||||
|
}
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
// DI
|
// DI
|
||||||
implementation(project.dependencies.platform(libs.koin.bom))
|
implementation(project.dependencies.platform(libs.koin.bom))
|
||||||
|
|||||||
@@ -1,23 +1,61 @@
|
|||||||
package fr.ajaury.gwenedeg.player
|
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 {
|
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() {
|
override fun play() {
|
||||||
TODO("Not yet implemented")
|
clip?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pause() {
|
override fun pause() {
|
||||||
TODO("Not yet implemented")
|
clip?.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
TODO("Not yet implemented")
|
clip?.apply {
|
||||||
|
stop()
|
||||||
|
framePosition = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun release() {
|
override fun release() {
|
||||||
TODO("Not yet implemented")
|
clip?.apply {
|
||||||
}
|
stop()
|
||||||
|
close()
|
||||||
override fun load(uri: String) {
|
}
|
||||||
TODO("Not yet implemented")
|
clip = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-2
@@ -1,6 +1,13 @@
|
|||||||
package fr.ajaury.gwenedeg.player.di
|
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.core.module.Module
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
actual val audioPlayerModule: Module
|
actual val audioPlayerModule: Module = module {
|
||||||
get() = TODO("Not yet implemented")
|
single<AudioPlayer> { JvmAudioPlayer() }
|
||||||
|
single<AudioSessionManager> { JvmAudioSessionManager() }
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ kotlinxCoroutines = "1.11.0"
|
|||||||
kotlinSerialization = "2.4.0"
|
kotlinSerialization = "2.4.0"
|
||||||
material3 = "1.11.0-alpha07"
|
material3 = "1.11.0-alpha07"
|
||||||
koin = "4.2.1"
|
koin = "4.2.1"
|
||||||
|
vorbisspi = "1.0.3.3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
|
||||||
@@ -40,6 +41,7 @@ lifecycle-viewmodelNavigation3 = { module = "org.jetbrains.androidx.lifecycle:li
|
|||||||
media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
|
media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
|
||||||
media3-common = { module = "androidx.media3:media3-common", version.ref = "media3" }
|
media3-common = { module = "androidx.media3:media3-common", version.ref = "media3" }
|
||||||
navigation3 = { module = "org.jetbrains.androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
|
navigation3 = { module = "org.jetbrains.androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
|
||||||
|
vorbisspi = { module = "com.googlecode.soundlibs:vorbisspi", version.ref = "vorbisspi" }
|
||||||
wrappers-browser = { module = "org.jetbrains.kotlin-wrappers:kotlin-browser", version.ref = "kotlinWrappers" }
|
wrappers-browser = { module = "org.jetbrains.kotlin-wrappers:kotlin-browser", version.ref = "kotlinWrappers" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
|||||||
Reference in New Issue
Block a user