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
| Requirement | iOS Simulator | Android Emulator |
|---|---|---|
| Ansight | CLI and running host with audio capabilities, audio inject, and task audio APIs | Same |
| Development tools | macOS with full Xcode and its Simulator app | Android SDK/ADB and a local emulator with an authenticated audio gRPC endpoint |
| App | A development/QA build connected through the Ansight SDK, with microphone permission | Same |
| Audio setup | BlackHole 2ch, Accessibility permission, and explicit Simulator input selection | Host microphone input disabled in emulator controls |
| Target | Exactly one booted iOS Simulator; Simulator UI open | The emulator belonging to the selected live session |
| Fixture | PCM16 WAV, mono, 16 kHz, at most 15 seconds and 1 MiB | Same |
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
- 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.
- 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.
- 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.
- 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, oransight test run-all. Do not repeat--device-idfor an iOS audio run. - Do not use
test run-all --parallelfor 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
| Symptom | Action |
|---|---|
accessibility-permission-required | Grant access to the host launcher, rerun doctor from that launcher, and restart the host if necessary. |
audio-helper-missing or an unknown doctor check | Install a complete matching CLI package, then restart any old resident host. |
audioCapabilities is not a function | Verify the running host’s build, not only the CLI on PATH. |
BlackHole not-registered | Complete driver installation and its restart steps, then rerun doctor. |
audio-route-ambiguous | Shut down other booted iOS simulators. |
input-route-unverified | Open the configured Xcode’s Simulator and select BlackHole explicitly under its English Audio Input/Sound Input menu. |
host-microphone-enabled | Disable host microphone input in Android Emulator controls. |
microphone-not-ready | Start normal app recording and check app permission and provider diagnostics. |
| Delivery completes but capture assertions fail | Inspect the exact app recording/transcript; delivery alone does not establish capture. |
The CLI audio reference lists every command option.