Skip to content

Commit a6f615f

Browse files
committed
Address feedback and fix documentation tests
1 parent 46e7ba3 commit a6f615f

File tree

3 files changed

+48
-137
lines changed

3 files changed

+48
-137
lines changed

Sources/StreamVideo/Utils/AudioSession/AudioDeviceModule/AudioDeviceModule.swift

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import StreamWebRTC
1111
/// audio pipeline can stay in sync with application logic.
1212
final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable, @unchecked Sendable {
1313

14+
enum Constant {
15+
// WebRTC interfaces are returning integer result codes. We use this typed/named
16+
// constant to define the Success of an operation.
17+
static let successResult = 0
18+
19+
// The down limit of audio pipeline in DB that is considered silence.
20+
static let silenceDB: Float = -160
21+
}
22+
1423
/// Events emitted as the underlying audio engine changes state.
1524
enum Event: Equatable {
1625
case speechActivityStarted
@@ -35,7 +44,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
3544
var isMicrophoneMuted: Bool { isMicrophoneMutedSubject.value }
3645
var isMicrophoneMutedPublisher: AnyPublisher<Bool, Never> { isMicrophoneMutedSubject.eraseToAnyPublisher() }
3746

38-
private let audioLevelSubject = CurrentValueSubject<Float, Never>(-160) // default to silence
47+
private let audioLevelSubject = CurrentValueSubject<Float, Never>(Constant.silenceDB) // default to silence
3948
var audioLevel: Float { audioLevelSubject.value }
4049
var audioLevelPublisher: AnyPublisher<Float, Never> { audioLevelSubject.eraseToAnyPublisher() }
4150

@@ -159,7 +168,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
159168
didCreateEngine engine: AVAudioEngine
160169
) -> Int {
161170
subject.send(.didCreateAudioEngine(engine))
162-
return 0
171+
return Constant.successResult
163172
}
164173

165174
func audioDeviceModule(
@@ -171,7 +180,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
171180
subject.send(.willEnableAudioEngine(engine))
172181
isPlayingSubject.send(isPlayoutEnabled)
173182
isRecordingSubject.send(isRecordingEnabled)
174-
return 0
183+
return Constant.successResult
175184
}
176185

177186
func audioDeviceModule(
@@ -183,7 +192,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
183192
subject.send(.willStartAudioEngine(engine))
184193
isPlayingSubject.send(isPlayoutEnabled)
185194
isRecordingSubject.send(isRecordingEnabled)
186-
return 0
195+
return Constant.successResult
187196
}
188197

189198
func audioDeviceModule(
@@ -196,7 +205,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
196205
audioLevelsAdapter.uninstall(on: 0)
197206
isPlayingSubject.send(isPlayoutEnabled)
198207
isRecordingSubject.send(isRecordingEnabled)
199-
return 0
208+
return Constant.successResult
200209
}
201210

202211
func audioDeviceModule(
@@ -209,7 +218,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
209218
audioLevelsAdapter.uninstall(on: 0)
210219
isPlayingSubject.send(isPlayoutEnabled)
211220
isRecordingSubject.send(isRecordingEnabled)
212-
return 0
221+
return Constant.successResult
213222
}
214223

215224
func audioDeviceModule(
@@ -218,7 +227,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
218227
) -> Int {
219228
subject.send(.willReleaseAudioEngine(engine))
220229
audioLevelsAdapter.uninstall(on: 0)
221-
return 0
230+
return Constant.successResult
222231
}
223232

224233
func audioDeviceModule(
@@ -235,7 +244,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
235244
bus: 0,
236245
bufferSize: 1024
237246
)
238-
return 0
247+
return Constant.successResult
239248
}
240249

241250
func audioDeviceModule(
@@ -246,13 +255,13 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
246255
format: AVAudioFormat,
247256
context: [AnyHashable: Any]
248257
) -> Int {
249-
0
258+
Constant.successResult
250259
}
251260

252261
func audioDeviceModuleDidUpdateDevices(
253262
_ audioDeviceModule: RTCAudioDeviceModule
254263
) {
255-
// TODO:
264+
/* No-op */
256265
}
257266

258267
private enum CodingKeys: String, CodingKey {
@@ -281,7 +290,7 @@ final class AudioDeviceModule: NSObject, RTCAudioDeviceModuleDelegate, Encodable
281290
) throws {
282291
let result = operation()
283292

284-
guard result != 0 else {
293+
guard result != Constant.successResult else {
285294
return
286295
}
287296

Sources/StreamVideo/Utils/AudioSession/AudioDeviceModule/AudioEngineLevelNodeAdapter.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,47 @@ final class AudioEngineLevelNodeAdapter: AudioEngineNodeAdapting {
7272
/// Processes the PCM buffer produced by the tap and computes a clamped RMS
7373
/// value which is forwarded to the publisher.
7474
private func processInputBuffer(_ buffer: AVAudioPCMBuffer) {
75+
// Safely unwrap the `subject` (used to publish updates) and the
76+
// `floatChannelData` (pointer to the interleaved or non-interleaved
77+
// channel samples in memory). If either is missing, exit early since
78+
// processing cannot continue.
7579
guard
7680
let subject,
7781
let channelData = buffer.floatChannelData
7882
else { return }
7983

84+
// Obtain the total number of frames in the buffer as a vDSP-compatible
85+
// length type (`vDSP_Length`). This represents how many samples exist
86+
// per channel in the current audio buffer.
8087
let frameCount = vDSP_Length(buffer.frameLength)
8188

89+
// Declare a variable to store the computed RMS (root-mean-square)
90+
// amplitude value for the buffer. It will represent the signal's
91+
// average power in linear scale (not decibels yet).
8292
var rms: Float = 0
93+
94+
// Use Apple's Accelerate framework to efficiently compute the RMS
95+
// (root mean square) of the float samples in the first channel.
96+
// - Parameters:
97+
// - channelData[0]: Pointer to the first channel’s samples.
98+
// - 1: Stride between consecutive elements (every sample).
99+
// - &rms: Output variable to store the computed RMS.
100+
// - frameCount: Number of samples to process.
83101
vDSP_rmsqv(channelData[0], 1, &rms, frameCount)
84102

103+
// Convert the linear RMS value to decibels using the formula
104+
// 20 * log10(rms). To avoid a log of zero (which is undefined),
105+
// use `max(rms, Float.ulpOfOne)` to ensure a minimal positive value.
85106
let rmsDB = 20 * log10(max(rms, Float.ulpOfOne))
107+
108+
// Clamp the computed decibel value to a reasonable audio level range
109+
// between -160 dB (silence) and 0 dB (maximum). This prevents extreme
110+
// or invalid values that may occur due to noise or computation errors.
86111
let clampedRMS = max(-160.0, min(0.0, Float(rmsDB)))
87112

113+
// Publish the clamped decibel value to the CurrentValueSubject so that
114+
// subscribers (e.g., UI level meters or analytics systems) receive the
115+
// updated level reading.
88116
subject.send(clampedRMS)
89117
}
90118
}

Sources/StreamVideo/Utils/AudioSession/RTCAudioStore/Effects/RTCAudioStore+RouteChangeEffect.swift

Lines changed: 0 additions & 126 deletions
This file was deleted.

0 commit comments

Comments
 (0)