Microphone Audio Input

Set up iOS Simulator and Android Emulator microphone injection, verify permissions and routing, and assert real app capture.

Use a recorded speech fixture to test an app’s normal microphone flow. Ansight plays a local WAV into the connected virtual device; your app records it through its usual microphone API. Tests can then verify the saved recording or transcript.

What the user needs

RequirementiOS SimulatorAndroid Emulator
AnsightCLI and running host with audio capabilities, audio inject, and task audio APIsSame
Development toolsmacOS with full Xcode and its Simulator appAndroid SDK/ADB and a local emulator with an authenticated audio gRPC endpoint
AppA development/QA build connected through the Ansight SDK, with microphone permissionSame
Audio setupBlackHole 2ch, Accessibility permission, and explicit Simulator input selectionHost microphone input disabled in emulator controls
TargetExactly one booted iOS Simulator; Simulator UI openThe emulator belonging to the selected live session
FixturePCM16 WAV, mono, 16 kHz, at most 15 seconds and 1 MiBSame

Physical phones and tablets are not supported by this feature. Audio injection itself does not require an OpenAI credential. Agentic workspace tests use the normal Ansight account and organisation AI configuration; a deterministic task or direct CLI injection does not need an AI model to deliver audio.

One-time iOS setup

1. Install the audio driver

Install BlackHole 2ch separately from Ansight. With Homebrew:

brew install --cask blackhole-2ch

Complete the installer’s administrator and restart prompts. If Simulator was already booted when the driver was installed or CoreAudio restarted, shut down and boot that simulator again. Ansight does not install the driver for you.

2. Grant Accessibility to the host launcher

Open the permission panel:

open 'x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility'

In System Settings > Privacy & Security > Accessibility, enable the app that launches the resident Ansight host: for example, Terminal, iTerm, or your IDE. If it is missing, click + and select that app. For Terminal, select Applications > Utilities > Terminal. Authenticate when macOS asks. Apple’s permission instructions explain this manual step; doctor does not grant the permission.

Use that same launcher to start the host and run doctor. The permission belongs to the execution context, not the individual test: running a CLI command from a different terminal does not change which app launched an already-running host. If you need to restart the host after granting access, stop it only when no other session or test is using it, then run these from the intended launcher:

ansight host stop
ansight host run

host run stays in the foreground. Keep that terminal open and run subsequent commands in a second terminal window from the same app.

macOS normally remembers grants across restarts. Switching launchers, resetting privacy permissions, or changing the executable’s signing identity may require approval again. A stable launcher and signed Ansight builds make repeated setup less likely. Recheck after changing your installation or startup method.

3. Verify the machine prerequisites

ansight doctor --require permission.accessibility --require device.ios.audio.blackhole

Both named checks must pass. Other optional checks may leave the overall report amber; the two required checks still fail the command if unavailable. The Accessibility check queries the current Ansight process through its native audio library without opening Simulator, playing sound, or requiring the driver. BlackHole registration is a separate check.

If permission.accessibility is unknown, install an Ansight build containing the new doctor check. If a task reports ansight.device.audioCapabilities is not a function, check both ansight --version and ansight host status --json: an old resident host can continue running after the CLI on PATH has been updated.

Before each iOS run

  1. Boot exactly one iOS Simulator and open the Simulator app from your configured Xcode installation. Shut down other booted iOS simulators: their audio route is shared.
  2. In Simulator > I/O > Audio Input, explicitly choose BlackHole 2ch. Some Xcode versions call this Sound Input. The current route inspector requires English menu labels. Keep the Mac’s system microphone and speaker defaults unchanged; selecting a system default is not a substitute for selecting BlackHole inside Simulator.
  3. Launch the SDK-enabled app, connect it to Ansight, and grant its microphone permission. If testing speech recognition, also complete whatever recognition permissions and service setup your app normally requires.
  4. Find the live session and check its route:
ansight session list --connected --json
ansight audio capabilities --session <live-session-id> --json

Require available: true before injection. Doctor proves machine prerequisites; audio capabilities checks the selected session and route. Recheck the route after Simulator restarts or audio settings change. These inspections do not start recording or prove that your app has captured anything.

Stale Simulator audio routes

Simulator can show BlackHole as selected while the app actually records from another input. Ansight checks the target app’s active CoreAudio input, including Simulator aggregate devices. A confirmed mismatch fails before playback with simulator-audio-route-stale: Simulator audio route is stale—restart required. The diagnostic includes the expected device and the observed input devices. An idle recorder remains a readiness issue, not proof of a stale route.

The failed run stays failed. Ansight saves the restart requirement and restarts that simulator before the next explicit launch of the same app, including a new app execute or workspace test run. It does not restart during injection or replay the failed task. If you already rebooted the simulator, Ansight uses the new boot instead. Recovery keeps the restart requirement if it fails or is cancelled, and refuses to run while another injection owns the audio route or other simulators are booted. Mac audio defaults remain unchanged.

Reusing a connected session with task run does not launch or restart a device. Start a fresh app run or restart the simulator manually to recover first.

