Skip to content

Scaffold MobBridge audio output probe methods (Mob.Audio.output_status/level)#25

Merged
GenericJam merged 2 commits into
masterfrom
feat/audio-output-probes
Jul 7, 2026
Merged

Scaffold MobBridge audio output probe methods (Mob.Audio.output_status/level)#25
GenericJam merged 2 commits into
masterfrom
feat/audio-output-probes

Conversation

@GenericJam

@GenericJam GenericJam commented Jun 29, 2026

Copy link
Copy Markdown
Owner

What

Scaffolds the app-owned Kotlin bridge methods backing mob's audio output probes (GenericJam/mob#54):

  • audioOutputStatus(): FloatArray — volume / mute / route / other-audio via AudioManager, as float[4].
  • audioOutputLevel(source): FloatArray — peak/RMS dB from a short-lived Visualizer on Mob.Audio's own player session (audioPlayer.audioSessionId), which works with RECORD_AUDIO. Returns a length-1 error code (1 unsupported / 2 needs_record_audio / 3 not_playing) the NIF maps to an atom.

Why not session 0 (global mix)

The first cut tapped session 0 to observe foreign audio. Device testing (moto g power 2021, Android 11) showed a session-0 Visualizer fails with ERROR_NO_INIT even with RECORD_AUDIO + MODIFY_AUDIO_SETTINGS — global output capture is privileged. So audioOutputLevel meters only the app's own player session; global device-audio capture moves to a separate MediaProjection-based plugin (test-env dep).

Tests

  • ktlint clean (mix test --only lint generates a project and lints the real Kotlin).
  • Template test asserts the own-session tap (audioPlayer?.audioSessionId) and that session 0 is not used.

Merge alongside GenericJam/mob#54.

🤖 Generated with Claude Code

Adds the app-owned Kotlin bridge methods that back mob's new audio
output probes (Mob.Audio.output_status/0, output_level/1):

- audioOutputStatus(): FloatArray — volume / mute / route / other-audio
  via AudioManager, returned as float[4] for the NIF to decode.
- audioOutputLevel(source): FloatArray? — peak/RMS dB from a short-lived
  Visualizer on the global output mix (session 0), so it observes audio
  from native players that bypass Mob.Audio (e.g. a game's own
  AudioTrack). Returns null on failure (usually RECORD_AUDIO not granted);
  the mob NIF caches it with cacheOptional so a drifted bridge no-ops.

ktlint clean (mix test --only lint); a focused template test pins both
methods, the session-0 mix tap, and the peak/RMS measurement mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Device verification (moto g power 2021, Android 11) showed a session-0
Visualizer fails with ERROR_NO_INIT for a normal app even with RECORD_AUDIO
+ MODIFY_AUDIO_SETTINGS — global output capture is privileged. So
audioOutputLevel now meters Mob.Audio's OWN player session
(audioPlayer.audioSessionId), which works with RECORD_AUDIO, and returns a
length-1 error code (1 unsupported / 2 needs_record_audio / 3 not_playing)
for the NIF to map. "mix" is reported unsupported; global capture moves to a
separate MediaProjection plugin.

Template test updated to assert the own-session tap and that session 0 is
not used. ktlint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@GenericJam

Copy link
Copy Markdown
Owner Author

Review — approve (scaffold half of the output probes)

The generated MobBridge methods (audioOutputStatus(): FloatArray, audioOutputLevel(String): FloatArray) match the zig cached-method descriptors and the mob #54 NIF arities exactly — FFI seam is clean. The template test asserts the own-session tap (audioPlayer?.audioSessionId) and refute Visualizer(0), which guards against the privileged-session regression — good.

Merge alongside mob #54 (this is its Android half). The mob side uses cacheOptional + null-guards, so an app that updates mob before regenerating its bridge no-ops gracefully rather than crashing — but for the Android probes to actually work, existing apps must regenerate MobBridge.kt from this template. Worth a line in the merge/release notes.

Two follow-ups tracked on mob #54 apply here: the Android Visualizer floor is -96 dB (so Mob.Audio's :silent at -120 never triggers on Android), and mapping all measurement failures to needs_record_audio can mislead callers about a permission issue when there isn't one.

@GenericJam
GenericJam merged commit a5aebd5 into master Jul 7, 2026
3 checks passed
@GenericJam
GenericJam deleted the feat/audio-output-probes branch July 7, 2026 21:37
@GenericJam GenericJam mentioned this pull request Jul 7, 2026
GenericJam added a commit that referenced this pull request Jul 7, 2026
Kotlin bridge methods backing mob 0.7.18's Mob.Audio probes: audioOutputStatus /
audioOutputLevel (#25) + mic input_level metering (#31, MOB-35). Device-verified.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant