@livekit/react-native 2.7.6 → 2.9.0

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.
Files changed (49) hide show
  1. package/README.md +56 -7
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/livekit/reactnative/LivekitReactNativeModule.kt +31 -2
  4. package/android/src/main/java/com/livekit/reactnative/audio/events/Events.kt +1 -0
  5. package/android/src/main/java/com/livekit/reactnative/audio/processing/AudioSinkProcessor.kt +57 -0
  6. package/ios/LiveKitReactNativeModule.swift +21 -1
  7. package/ios/LivekitReactNativeModule.m +6 -0
  8. package/ios/audio/AudioSinkRenderer.swift +51 -0
  9. package/lib/commonjs/audio/MediaRecorder.js +132 -0
  10. package/lib/commonjs/audio/MediaRecorder.js.map +1 -0
  11. package/lib/commonjs/components/BarVisualizer.js +2 -5
  12. package/lib/commonjs/components/BarVisualizer.js.map +1 -1
  13. package/lib/commonjs/components/LiveKitRoom.js.map +1 -1
  14. package/lib/commonjs/components/VideoTrack.js.map +1 -1
  15. package/lib/commonjs/events/EventEmitter.js +1 -1
  16. package/lib/commonjs/events/EventEmitter.js.map +1 -1
  17. package/lib/commonjs/index.js +2 -0
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/commonjs/polyfills/MediaRecorderShim.js +13 -0
  20. package/lib/commonjs/polyfills/MediaRecorderShim.js.map +1 -0
  21. package/lib/module/audio/MediaRecorder.js +125 -0
  22. package/lib/module/audio/MediaRecorder.js.map +1 -0
  23. package/lib/module/components/BarVisualizer.js +2 -5
  24. package/lib/module/components/BarVisualizer.js.map +1 -1
  25. package/lib/module/components/LiveKitRoom.js.map +1 -1
  26. package/lib/module/components/VideoTrack.js.map +1 -1
  27. package/lib/module/events/EventEmitter.js +1 -1
  28. package/lib/module/events/EventEmitter.js.map +1 -1
  29. package/lib/module/index.js +2 -0
  30. package/lib/module/index.js.map +1 -1
  31. package/lib/module/polyfills/MediaRecorderShim.js +11 -0
  32. package/lib/module/polyfills/MediaRecorderShim.js.map +1 -0
  33. package/lib/typescript/lib/commonjs/audio/MediaRecorder.d.ts +24 -0
  34. package/lib/typescript/lib/commonjs/polyfills/MediaRecorderShim.d.ts +1 -0
  35. package/lib/typescript/lib/module/audio/MediaRecorder.d.ts +22 -0
  36. package/lib/typescript/lib/module/polyfills/MediaRecorderShim.d.ts +1 -0
  37. package/lib/typescript/src/audio/MediaRecorder.d.ts +54 -0
  38. package/lib/typescript/src/components/LiveKitRoom.d.ts +1 -1
  39. package/lib/typescript/src/components/VideoTrack.d.ts +1 -1
  40. package/lib/typescript/src/index.d.ts +2 -0
  41. package/lib/typescript/src/polyfills/MediaRecorderShim.d.ts +1 -0
  42. package/package.json +7 -5
  43. package/src/audio/MediaRecorder.ts +158 -0
  44. package/src/components/BarVisualizer.tsx +2 -9
  45. package/src/components/LiveKitRoom.tsx +1 -1
  46. package/src/components/VideoTrack.tsx +3 -2
  47. package/src/events/EventEmitter.ts +5 -1
  48. package/src/index.tsx +2 -0
  49. package/src/polyfills/MediaRecorderShim.ts +12 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  <!--END_BANNER_IMAGE-->
10
10
 
11
- # livekit-react-native
11
+ # React Native client SDK for LiveKit
12
12
 
13
13
  <!--BEGIN_DESCRIPTION-->
14
14
  Use this SDK to add realtime video, audio and data features to your React Native app. By connecting to <a href="https://livekit.io/">LiveKit</a> Cloud or a self-hosted server, you can quickly build applications such as multi-modal AI, live streaming, or video calls with just a few lines of code.
@@ -43,9 +43,12 @@ Once the `@livekit/react-native-webrtc` dependency is installed, one last step i
43
43
 
44
44
  ### Android
45
45
 
