feat: animate local speaker ring from MoQ frame transmission
Wire the local user's avatar into the existing speaking-ring animation so the host/speaker sees the same green ring + amplitude glow on their own cell that remote speakers already get. Ground truth is the broadcaster's `publisher.send(opus)` success: the new `onLevel` callback fires only after a frame actually leaves on the wire, computed from the raw PCM peak (shared `peakAmplitude` util used by both the player decode loop and the broadcaster capture loop). This means the animation reflects what the relay sees, not any UI-side mute / role state that could be stale or buggy — if frames are flowing, the ring plays. Plumbing: `NestsSpeaker.startBroadcasting` gains an optional `onLevel` that the moq-lite + IETF broadcasters both honour, the reconnecting wrapper replays it on every session reissue (alongside the existing mute-intent replay), and `NestViewModel.startBroadcast` hands it a lambda that calls the existing `onSpeakerActivity` / `onAudioLevel` plumbing keyed on the local pubkey. The 250 ms speaking-timeout fades the ring naturally when the user mutes or stops broadcasting.
This commit is contained in:
@@ -260,7 +260,7 @@ private fun failedSpeaker(state: MutableStateFlow<NestsSpeakerState>): NestsSpea
|
||||
object : NestsSpeaker {
|
||||
override val state = state
|
||||
|
||||
override suspend fun startBroadcasting(): BroadcastHandle = error("speaker never connected: ${state.value}")
|
||||
override suspend fun startBroadcasting(onLevel: (Float) -> Unit): BroadcastHandle = error("speaker never connected: ${state.value}")
|
||||
|
||||
override suspend fun close() {
|
||||
if (state.value !is NestsSpeakerState.Closed) {
|
||||
|
||||
Reference in New Issue
Block a user