Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

AudioPreview native module for android #207

Merged
merged 3 commits into from
Jul 10, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import com.voxeet.VoxeetSDK
import io.dolby.sdk.comms.reactnative.eventemitters.RNAudioPreviewEventEmitter
import io.dolby.sdk.comms.reactnative.eventemitters.RNCommandEventEmitter
import io.dolby.sdk.comms.reactnative.eventemitters.RNConferenceEventEmitter
import io.dolby.sdk.comms.reactnative.eventemitters.RNFilePresentationEventEmitter
Expand Down Expand Up @@ -37,6 +38,7 @@ import io.dolby.sdk.comms.reactnative.services.RNRecordingServiceModule
import io.dolby.sdk.comms.reactnative.services.RNSessionServiceModule
import io.dolby.sdk.comms.reactnative.services.RNSystemPermissionsModule
import io.dolby.sdk.comms.reactnative.services.RNVideoPresentationServiceModule
import io.dolby.sdk.comms.reactnative.services.audio.RNAudioPreviewModule
import io.dolby.sdk.comms.reactnative.services.audio.RNLocalAudioModule
import io.dolby.sdk.comms.reactnative.services.audio.RNRemoteAudioModule
import io.dolby.sdk.comms.reactnative.services.video.RNLocalVideoModule
Expand Down Expand Up @@ -104,6 +106,11 @@ class RNCommsAPISdkPackage : ReactPackage {
val recordingEventEmitter = RNRecordingEventEmitter(
reactContext = reactContext,
)

val audioPreviewEmitter = RNAudioPreviewEventEmitter(
reactContext,
audioService = VoxeetSDK.audio()
)
return listOf(
RNCommsAPISdkModule(
reactContext = reactContext,
Expand Down Expand Up @@ -180,6 +187,12 @@ class RNCommsAPISdkPackage : ReactPackage {
mediaDeviceService = VoxeetSDK.mediaDevice(),
audioMapper = audioMapper
),
RNAudioPreviewModule(
reactContext = reactContext,
localAudio = VoxeetSDK.audio().local,
audioMapper = audioMapper,
eventEmitter = audioPreviewEmitter
),
RNRemoteAudioModule(
reactContext = reactContext,
audioService = VoxeetSDK.audio(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.dolby.sdk.comms.reactnative.eventemitters

import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext
import com.voxeet.android.media.capture.audio.preview.RecorderStatus
import com.voxeet.sdk.services.AudioService

/**
* The audio preview event emitter
* @param reactContext react application context for sending event
* @param audioService [AudioService] from Android SDK
*/
class RNAudioPreviewEventEmitter (
reactContext: ReactApplicationContext,
private val audioService: AudioService
) : RNEventEmitter(reactContext) {
private var isRegister = false

/**
* Emitted when the application user received an audio preview status changed.
*/
private val previewCallback: (RecorderStatus) -> Unit = { status ->
if (isRegister) {
Arguments.createMap()
.apply {
putString(KEY_ON_STATUS_CHANGED, status.name)
}
.also {
send(NotificationEvent.OnStatusChanged.withData(it))
}
}
}

override fun registerNativeEventBus() {
isRegister = true
audioService.local.preview().callback = previewCallback
}

override fun unregisterNativeEventBus() {
isRegister = false
audioService.local.preview().callback = null
}

private object NotificationEvent {
object OnStatusChanged : RNEvent("EVENT_AUDIO_PREVIEW_STATUS_CHANGED")
}

companion object {
private const val KEY_ON_STATUS_CHANGED = "status"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ abstract class RNEventEmitter(
/**
* Register the event emitter for native events
*/
fun registerNativeEventBus() {
open fun registerNativeEventBus() {
VoxeetSDK.instance().register(this)
}

/**
* Unregister the event emitter, and it won't receive any native events
*/
fun unregisterNativeEventBus() {
open fun unregisterNativeEventBus() {
VoxeetSDK.instance().unregister(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package io.dolby.sdk.comms.reactnative.services.audio

import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
import com.voxeet.sdk.services.AudioService
import com.voxeet.sdk.services.MediaDeviceService
import com.voxeet.sdk.services.audio.LocalAudio
import io.dolby.sdk.comms.reactnative.eventemitters.RNAudioPreviewEventEmitter
import io.dolby.sdk.comms.reactnative.mapper.AudioMapper
import io.dolby.sdk.comms.reactnative.services.RNEventEmitterModule
import io.dolby.sdk.comms.reactnative.utils.Promises
import io.dolby.sdk.comms.reactnative.utils.Promises.forward
import io.dolby.sdk.comms.reactnative.utils.Promises.thenValue
import io.dolby.sdk.comms.reactnative.utils.ReactPromise

/**
* The [RNAudioPreviewModule] allows recording and playing audio sample to test audio before joinning conference
* checking the capture mode and comfort noise level.
*
* @constructor
* Creates a bridge wrapper for [com.voxeet.sdk.services.audio.AudioPreview] model.
*
* @param reactContext react context
* @param localAudio [LocalAudio] from Android SDK
* @param audioMapper mapper for a audio related models
*/
class RNAudioPreviewModule(
reactContext: ReactApplicationContext,
eventEmitter: RNAudioPreviewEventEmitter,
private val localAudio: LocalAudio,
private val audioMapper: AudioMapper
) : RNEventEmitterModule(reactContext, eventEmitter) {
override fun getName() = "CommsAPIAudioPreviewModule"

/**
* Returns the audio preview status.
*
* @param promise returns preview status
*/
@ReactMethod
fun status(promise: ReactPromise) {
Promises.promise(localAudio.preview().status.name)
.forward(promise)
}

/**
* Returns the local participant's audio capture mode from audio preview.
*
* @param promise returns capture mode
*/
@ReactMethod
fun getCaptureMode(promise: ReactPromise) {
Promises.promise(localAudio.preview().captureMode) { "Could not get capture mode" }
.thenValue(audioMapper::audioCaptureToRN)
.forward(promise)
}

/**
* Sets the local participant's audio capture mode in audio preview.
*
* @param promise returns null
*/
@ReactMethod
fun setCaptureMode(captureMode: ReadableMap, promise: ReactPromise) {
Promises.promise({ audioMapper.audioCaptureFromRN(captureMode) }) { "Invalid capture mode" }
.thenValue { cm ->
localAudio.preview().captureMode = cm
}
.forward(promise, ignoreReturnType = true)
}

/**
* Starts recording an audio sample if no recording is in progress. This method requires the Android AudioRecord permission.
* Calling this method without the permission granted results in the promise to reject the call.
* @param The preferred recording duration that ranges from 0 to 5, in seconds.
* @param promise returns null
*/
@ReactMethod
fun record(duration: Int?, promise: ReactPromise) {
Promises.promise({ duration }) { "Invalid params: duration is required" }
.thenValue { d ->
localAudio.preview().record(d)
}
.forward(promise, ignoreReturnType = true)
}

/**
* Plays back the recorded audio sample. To test how your audio sounds while using different capture modes and voice fonts,
* set the setCaptureMode method to a preferred setting before using the method.
* @param loop A boolean that indicates whether the SDK should play the recorded audio in a loop.
* @param promise returns null
*/
@ReactMethod
fun play(loop: Boolean, promise: ReactPromise) {
localAudio.preview().play(loop)
.forward(promise, ignoreReturnType = true)
}

/**
* Cancels recording or playing an audio sample.
* @param promise return Boolean
*/
@ReactMethod
fun cancel(promise: ReactPromise) {
Promises.promise({ localAudio.preview().cancel() })
.forward(promise)
}

/**
* Release the internal memory and restart the audio session configuration.
* @param promise returns null
*/
@ReactMethod
fun release(promise: ReactPromise) {
Promises.promise({ localAudio.preview().release() })
.forward(promise, ignoreReturnType = true)
}

/**
* Every emitter module must implement this method in place, otherwise JS cannot receive event
*/
@ReactMethod
override fun addListener(eventName: String) = super.addListener(eventName)

/**
* Every emitter module must implement this method in place, otherwise JS cannot receive event
*/
@ReactMethod
override fun removeListeners(count: Int) = super.removeListeners(count)
}
10 changes: 5 additions & 5 deletions docs/enums/internal.RecorderStatus.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,38 @@ This model is available in SDK 3.10 and later.

### NoRecordingAvailable

• **NoRecordingAvailable** = ``0``
• **NoRecordingAvailable** = ``"NoRecordingAvailable"``

There is no recording available.

___

### RecordingAvailable

• **RecordingAvailable** = ``1``
• **RecordingAvailable** = ``"RecordingAvailable"``

The recording is available.

___

### Recording

• **Recording** = ``2``
• **Recording** = ``"Recording"``

Recording is in progress.

___

### Playing

• **Playing** = ``3``
• **Playing** = ``"Playing"``

The recording is played.

___

### Released

• **Released** = ``4``
• **Released** = ``"Released"``

The audio session configuration is restarted; there are no recording in the memory.
10 changes: 5 additions & 5 deletions src/services/audio/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,13 @@ export enum VoiceFont {
*/
export enum RecorderStatus {
/** There is no recording available. */
NoRecordingAvailable,
NoRecordingAvailable = 'NoRecordingAvailable',
/** The recording is available. */
RecordingAvailable,
RecordingAvailable = 'RecordingAvailable',
/** Recording is in progress. */
Recording,
Recording = 'Recording',
/** The recording is played. */
Playing,
Playing = 'Playing',
/** The audio session configuration is restarted; there are no recording in the memory. */
Released,
Released = 'Released',
}
Loading