expo-audio 55.0.6 → 55.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/android/build.gradle +4 -2
- package/android/src/main/java/expo/modules/audio/AudioModule.kt +274 -45
- package/android/src/main/java/expo/modules/audio/AudioPlayer.kt +78 -67
- package/android/src/main/java/expo/modules/audio/AudioPlaylist.kt +347 -0
- package/android/src/main/java/expo/modules/audio/AudioPreloadManager.kt +55 -0
- package/android/src/main/java/expo/modules/audio/AudioRecords.kt +8 -1
- package/android/src/main/java/expo/modules/audio/Playable.kt +48 -0
- package/build/Audio.types.d.ts +123 -2
- package/build/Audio.types.d.ts.map +1 -1
- package/build/Audio.types.js.map +1 -1
- package/build/AudioEventKeys.d.ts +2 -0
- package/build/AudioEventKeys.d.ts.map +1 -1
- package/build/AudioEventKeys.js +2 -0
- package/build/AudioEventKeys.js.map +1 -1
- package/build/AudioModule.types.d.ts +142 -4
- package/build/AudioModule.types.d.ts.map +1 -1
- package/build/AudioModule.types.js.map +1 -1
- package/build/AudioModule.web.d.ts +8 -1
- package/build/AudioModule.web.d.ts.map +1 -1
- package/build/AudioModule.web.js +48 -2
- package/build/AudioModule.web.js.map +1 -1
- package/build/AudioPlayer.web.d.ts +6 -1
- package/build/AudioPlayer.web.d.ts.map +1 -1
- package/build/AudioPlayer.web.js +64 -27
- package/build/AudioPlayer.web.js.map +1 -1
- package/build/AudioPlaylist.web.d.ts +58 -0
- package/build/AudioPlaylist.web.d.ts.map +1 -0
- package/build/AudioPlaylist.web.js +449 -0
- package/build/AudioPlaylist.web.js.map +1 -0
- package/build/AudioRecorder.web.d.ts +1 -1
- package/build/AudioUtils.web.d.ts +7 -2
- package/build/AudioUtils.web.d.ts.map +1 -1
- package/build/AudioUtils.web.js +7 -2
- package/build/AudioUtils.web.js.map +1 -1
- package/build/ExpoAudio.d.ts +121 -2
- package/build/ExpoAudio.d.ts.map +1 -1
- package/build/ExpoAudio.js +157 -6
- package/build/ExpoAudio.js.map +1 -1
- package/build/ExpoAudio.web.d.ts +8 -1
- package/build/ExpoAudio.web.d.ts.map +1 -1
- package/build/ExpoAudio.web.js +52 -57
- package/build/ExpoAudio.web.js.map +1 -1
- package/build/utils/resolveSource.d.ts +1 -0
- package/build/utils/resolveSource.d.ts.map +1 -1
- package/build/utils/resolveSource.js +9 -11
- package/build/utils/resolveSource.js.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/AudioComponentRegistry.swift +61 -0
- package/ios/AudioModule.swift +209 -4
- package/ios/AudioPlayer.swift +21 -2
- package/ios/AudioPlaylist.swift +374 -0
- package/ios/AudioRecords.swift +6 -0
- package/ios/AudioSource.swift +3 -0
- package/ios/AudioUtils.swift +2 -15
- package/ios/MediaController.swift +9 -9
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8-sources.jar +0 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.aar +0 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/{55.0.6/expo.modules.audio-55.0.6.module → 55.0.8/expo.modules.audio-55.0.8.module} +36 -22
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.module.md5 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/{55.0.6/expo.modules.audio-55.0.6.pom → 55.0.8/expo.modules.audio-55.0.8.pom} +25 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.8/expo.modules.audio-55.0.8.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/maven-metadata.xml.sha512 +1 -1
- package/package.json +2 -2
- package/src/Audio.types.ts +131 -2
- package/src/AudioEventKeys.ts +2 -0
- package/src/AudioModule.types.ts +173 -3
- package/src/AudioModule.web.ts +57 -3
- package/src/AudioPlayer.web.ts +79 -31
- package/src/AudioPlaylist.web.ts +529 -0
- package/src/AudioUtils.web.ts +10 -3
- package/src/ExpoAudio.ts +194 -7
- package/src/ExpoAudio.web.ts +71 -64
- package/src/utils/resolveSource.ts +10 -12
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6-sources.jar +0 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.aar +0 -0
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.module.md5 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/audio/expo.modules.audio/55.0.6/expo.modules.audio-55.0.6.pom.sha512 +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,20 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 55.0.8 — 2026-02-25
|
|
14
|
+
|
|
15
|
+
_This version does not introduce any user-facing changes._
|
|
16
|
+
|
|
17
|
+
## 55.0.7 — 2026-02-20
|
|
18
|
+
|
|
19
|
+
### 🐛 Bug fixes
|
|
20
|
+
|
|
21
|
+
- [Web] Fix potential `nan` duration. ([#43268](https://github.com/expo/expo/pull/43268) by [@alanjhughes](https://github.com/alanjhughes))
|
|
22
|
+
|
|
23
|
+
### 💡 Others
|
|
24
|
+
|
|
25
|
+
- [android] Make in-memory preload cache. ([#43293](https://github.com/expo/expo/pull/43293) by [@alanjhughes](https://github.com/alanjhughes))
|
|
26
|
+
|
|
13
27
|
## 55.0.6 — 2026-02-16
|
|
14
28
|
|
|
15
29
|
### 🎉 New features
|
|
@@ -20,6 +34,8 @@
|
|
|
20
34
|
- [Web] Add support for media controls. ([#43150](https://github.com/expo/expo/pull/43150) by [@alanjhughes](https://github.com/alanjhughes))
|
|
21
35
|
- [Web] Add support for selecting recording inputs. ([#43151](https://github.com/expo/expo/pull/43151) by [@alanjhughes](https://github.com/alanjhughes))
|
|
22
36
|
- [Web] Add support for recording metering. ([#43152](https://github.com/expo/expo/pull/43152) by [@alanjhughes](https://github.com/alanjhughes))
|
|
37
|
+
- [Web] Enable `setIsAudioActiveAsync`. ([#43142](https://github.com/expo/expo/pull/43142) by [@alanjhughes](https://github.com/alanjhughes))
|
|
38
|
+
- Add support for preloading audio sources. ([#43063](https://github.com/expo/expo/pull/43063) by [@alanjhughes](https://github.com/alanjhughes))
|
|
23
39
|
|
|
24
40
|
### 🐛 Bug fixes
|
|
25
41
|
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'expo.modules.audio'
|
|
7
|
-
version = '55.0.
|
|
7
|
+
version = '55.0.8'
|
|
8
8
|
|
|
9
9
|
android {
|
|
10
10
|
namespace "expo.modules.audio"
|
|
11
11
|
defaultConfig {
|
|
12
12
|
versionCode 1
|
|
13
|
-
versionName "55.0.
|
|
13
|
+
versionName "55.0.8"
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
compileOptions {
|
|
@@ -24,12 +24,14 @@ repositories {
|
|
|
24
24
|
|
|
25
25
|
dependencies {
|
|
26
26
|
implementation 'androidx.appcompat:appcompat:1.7.1'
|
|
27
|
+
implementation 'androidx.core:core-ktx:1.15.0'
|
|
27
28
|
|
|
28
29
|
def androidxMedia3Version = "1.8.0"
|
|
29
30
|
implementation "androidx.media3:media3-session:$androidxMedia3Version"
|
|
30
31
|
implementation "androidx.media3:media3-ui:$androidxMedia3Version"
|
|
31
32
|
implementation "androidx.media3:media3-exoplayer:$androidxMedia3Version"
|
|
32
33
|
implementation "androidx.media3:media3-exoplayer-dash:$androidxMedia3Version"
|
|
34
|
+
implementation "androidx.media3:media3-datasource:$androidxMedia3Version"
|
|
33
35
|
implementation "androidx.media3:media3-datasource-okhttp:$androidxMedia3Version"
|
|
34
36
|
implementation "androidx.media3:media3-exoplayer-hls:$androidxMedia3Version"
|
|
35
37
|
implementation "androidx.media3:media3-exoplayer-smoothstreaming:$androidxMedia3Version"
|
|
@@ -17,7 +17,6 @@ import androidx.media3.common.C.CONTENT_TYPE_HLS
|
|
|
17
17
|
import androidx.media3.common.C.CONTENT_TYPE_OTHER
|
|
18
18
|
import androidx.media3.common.C.CONTENT_TYPE_SS
|
|
19
19
|
import androidx.media3.common.MediaItem
|
|
20
|
-
import androidx.media3.common.PlaybackParameters
|
|
21
20
|
import androidx.media3.common.Player
|
|
22
21
|
import androidx.media3.common.util.Util
|
|
23
22
|
import androidx.media3.datasource.DataSource
|
|
@@ -35,13 +34,16 @@ import expo.modules.kotlin.functions.Coroutine
|
|
|
35
34
|
import expo.modules.kotlin.functions.Queues
|
|
36
35
|
import expo.modules.kotlin.modules.Module
|
|
37
36
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
37
|
+
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
38
|
+
import kotlinx.coroutines.Dispatchers
|
|
39
|
+
import kotlinx.coroutines.GlobalScope
|
|
38
40
|
import kotlinx.coroutines.launch
|
|
39
41
|
import kotlinx.coroutines.runBlocking
|
|
40
42
|
import okhttp3.OkHttpClient
|
|
41
43
|
import java.io.File
|
|
42
44
|
import java.util.concurrent.ConcurrentHashMap
|
|
43
|
-
import kotlin.math.min
|
|
44
45
|
|
|
46
|
+
@DelicateCoroutinesApi
|
|
45
47
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
|
46
48
|
class AudioModule : Module() {
|
|
47
49
|
private lateinit var audioManager: AudioManager
|
|
@@ -51,6 +53,7 @@ class AudioModule : Module() {
|
|
|
51
53
|
|
|
52
54
|
private val players = ConcurrentHashMap<String, AudioPlayer>()
|
|
53
55
|
private val recorders = ConcurrentHashMap<String, AudioRecorder>()
|
|
56
|
+
private val playlists = ConcurrentHashMap<String, AudioPlaylist>()
|
|
54
57
|
private var shouldPlayInBackground = false
|
|
55
58
|
private var audioEnabled = true
|
|
56
59
|
private var shouldRouteThroughEarpiece = false
|
|
@@ -58,40 +61,41 @@ class AudioModule : Module() {
|
|
|
58
61
|
private var interruptionMode: InterruptionMode? = null
|
|
59
62
|
private var allowsBackgroundRecording = false
|
|
60
63
|
|
|
64
|
+
private val allPlayables: Sequence<Playable>
|
|
65
|
+
get() = players.values.asSequence() + playlists.values.asSequence()
|
|
66
|
+
|
|
61
67
|
private var audioFocusRequest: AudioFocusRequest? = null
|
|
62
68
|
private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
|
|
63
69
|
appContext.mainQueue.launch {
|
|
64
70
|
when (focusChange) {
|
|
65
71
|
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
66
72
|
focusAcquired = false
|
|
67
|
-
|
|
68
|
-
player.ref.pause()
|
|
69
|
-
}
|
|
73
|
+
allPlayables.forEach { it.pause() }
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
73
77
|
focusAcquired = false
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
allPlayables.forEach { playable ->
|
|
79
|
+
if (playable.isPlaying) {
|
|
80
|
+
playable.isPaused = true
|
|
81
|
+
playable.pause()
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
83
87
|
if (interruptionMode == InterruptionMode.DUCK_OTHERS) {
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
|
|
88
|
+
allPlayables.forEach { playable ->
|
|
89
|
+
if (playable.previousVolume != playable.volume) {
|
|
90
|
+
playable.previousVolume = playable.volume
|
|
87
91
|
}
|
|
88
|
-
|
|
92
|
+
playable.setVolume(playable.previousVolume * 0.5f)
|
|
89
93
|
}
|
|
90
94
|
} else {
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
allPlayables.forEach { playable ->
|
|
96
|
+
if (playable.isPlaying) {
|
|
97
|
+
playable.isPaused = true
|
|
98
|
+
playable.pause()
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
}
|
|
@@ -99,11 +103,11 @@ class AudioModule : Module() {
|
|
|
99
103
|
|
|
100
104
|
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
101
105
|
focusAcquired = true
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
allPlayables.forEach { playable ->
|
|
107
|
+
playable.setVolume(playable.previousVolume)
|
|
108
|
+
if (playable.isPaused) {
|
|
109
|
+
playable.isPaused = false
|
|
110
|
+
playable.play()
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
113
|
}
|
|
@@ -112,7 +116,7 @@ class AudioModule : Module() {
|
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
private fun shouldReleaseFocus(): Boolean {
|
|
115
|
-
return
|
|
119
|
+
return allPlayables.none { it.isPlaying }
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
private fun requestAudioFocus() {
|
|
@@ -197,9 +201,9 @@ class AudioModule : Module() {
|
|
|
197
201
|
if (!enabled) {
|
|
198
202
|
releaseAudioFocus()
|
|
199
203
|
runOnMain {
|
|
200
|
-
|
|
201
|
-
if (it.
|
|
202
|
-
it.
|
|
204
|
+
allPlayables.forEach {
|
|
205
|
+
if (it.isPlaying) {
|
|
206
|
+
it.pause()
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
209
|
}
|
|
@@ -218,13 +222,35 @@ class AudioModule : Module() {
|
|
|
218
222
|
Permissions.getPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.RECORD_AUDIO)
|
|
219
223
|
}
|
|
220
224
|
|
|
225
|
+
AsyncFunction("preload") Coroutine { source: AudioSource, _: Double ->
|
|
226
|
+
val uri = source.uri ?: return@Coroutine
|
|
227
|
+
val factory = when (uri.toUri().scheme) {
|
|
228
|
+
"http", "https" -> httpDataSourceFactory(source.headers)
|
|
229
|
+
else -> DefaultDataSource.Factory(context)
|
|
230
|
+
}
|
|
231
|
+
AudioPreloadManager.preload(uri, factory)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
AsyncFunction("clearPreloadedSource") Coroutine { source: AudioSource ->
|
|
235
|
+
val uri = source.uri ?: return@Coroutine
|
|
236
|
+
AudioPreloadManager.clearSource(uri)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
AsyncFunction("clearAllPreloadedSources") Coroutine { ->
|
|
240
|
+
AudioPreloadManager.clearAll()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
AsyncFunction("getPreloadedSources") {
|
|
244
|
+
AudioPreloadManager.getPreloadedSources()
|
|
245
|
+
}
|
|
246
|
+
|
|
221
247
|
OnActivityEntersBackground {
|
|
222
248
|
if (!shouldPlayInBackground) {
|
|
223
249
|
releaseAudioFocus()
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
250
|
+
allPlayables.forEach { playable ->
|
|
251
|
+
if (playable.isPlaying) {
|
|
252
|
+
playable.isPaused = true
|
|
253
|
+
playable.pause()
|
|
228
254
|
}
|
|
229
255
|
}
|
|
230
256
|
}
|
|
@@ -239,15 +265,14 @@ class AudioModule : Module() {
|
|
|
239
265
|
|
|
240
266
|
OnActivityEntersForeground {
|
|
241
267
|
if (!shouldPlayInBackground) {
|
|
242
|
-
|
|
243
|
-
if (hasPlayersToResume) {
|
|
268
|
+
if (allPlayables.any { it.isPaused }) {
|
|
244
269
|
requestAudioFocus()
|
|
245
270
|
}
|
|
246
271
|
|
|
247
|
-
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
272
|
+
allPlayables.forEach { playable ->
|
|
273
|
+
if (playable.isPaused) {
|
|
274
|
+
playable.isPaused = false
|
|
275
|
+
playable.play()
|
|
251
276
|
}
|
|
252
277
|
}
|
|
253
278
|
}
|
|
@@ -264,27 +289,34 @@ class AudioModule : Module() {
|
|
|
264
289
|
}
|
|
265
290
|
|
|
266
291
|
OnDestroy {
|
|
267
|
-
|
|
292
|
+
GlobalScope.launch(Dispatchers.Main) {
|
|
268
293
|
releaseAudioFocus()
|
|
269
294
|
players.values.forEach {
|
|
270
295
|
it.ref.stop()
|
|
271
296
|
}
|
|
272
297
|
|
|
298
|
+
playlists.values.forEach {
|
|
299
|
+
it.ref.stop()
|
|
300
|
+
}
|
|
301
|
+
|
|
273
302
|
recorders.values.forEach {
|
|
274
303
|
it.stopRecording()
|
|
275
304
|
}
|
|
305
|
+
AudioPreloadManager.clearAll()
|
|
276
306
|
}
|
|
277
307
|
}
|
|
278
308
|
|
|
279
309
|
Class(AudioPlayer::class) {
|
|
280
|
-
Constructor { source: AudioSource?, updateInterval: Double, keepAudioSessionActive: Boolean ->
|
|
310
|
+
Constructor { source: AudioSource?, updateInterval: Double, keepAudioSessionActive: Boolean, preferredForwardBufferDuration: Double ->
|
|
281
311
|
val mediaSource = createMediaItem(source)
|
|
312
|
+
val bufferDurationMs = (preferredForwardBufferDuration * 1000).toLong()
|
|
282
313
|
runOnMain {
|
|
283
314
|
val player = AudioPlayer(
|
|
284
315
|
context,
|
|
285
316
|
appContext,
|
|
286
317
|
mediaSource,
|
|
287
|
-
updateInterval
|
|
318
|
+
updateInterval,
|
|
319
|
+
bufferDurationMs
|
|
288
320
|
)
|
|
289
321
|
player.onPlaybackStateChange = { isPlaying ->
|
|
290
322
|
if (!isPlaying && shouldReleaseFocus()) {
|
|
@@ -455,9 +487,7 @@ class AudioModule : Module() {
|
|
|
455
487
|
|
|
456
488
|
Function("setPlaybackRate") { player: AudioPlayer, rate: Float ->
|
|
457
489
|
appContext.mainQueue.launch {
|
|
458
|
-
|
|
459
|
-
val pitch = if (player.preservesPitch) 1f else playbackRate
|
|
460
|
-
player.ref.playbackParameters = PlaybackParameters(playbackRate, pitch)
|
|
490
|
+
player.setPlaybackRate(rate)
|
|
461
491
|
}
|
|
462
492
|
}
|
|
463
493
|
|
|
@@ -552,6 +582,200 @@ class AudioModule : Module() {
|
|
|
552
582
|
}
|
|
553
583
|
}
|
|
554
584
|
}
|
|
585
|
+
|
|
586
|
+
Class(AudioPlaylist::class) {
|
|
587
|
+
Constructor { sources: List<AudioSource>, updateInterval: Double, loop: LoopMode ->
|
|
588
|
+
runOnMain {
|
|
589
|
+
val playlist = AudioPlaylist(
|
|
590
|
+
context,
|
|
591
|
+
appContext,
|
|
592
|
+
sources,
|
|
593
|
+
updateInterval,
|
|
594
|
+
DefaultDataSource.Factory(context)
|
|
595
|
+
)
|
|
596
|
+
playlist.loopMode = loop
|
|
597
|
+
playlist.setMediaItemCreator { source ->
|
|
598
|
+
source.uri?.let { uriString ->
|
|
599
|
+
val uri = uriString.toUri()
|
|
600
|
+
when {
|
|
601
|
+
isRawResource(uri) -> {
|
|
602
|
+
val file = getResourceName(uri, uriString)
|
|
603
|
+
MediaItem.fromUri(getRawResourceURI(file))
|
|
604
|
+
}
|
|
605
|
+
else -> MediaItem.fromUri(uri)
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
playlist.loadInitialPlaylist()
|
|
610
|
+
playlist.onPlaybackStateChange = { isPlaying ->
|
|
611
|
+
if (!isPlaying && shouldReleaseFocus()) {
|
|
612
|
+
releaseAudioFocus()
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
playlists[playlist.id] = playlist
|
|
616
|
+
playlist
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
Property("id") { playlist ->
|
|
621
|
+
playlist.id
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
Property("currentIndex") { playlist ->
|
|
625
|
+
runOnMain {
|
|
626
|
+
playlist.currentTrackIndex
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
Property("trackCount") { playlist ->
|
|
631
|
+
runOnMain {
|
|
632
|
+
playlist.trackCount
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
Property("sources") { playlist ->
|
|
637
|
+
playlist.getSources()
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
Property("playing") { playlist ->
|
|
641
|
+
runOnMain {
|
|
642
|
+
playlist.ref.isPlaying
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
Property("muted") { playlist ->
|
|
647
|
+
playlist.isMuted
|
|
648
|
+
}.set { playlist, muted: Boolean? ->
|
|
649
|
+
val newMuted = muted ?: false
|
|
650
|
+
playlist.isMuted = newMuted
|
|
651
|
+
playlist.setVolume(if (newMuted) 0f else playlist.previousVolume)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
Property("isLoaded") { playlist ->
|
|
655
|
+
runOnMain {
|
|
656
|
+
playlist.ref.playbackState == Player.STATE_READY
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
Property("isBuffering") { playlist ->
|
|
661
|
+
runOnMain {
|
|
662
|
+
playlist.ref.playbackState == Player.STATE_BUFFERING
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
Property("currentTime") { playlist ->
|
|
667
|
+
runOnMain {
|
|
668
|
+
playlist.currentTime
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
Property("duration") { playlist ->
|
|
673
|
+
runOnMain {
|
|
674
|
+
playlist.duration
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
Property("volume") { playlist ->
|
|
679
|
+
runOnMain {
|
|
680
|
+
playlist.ref.volume
|
|
681
|
+
}
|
|
682
|
+
}.set { playlist, volume: Float? ->
|
|
683
|
+
playlist.setVolume(volume)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
Property("playbackRate") { playlist ->
|
|
687
|
+
runOnMain {
|
|
688
|
+
playlist.ref.playbackParameters.speed
|
|
689
|
+
}
|
|
690
|
+
}.set { playlist, rate: Float ->
|
|
691
|
+
appContext.mainQueue.launch {
|
|
692
|
+
playlist.setPlaybackRate(rate)
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
Property("loop") { playlist ->
|
|
697
|
+
playlist.loopMode.value
|
|
698
|
+
}.set { playlist, mode: LoopMode ->
|
|
699
|
+
runOnMain {
|
|
700
|
+
playlist.loopMode = mode
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
Property("currentStatus") { playlist ->
|
|
705
|
+
runOnMain {
|
|
706
|
+
playlist.currentStatus()
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
Function("play") { playlist: AudioPlaylist ->
|
|
711
|
+
if (!audioEnabled) {
|
|
712
|
+
Log.e(TAG, "Audio has been disabled. Re-enable to start playing")
|
|
713
|
+
return@Function
|
|
714
|
+
}
|
|
715
|
+
runOnMain {
|
|
716
|
+
if (!focusAcquired) {
|
|
717
|
+
requestAudioFocus()
|
|
718
|
+
}
|
|
719
|
+
playlist.ref.play()
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
Function("pause") { playlist: AudioPlaylist ->
|
|
724
|
+
runOnMain {
|
|
725
|
+
playlist.ref.pause()
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
Function("next") { playlist: AudioPlaylist ->
|
|
730
|
+
runOnMain {
|
|
731
|
+
playlist.next()
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
Function("previous") { playlist: AudioPlaylist ->
|
|
736
|
+
runOnMain {
|
|
737
|
+
playlist.previous()
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
Function("skipTo") { playlist: AudioPlaylist, index: Int ->
|
|
742
|
+
runOnMain {
|
|
743
|
+
playlist.skipTo(index)
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
AsyncFunction("seekTo") { playlist: AudioPlaylist, seconds: Double ->
|
|
748
|
+
playlist.seekTo(seconds)
|
|
749
|
+
}.runOnQueue(Queues.MAIN)
|
|
750
|
+
|
|
751
|
+
Function("add") { playlist: AudioPlaylist, source: AudioSource ->
|
|
752
|
+
runOnMain {
|
|
753
|
+
playlist.add(source)
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
Function("insert") { playlist: AudioPlaylist, source: AudioSource, index: Int ->
|
|
758
|
+
runOnMain {
|
|
759
|
+
playlist.insert(source, index)
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
Function("remove") { playlist: AudioPlaylist, index: Int ->
|
|
764
|
+
runOnMain {
|
|
765
|
+
playlist.remove(index)
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
Function("clear") { playlist: AudioPlaylist ->
|
|
770
|
+
runOnMain {
|
|
771
|
+
playlist.clear()
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
Function("destroy") { playlist: AudioPlaylist ->
|
|
776
|
+
playlists.remove(playlist.id)
|
|
777
|
+
}
|
|
778
|
+
}
|
|
555
779
|
}
|
|
556
780
|
|
|
557
781
|
private fun createMediaItem(source: AudioSource?): MediaSource? = source?.uri?.let { uriString ->
|
|
@@ -564,9 +788,14 @@ class AudioModule : Module() {
|
|
|
564
788
|
else -> MediaItem.fromUri(uri)
|
|
565
789
|
}
|
|
566
790
|
|
|
567
|
-
val
|
|
568
|
-
|
|
569
|
-
|
|
791
|
+
val preloadedBytes = AudioPreloadManager.get(uriString)
|
|
792
|
+
val factory: DataSource.Factory = if (preloadedBytes != null) {
|
|
793
|
+
InMemoryDataSourceFactory(preloadedBytes)
|
|
794
|
+
} else {
|
|
795
|
+
when (uri.scheme) {
|
|
796
|
+
"http", "https" -> httpDataSourceFactory(source.headers)
|
|
797
|
+
else -> DefaultDataSource.Factory(context)
|
|
798
|
+
}
|
|
570
799
|
}
|
|
571
800
|
return buildMediaSourceFactory(factory, mediaItem)
|
|
572
801
|
}
|