Skip to content

Commit dcf7b45

Browse files
authored
feat: use chime audio manager (#11)
* feat: use chime audio manager * add built in mic * remove device id and use device label instead
1 parent 7ea9ab4 commit dcf7b45

10 files changed

Lines changed: 122 additions & 61 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
docs
12
*.iml
23
.kotlin
34
.gradle

chime-sdk/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ plugins {
1212
}
1313

1414
group = "com.wannaverse"
15-
version = "0.2.0"
15+
version = "0.2.1"
1616

1717
kotlin {
1818
androidTarget {

chime-sdk/chime_sdk.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |spec|
22
spec.name = 'chime_sdk'
3-
spec.version = '0.2.0'
3+
spec.version = '0.2.1'
44
spec.homepage = 'https://github.com/WannaverseOfficial/kmp-chime-sdk'
55
spec.source = { :http=> ''}
66
spec.authors = ''

chime-sdk/src/androidMain/kotlin/com/wannaverse/chimesdk/ChimeSDK.android.kt

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,44 @@ actual fun joinMeeting(
152152
meetingSession!!.audioVideo.startRemoteVideo()
153153
}
154154

155+
actual fun getAvailableInputDevices(): List<AudioDevice> =
156+
meetingSession?.audioVideo
157+
?.listAudioDevices()
158+
?.mapNotNull { device ->
159+
val type = when (device.type) {
160+
MediaDeviceType.AUDIO_BLUETOOTH -> AudioDeviceType.BLUETOOTH
161+
MediaDeviceType.AUDIO_WIRED_HEADSET -> AudioDeviceType.WIRED_HEADSET
162+
MediaDeviceType.AUDIO_USB_HEADSET -> AudioDeviceType.EARPIECE
163+
MediaDeviceType.AUDIO_HANDSET -> AudioDeviceType.BUILT_IN_MIC
164+
else -> return@mapNotNull null
165+
}
166+
167+
AudioDevice(
168+
type = type,
169+
label = device.label
170+
)
171+
}
172+
.orEmpty()
173+
174+
actual fun getAvailableOutputDevices(): List<AudioDevice> =
175+
meetingSession?.audioVideo
176+
?.listAudioDevices()
177+
?.mapNotNull { device ->
178+
val type = when (device.type) {
179+
MediaDeviceType.AUDIO_BLUETOOTH -> AudioDeviceType.BLUETOOTH
180+
MediaDeviceType.AUDIO_WIRED_HEADSET -> AudioDeviceType.WIRED_HEADSET
181+
MediaDeviceType.AUDIO_USB_HEADSET -> AudioDeviceType.EARPIECE
182+
MediaDeviceType.AUDIO_BUILTIN_SPEAKER -> AudioDeviceType.SPEAKER
183+
else -> return@mapNotNull null
184+
}
185+
186+
AudioDevice(
187+
type = type,
188+
label = device.label
189+
)
190+
}
191+
.orEmpty()
192+
155193
actual fun leaveMeeting() {
156194
try {
157195
if (cameraOn || cameraCaptureSource != null) {
@@ -289,35 +327,12 @@ actual fun switchCamera() {
289327
source.device = newDevice
290328
}
291329

292-
actual fun switchAudioDevice(deviceId: String?) {
293-
if (deviceId.isNullOrBlank()) return
294-
val devices = meetingSession?.audioVideo?.listAudioDevices() ?: return
295-
296-
val target = devices.firstOrNull { device ->
297-
if (device.id == deviceId) {
298-
true
299-
} else if (device.id.isNullOrBlank()) {
300-
val typeConst = when (device.type) {
301-
MediaDeviceType.AUDIO_BUILTIN_SPEAKER -> AudioDeviceType.SPEAKER
302-
MediaDeviceType.AUDIO_HANDSET -> AudioDeviceType.EARPIECE
303-
MediaDeviceType.AUDIO_BLUETOOTH -> AudioDeviceType.BLUETOOTH
304-
MediaDeviceType.AUDIO_WIRED_HEADSET -> AudioDeviceType.WIRED_HEADSET
305-
else -> AudioDeviceType.UNKNOWN
306-
}
307-
val typeName = typeConst.name
308-
val suffix = when (typeConst) {
309-
AudioDeviceType.SPEAKER -> "PHONE_SPEAKER"
310-
AudioDeviceType.EARPIECE -> "HANDSET_EARPIECE"
311-
AudioDeviceType.BLUETOOTH -> deviceId.substringAfter("${typeName}_", "")
312-
else -> device.label.replace("[^A-Za-z0-9]".toRegex(), "_").uppercase()
313-
}
314-
"${typeName}_$suffix" == deviceId
315-
} else {
316-
false
317-
}
318-
}
319-
320-
target?.let { meetingSession?.audioVideo?.chooseAudioDevice(it) }
330+
actual fun switchAudioDevice(device: String?) {
331+
val targetChimeDevice = meetingSession
332+
?.audioVideo
333+
?.listAudioDevices()
334+
?.firstOrNull { it.label == device } ?: return
335+
meetingSession?.audioVideo?.chooseAudioDevice(targetChimeDevice)
321336
}
322337

323338
actual fun subscribeToTopic(topic: String, listener: (TextMessage) -> Unit) {
@@ -339,4 +354,4 @@ actual fun subscribeToTopic(topic: String, listener: (TextMessage) -> Unit) {
339354
actual fun unsubscribeFromTopic(topic: String) {
340355
meetingSession?.audioVideo?.removeRealtimeDataMessageObserverFromTopic(topic)
341356
topicObservers.remove(topic)
342-
}
357+
}

chime-sdk/src/androidMain/kotlin/com/wannaverse/chimesdk/DeviceObserver.kt

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.wannaverse.chimesdk
22

33
import com.amazonaws.services.chime.sdk.meetings.device.DeviceChangeObserver
44
import com.amazonaws.services.chime.sdk.meetings.device.MediaDevice
5+
import com.amazonaws.services.chime.sdk.meetings.device.MediaDeviceType
56
import com.amazonaws.services.chime.sdk.meetings.session.DefaultMeetingSession
67

78
class DeviceObserver(
@@ -13,27 +14,27 @@ class DeviceObserver(
1314

1415
override fun onAudioDeviceChanged(freshAudioDeviceList: List<MediaDevice>) {
1516
val devices = freshAudioDeviceList.map { device ->
17+
val type = when (device.type) {
18+
MediaDeviceType.AUDIO_BLUETOOTH -> AudioDeviceType.BLUETOOTH
19+
MediaDeviceType.AUDIO_BUILTIN_SPEAKER -> AudioDeviceType.SPEAKER
20+
MediaDeviceType.AUDIO_WIRED_HEADSET -> AudioDeviceType.WIRED_HEADSET
21+
MediaDeviceType.AUDIO_USB_HEADSET -> AudioDeviceType.EARPIECE
22+
MediaDeviceType.AUDIO_HANDSET -> AudioDeviceType.BUILT_IN_MIC
23+
else -> AudioDeviceType.UNKNOWN
24+
}
25+
1626
AudioDevice(
1727
label = device.label,
18-
type = device.type.ordinal,
19-
id = device.id
28+
type = type,
2029
)
2130
}
2231

23-
if (currentSelectedDevice == null && freshAudioDeviceList.isNotEmpty()) {
24-
selectAudioDevice(freshAudioDeviceList[0])
25-
} else if (currentSelectedDevice != null) {
26-
val stillAvailable = freshAudioDeviceList.any { it.id == currentSelectedDevice?.id }
27-
if (!stillAvailable && freshAudioDeviceList.isNotEmpty()) {
28-
selectAudioDevice(freshAudioDeviceList[0])
29-
}
30-
}
31-
32-
val selected = currentSelectedDevice?.let { sel ->
33-
devices.firstOrNull { it.id == sel.id }
34-
}
32+
if (!freshAudioDeviceList.contains(currentSelectedDevice)) selectAudioDevice(
33+
freshAudioDeviceList.first()
34+
)
3535

36-
realTimeEventListener.onAudioDevicesUpdated(devices, selected)
36+
val selected = devices.firstOrNull { it.label == currentSelectedDevice?.label }
37+
realTimeEventListener.onAudioDevicesUpdated(devices, selectedDevice = selected)
3738
}
3839

3940
fun selectAudioDevice(device: MediaDevice) {

chime-sdk/src/commonMain/kotlin/com/wannaverse/chimesdk/AudioDevice.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ package com.wannaverse.chimesdk
55
*
66
* @property type Platform-specific device type constant.
77
* @property label Human-readable device name.
8-
* @property id Opaque device identifier. Pass to [switchAudioDevice] to activate this device.
98
*/
10-
data class AudioDevice(val type: Int, val label: String, val id: String?)
9+
data class AudioDevice(val type: AudioDeviceType, val label: String)

chime-sdk/src/androidMain/kotlin/com/wannaverse/chimesdk/AudioDeviceType.kt renamed to chime-sdk/src/commonMain/kotlin/com/wannaverse/chimesdk/AudioDeviceType.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ enum class AudioDeviceType {
55
EARPIECE,
66
BLUETOOTH,
77
WIRED_HEADSET,
8+
BUILT_IN_MIC,
89
UNKNOWN
9-
}
10+
}

chime-sdk/src/commonMain/kotlin/com/wannaverse/chimesdk/ChimeSDK.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ expect fun joinMeeting(
6060
onLocalAttendeeIdAvailable: (String) -> Unit = {}
6161
)
6262

63+
expect fun getAvailableInputDevices(): List<AudioDevice>
64+
65+
expect fun getAvailableOutputDevices(): List<AudioDevice>
66+
6367
/** Ends the active meeting session and releases all resources. */
6468
expect fun leaveMeeting()
6569

@@ -99,9 +103,9 @@ expect fun switchCamera()
99103
/**
100104
* Routes audio output to the given device.
101105
*
102-
* @param deviceId [AudioDevice.id] of the target device, or null to use the platform default.
106+
* @param device [AudioDevice.label] of the target device, or null to use the platform default.
103107
*/
104-
expect fun switchAudioDevice(deviceId: String?)
108+
expect fun switchAudioDevice(device: String?)
105109

106110
/**
107111
* Subscribes to incoming data messages on [topic]. Call after [joinMeeting].

chime-sdk/src/iosMain/kotlin/com/wannaverse/chimesdk/ChimeMeetingImpl.kt

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import platform.CoreGraphics.CGRect
1010
import platform.Foundation.*
1111
import platform.UIKit.UIView
1212
import platform.darwin.NSObject
13-
import platform.darwin.dispatch_async
14-
import platform.darwin.dispatch_get_main_queue
1513
import platform.objc.class_addProtocol
1614
import platform.objc.object_getClass
1715
import platform.objc.objc_getProtocol
@@ -223,6 +221,44 @@ internal class ChimeMeetingImpl : NSObject(),
223221
}
224222
}
225223

224+
fun getAvailableInputDevices(): List<AudioDevice> =
225+
meetingSession?.audioVideo()
226+
?.listAudioDevices()
227+
?.mapNotNull { device ->
228+
if (device !is MediaDevice) return@mapNotNull null
229+
val type = when (device.type()) {
230+
MediaDeviceTypeAudioBluetooth -> AudioDeviceType.BLUETOOTH
231+
MediaDeviceTypeAudioWiredHeadset -> AudioDeviceType.WIRED_HEADSET
232+
MediaDeviceTypeAudioHandset -> AudioDeviceType.BUILT_IN_MIC
233+
else -> return@mapNotNull null
234+
}
235+
236+
AudioDevice(
237+
type = type,
238+
label = device.label()
239+
)
240+
}
241+
.orEmpty()
242+
243+
fun getAvailableOutputDevices(): List<AudioDevice> =
244+
meetingSession?.audioVideo()
245+
?.listAudioDevices()
246+
?.mapNotNull { device ->
247+
if (device !is MediaDevice) return@mapNotNull null
248+
val type = when (device.type()) {
249+
MediaDeviceTypeAudioBluetooth -> AudioDeviceType.BLUETOOTH
250+
MediaDeviceTypeAudioWiredHeadset -> AudioDeviceType.WIRED_HEADSET
251+
MediaDeviceTypeAudioBuiltInSpeaker -> AudioDeviceType.SPEAKER
252+
else -> return@mapNotNull null
253+
}
254+
255+
AudioDevice(
256+
type = type,
257+
label = device.label()
258+
)
259+
}
260+
.orEmpty()
261+
226262
private fun configureAudioSession() {
227263
val s = AVAudioSession.sharedInstance()
228264
s.setCategory(
@@ -319,13 +355,13 @@ internal class ChimeMeetingImpl : NSObject(),
319355
return if (shouldMute) av.realtimeLocalMute() else av.realtimeLocalUnmute()
320356
}
321357

322-
fun switchAudioDevice(deviceId: String?) {
323-
val id = deviceId ?: return
324-
meetingSession?.audioVideo()
358+
fun switchAudioDevice(device: String?) {
359+
val targetChimeDevice = meetingSession
360+
?.audioVideo()
325361
?.listAudioDevices()
326362
?.filterIsInstance<MediaDevice>()
327-
?.firstOrNull { it.label() == id }
328-
?.let { meetingSession?.audioVideo()?.chooseAudioDeviceWithMediaDevice(mediaDevice = it) }
363+
?.firstOrNull { it.label() == device } ?: return
364+
meetingSession?.audioVideo()?.chooseAudioDeviceWithMediaDevice(targetChimeDevice)
329365
}
330366

331367
fun sendRealtimeMessage(topic: String, data: String, lifetimeMs: Long) {

chime-sdk/src/iosMain/kotlin/com/wannaverse/chimesdk/ChimeSDK.ios.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ actual fun joinMeeting(
8383
)
8484
}
8585

86+
actual fun getAvailableInputDevices(): List<AudioDevice> = chimeMeeting.getAvailableInputDevices()
87+
88+
actual fun getAvailableOutputDevices(): List<AudioDevice> = chimeMeeting.getAvailableOutputDevices()
89+
8690
actual fun leaveMeeting() {
8791
chimeMeeting.leave()
8892
}
@@ -133,8 +137,8 @@ actual fun switchCamera() {
133137
chimeMeeting.switchCamera()
134138
}
135139

136-
actual fun switchAudioDevice(deviceId: String?) {
137-
chimeMeeting.switchAudioDevice(deviceId)
140+
actual fun switchAudioDevice(device: String?) {
141+
chimeMeeting.switchAudioDevice(device)
138142
}
139143

140144
actual fun subscribeToTopic(topic: String, listener: (TextMessage) -> Unit) {

0 commit comments

Comments
 (0)