@livekit/react-native 2.5.1 → 2.6.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 (85) hide show
  1. package/README.md +4 -3
  2. package/android/build.gradle +2 -1
  3. package/android/src/main/java/com/livekit/reactnative/LiveKitReactNative.kt +61 -5
  4. package/android/src/main/java/com/livekit/reactnative/LivekitReactNativeModule.kt +81 -4
  5. package/android/src/main/java/com/livekit/reactnative/audio/events/Events.kt +6 -0
  6. package/android/src/main/java/com/livekit/reactnative/audio/processing/AudioFormat.kt +2 -0
  7. package/android/src/main/java/com/livekit/reactnative/audio/processing/AudioProcessingController.kt +27 -0
  8. package/android/src/main/java/com/livekit/reactnative/audio/processing/AudioProcessorInterface.kt +52 -0
  9. package/android/src/main/java/com/livekit/reactnative/audio/processing/AudioRecordSamplesDispatcher.kt +72 -0
  10. package/android/src/main/java/com/livekit/reactnative/audio/processing/AudioSinkManager.kt +75 -0
  11. package/android/src/main/java/com/livekit/reactnative/audio/processing/CustomAudioProcessingFactory.kt +78 -0
  12. package/android/src/main/java/com/livekit/reactnative/audio/processing/MultibandVolumeProcessor.kt +181 -0
  13. package/android/src/main/java/com/livekit/reactnative/audio/processing/VolumeProcessor.kt +67 -0
  14. package/android/src/main/java/com/livekit/reactnative/audio/processing/fft/FFTAudioAnalyzer.kt +224 -0
  15. package/ios/LKAudioProcessingAdapter.h +26 -0
  16. package/ios/LKAudioProcessingAdapter.m +117 -0
  17. package/ios/LKAudioProcessingManager.h +34 -0
  18. package/ios/LKAudioProcessingManager.m +63 -0
  19. package/ios/LivekitReactNative-Bridging-Header.h +2 -0
  20. package/ios/LivekitReactNative.h +9 -4
  21. package/ios/LivekitReactNative.m +83 -5
  22. package/ios/Logging.swift +4 -0
  23. package/ios/audio/AVAudioPCMBuffer.swift +136 -0
  24. package/ios/audio/AudioProcessing.swift +163 -0
  25. package/ios/audio/AudioRendererManager.swift +72 -0
  26. package/ios/audio/FFTProcessor.swift +147 -0
  27. package/ios/audio/MultibandVolumeAudioRenderer.swift +65 -0
  28. package/ios/audio/RingBuffer.swift +51 -0
  29. package/ios/audio/VolumeAudioRenderer.swift +48 -0
  30. package/lib/commonjs/LKNativeModule.js +18 -0
  31. package/lib/commonjs/LKNativeModule.js.map +1 -0
  32. package/lib/commonjs/components/BarVisualizer.js +192 -0
  33. package/lib/commonjs/components/BarVisualizer.js.map +1 -0
  34. package/lib/commonjs/events/EventEmitter.js +45 -0
  35. package/lib/commonjs/events/EventEmitter.js.map +1 -0
  36. package/lib/commonjs/hooks/useMultibandTrackVolume.js +64 -0
  37. package/lib/commonjs/hooks/useMultibandTrackVolume.js.map +1 -0
  38. package/lib/commonjs/hooks/useTrackVolume.js +45 -0
  39. package/lib/commonjs/hooks/useTrackVolume.js.map +1 -0
  40. package/lib/commonjs/hooks.js +24 -0
  41. package/lib/commonjs/hooks.js.map +1 -1
  42. package/lib/commonjs/index.js +14 -0
  43. package/lib/commonjs/index.js.map +1 -1
  44. package/lib/module/LKNativeModule.js +12 -0
  45. package/lib/module/LKNativeModule.js.map +1 -0
  46. package/lib/module/components/BarVisualizer.js +182 -0
  47. package/lib/module/components/BarVisualizer.js.map +1 -0
  48. package/lib/module/events/EventEmitter.js +36 -0
  49. package/lib/module/events/EventEmitter.js.map +1 -0
  50. package/lib/module/hooks/useMultibandTrackVolume.js +58 -0
  51. package/lib/module/hooks/useMultibandTrackVolume.js.map +1 -0
  52. package/lib/module/hooks/useTrackVolume.js +39 -0
  53. package/lib/module/hooks/useTrackVolume.js.map +1 -0
  54. package/lib/module/hooks.js +2 -0
  55. package/lib/module/hooks.js.map +1 -1
  56. package/lib/module/index.js +3 -0
  57. package/lib/module/index.js.map +1 -1
  58. package/lib/typescript/lib/commonjs/LKNativeModule.d.ts +3 -0
  59. package/lib/typescript/lib/commonjs/components/BarVisualizer.d.ts +32 -0
  60. package/lib/typescript/lib/commonjs/events/EventEmitter.d.ts +4 -0
  61. package/lib/typescript/lib/commonjs/hooks/useMultibandTrackVolume.d.ts +8 -0
  62. package/lib/typescript/lib/commonjs/hooks/useTrackVolume.d.ts +8 -0
  63. package/lib/typescript/lib/module/LKNativeModule.d.ts +2 -0
  64. package/lib/typescript/lib/module/components/BarVisualizer.d.ts +10 -0
  65. package/lib/typescript/lib/module/events/EventEmitter.d.ts +3 -0
  66. package/lib/typescript/lib/module/hooks/useMultibandTrackVolume.d.ts +7 -0
  67. package/lib/typescript/lib/module/hooks/useTrackVolume.d.ts +7 -0
  68. package/lib/typescript/lib/module/hooks.d.ts +2 -0
  69. package/lib/typescript/lib/module/index.d.ts +1 -0
  70. package/lib/typescript/src/LKNativeModule.d.ts +2 -0
  71. package/lib/typescript/src/components/BarVisualizer.d.ts +49 -0
  72. package/lib/typescript/src/events/EventEmitter.d.ts +6 -0
  73. package/lib/typescript/src/hooks/useMultibandTrackVolume.d.ts +31 -0
  74. package/lib/typescript/src/hooks/useTrackVolume.d.ts +9 -0
  75. package/lib/typescript/src/hooks.d.ts +2 -0
  76. package/lib/typescript/src/index.d.ts +1 -0
  77. package/livekit-react-native.podspec +1 -1
  78. package/package.json +5 -5
  79. package/src/LKNativeModule.ts +19 -0
  80. package/src/components/BarVisualizer.tsx +252 -0
  81. package/src/events/EventEmitter.ts +51 -0
  82. package/src/hooks/useMultibandTrackVolume.ts +97 -0
  83. package/src/hooks/useTrackVolume.ts +62 -0
  84. package/src/hooks.ts +2 -0
  85. package/src/index.tsx +3 -0
