Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Experimental WASM support, Android improvements #7

Merged
merged 3 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/actions/wrapper-validation@v3
- uses: actions/cache@v3
with:
path: |
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Gadulka is available from Maven Central at the following coordinates:
implementation("eu.iamkonstantin.kotlin:gadulka:x.x.x")
```

### Example

Instantiate the player and call play!

```kotlin
Expand All @@ -40,6 +42,7 @@ player.stop()
player.release()
```

### Jetpack Compose
Example using Jetpack Compose:

```kotlin
Expand All @@ -66,7 +69,15 @@ fun AudioPlayer(player: GadulkaPlayer = GadulkaPlayer()) {
}
```

For Android, the player needs a reference to the android context. For example, using Koin (or another DI library):
### Android

For Android, the player needs a reference to the android context.

```kotlin
GadulkaPlayer(LocalContext.current)
```

Or using Koin (or another DI library):

```kotlin
factory<GadulkaPlayer> {
Expand Down
12 changes: 12 additions & 0 deletions gadulka/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import com.vanniktech.maven.publish.SonatypeHost
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl

plugins {
alias(libs.plugins.kotlinMultiplatform)
Expand All @@ -24,6 +25,11 @@ kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
binaries.executable()
}

sourceSets {
val commonMain by getting {
Expand Down Expand Up @@ -55,6 +61,12 @@ kotlin {
}
}

val wasmJsMain by getting {
dependencies {
implementation(libs.kotlinx.browser)
}
}

androidMain.dependencies {
implementation(libs.androix.media3.exploplayer)
}
Expand Down
38 changes: 15 additions & 23 deletions gadulka/src/androidMain/kotlin/GadulkaAudioPlayer.android.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,13 @@
package eu.iamkonstantin.kotlin.gadulka

import android.content.ContentResolver
import android.media.MediaPlayer

import android.content.Context
import android.net.Uri
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

//
//actual class AudioPlayer(private val context: Context) {
//
// private val mediaPlayer = ExoPlayer.Builder(context).build()
// private val mediaItems = soundResList.map {
// MediaItem.fromUri(Res.getUri(it))
// }
//
// init {
// mediaPlayer.prepare()
// }
//
// @OptIn(ExperimentalResourceApi::class)
// actual fun playSound(id: Int) {
// mediaPlayer.setMediaItem(mediaItems[id])
// mediaPlayer.play()
// }
//
// actual fun release() {
// mediaPlayer.release()
// }
//}
//
actual class GadulkaPlayer(private val context: Context) {
private val mediaPlayer = ExoPlayer.Builder(context).build()

Expand All @@ -43,7 +22,20 @@ actual class GadulkaPlayer(private val context: Context) {
mediaPlayer.play()
}


/**
* Android-specific implementation of the [play] method which uses a ContentResolver to calculate the Uri of a raw file resource bundled with the app.
*/
fun play(rawResourceId: Int) {
val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.appendPath("$rawResourceId")
.build().toString()
play(uri)
}

actual fun release() {
mediaPlayer.pause()
mediaPlayer.release()
}

Expand Down
43 changes: 42 additions & 1 deletion gadulka/src/commonMain/kotlin/GadulkaAudioPlayer.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,50 @@
package eu.iamkonstantin.kotlin.gadulka

/**
* A minimalistic audio player
*
*
* Example:
*
* ```kotlin
val player = GadulkaPlayer()
player.play(url = "...")
player.stop()
player.release()
```
*/
expect class GadulkaPlayer {
/**
* Start playback of the audio resource at the provided [url].
*
* ### Resource URI
*
* Can be a remote HTTP(s) url, or a `files` URI obtained via `Res.getUri("files/sample.mp3")`.
*
* Check the [JetBrains docs on how to store raw](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-resources-usage.html#raw-files) files as part of multiplatform project resources.
*
* On Android, you can resolve the resource URI using something like:
*
* ```kotlin
* val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.appendPath("${R.raw.name_of_your_resource}")
.build().toString()
* ```
*
*/
fun play(url: String)

/**
* Stop playback.
*
* Note: If the player is currently not playing, this action has no effect.
*/
fun stop()


/**
* Pause and attempts to perform cleanup in order to dispose of any player resources.
*
*/
fun release()
}
47 changes: 47 additions & 0 deletions gadulka/src/wasmJsMain/kotlin/GadulkaAudioPlayer.wasmJs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package eu.iamkonstantin.kotlin.gadulka

import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.dom.appendElement
import kotlinx.dom.appendText
import org.w3c.dom.Element
import org.w3c.dom.HTMLAudioElement
import org.w3c.dom.HTMLInputElement


actual class GadulkaPlayer(val htmlId: String) {
actual fun play(url: String) {
release()
document.body?.appendElement("audio") {
this as HTMLAudioElement
this.id = htmlId
this.src = url
}

val playerEl = getPlayerElement()
playerEl?.play()
}

/**
* Stop playback.
*
* Note: the player element remains in the DOM.
*/
actual fun stop() {
val playerEl = getPlayerElement()
playerEl?.pause()
}

/**
* Stops playback and removes the player element from the DOM.
*/
actual fun release() {
val playerEl = getPlayerElement()
playerEl?.pause()
}

private fun getPlayerElement(): HTMLAudioElement? {
return document.getElementById(htmlId) as? HTMLAudioElement
}

}
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ kotlin = "2.0.21"
android-minSdk = "24"
android-compileSdk = "34"
media3_version = "1.4.1"
kotlinx-browser = "0.3"

[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
androix-media3-exploplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3_version" }

kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser-wasm-js", version.ref = "kotlinx-browser" }

[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }
Expand Down
Loading
Loading