Parallel execution and tests

Audio APIs are rejected in parallel test batches on both iOS and Android. Calling ansight.device.audioCapabilities() or ansight.device.injectMicrophoneAudio() rejects the TypeScript promise with audio-parallel-execution-unsupported in the error message, before provider inspection or playback. Ansight logs the rejection in test progress and the task call audit. This applies whenever --parallel is selected (even with one worker), and to multi-device matrix batches without that flag. Non-audio APIs remain available.

Run audio tests in a separate serial batch with one selected device. The guard also follows nested task calls; catching the error in TypeScript does not enable audio. It does not coordinate separately launched serial jobs.

Additionally, parallel iOS Simulator audio workflows are unsupported on the same Mac because the Simulator input route and loopback device are shared across the host.

  • Select exactly one device for ansight execute, ansight test run, or ansight test run-all. Do not repeat --device-id for an iOS audio run.
  • Do not use test run-all --parallel for iOS audio tests. Omitting the flag alone is insufficient: matrix scheduling still runs a test concurrently on all selected devices.
  • Run the entire workflow serially: start recording, inject, stop recording, verify capture/transcription, and reset. Do not overlap audio runs from other terminals, agents, or CI jobs on the same Mac.
  • Keep exactly one iOS Simulator booted. Run audio tests on different simulator models sequentially, shutting down the previous simulator before the next run. Use separate Mac hosts for concurrent iOS audio testing.

For example, run the audio test on one explicitly selected simulator:

ansight test run . audio.synthetic-microphone --device-id <ios-simulator-id>

Ansight fails closed with audio-route-ambiguous when multiple iOS simulators are booted, and audio-route-busy when another injection owns the route. It does not shut down other simulators or silently queue/replay audio. The route lock covers injection only; it does not reserve the whole recording workflow. In CI, serialize complete audio jobs using a shared concurrency group for each Mac. Check audioCapabilities() before starting app recording and abort if unavailable. A route change during playback can fail after partial delivery; reset the app and discard that recording before deliberately rerunning the whole test.

The single-booted-simulator rule is specific to iOS. Android uses a separate emulator route, but the parallel test batch API guard applies to both platforms.

Android setup

Boot a local Android Emulator, install and connect the SDK-enabled app, and grant its Android microphone permission. In the emulator’s Extended controls > Microphone, turn Virtual microphone uses host audio input off so the test uses synthetic audio. See the Android emulator controls. BlackHole and the macOS Simulator Accessibility setup above are not used by the Android provider.

Ansight discovers the emulator’s authenticated gRPC endpoint from its running emulator discovery record. A usable token and endpoint must be present; an unauthenticated endpoint is not sufficient. Run ansight doctor for SDK/ADB readiness, start recording in the app, then run audio capabilities for that session. Resolve endpoint-unavailable or endpoint-authentication-unavailable before attempting injection.

Android also requires a healthy, active guest microphone stream and disabled host microphone access. Emulator 37.1.11 has an observed native audio-injection race that can crash the emulator; Ansight’s readiness checks do not guarantee stability on that version. Ansight does not automatically restart the emulator or retry an interrupted injection.

Deliver a fixture and verify capture

Start recording through the app’s normal UI or your repository task. Then inject:

ansight audio inject --session <live-session-id> \
  --file ./fixtures/audio/quote.wav --timeout-ms 30000 --json

Stop recording through the app after delivery completes. Assert against the recording or transcript from that exact run. A completed injection means provider delivery finished; it does not mean the app captured or transcribed the fixture. For capture tests, compare the recording to the fixture and include a different fixture as a negative control. Reset the app after verification.

For a reusable task, call await ansight.device.audioCapabilities() and await ansight.device.injectMicrophoneAudio({ file: "fixtures/audio/quote.wav" }) after starting recording, then add named assertions for the app’s result. Task file paths are relative to the pinned repository root; CLI paths are relative to the calling directory. The fixture must exist on the host executing the operation. See the task audio API for return values, cancellation, readiness waits and fixture limits.

If a repository task already owns recording, injection, stopping and reset, run that task once rather than manually duplicating its actions. Interrupted or uncertain delivery needs inspection before another attempt.

Troubleshooting

SymptomAction
accessibility-permission-requiredGrant access to the host launcher, rerun doctor from that launcher, and restart the host if necessary.
audio-helper-missing or an unknown doctor checkInstall a complete matching CLI package, then restart any old resident host.
audioCapabilities is not a functionVerify the running host’s build, not only the CLI on PATH.
BlackHole not-registeredComplete driver installation and its restart steps, then rerun doctor.
audio-route-ambiguousShut down other booted iOS simulators.
input-route-unverifiedOpen the configured Xcode’s Simulator and select BlackHole explicitly under its English Audio Input/Sound Input menu.
host-microphone-enabledDisable host microphone input in Android Emulator controls.
microphone-not-readyStart normal app recording and check app permission and provider diagnostics.
Delivery completes but capture assertions failInspect the exact app recording/transcript; delivery alone does not establish capture.

The CLI audio reference lists every command option.