@@ -0,0 +1,147 @@
1
+ /*
2
+ * Copyright 2025 LiveKit
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import Accelerate
18
+ import AVFoundation
19
+
20
+ extension Float {
21
+ var nyquistFrequency: Float { self / 2.0 }
22
+ }
23
+
24
+ public struct FFTComputeBandsResult {
25
+ let count: Int
26
+ let magnitudes: [Float]
27
+ let frequencies: [Float]
28
+ }
29
+
30
+ public class FFTResult {
31
+ public let magnitudes: [Float]
32
+
33
+ init(magnitudes: [Float]) {
34
+ self.magnitudes = magnitudes
35
+ }
36
+
37
+ func computeBands(minFrequency: Float, maxFrequency: Float, bandsCount: Int, sampleRate: Float) -> FFTComputeBandsResult {
38
+ let actualMaxFrequency = min(sampleRate.nyquistFrequency, maxFrequency)
39
+ var bandMagnitudes = [Float](repeating: 0.0, count: bandsCount)
40
+ var bandFrequencies = [Float](repeating: 0.0, count: bandsCount)
41
+
42
+ let magLowerRange = _magnitudeIndex(for: minFrequency, sampleRate: sampleRate)
43
+ let magUpperRange = _magnitudeIndex(for: actualMaxFrequency, sampleRate: sampleRate)
44
+ let ratio = Float(magUpperRange - magLowerRange) / Float(bandsCount)
45
+
46
+ return magnitudes.withUnsafeBufferPointer { magnitudesPtr in
47
+ for i in 0 ..< bandsCount {
48
+ let magsStartIdx = vDSP_Length(floorf(Float(i) * ratio)) + magLowerRange
49
+ let magsEndIdx = vDSP_Length(floorf(Float(i + 1) * ratio)) + magLowerRange
50
+
51
+ let count = magsEndIdx - magsStartIdx
52
+ if count > 0 {
53
+ var sum: Float = 0
54
+ vDSP_sve(magnitudesPtr.baseAddress! + Int(magsStartIdx), 1, &sum, count)
55
+ bandMagnitudes[i] = sum / Float(count)
56
+ } else {
57
+ bandMagnitudes[i] = magnitudes[Int(magsStartIdx)]
58
+ }
59
+
60
+ // Compute average frequency
61
+ let bandwidth = sampleRate.nyquistFrequency / Float(magnitudes.count)
62
+ bandFrequencies[i] = (bandwidth * Float(magsStartIdx) + bandwidth * Float(magsEndIdx)) / 2
63
+ }
64
+
65
+ return FFTComputeBandsResult(count: bandsCount, magnitudes: bandMagnitudes, frequencies: bandFrequencies)
66
+ }
67
+ }
68
+
69
+ @inline(__always) private func _magnitudeIndex(for frequency: Float, sampleRate: Float) -> vDSP_Length {
70
+ vDSP_Length(Float(magnitudes.count) * frequency / sampleRate.nyquistFrequency)
71
+ }
72
+ }
73
+
74
+ class FFTProcessor {
75
+ public enum WindowType {
76
+ case none
77
+ case hanning
78
+ case hamming
79
+ }
80
+
81
+ public let bufferSize: vDSP_Length
82
+ public let windowType: WindowType
83
+
84
+ private let bufferHalfSize: vDSP_Length
85
+ private let bufferLog2Size: vDSP_Length
86
+ private var window: [Float] = []
87
+ private var fftSetup: FFTSetup
88
+ private var realBuffer: [Float]
89
+ private var imaginaryBuffer: [Float]
90
+ private var zeroDBReference: Float = 1.0
91
+
92
+ init(bufferSize: Int, windowType: WindowType = .hanning) {
93
+ self.bufferSize = vDSP_Length(bufferSize)
94
+ self.windowType = windowType
95
+
96
+ bufferHalfSize = vDSP_Length(bufferSize / 2)
97
+ bufferLog2Size = vDSP_Length(log2f(Float(bufferSize)))
98
+
99
+ realBuffer = [Float](repeating: 0.0, count: Int(bufferHalfSize))
100
+ imaginaryBuffer = [Float](repeating: 0.0, count: Int(bufferHalfSize))
101
+ window = [Float](repeating: 1.0, count: Int(bufferSize))
102
+
103
+ fftSetup = vDSP_create_fftsetup(UInt(bufferLog2Size), FFTRadix(FFT_RADIX2))!
104
+
105
+ switch windowType {
106
+ case .none:
107
+ break
108
+ case .hanning:
109
+ vDSP_hann_window(&window, vDSP_Length(bufferSize), Int32(vDSP_HANN_NORM))
110
+ case .hamming:
111
+ vDSP_hamm_window(&window, vDSP_Length(bufferSize), 0)
112
+ }
113
+ }
114
+
115
+ deinit {
116
+ vDSP_destroy_fftsetup(fftSetup)
117
+ }
118
+
119
+ func process(buffer: [Float]) -> FFTResult {
120
+ precondition(buffer.count == Int(bufferSize), "Input buffer size mismatch.")
121
+
122
+ var windowedBuffer = [Float](repeating: 0.0, count: Int(bufferSize))
123
+
124
+ vDSP_vmul(buffer, 1, window, 1, &windowedBuffer, 1, bufferSize)
125
+
126
+ return realBuffer.withUnsafeMutableBufferPointer { realPtr in
127
+ imaginaryBuffer.withUnsafeMutableBufferPointer { imagPtr in
128
+ var complexBuffer = DSPSplitComplex(realp: realPtr.baseAddress!, imagp: imagPtr.baseAddress!)
129
+
130
+ windowedBuffer.withUnsafeBufferPointer { bufferPtr in
131
+ let complexPtr = UnsafeRawPointer(bufferPtr.baseAddress!).bindMemory(to: DSPComplex.self, capacity: Int(bufferHalfSize))
132
+ vDSP_ctoz(complexPtr, 2, &complexBuffer, 1, bufferHalfSize)
133
+ }
134
+
135
+ vDSP_fft_zrip(fftSetup, &complexBuffer, 1, bufferLog2Size, FFTDirection(FFT_FORWARD))
136
+
137
+ var magnitudes = [Float](repeating: 0.0, count: Int(bufferHalfSize))
138
+ vDSP_zvabs(&complexBuffer, 1, &magnitudes, 1, bufferHalfSize)
139
+
140
+ // Convert magnitudes to decibels
141
+ vDSP_vdbcon(magnitudes, 1, &zeroDBReference, &magnitudes, 1, vDSP_Length(magnitudes.count), 1)
142
+
143
+ return FFTResult(magnitudes: magnitudes)
144
+ }
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,65 @@
1
+ import WebRTC
2
+
3
+ public class MultibandVolumeAudioRenderer: BaseMultibandVolumeAudioRenderer {
4
+ private let eventEmitter: RCTEventEmitter
5
+
6
+ @objc
7
+ public var reactTag: String? = nil
8
+
9
+ @objc
10
+ public init(
11
+ bands: Int,
12
+ minFrequency: Float,
13
+ maxFrequency: Float,
14
+ intervalMs: Float,
15
+ eventEmitter: RCTEventEmitter
16
+ ) {
17
+ self.eventEmitter = eventEmitter
18
+ super.init(bands: bands,
19
+ minFrequency: minFrequency,
20
+ maxFrequency: maxFrequency,
21
+ intervalMs: intervalMs)
22
+ }
23
+
24
+ override func onMagnitudesCalculated(_ magnitudes: [Float]) {
25
+ guard !magnitudes.isEmpty, let reactTag = self.reactTag
26
+ else { return }
27
+ eventEmitter.sendEvent(withName: kEventMultibandProcessed, body: [
28
+ "magnitudes": magnitudes,
29
+ "id": reactTag
30
+ ])
31
+ }
32
+
33
+ }
34
+
35
+ public class BaseMultibandVolumeAudioRenderer: NSObject, RTCAudioRenderer {
36
+ private let frameInterval: Int
37
+ private var skippedFrames = 0
38
+ private let audioProcessor: AudioVisualizeProcessor
39
+
40
+ init(
41
+ bands: Int,
42
+ minFrequency: Float,
43
+ maxFrequency: Float,
44
+ intervalMs: Float
45
+ ) {
46
+ self.frameInterval = Int((intervalMs / 10.0).rounded())
47
+ self.audioProcessor = AudioVisualizeProcessor(minFrequency: minFrequency, maxFrequency: maxFrequency, bandsCount: bands)
48
+ }
49
+
50
+ public func render(pcmBuffer: AVAudioPCMBuffer) {
51
+ if(skippedFrames < frameInterval - 1) {
52
+ skippedFrames += 1
53
+ return
54
+ }
55
+
56
+ skippedFrames = 0
57
+ guard let magnitudes = audioProcessor.process(pcmBuffer: pcmBuffer)
58
+ else {
59
+ return
60
+ }
61
+ onMagnitudesCalculated(magnitudes)
62
+ }
63
+
64
+ func onMagnitudesCalculated(_ magnitudes: [Float]) { }
65
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright 2025 LiveKit
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import Foundation
18
+
19
+ // Simple ring-buffer used for internal audio processing. Not thread-safe.
20
+ class RingBuffer<T: Numeric> {
21
+ private var _isFull = false
22
+ private var _buffer: [T]
23
+ private var _head: Int = 0
24
+
25
+ init(size: Int) {
26
+ _buffer = [T](repeating: 0, count: size)
27
+ }
28
+
29
+ func write(_ value: T) {
30
+ _buffer[_head] = value
31
+ _head = (_head + 1) % _buffer.count
32
+ if _head == 0 { _isFull = true }
33
+ }
34
+
35
+ func write(_ sequence: [T]) {
36
+ for value in sequence {
37
+ write(value)
38
+ }
39
+ }
40
+
41
+ func read() -> [T]? {
42
+ guard _isFull else { return nil }
43
+
44
+ if _head == 0 {
45
+ return _buffer // Return the entire buffer if _head is at the start
46
+ } else {
47
+ // Return the buffer in the correct order
48
+ return Array(_buffer[_head ..< _buffer.count] + _buffer[0 ..< _head])
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,48 @@
1
+ import WebRTC
2
+
3
+ public class VolumeAudioRenderer: BaseVolumeAudioRenderer {
4
+ private let eventEmitter: RCTEventEmitter
5
+
6
+ @objc
7
+ public var reactTag: String? = nil
8
+
9
+ @objc
10
+ public init(intervalMs: Double, eventEmitter: RCTEventEmitter) {
11
+ self.eventEmitter = eventEmitter
12
+ super.init(intervalMs: intervalMs)
13
+ }
14
+
15
+ override public func onVolumeCalculated(_ audioLevels: [AudioLevel]) {
16
+ guard let rmsAvg = audioLevels.combine()?.average,
17
+ let reactTag = self.reactTag
18
+ else { return }
19
+ eventEmitter.sendEvent(withName: kEventVolumeProcessed, body: [
20
+ "volume": rmsAvg,
21
+ "id": reactTag
22
+ ])
23
+ }
24
+ }
25
+
26
+ public class BaseVolumeAudioRenderer: NSObject, RTCAudioRenderer {
27
+ private let frameInterval: Int
28
+ private var skippedFrames = 0
29
+ public init(intervalMs: Double = 30) {
30
+ self.frameInterval = Int((intervalMs / 10.0).rounded())
31
+ }
32
+
33
+ public func render(pcmBuffer: AVAudioPCMBuffer) {
34
+ if(skippedFrames < frameInterval - 1) {
35
+ skippedFrames += 1
36
+ return
37
+ }
38
+
39
+ skippedFrames = 0
40
+ guard let pcmBuffer = pcmBuffer.convert(toCommonFormat: .pcmFormatFloat32) else { return }
41
+ let audioLevels = pcmBuffer.audioLevels()
42
+ onVolumeCalculated(audioLevels)
43
+ }
44
+
45
+ public func onVolumeCalculated(_ audioLevels: [AudioLevel]) {
46
+
47
+ }
48
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _reactNative = require("react-native");
8
+ const LINKING_ERROR = `The package '@livekit/react-native' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
9
+ ios: "- You have run 'pod install'\n",
10
+ default: ''
11
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo managed workflow\n';
12
+ const LiveKitModule = _reactNative.NativeModules.LivekitReactNative ? _reactNative.NativeModules.LivekitReactNative : new Proxy({}, {
13
+ get() {
14
+ throw new Error(LINKING_ERROR);
15
+ }
16
+ });
17
+ var _default = exports.default = LiveKitModule;
18
+ //# sourceMappingURL=LKNativeModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","LiveKitModule","NativeModules","LivekitReactNative","Proxy","get","Error","_default","exports"],"sources":["LKNativeModule.ts"],"sourcesContent":["import { NativeModules, Platform } from 'react-native';\nconst LINKING_ERROR =\n `The package '@livekit/react-native' doesn't seem to be linked. Make sure: \\n\\n` +\n Platform.select({ ios: \"- You have run 'pod install'\\n\", default: '' }) +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo managed workflow\\n';\n\nconst LiveKitModule = NativeModules.LivekitReactNative\n ? NativeModules.LivekitReactNative\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\nexport default LiveKitModule;\n"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,MAAMC,aAAa,GACjB,gFAAgF,GAChFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,6CAA6C;AAE/C,MAAMC,aAAa,GAAGC,0BAAa,CAACC,kBAAkB,GAClDD,0BAAa,CAACC,kBAAkB,GAChC,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACV,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAAC,IAAAW,QAAA,GAAAC,OAAA,CAAAR,OAAA,GAESC,aAAa","ignoreList":[]}
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useBarAnimator = exports.BarVisualizer = void 0;
7
+ var _componentsReact = require("@livekit/components-react");
8
+ var _reactNative = require("react-native");
9
+ var _hooks = require("../hooks");
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
12
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
13
+ const defaultBarOptions = {
14
+ maxHeight: 1,
15
+ minHeight: 0.2,
16
+ barColor: '#888888',
17
+ barWidth: 24,
18
+ barBorderRadius: 12
19
+ };
20
+ const sequencerIntervals = new Map([['connecting', 2000], ['initializing', 2000], ['listening', 500], ['thinking', 150]]);
21
+ const getSequencerInterval = (state, barCount) => {
22
+ if (state === undefined) {
23
+ return 1000;
24
+ }
25
+ let interval = sequencerIntervals.get(state);
26
+ if (interval) {
27
+ switch (state) {
28
+ case 'connecting':
29
+ // case 'thinking':
30
+ interval /= barCount;
31
+ break;
32
+ default:
33
+ break;
34
+ }
35
+ }
36
+ return interval;
37
+ };
38
+ /**
39
+ * @beta
40
+ */
41
+
42
+ /**
43
+ * Visualizes audio signals from a TrackReference as bars.
44
+ * If the `state` prop is set, it automatically transitions between VoiceAssistant states.
45
+ * @beta
46
+ *
47
+ * @remarks For VoiceAssistant state transitions this component requires a voice assistant agent running with livekit-agents \>= 0.9.0
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * function SimpleVoiceAssistant() {
52
+ * const { state, audioTrack } = useVoiceAssistant();
53
+ * return (
54
+ * <BarVisualizer
55
+ * state={state}
56
+ * trackRef={audioTrack}
57
+ * />
58
+ * );
59
+ * }
60
+ * ```
61
+ */
62
+ const BarVisualizer = ({
63
+ style = {},
64
+ state,
65
+ barCount = 5,
66
+ trackRef,
67
+ options
68
+ }) => {
69
+ let trackReference = (0, _componentsReact.useMaybeTrackRefContext)();
70
+ if (trackRef) {
71
+ trackReference = trackRef;
72
+ }
73
+ const opacityAnimations = (0, _react.useRef)([]).current;
74
+ let magnitudes = (0, _hooks.useMultibandTrackVolume)(trackReference, {
75
+ bands: barCount
76
+ });
77
+ let opts = {
78
+ ...defaultBarOptions,
79
+ ...options
80
+ };
81
+ const highlightedIndices = useBarAnimator(state, barCount, getSequencerInterval(state, barCount) ?? 100);
82
+ (0, _react.useEffect)(() => {
83
+ let animations = [];
84
+ for (let i = 0; i < barCount; i++) {
85
+ if (!opacityAnimations[i]) {
86
+ opacityAnimations[i] = new _reactNative.Animated.Value(0.3);
87
+ }
88
+ let targetOpacity = 0.3;
89
+ if (highlightedIndices.includes(i)) {
90
+ targetOpacity = 1;
91
+ }
92
+ animations.push(_reactNative.Animated.timing(opacityAnimations[i], {
93
+ toValue: targetOpacity,
94
+ duration: 250,
95
+ useNativeDriver: true
96
+ }));
97
+ }
98
+ let parallel = _reactNative.Animated.parallel(animations);
99
+ parallel.start();
100
+ return () => {
101
+ parallel.stop();
102
+ };
103
+ }, [highlightedIndices, barCount, opacityAnimations]);
104
+ let bars = [];
105
+ magnitudes.forEach((value, index) => {
106
+ let coerced = Math.min(opts.maxHeight, Math.max(opts.minHeight, value));
107
+ let coercedPercent = Math.min(100, Math.max(0, coerced * 100 + 5));
108
+ let opacity = opacityAnimations[index] ?? new _reactNative.Animated.Value(0.3);
109
+ let barStyle = {
110
+ opacity: opacity,
111
+ backgroundColor: opts.barColor,
112
+ borderRadius: opts.barBorderRadius,
113
+ width: opts.barWidth
114
+ };
115
+ bars.push( /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
116
+ key: index,
117
+ style: [{
118
+ height: `${coercedPercent}%`
119
+ }, barStyle, styles.volumeIndicator]
120
+ }));
121
+ });
122
+ return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
123
+ style: {
124
+ ...style,
125
+ ...styles.container
126
+ }
127
+ }, bars);
128
+ };
129
+ exports.BarVisualizer = BarVisualizer;
130
+ const styles = _reactNative.StyleSheet.create({
131
+ container: {
132
+ flexDirection: 'row',
133
+ alignItems: 'center',
134
+ justifyContent: 'space-evenly'
135
+ },
136
+ volumeIndicator: {
137
+ borderRadius: 12
138
+ }
139
+ });
140
+ const useBarAnimator = (state, columns, interval) => {
141
+ const [index, setIndex] = (0, _react.useState)(0);
142
+ const [sequence, setSequence] = (0, _react.useState)([[]]);
143
+ (0, _react.useEffect)(() => {
144
+ if (state === 'thinking') {
145
+ setSequence(generateListeningSequenceBar(columns));
146
+ } else if (state === 'connecting' || state === 'initializing') {
147
+ const seq = [...generateConnectingSequenceBar(columns)];
148
+ setSequence(seq);
149
+ } else if (state === 'listening') {
150
+ setSequence(generateListeningSequenceBar(columns));
151
+ } else if (state === undefined) {
152
+ // highlight everything
153
+ setSequence([new Array(columns).fill(0).map((_, idx) => idx)]);
154
+ } else {
155
+ setSequence([[]]);
156
+ }
157
+ setIndex(0);
158
+ }, [state, columns]);
159
+ const animationFrameId = (0, _react.useRef)(null);
160
+ (0, _react.useEffect)(() => {
161
+ let startTime = performance.now();
162
+ const animate = time => {
163
+ const timeElapsed = time - startTime;
164
+ if (timeElapsed >= interval) {
165
+ setIndex(prev => prev + 1);
166
+ startTime = time;
167
+ }
168
+ animationFrameId.current = requestAnimationFrame(animate);
169
+ };
170
+ animationFrameId.current = requestAnimationFrame(animate);
171
+ return () => {
172
+ if (animationFrameId.current !== null) {
173
+ cancelAnimationFrame(animationFrameId.current);
174
+ }
175
+ };
176
+ }, [interval, columns, state, sequence.length]);
177
+ return sequence[index % sequence.length];
178
+ };
179
+ exports.useBarAnimator = useBarAnimator;
180
+ const generateListeningSequenceBar = columns => {
181
+ const center = Math.floor(columns / 2);
182
+ const noIndex = -1;
183
+ return [[center], [noIndex]];
184
+ };
185
+ const generateConnectingSequenceBar = columns => {
186
+ const seq = [[]];
187
+ for (let x = 0; x < columns; x++) {
188
+ seq.push([x, columns - 1 - x]);
189
+ }
190
+ return seq;
191
+ };
192
+ //# sourceMappingURL=BarVisualizer.js.map
@@ -0,0 +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":[]}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addListener = addListener;
7
+ exports.removeListener = removeListener;
8
+ exports.setupNativeEvents = setupNativeEvents;
9
+ var _reactNative = require("react-native");
10
+ var _EventEmitter = _interopRequireDefault(require("react-native/Libraries/vendor/emitter/EventEmitter"));
11
+ var _LKNativeModule = _interopRequireDefault(require("../LKNativeModule"));
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ // @ts-ignore
14
+
15
+ // This emitter is going to be used to listen to all the native events (once) and then
16
+ // re-emit them on a JS-only emitter.
17
+ const nativeEmitter = new _reactNative.NativeEventEmitter(_LKNativeModule.default);
18
+ const NATIVE_EVENTS = ['LK_VOLUME_PROCESSED', 'LK_MULTIBAND_PROCESSED'];
19
+ const eventEmitter = new _EventEmitter.default();
20
+ function setupNativeEvents() {
21
+ for (const eventName of NATIVE_EVENTS) {
22
+ nativeEmitter.addListener(eventName, (...args) => {
23
+ eventEmitter.emit(eventName, ...args);
24
+ });
25
+ }
26
+ }
27
+ const _subscriptions = new Map();
28
+ function addListener(listener, eventName, eventHandler) {
29
+ var _subscriptions$get;
30
+ if (!NATIVE_EVENTS.includes(eventName)) {
31
+ throw new Error(`Invalid event: ${eventName}`);
32
+ }
33
+ if (!_subscriptions.has(listener)) {
34
+ _subscriptions.set(listener, []);
35
+ }
36
+ (_subscriptions$get = _subscriptions.get(listener)) === null || _subscriptions$get === void 0 || _subscriptions$get.push(eventEmitter.addListener(eventName, eventHandler));
37
+ }
38
+ function removeListener(listener) {
39
+ var _subscriptions$get2;
40
+ (_subscriptions$get2 = _subscriptions.get(listener)) === null || _subscriptions$get2 === void 0 || _subscriptions$get2.forEach(sub => {
41
+ sub.remove();
42
+ });
43
+ _subscriptions.delete(listener);
44
+ }
45
+ //# sourceMappingURL=EventEmitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","_EventEmitter","_interopRequireDefault","_LKNativeModule","e","__esModule","default","nativeEmitter","NativeEventEmitter","LiveKitModule","NATIVE_EVENTS","eventEmitter","EventEmitter","setupNativeEvents","eventName","addListener","args","emit","_subscriptions","Map","listener","eventHandler","_subscriptions$get","includes","Error","has","set","get","push","removeListener","_subscriptions$get2","forEach","sub","remove","delete"],"sources":["EventEmitter.ts"],"sourcesContent":["import { NativeEventEmitter, type EmitterSubscription } from 'react-native';\n// @ts-ignore\nimport EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';\nimport LiveKitModule from '../LKNativeModule';\n\n// This emitter is going to be used to listen to all the native events (once) and then\n// re-emit them on a JS-only emitter.\nconst nativeEmitter = new NativeEventEmitter(LiveKitModule);\n\nconst NATIVE_EVENTS = ['LK_VOLUME_PROCESSED', 'LK_MULTIBAND_PROCESSED'];\n\nconst eventEmitter = new EventEmitter();\n\nexport function setupNativeEvents() {\n for (const eventName of NATIVE_EVENTS) {\n nativeEmitter.addListener(eventName, (...args) => {\n eventEmitter.emit(eventName, ...args);\n });\n }\n}\n\ntype EventHandler = (event: unknown) => void;\ntype Listener = unknown;\n\nconst _subscriptions: Map<Listener, EmitterSubscription[]> = new Map();\n\nexport function addListener(\n listener: Listener,\n eventName: string,\n eventHandler: EventHandler\n): void {\n if (!NATIVE_EVENTS.includes(eventName)) {\n throw new Error(`Invalid event: ${eventName}`);\n }\n\n if (!_subscriptions.has(listener)) {\n _subscriptions.set(listener, []);\n }\n\n _subscriptions\n .get(listener)\n ?.push(eventEmitter.addListener(eventName, eventHandler));\n}\n\nexport function removeListener(listener: Listener): void {\n _subscriptions.get(listener)?.forEach((sub) => {\n sub.remove();\n });\n\n _subscriptions.delete(listener);\n}\n"],"mappings":";;;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,IAAAC,aAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,eAAA,GAAAD,sBAAA,CAAAF,OAAA;AAA8C,SAAAE,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAF9C;;AAIA;AACA;AACA,MAAMG,aAAa,GAAG,IAAIC,+BAAkB,CAACC,uBAAa,CAAC;AAE3D,MAAMC,aAAa,GAAG,CAAC,qBAAqB,EAAE,wBAAwB,CAAC;AAEvE,MAAMC,YAAY,GAAG,IAAIC,qBAAY,CAAC,CAAC;AAEhC,SAASC,iBAAiBA,CAAA,EAAG;EAClC,KAAK,MAAMC,SAAS,IAAIJ,aAAa,EAAE;IACrCH,aAAa,CAACQ,WAAW,CAACD,SAAS,EAAE,CAAC,GAAGE,IAAI,KAAK;MAChDL,YAAY,CAACM,IAAI,CAACH,SAAS,EAAE,GAAGE,IAAI,CAAC;IACvC,CAAC,CAAC;EACJ;AACF;AAKA,MAAME,cAAoD,GAAG,IAAIC,GAAG,CAAC,CAAC;AAE/D,SAASJ,WAAWA,CACzBK,QAAkB,EAClBN,SAAiB,EACjBO,YAA0B,EACpB;EAAA,IAAAC,kBAAA;EACN,IAAI,CAACZ,aAAa,CAACa,QAAQ,CAACT,SAAS,CAAC,EAAE;IACtC,MAAM,IAAIU,KAAK,CAAC,kBAAkBV,SAAS,EAAE,CAAC;EAChD;EAEA,IAAI,CAACI,cAAc,CAACO,GAAG,CAACL,QAAQ,CAAC,EAAE;IACjCF,cAAc,CAACQ,GAAG,CAACN,QAAQ,EAAE,EAAE,CAAC;EAClC;EAEA,CAAAE,kBAAA,GAAAJ,cAAc,CACXS,GAAG,CAACP,QAAQ,CAAC,cAAAE,kBAAA,eADhBA,kBAAA,CAEIM,IAAI,CAACjB,YAAY,CAACI,WAAW,CAACD,SAAS,EAAEO,YAAY,CAAC,CAAC;AAC7D;AAEO,SAASQ,cAAcA,CAACT,QAAkB,EAAQ;EAAA,IAAAU,mBAAA;EACvD,CAAAA,mBAAA,GAAAZ,cAAc,CAACS,GAAG,CAACP,QAAQ,CAAC,cAAAU,mBAAA,eAA5BA,mBAAA,CAA8BC,OAAO,CAAEC,GAAG,IAAK;IAC7CA,GAAG,CAACC,MAAM,CAAC,CAAC;EACd,CAAC,CAAC;EAEFf,cAAc,CAACgB,MAAM,CAACd,QAAQ,CAAC;AACjC","ignoreList":[]}