46
+ <details>
47
+
48
+ <summary>Java</summary>
49
+
46
50
  In your [MainApplication.java](https://github.com/livekit/client-sdk-react-native/blob/main/example/android/app/src/main/java/com/example/livekitreactnative/MainApplication.java) file:
47
51
 
48
- #### Java
49
52
  ```java
50
53
  import com.livekit.reactnative.LiveKitReactNative;
51
54
  import com.livekit.reactnative.audio.AudioType;
@@ -64,8 +67,14 @@ public class MainApplication extends Application implements ReactApplication {
64
67
  }
65
68
  ```
66
69
 
67
- Or in your **MainApplication.kt** if you are using RN 0.73+
68
- #### Kotlin
70
+ </details>
71
+
72
+ <details>
73
+
74
+ <summary>Kotlin</summary>
75
+
76
+ In your **MainApplication.kt** file:
77
+
69
78
  ```kotlin
70
79
  import com.livekit.reactnative.LiveKitReactNative
71
80
  import com.livekit.reactnative.audio.AudioType
@@ -81,10 +90,15 @@ class MainApplication : Application, ReactApplication() {
81
90
  }
82
91
  }
83
92
  ```
84
- ----
93
+
94
+ </details>
85
95
 
86
96
  ### iOS
87
97
 
98
+ <details>
99
+
100
+ <summary>Objective-C</summary>
101
+
88
102
  In your [AppDelegate.m](https://github.com/livekit/client-sdk-react-native/blob/main/example/ios/LivekitReactNativeExample/AppDelegate.mm) file:
89
103
 
90
104
  ```objc
@@ -108,6 +122,41 @@ In your [AppDelegate.m](https://github.com/livekit/client-sdk-react-native/blob/
108
122
  }
109
123
  ```
110
124
 
125
+ </details>
126
+
127
+ <details>
128
+
129
+ <summary>Swift</summary>
130
+
131
+ In your **AppDelegate.swift** file:
132
+ ```swift
133
+ import livekit_react_native
134
+ import livekit_react_native_webrtc
135
+
136
+ @main
137
+ class AppDelegate: UIResponder, UIApplicationDelegate {
138
+ func application(
139
+ _ application: UIApplication,
140
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
141
+ ) -> Bool {
142
+
143
+ // Place this above any other RN related initialization
144
+ LivekitReactNative.setup()
145
+
146
+ // Uncomment the following lines if you want to use the camera in the background
147
+ // Requires voip background mode and iOS 18+.
148
+
149
+ // let options = WebRTCModuleOptions.sharedInstance()
150
+ // options.enableMultitaskingCameraAccess = true
151
+
152
+ // ...
153
+ }
154
+ }
155
+
156
+ ```
157
+
158
+ </details>
159
+
111
160
  ### Expo
112
161
 
113
162
  LiveKit is available on Expo through development builds. You can find our Expo plugin and setup instructions [here](https://github.com/livekit/client-sdk-react-native-expo-plugin).
@@ -423,9 +472,9 @@ Apache License 2.0
423
472
  <br/><table>
424
473
  <thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
425
474
  <tbody>
426
- <tr><td>LiveKit SDKs</td><td><a href="https://github.com/livekit/client-sdk-js">Browser</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS/visionOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <b>React Native</b> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (WebGL)</a></td></tr><tr></tr>
475
+ <tr><td>LiveKit SDKs</td><td><a href="https://github.com/livekit/client-sdk-js">Browser</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS/visionOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <b>React Native</b> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (WebGL)</a> · <a href="https://github.com/livekit/client-sdk-esp32">ESP32</a></td></tr><tr></tr>
427
476
  <tr><td>Server APIs</td><td><a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/server-sdk-go">Golang</a> · <a href="https://github.com/livekit/server-sdk-ruby">Ruby</a> · <a href="https://github.com/livekit/server-sdk-kotlin">Java/Kotlin</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/agence104/livekit-server-sdk-php">PHP (community)</a> · <a href="https://github.com/pabloFuente/livekit-server-sdk-dotnet">.NET (community)</a></td></tr><tr></tr>
428
- <tr><td>UI Components</td><td><a href="https://github.com/livekit/components-js">React</a> · <a href="https://github.com/livekit/components-android">Android Compose</a> · <a href="https://github.com/livekit/components-swift">SwiftUI</a></td></tr><tr></tr>
477
+ <tr><td>UI Components</td><td><a href="https://github.com/livekit/components-js">React</a> · <a href="https://github.com/livekit/components-android">Android Compose</a> · <a href="https://github.com/livekit/components-swift">SwiftUI</a> · <a href="https://github.com/livekit/components-flutter">Flutter</a></td></tr><tr></tr>
429
478
  <tr><td>Agents Frameworks</td><td><a href="https://github.com/livekit/agents">Python</a> · <a href="https://github.com/livekit/agents-js">Node.js</a> · <a href="https://github.com/livekit/agent-playground">Playground</a></td></tr><tr></tr>
430
479
  <tr><td>Services</td><td><a href="https://github.com/livekit/livekit">LiveKit server</a> · <a href="https://github.com/livekit/egress">Egress</a> · <a href="https://github.com/livekit/ingress">Ingress</a> · <a href="https://github.com/livekit/sip">SIP</a></td></tr><tr></tr>
431
480
  <tr><td>Resources</td><td><a href="https://docs.livekit.io">Docs</a> · <a href="https://github.com/livekit-examples">Example apps</a> · <a href="https://livekit.io/cloud">Cloud</a> · <a href="https://docs.livekit.io/home/self-hosting/deployment">Self-hosting</a> · <a href="https://github.com/livekit/livekit-cli">CLI</a></td></tr>
@@ -131,7 +131,7 @@ dependencies {
131
131
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
132
132
  api 'com.github.davidliu:audioswitch:89582c47c9a04c62f90aa5e57251af4800a62c9a'
133
133
  api 'io.github.webrtc-sdk:android:125.6422.02'
134
- implementation "com.github.paramsen:noise:2.0.0"
134
+ implementation "io.livekit:noise:2.0.0"
135
135
  implementation project(':livekit_react-native-webrtc')
136
136
  implementation "androidx.annotation:annotation:1.9.1"
137
137
  }
@@ -8,16 +8,24 @@ import com.facebook.react.bridge.ReactApplicationContext
8
8
  import com.facebook.react.bridge.ReactContextBaseJavaModule
9
9
  import com.facebook.react.bridge.ReactMethod
10
10
  import com.facebook.react.bridge.ReadableMap
11
+ import com.facebook.react.modules.core.DeviceEventManagerModule
11
12
  import com.livekit.reactnative.audio.AudioDeviceKind
12
13
  import com.livekit.reactnative.audio.AudioManagerUtils
13
14
  import com.livekit.reactnative.audio.AudioSwitchManager
15
+ import com.livekit.reactnative.audio.events.Events
14
16
  import com.livekit.reactnative.audio.processing.AudioSinkManager
17
+ import com.livekit.reactnative.audio.processing.AudioSinkProcessor
15
18
  import com.livekit.reactnative.audio.processing.MultibandVolumeProcessor
16
19
  import com.livekit.reactnative.audio.processing.VolumeProcessor
17
20
  import com.oney.WebRTCModule.WebRTCModuleOptions
18
21
  import org.webrtc.audio.WebRtcAudioTrackHelper
22
+ import java.lang.Thread.sleep
23
+ import kotlin.concurrent.thread
19
24
  import kotlin.time.Duration.Companion.milliseconds
20
25
 
26
+ // NOTE: As of 0.80 react-native new architecture requires all
27
+ // @ReactMethod(isBlockingSynchronousMethod = true)
28
+ // annotated methods to be non-void.
21
29
 
22
30
  class LivekitReactNativeModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
23
31
 
@@ -131,6 +139,23 @@ class LivekitReactNativeModule(reactContext: ReactApplicationContext) : ReactCon
131
139
  promise.resolve(null)
132
140
  }
133
141
 
142
+ @ReactMethod(isBlockingSynchronousMethod = true)
143
+ fun createAudioSinkListener(pcId: Int, trackId: String): String {
144
+ val processor = AudioSinkProcessor(reactApplicationContext)
145
+ val reactTag = audioSinkManager.registerSink(processor)
146
+ audioSinkManager.attachSinkToTrack(processor, pcId, trackId)
147
+ processor.reactTag = reactTag
148
+
149
+ return reactTag
150
+ }
151
+
152
+ @ReactMethod(isBlockingSynchronousMethod = true)
153
+ fun deleteAudioSinkListener(reactTag: String, pcId: Int, trackId: String): Boolean {
154
+ audioSinkManager.detachSinkFromTrack(reactTag, pcId, trackId)
155
+ audioSinkManager.unregisterSink(reactTag)
156
+ return true
157
+ }
158
+
134
159
  @ReactMethod(isBlockingSynchronousMethod = true)
135
160
  fun createVolumeProcessor(pcId: Int, trackId: String): String {
136
161
  val processor = VolumeProcessor(reactApplicationContext)
@@ -142,9 +167,10 @@ class LivekitReactNativeModule(reactContext: ReactApplicationContext) : ReactCon
142
167
  }
143
168
 
144
169
  @ReactMethod(isBlockingSynchronousMethod = true)
145
- fun deleteVolumeProcessor(reactTag: String, pcId: Int, trackId: String) {
170
+ fun deleteVolumeProcessor(reactTag: String, pcId: Int, trackId: String): Boolean {
146
171
  audioSinkManager.detachSinkFromTrack(reactTag, pcId, trackId)
147
172
  audioSinkManager.unregisterSink(reactTag)
173
+ return true
148
174
  }
149
175
 
150
176
  @ReactMethod(isBlockingSynchronousMethod = true)
@@ -171,7 +197,7 @@ class LivekitReactNativeModule(reactContext: ReactApplicationContext) : ReactCon
171
197
  }
172
198
 
173
199
  @ReactMethod(isBlockingSynchronousMethod = true)
174
- fun deleteMultibandVolumeProcessor(reactTag: String, pcId: Int, trackId: String) {
200
+ fun deleteMultibandVolumeProcessor(reactTag: String, pcId: Int, trackId: String): Boolean {
175
201
  val volumeProcessor =
176
202
  audioSinkManager.getSink(reactTag) ?: throw IllegalArgumentException("Can't find volume processor for $reactTag")
177
203
  audioSinkManager.detachSinkFromTrack(volumeProcessor, pcId, trackId)
@@ -182,7 +208,10 @@ class LivekitReactNativeModule(reactContext: ReactApplicationContext) : ReactCon
182
208
  multibandVolumeProcessor.release()
183
209
  } else {
184
210
  Log.w(name, "deleteMultibandVolumeProcessor called, but non-MultibandVolumeProcessor found?!")
211
+ return false
185
212
  }
213
+
214
+ return true
186
215
  }
187
216
 
188
217
  @ReactMethod
@@ -3,4 +3,5 @@ package com.livekit.reactnative.audio.events
3
3
  enum class Events {
4
4
  LK_VOLUME_PROCESSED,
5
5
  LK_MULTIBAND_PROCESSED,
6
+ LK_AUDIO_DATA,
6
7
  }
@@ -0,0 +1,57 @@
1
+ package com.livekit.reactnative.audio.processing
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReactContext
5
+ import com.facebook.react.modules.core.DeviceEventManagerModule
6
+ import com.livekit.reactnative.audio.events.Events
7
+ import org.webrtc.AudioTrackSink
8
+ import java.nio.ByteBuffer
9
+ import java.util.Arrays
10
+ import kotlin.io.encoding.Base64
11
+ import kotlin.io.encoding.ExperimentalEncodingApi
12
+
13
+ class AudioSinkProcessor(private val reactContext: ReactContext) : BaseAudioSinkProcessor() {
14
+ var reactTag: String? = null
15
+
16
+ @OptIn(ExperimentalEncodingApi::class)
17
+ override fun onAudioData(byteArray: ByteArray) {
18
+ val reactTag = this.reactTag ?: return
19
+
20
+ val encodedString = Base64.encode(byteArray)
21
+ val event = Arguments.createMap().apply {
22
+ putString("data", encodedString)
23
+ putString("id", reactTag)
24
+ }
25
+ reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
26
+ .emit(Events.LK_AUDIO_DATA.name, event)
27
+ }
28
+ }
29
+
30
+ abstract class BaseAudioSinkProcessor : AudioTrackSink {
31
+ abstract fun onAudioData(byteArray: ByteArray)
32
+
33
+ override fun onData(
34
+ audioData: ByteBuffer,
35
+ bitsPerSample: Int,
36
+ sampleRate: Int,
37
+ numberOfChannels: Int,
38
+ numberOfFrames: Int,
39
+ absoluteCaptureTimestampMs: Long
40
+ ) {
41
+ val byteArray: ByteArray
42
+
43
+ if (audioData.hasArray()) {
44
+ val audioArray = audioData.array()
45
+ byteArray = Arrays.copyOfRange(audioArray, audioData.arrayOffset(), audioArray.size)
46
+ } else {
47
+ audioData.mark()
48
+ audioData.position(0)
49
+
50
+ byteArray = ByteArray(audioData.remaining())
51
+ audioData.get(byteArray)
52
+ audioData.reset()
53
+ }
54
+
55
+ onAudioData(byteArray)
56
+ }
57
+ }
@@ -6,6 +6,7 @@ import React
6
6
  struct LKEvents {
7
7
  static let kEventVolumeProcessed = "LK_VOLUME_PROCESSED";
8
8
  static let kEventMultibandProcessed = "LK_MULTIBAND_PROCESSED";
9
+ static let kEventAudioData = "LK_AUDIO_DATA";
9
10
  }
10
11
 
11
12
  @objc(LivekitReactNativeModule)
@@ -178,6 +179,24 @@ public class LivekitReactNativeModule: RCTEventEmitter {
178
179
  session.unlockForConfiguration()
179
180
  }
180
181
 
182
+ @objc(createAudioSinkListener:trackId:)
183
+ public func createAudioSinkListener(_ pcId: NSNumber, trackId: String) -> String {
184
+ let renderer = AudioSinkRenderer(eventEmitter: self)
185
+ let reactTag = self.audioRendererManager.registerRenderer(renderer)
186
+ renderer.reactTag = reactTag
187
+ self.audioRendererManager.attach(renderer: renderer, pcId: pcId, trackId: trackId)
188
+
189
+ return reactTag
190
+ }
191
+
192
+ @objc(deleteAudioSinkListener:pcId:trackId:)
193
+ public func deleteAudioSinkListener(_ reactTag: String, pcId: NSNumber, trackId: String) -> Any? {
194
+ self.audioRendererManager.detach(rendererByTag: reactTag, pcId: pcId, trackId: trackId)
195
+ self.audioRendererManager.unregisterRenderer(forReactTag: reactTag)
196
+
197
+ return nil
198
+ }
199
+
181
200
  @objc(createVolumeProcessor:trackId:)
182
201
  public func createVolumeProcessor(_ pcId: NSNumber, trackId: String) -> String {
183
202
  let renderer = VolumeAudioRenderer(intervalMs: 40.0, eventEmitter: self)
@@ -195,7 +214,7 @@ public class LivekitReactNativeModule: RCTEventEmitter {
195
214
 
196
215
  return nil
197
216
  }
198
-
217
+
199
218
  @objc(createMultibandVolumeProcessor:pcId:trackId:)
200
219
  public func createMultibandVolumeProcessor(_ options: NSDictionary, pcId: NSNumber, trackId: String) -> String {
201
220
  let bands = (options["bands"] as? NSNumber)?.intValue ?? 5
@@ -237,6 +256,7 @@ public class LivekitReactNativeModule: RCTEventEmitter {
237
256
  return [
238
257
  LKEvents.kEventVolumeProcessed,
239
258
  LKEvents.kEventMultibandProcessed,
259
+ LKEvents.kEventAudioData,
240
260
  ]
241
261
  }
242
262
  }
@@ -21,6 +21,12 @@ RCT_EXTERN_METHOD(selectAudioOutput:(NSString *)deviceId
21
21
  /// Configure audio config for WebRTC
22
22
  RCT_EXTERN_METHOD(setAppleAudioConfiguration:(NSDictionary *) configuration)
23
23
 
24
+ RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(createAudioSinkListener:(nonnull NSNumber *)pcId
25
+ trackId:(nonnull NSString *)trackId)
26
+
27
+ RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(deleteAudioSinkListener:(nonnull NSString *)reactTag
28
+ pcId:(nonnull NSNumber *)pcId
29
+ trackId:(nonnull NSString *)trackId)
24
30
 
25
31
  RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(createVolumeProcessor:(nonnull NSNumber *)pcId
26
32
  trackId:(nonnull NSString *)trackId)
@@ -0,0 +1,51 @@
1
+ import livekit_react_native_webrtc
2
+ import React
3
+
4
+ @objc
5
+ public class AudioSinkRenderer: BaseAudioSinkRenderer {
6
+ private let eventEmitter: RCTEventEmitter
7
+
8
+ @objc
9
+ public var reactTag: String? = nil
10
+
11
+ @objc
12
+ public init(eventEmitter: RCTEventEmitter) {
13
+ self.eventEmitter = eventEmitter
14
+ super.init()
15
+ }
16
+
17
+ override public func onData(_ pcmBuffer: AVAudioPCMBuffer) {
18
+ guard pcmBuffer.format.commonFormat == .pcmFormatInt16,
19
+ let channelData = pcmBuffer.int16ChannelData else {
20
+ return
21
+ }
22
+ let channelCount = Int(pcmBuffer.format.channelCount)
23
+ let channels = UnsafeBufferPointer(start: channelData, count: channelCount)
24
+ let length = Int(pcmBuffer.frameCapacity * pcmBuffer.format.streamDescription.pointee.mBytesPerFrame)
25
+ let data = NSData(bytes: channels[0], length: length)
26
+ let base64 = data.base64EncodedString()
27
+ NSLog("AUDIO DATA!!!!")
28
+ NSLog("\(data.length)")
29
+ NSLog(base64)
30
+ NSLog("\(base64.count)")
31
+ NSLog("\(length)")
32
+ eventEmitter.sendEvent(withName: LKEvents.kEventAudioData, body: [
33
+ "data": base64,
34
+ "id": reactTag
35
+ ])
36
+ }
37
+ }
38
+
39
+ public class BaseAudioSinkRenderer: NSObject, RTCAudioRenderer {
40
+
41
+ public override init() {
42
+ super.init()
43
+ }
44
+
45
+ public func render(pcmBuffer: AVAudioPCMBuffer) {
46
+ onData(pcmBuffer)
47
+ }
48
+
49
+ public func onData(_ pcmBuffer: AVAudioPCMBuffer) {
50
+ }
51
+ }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MediaRecorder = void 0;
7
+ var _EventEmitter = require("../events/EventEmitter");
8
+ var _index = require("event-target-shim/index");
9
+ var _reactNativeQuickBase = require("react-native-quick-base64");
10
+ var _LKNativeModule = _interopRequireDefault(require("../LKNativeModule"));
11
+ var _logger = require("../logger");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
14
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
15
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
16
+ // typeof MediaRecorder
17
+ // const Tester = (stream: MediaStream) => {
18
+ // return new AudioRecorder(stream) satisfies MediaRecorder;
19
+ // };
20
+
21
+ /**
22
+ * A MediaRecord implementation only meant for recording audio streams.
23
+ *
24
+ * @private
25
+ */
26
+ class MediaRecorder extends _index.EventTarget {
27
+ constructor(stream) {
28
+ super();
29
+ _defineProperty(this, "mimeType", 'audio/pcm');
30
+ _defineProperty(this, "audioBitsPerSecond", 0);
31
+ // TODO?
32
+ _defineProperty(this, "state", 'inactive');
33
+ _defineProperty(this, "stream", void 0);
34
+ _defineProperty(this, "videoBitsPerSecond", 0);
35
+ // TODO?
36
+ _defineProperty(this, "audioBitrateMode", 'constant');
37
+ _defineProperty(this, "_reactTag", undefined);
38
+ _defineProperty(this, "_parts", []);
39
+ this.stream = stream;
40
+ }
41
+ registerListener() {
42
+ let audioTracks = this.stream.getAudioTracks();
43
+ if (audioTracks.length !== 1) {
44
+ return;
45
+ }
46
+ const mediaStreamTrack = audioTracks[0];
47
+ const peerConnectionId = mediaStreamTrack._peerConnectionId ?? -1;
48
+ const mediaStreamTrackId = mediaStreamTrack === null || mediaStreamTrack === void 0 ? void 0 : mediaStreamTrack.id;
49
+ this._reactTag = _LKNativeModule.default.createAudioSinkListener(peerConnectionId, mediaStreamTrackId);
50
+ (0, _EventEmitter.addListener)(this, 'LK_AUDIO_DATA', event => {
51
+ if (this._reactTag && event.id === this._reactTag && this.state === 'recording') {
52
+ let str = event.data;
53
+ this._parts.push(str);
54
+ }
55
+ });
56
+ }
57
+ unregisterListener() {
58
+ if (this._reactTag) {
59
+ let audioTracks = this.stream.getAudioTracks();
60
+ if (audioTracks.length !== 1) {
61
+ _logger.log.error("couldn't find any audio tracks to record from!");
62
+ return;
63
+ }
64
+ const mediaStreamTrack = audioTracks[0];
65
+ const peerConnectionId = mediaStreamTrack._peerConnectionId ?? -1;
66
+ const mediaStreamTrackId = mediaStreamTrack === null || mediaStreamTrack === void 0 ? void 0 : mediaStreamTrack.id;
67
+ _LKNativeModule.default.deleteAudioSinkListener(this._reactTag, peerConnectionId, mediaStreamTrackId);
68
+ }
69
+ }
70
+ pause() {
71
+ this.state = 'paused';
72
+ this.dispatchEvent(new _index.Event('pause'));
73
+ }
74
+ resume() {
75
+ this.state = 'recording';
76
+ this.dispatchEvent(new _index.Event('resume'));
77
+ }
78
+ start() {
79
+ this.registerListener();
80
+ this.state = 'recording';
81
+ this.dispatchEvent(new _index.Event('start'));
82
+ }
83
+ stop() {
84
+ // dispatch data must come before stopping.
85
+ this.dispatchData();
86
+ this.unregisterListener();
87
+ this.state = 'inactive';
88
+ this.dispatchEvent(new _index.Event('stop'));
89
+ }
90
+ requestData() {
91
+ this.dispatchData();
92
+ }
93
+ dispatchData() {
94
+ let combinedStr = this._parts.reduce((sum, cur) => sum + cur, '');
95
+ let data = (0, _reactNativeQuickBase.toByteArray)(combinedStr);
96
+ this._parts = [];
97
+ this.dispatchEvent(new BlobEvent('dataavailable', {
98
+ data: {
99
+ byteArray: data
100
+ }
101
+ }));
102
+ }
103
+ }
104
+
105
+ /**
106
+ * @eventClass
107
+ * This event is fired whenever the Track is changed in PeerConnection.
108
+ * @param {TRACK_EVENTS} type - The type of event.
109
+ * @param {IRTCTrackEventInitDict} eventInitDict - The event init properties.
110
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/track_event MDN} for details.
111
+ */
112
+ exports.MediaRecorder = MediaRecorder;
113
+ class BlobEvent extends _index.Event {
114
+ constructor(type, eventInitDict) {
115
+ super(type, eventInitDict);
116
+ /** @eventProperty */
117
+ _defineProperty(this, "data", void 0);
118
+ this.data = eventInitDict.data;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Define the `onxxx` event handlers.
124
+ */
125
+ const proto = MediaRecorder.prototype;
126
+ (0, _index.defineEventAttribute)(proto, 'dataavailable');
127
+ (0, _index.defineEventAttribute)(proto, 'error');
128
+ (0, _index.defineEventAttribute)(proto, 'pause');
129
+ (0, _index.defineEventAttribute)(proto, 'resume');
130
+ (0, _index.defineEventAttribute)(proto, 'start');
131
+ (0, _index.defineEventAttribute)(proto, 'stop');
132
+ //# sourceMappingURL=MediaRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_EventEmitter","require","_index","_reactNativeQuickBase","_LKNativeModule","_interopRequireDefault","_logger","e","__esModule","default","_defineProperty","r","t","_toPropertyKey","Object","defineProperty","value","enumerable","configurable","writable","i","_toPrimitive","Symbol","toPrimitive","call","TypeError","String","Number","MediaRecorder","EventTarget","constructor","stream","undefined","registerListener","audioTracks","getAudioTracks","length","mediaStreamTrack","peerConnectionId","_peerConnectionId","mediaStreamTrackId","id","_reactTag","LiveKitModule","createAudioSinkListener","addListener","event","state","str","data","_parts","push","unregisterListener","log","error","deleteAudioSinkListener","pause","dispatchEvent","Event","resume","start","stop","dispatchData","requestData","combinedStr","reduce","sum","cur","toByteArray","BlobEvent","byteArray","exports","type","eventInitDict","proto","prototype","defineEventAttribute"],"sources":["MediaRecorder.ts"],"sourcesContent":["import type { MediaStream } from '@livekit/react-native-webrtc';\nimport { addListener } from '../events/EventEmitter';\nimport {\n EventTarget,\n Event,\n defineEventAttribute,\n} from 'event-target-shim/index';\nimport { toByteArray } from 'react-native-quick-base64';\nimport LiveKitModule from '../LKNativeModule';\nimport { log } from '../logger';\n\n// typeof MediaRecorder\n// const Tester = (stream: MediaStream) => {\n// return new AudioRecorder(stream) satisfies MediaRecorder;\n// };\n\ntype MediaRecorderState = 'inactive' | 'recording' | 'paused';\ntype MediaRecorderEventMap = {\n dataavailable: BlobEvent<'dataavailable'>;\n error: Event<'error'>;\n pause: Event<'pause'>;\n resume: Event<'resume'>;\n start: Event<'start'>;\n stop: Event<'stop'>;\n};\n\n/**\n * A MediaRecord implementation only meant for recording audio streams.\n *\n * @private\n */\nexport class MediaRecorder extends EventTarget<MediaRecorderEventMap> {\n mimeType: String = 'audio/pcm';\n audioBitsPerSecond: number = 0; // TODO?\n state: MediaRecorderState = 'inactive';\n stream: MediaStream;\n videoBitsPerSecond: number = 0; // TODO?\n audioBitrateMode = 'constant';\n\n _reactTag: string | undefined = undefined;\n _parts: string[] = [];\n constructor(stream: MediaStream) {\n super();\n this.stream = stream;\n }\n\n registerListener() {\n let audioTracks = this.stream.getAudioTracks();\n if (audioTracks.length !== 1) {\n return;\n }\n const mediaStreamTrack = audioTracks[0];\n const peerConnectionId = mediaStreamTrack._peerConnectionId ?? -1;\n const mediaStreamTrackId = mediaStreamTrack?.id;\n this._reactTag = LiveKitModule.createAudioSinkListener(\n peerConnectionId,\n mediaStreamTrackId\n );\n addListener(this, 'LK_AUDIO_DATA', (event: any) => {\n if (\n this._reactTag &&\n event.id === this._reactTag &&\n this.state === 'recording'\n ) {\n let str = event.data as string;\n this._parts.push(str);\n }\n });\n }\n\n unregisterListener() {\n if (this._reactTag) {\n let audioTracks = this.stream.getAudioTracks();\n if (audioTracks.length !== 1) {\n log.error(\"couldn't find any audio tracks to record from!\");\n return;\n }\n const mediaStreamTrack = audioTracks[0];\n const peerConnectionId = mediaStreamTrack._peerConnectionId ?? -1;\n const mediaStreamTrackId = mediaStreamTrack?.id;\n\n LiveKitModule.deleteAudioSinkListener(\n this._reactTag,\n peerConnectionId,\n mediaStreamTrackId\n );\n }\n }\n\n pause() {\n this.state = 'paused';\n this.dispatchEvent(new Event('pause'));\n }\n\n resume() {\n this.state = 'recording';\n this.dispatchEvent(new Event('resume'));\n }\n\n start() {\n this.registerListener();\n this.state = 'recording';\n this.dispatchEvent(new Event('start'));\n }\n\n stop() {\n // dispatch data must come before stopping.\n this.dispatchData();\n\n this.unregisterListener();\n this.state = 'inactive';\n this.dispatchEvent(new Event('stop'));\n }\n\n requestData() {\n this.dispatchData();\n }\n dispatchData() {\n let combinedStr = this._parts.reduce((sum, cur) => sum + cur, '');\n let data = toByteArray(combinedStr);\n this._parts = [];\n this.dispatchEvent(\n new BlobEvent('dataavailable', { data: { byteArray: data } })\n );\n }\n}\n\n/**\n * @eventClass\n * This event is fired whenever the Track is changed in PeerConnection.\n * @param {TRACK_EVENTS} type - The type of event.\n * @param {IRTCTrackEventInitDict} eventInitDict - The event init properties.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/track_event MDN} for details.\n */\nclass BlobEvent<TEventType extends string> extends Event<TEventType> {\n /** @eventProperty */\n readonly data: { byteArray: Uint8Array };\n\n constructor(\n type: TEventType,\n eventInitDict: { data: { byteArray: Uint8Array } } & Event.EventInit\n ) {\n super(type, eventInitDict);\n this.data = eventInitDict.data;\n }\n}\n\n/**\n * Define the `onxxx` event handlers.\n */\nconst proto = MediaRecorder.prototype;\n\ndefineEventAttribute(proto, 'dataavailable');\ndefineEventAttribute(proto, 'error');\ndefineEventAttribute(proto, 'pause');\ndefineEventAttribute(proto, 'resume');\ndefineEventAttribute(proto, 'start');\ndefineEventAttribute(proto, 'stop');\n"],"mappings":"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","ignoreList":[]}
@@ -104,7 +104,7 @@ const BarVisualizer = ({
104
104
  let bars = [];
105
105
  magnitudes.forEach((value, index) => {
106
106
  let coerced = Math.min(opts.maxHeight, Math.max(opts.minHeight, value));
107
- let coercedPercent = Math.min(100, Math.max(0, coerced * 100 + 5));
107
+ let coercedPercent = Math.min(100, Math.max(0, coerced * 100));
108
108
  let opacity = opacityAnimations[index] ?? new _reactNative.Animated.Value(0.3);
109
109
  let barStyle = {
110
110
  opacity: opacity,
@@ -116,7 +116,7 @@ const BarVisualizer = ({
116
116
  key: index,
117
117
  style: [{
118
118
  height: `${coercedPercent}%`
119
- }, barStyle, styles.volumeIndicator]
119
+ }, barStyle]
120
120
  }));
121
121
  });
122
122
  return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
@@ -132,9 +132,6 @@ const styles = _reactNative.StyleSheet.create({
132
132
  flexDirection: 'row',
133
133
  alignItems: 'center',
134
134
  justifyContent: 'space-evenly'
135
- },
136
- volumeIndicator: {
137
- borderRadius: 12
138
135
  }
139
136
  });
140
137
  const useBarAnimator = (state, columns, interval) => {
@@ -1 +1 @@
1
- {"version":3,"names":["_componentsReact","require","_reactNative","_hooks","_react","_interopRequireWildcard","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","hasOwnProperty","call","i","set","defaultBarOptions","maxHeight","minHeight","barColor","barWidth","barBorderRadius","sequencerIntervals","Map","getSequencerInterval","state","barCount","undefined","interval","BarVisualizer","style","trackRef","options","trackReference","useMaybeTrackRefContext","opacityAnimations","useRef","current","magnitudes","useMultibandTrackVolume","bands","opts","highlightedIndices","useBarAnimator","useEffect","animations","Animated","Value","targetOpacity","includes","push","timing","toValue","duration","useNativeDriver","parallel","start","stop","bars","forEach","value","index","coerced","Math","min","max","coercedPercent","opacity","barStyle","backgroundColor","borderRadius","width","createElement","View","key","height","styles","volumeIndicator","container","exports","StyleSheet","create","flexDirection","alignItems","justifyContent","columns","setIndex","useState","sequence","setSequence","generateListeningSequenceBar","seq","generateConnectingSequenceBar","Array","fill","map","_","idx","animationFrameId","startTime","performance","now","animate","time","timeElapsed","prev","requestAnimationFrame","cancelAnimationFrame","length","center","floor","noIndex","x"],"sources":["BarVisualizer.tsx"],"sourcesContent":["import {\n type AgentState,\n type TrackReferenceOrPlaceholder,\n useMaybeTrackRefContext,\n} from '@livekit/components-react';\nimport {\n Animated,\n StyleSheet,\n View,\n type ColorValue,\n type DimensionValue,\n type ViewStyle,\n} from 'react-native';\nimport { useMultibandTrackVolume } from '../hooks';\nimport React, { useEffect, useRef, useState } from 'react';\nexport type BarVisualizerOptions = {\n /** decimal values from 0 to 1 */\n maxHeight?: number;\n /** decimal values from 0 to 1 */\n minHeight?: number;\n\n barColor?: ColorValue;\n barWidth?: DimensionValue;\n barBorderRadius?: number;\n};\n\nconst defaultBarOptions = {\n maxHeight: 1,\n minHeight: 0.2,\n barColor: '#888888',\n barWidth: 24,\n barBorderRadius: 12,\n} as const satisfies BarVisualizerOptions;\n\nconst sequencerIntervals = new Map<AgentState, number>([\n ['connecting', 2000],\n ['initializing', 2000],\n ['listening', 500],\n ['thinking', 150],\n]);\n\nconst getSequencerInterval = (\n state: AgentState | undefined,\n barCount: number\n): number | undefined => {\n if (state === undefined) {\n return 1000;\n }\n let interval = sequencerIntervals.get(state);\n if (interval) {\n switch (state) {\n case 'connecting':\n // case 'thinking':\n interval /= barCount;\n break;\n\n default:\n break;\n }\n }\n return interval;\n};\n/**\n * @beta\n */\nexport interface BarVisualizerProps {\n /** If set, the visualizer will transition between different voice assistant states */\n state?: AgentState;\n /** Number of bars that show up in the visualizer */\n barCount?: number;\n trackRef?: TrackReferenceOrPlaceholder;\n options?: BarVisualizerOptions;\n /**\n * Custom React Native styles for the container.\n */\n style?: ViewStyle;\n}\n\n/**\n * Visualizes audio signals from a TrackReference as bars.\n * If the `state` prop is set, it automatically transitions between VoiceAssistant states.\n * @beta\n *\n * @remarks For VoiceAssistant state transitions this component requires a voice assistant agent running with livekit-agents \\>= 0.9.0\n *\n * @example\n * ```tsx\n * function SimpleVoiceAssistant() {\n * const { state, audioTrack } = useVoiceAssistant();\n * return (\n * <BarVisualizer\n * state={state}\n * trackRef={audioTrack}\n * />\n * );\n * }\n * ```\n */\nexport const BarVisualizer = ({\n style = {},\n state,\n barCount = 5,\n trackRef,\n options,\n}: BarVisualizerProps) => {\n let trackReference = useMaybeTrackRefContext();\n\n if (trackRef) {\n trackReference = trackRef;\n }\n\n const opacityAnimations = useRef<Animated.Value[]>([]).current;\n let magnitudes = useMultibandTrackVolume(trackReference, { bands: barCount });\n\n let opts = { ...defaultBarOptions, ...options };\n\n const highlightedIndices = useBarAnimator(\n state,\n barCount,\n getSequencerInterval(state, barCount) ?? 100\n );\n\n useEffect(() => {\n let animations = [];\n for (let i = 0; i < barCount; i++) {\n if (!opacityAnimations[i]) {\n opacityAnimations[i] = new Animated.Value(0.3);\n }\n let targetOpacity = 0.3;\n if (highlightedIndices.includes(i)) {\n targetOpacity = 1;\n }\n animations.push(\n Animated.timing(opacityAnimations[i], {\n toValue: targetOpacity,\n duration: 250,\n useNativeDriver: true,\n })\n );\n }\n\n let parallel = Animated.parallel(animations);\n parallel.start();\n return () => {\n parallel.stop();\n };\n }, [highlightedIndices, barCount, opacityAnimations]);\n\n let bars: React.ReactNode[] = [];\n magnitudes.forEach((value, index) => {\n let coerced = Math.min(opts.maxHeight, Math.max(opts.minHeight, value));\n let coercedPercent = Math.min(100, Math.max(0, coerced * 100 + 5));\n let opacity = opacityAnimations[index] ?? new Animated.Value(0.3);\n let barStyle = {\n opacity: opacity,\n backgroundColor: opts.barColor,\n borderRadius: opts.barBorderRadius,\n width: opts.barWidth,\n };\n bars.push(\n <Animated.View\n key={index}\n style={[\n { height: `${coercedPercent}%` },\n barStyle,\n styles.volumeIndicator,\n ]}\n />\n );\n });\n\n return <View style={{ ...style, ...styles.container }}>{bars}</View>;\n};\nconst styles = StyleSheet.create({\n container: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-evenly',\n },\n volumeIndicator: {\n borderRadius: 12,\n },\n});\n\nexport const useBarAnimator = (\n state: AgentState | undefined,\n columns: number,\n interval: number\n): number[] => {\n const [index, setIndex] = useState(0);\n const [sequence, setSequence] = useState<number[][]>([[]]);\n\n useEffect(() => {\n if (state === 'thinking') {\n setSequence(generateListeningSequenceBar(columns));\n } else if (state === 'connecting' || state === 'initializing') {\n const seq = [...generateConnectingSequenceBar(columns)];\n setSequence(seq);\n } else if (state === 'listening') {\n setSequence(generateListeningSequenceBar(columns));\n } else if (state === undefined) {\n // highlight everything\n setSequence([new Array(columns).fill(0).map((_, idx) => idx)]);\n } else {\n setSequence([[]]);\n }\n setIndex(0);\n }, [state, columns]);\n\n const animationFrameId = useRef<number | null>(null);\n useEffect(() => {\n let startTime = performance.now();\n\n const animate = (time: number) => {\n const timeElapsed = time - startTime;\n\n if (timeElapsed >= interval) {\n setIndex((prev) => prev + 1);\n startTime = time;\n }\n\n animationFrameId.current = requestAnimationFrame(animate);\n };\n\n animationFrameId.current = requestAnimationFrame(animate);\n\n return () => {\n if (animationFrameId.current !== null) {\n cancelAnimationFrame(animationFrameId.current);\n }\n };\n }, [interval, columns, state, sequence.length]);\n\n return sequence[index % sequence.length];\n};\n\nconst generateListeningSequenceBar = (columns: number): number[][] => {\n const center = Math.floor(columns / 2);\n const noIndex = -1;\n\n return [[center], [noIndex]];\n};\n\nconst generateConnectingSequenceBar = (columns: number): number[][] => {\n const seq: number[][] = [[]];\n\n for (let x = 0; x < columns; x++) {\n seq.push([x, columns - 1 - x]);\n }\n\n return seq;\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_componentsReact","require","_reactNative","_hooks","_react","_interopRequireWildcard","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","hasOwnProperty","call","i","set","defaultBarOptions","maxHeight","minHeight","barColor","barWidth","barBorderRadius","sequencerIntervals","Map","getSequencerInterval","state","barCount","undefined","interval","BarVisualizer","style","trackRef","options","trackReference","useMaybeTrackRefContext","opacityAnimations","useRef","current","magnitudes","useMultibandTrackVolume","bands","opts","highlightedIndices","useBarAnimator","useEffect","animations","Animated","Value","targetOpacity","includes","push","timing","toValue","duration","useNativeDriver","parallel","start","stop","bars","forEach","value","index","coerced","Math","min","max","coercedPercent","opacity","barStyle","backgroundColor","borderRadius","width","createElement","View","key","height","styles","container","exports","StyleSheet","create","flexDirection","alignItems","justifyContent","columns","setIndex","useState","sequence","setSequence","generateListeningSequenceBar","seq","generateConnectingSequenceBar","Array","fill","map","_","idx","animationFrameId","startTime","performance","now","animate","time","timeElapsed","prev","requestAnimationFrame","cancelAnimationFrame","length","center","floor","noIndex","x"],"sources":["BarVisualizer.tsx"],"sourcesContent":["import {\n type AgentState,\n type TrackReferenceOrPlaceholder,\n useMaybeTrackRefContext,\n} from '@livekit/components-react';\nimport {\n Animated,\n StyleSheet,\n View,\n type ColorValue,\n type DimensionValue,\n type ViewStyle,\n} from 'react-native';\nimport { useMultibandTrackVolume } from '../hooks';\nimport React, { useEffect, useRef, useState } from 'react';\nexport type BarVisualizerOptions = {\n /** decimal values from 0 to 1 */\n maxHeight?: number;\n /** decimal values from 0 to 1 */\n minHeight?: number;\n\n barColor?: ColorValue;\n barWidth?: DimensionValue;\n barBorderRadius?: number;\n};\n\nconst defaultBarOptions = {\n maxHeight: 1,\n minHeight: 0.2,\n barColor: '#888888',\n barWidth: 24,\n barBorderRadius: 12,\n} as const satisfies BarVisualizerOptions;\n\nconst sequencerIntervals = new Map<AgentState, number>([\n ['connecting', 2000],\n ['initializing', 2000],\n ['listening', 500],\n ['thinking', 150],\n]);\n\nconst getSequencerInterval = (\n state: AgentState | undefined,\n barCount: number\n): number | undefined => {\n if (state === undefined) {\n return 1000;\n }\n let interval = sequencerIntervals.get(state);\n if (interval) {\n switch (state) {\n case 'connecting':\n // case 'thinking':\n interval /= barCount;\n break;\n\n default:\n break;\n }\n }\n return interval;\n};\n/**\n * @beta\n */\nexport interface BarVisualizerProps {\n /** If set, the visualizer will transition between different voice assistant states */\n state?: AgentState;\n /** Number of bars that show up in the visualizer */\n barCount?: number;\n trackRef?: TrackReferenceOrPlaceholder;\n options?: BarVisualizerOptions;\n /**\n * Custom React Native styles for the container.\n */\n style?: ViewStyle;\n}\n\n/**\n * Visualizes audio signals from a TrackReference as bars.\n * If the `state` prop is set, it automatically transitions between VoiceAssistant states.\n * @beta\n *\n * @remarks For VoiceAssistant state transitions this component requires a voice assistant agent running with livekit-agents \\>= 0.9.0\n *\n * @example\n * ```tsx\n * function SimpleVoiceAssistant() {\n * const { state, audioTrack } = useVoiceAssistant();\n * return (\n * <BarVisualizer\n * state={state}\n * trackRef={audioTrack}\n * />\n * );\n * }\n * ```\n */\nexport const BarVisualizer = ({\n style = {},\n state,\n barCount = 5,\n trackRef,\n options,\n}: BarVisualizerProps) => {\n let trackReference = useMaybeTrackRefContext();\n\n if (trackRef) {\n trackReference = trackRef;\n }\n\n const opacityAnimations = useRef<Animated.Value[]>([]).current;\n let magnitudes = useMultibandTrackVolume(trackReference, { bands: barCount });\n\n let opts = { ...defaultBarOptions, ...options };\n\n const highlightedIndices = useBarAnimator(\n state,\n barCount,\n getSequencerInterval(state, barCount) ?? 100\n );\n\n useEffect(() => {\n let animations = [];\n for (let i = 0; i < barCount; i++) {\n if (!opacityAnimations[i]) {\n opacityAnimations[i] = new Animated.Value(0.3);\n }\n let targetOpacity = 0.3;\n if (highlightedIndices.includes(i)) {\n targetOpacity = 1;\n }\n animations.push(\n Animated.timing(opacityAnimations[i], {\n toValue: targetOpacity,\n duration: 250,\n useNativeDriver: true,\n })\n );\n }\n\n let parallel = Animated.parallel(animations);\n parallel.start();\n return () => {\n parallel.stop();\n };\n }, [highlightedIndices, barCount, opacityAnimations]);\n\n let bars: React.ReactNode[] = [];\n magnitudes.forEach((value, index) => {\n let coerced = Math.min(opts.maxHeight, Math.max(opts.minHeight, value));\n let coercedPercent = Math.min(100, Math.max(0, coerced * 100));\n let opacity = opacityAnimations[index] ?? new Animated.Value(0.3);\n let barStyle = {\n opacity: opacity,\n backgroundColor: opts.barColor,\n borderRadius: opts.barBorderRadius,\n width: opts.barWidth,\n };\n bars.push(\n <Animated.View\n key={index}\n style={[{ height: `${coercedPercent}%` }, barStyle]}\n />\n );\n });\n\n return <View style={{ ...style, ...styles.container }}>{bars}</View>;\n};\nconst styles = StyleSheet.create({\n container: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'space-evenly',\n },\n});\n\nexport const useBarAnimator = (\n state: AgentState | undefined,\n columns: number,\n interval: number\n): number[] => {\n const [index, setIndex] = useState(0);\n const [sequence, setSequence] = useState<number[][]>([[]]);\n\n useEffect(() => {\n if (state === 'thinking') {\n setSequence(generateListeningSequenceBar(columns));\n } else if (state === 'connecting' || state === 'initializing') {\n const seq = [...generateConnectingSequenceBar(columns)];\n setSequence(seq);\n } else if (state === 'listening') {\n setSequence(generateListeningSequenceBar(columns));\n } else if (state === undefined) {\n // highlight everything\n setSequence([new Array(columns).fill(0).map((_, idx) => idx)]);\n } else {\n setSequence([[]]);\n }\n setIndex(0);\n }, [state, columns]);\n\n const animationFrameId = useRef<number | null>(null);\n useEffect(() => {\n let startTime = performance.now();\n\n const animate = (time: number) => {\n const timeElapsed = time - startTime;\n\n if (timeElapsed >= interval) {\n setIndex((prev) => prev + 1);\n startTime = time;\n }\n\n animationFrameId.current = requestAnimationFrame(animate);\n };\n\n animationFrameId.current = requestAnimationFrame(animate);\n\n return () => {\n if (animationFrameId.current !== null) {\n cancelAnimationFrame(animationFrameId.current);\n }\n };\n }, [interval, columns, state, sequence.length]);\n\n return sequence[index % sequence.length];\n};\n\nconst generateListeningSequenceBar = (columns: number): number[][] => {\n const center = Math.floor(columns / 2);\n const noIndex = -1;\n\n return [[center], [noIndex]];\n};\n\nconst generateConnectingSequenceBar = (columns: number): number[][] => {\n const seq: number[][] = [[]];\n\n for (let x = 0; x < columns; x++) {\n seq.push([x, columns - 1 - x]);\n }\n\n return seq;\n};\n"],"mappings":"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","ignoreList":[]}