Skip to content

About

Never let AirPods hijack your Mac's microphone: pins the default input to the built-in mic and locks input volume. Event-driven CoreAudio daemon, 0% idle CPU.

Topics

Resources

Stars

11 stars

Watchers

0 watching

Forks

Repository files navigation

NoMicSwitch icon: a microphone with a small green lock

NoMicSwitch

Read this in: English (this page) · Português · Español

nomicswitch is a small macOS daemon that keeps the default audio input on the MacBook's built-in microphone and keeps the input volume at 100%. When macOS, an app or your AirPods move the input somewhere else, it moves it back in under 100 ms. Between events it sleeps, so idle CPU use is zero.

The problem

"AirPods sound bad on Mac", "my Mac keeps switching the microphone to AirPods", "MacBook keeps using my iPhone as a mic". If you searched for any of those, this is the fix that sticks.

When AirPods connect, macOS switches the default input to the AirPods microphone. Two things go wrong:

  1. The AirPods mic is much worse than the microphone array built into a modern MacBook.
  2. In most conferencing apps, what you hear gets worse too. As soon as the Bluetooth mic is in use, AirPods drop to a low-bandwidth headset mode. Apple describes the two modes in support.apple.com/102217 and suggests closing the app that uses the mic.

Zoom, Teams, browsers and macOS itself keep flipping the input back. Fixing it by hand every time gets old fast.

Didn't Apple fix this? Partly. On macOS 15 or newer with recent AirPods Pro 2 or AirPods 4 firmware, some apps (Safari, FaceTime, games) can keep 48 kHz in headset mode. Reports from 2026 still show Teams and Chrome degrading, and none of that changes the first point: the AirPods mic itself. Pinning the built-in mic takes care of both.

What it does

  • Keeps the default input device on the built-in microphone, or on any device you choose.
  • Keeps the input volume at 100%, or at any level you choose, or leaves it alone.
  • Listens for three CoreAudio events: input device switched, input volume changed, device connected or removed.
  • Uses 0.0% CPU while idle and about 14 MB of RAM. It only wakes up when macOS announces a change.
  • Leaves your AirPods connected for output. Music and calls keep playing through them; only the microphone is protected.

Log excerpt: nomicswitch restores the built-in mic after AirPods take over, then restores the input volume after an app lowers it

Why revert instead of block

macOS has no API to lock the default input device. Any app, and the system itself, can change it at any time. Every tool in this space works the same way, including paid ones like ToothFairy and SoundSource: listen for the change event and switch back at once. The window is a few milliseconds, which is too short for an app to record through the AirPods mic.

Install

One line, no dependencies (recommended)

curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/install.sh | bash

Terminal showing the one-line install finishing with "nomicswitch 1.1.0 is running and will start at login"

This downloads the prebuilt universal binary (Intel and Apple Silicon, built by GitHub Actions from this repository), checks its SHA-256, installs it to ~/.local/bin and loads a LaunchAgent. You do not need Xcode or Homebrew. install.sh is about 100 lines if you want to read it first.

To pin a different mic or a different volume from the start:

NOMICSWITCH_ARGS="--uid <UID> --volume 80" bash -c "$(curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/install.sh)"

Homebrew

brew install marks-zyz/tap/nomicswitch
brew services start nomicswitch

From source

You need the Xcode Command Line Tools (xcode-select --install).

git clone https://github.com/marks-zyz/NoMicSwitch.git
cd NoMicSwitch
make install    # builds, installs to ~/.local/bin, loads a LaunchAgent

All three start the daemon right away and bring it back after a reboot.

Turning it on and off

Action One-line or source install Homebrew
Start (and enable at login) the install command above brew services start nomicswitch
Pause (to use another mic for a while) launchctl bootout gui/$(id -u)/com.nomicswitch brew services stop nomicswitch
Start again launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.nomicswitch.plist brew services start nomicswitch
Remove curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/uninstall.sh | bash brew services stop nomicswitch && brew uninstall nomicswitch
Watch the log tail -f ~/Library/Logs/nomicswitch.log tail -f $(brew --prefix)/var/log/nomicswitch.log

Options

nomicswitch [options]     run the guard (meant for launchd / brew services)
nomicswitch --list        list input-capable devices with their UIDs, then exit

-u, --uid <UID>       device UID to pin (default: BuiltInMicrophoneDevice)
-v, --volume <0-100>  input volume percentage to lock (default: 100)
    --no-volume       pin the device only, leave the volume alone
    --version         print version and exit
-h, --help            show help

To pin a USB microphone instead of the built-in one, run nomicswitch --list, copy its UID, and add --uid <UID> to the LaunchAgent plist (or to the brew services plist). The daemon finds devices by UID because UIDs are stable and the same in every language, while macOS translates device names.

How it works

The daemon registers three CoreAudio property listeners:

Listener Fires when Response
kAudioHardwarePropertyDefaultInputDevice anything switches the default input switch it back
kAudioDevicePropertyVolumeScalar (input scope) anything lowers the input volume raise it back
kAudioHardwarePropertyDevices a device connects or disconnects re-check everything, re-attach volume listeners

Between events the process is asleep. On macOS 26 I measured 0.0% idle CPU, reaction under 100 ms and about 14 MB of RAM. There are no runtime dependencies: no Homebrew packages, no scripting bridges, only CoreAudio.

Design note: the first version was a shell script that polled every two seconds with SwitchAudioSource and osascript. It worked, but every tick spawned two processes and cost about 0.1 s of CPU, forever. The Swift daemon replaced it so the cost between events is exactly zero.

Requirements and compatibility

Tested on macOS 26 (Tahoe) 26.6.2 on Apple Silicon. It should work on macOS 12 Monterey or newer, Intel or Apple Silicon, since it builds from source. The build uses kAudioObjectPropertyElementMain from the macOS 12 SDK; on older SDKs, replace it with kAudioObjectPropertyElementMaster and it should compile. Building needs the Swift compiler, which comes with the Xcode Command Line Tools. Running needs nothing.

What it handles

  • AirPods, or any Bluetooth headset, connecting and grabbing the mic.
  • The iPhone "Continuity" microphone showing up as an input.
  • Apps such as Zoom, Teams or a browser switching the input device, or turning the input volume down through auto-gain.
  • Devices reconnecting after sleep, reboots (LaunchAgent with KeepAlive), and daemon crashes (launchd restarts it).

Limits

  • It reverts the change within milliseconds; it cannot prevent it, because macOS offers no API for that (see above).
  • While the daemon runs you cannot pick another microphone by hand. Pause the service first (one command, table above).
  • If you pin a removable device such as a USB mic and unplug it, the daemon waits quietly and pins it again the moment it returns.
  • There is no .pkg or .app. Without a paid Apple Developer ID, Gatekeeper blocks unsigned downloads on recent macOS, which would be worse than a one-line installer. The binary is ad-hoc signed, built in public CI, and its checksum is checked before install.

Alternatives

Other projects that solve the same problem (star counts from September 2026):

NoMicSwitch puts device pinning and input volume locking in one event-driven process, works with any device by UID, runs under brew services, installs with one dependency-free line, is public domain, and has this README in English, Portuguese and Spanish.

License

Unlicense: public domain. Do whatever you want with it, no attribution required. Icon glyphs are from Phosphor Icons (MIT).


Português

nomicswitch é um daemon pequeno para macOS que mantém a entrada de áudio padrão no microfone embutido do MacBook e o volume de entrada em 100%. Quando o macOS, um app ou seus AirPods movem a entrada para outro lugar, ele devolve em menos de 100 ms. Entre um evento e outro ele dorme, então o uso de CPU em repouso é zero.

O problema

Quando os AirPods conectam, o macOS troca a entrada padrão para o microfone deles. Duas coisas dão errado: (1) o microfone dos AirPods é muito pior que o conjunto de microfones embutidos de um MacBook moderno; (2) na maioria dos apps de chamada, o que você ouve também piora, porque ao ativar o microfone Bluetooth os AirPods caem para um modo headset de baixa banda (a Apple descreve os dois modos em support.apple.com/102217). Firmwares recentes dos AirPods Pro 2 e AirPods 4 no macOS 15 ou mais novo amenizam o segundo problema em alguns apps, mas não o primeiro.

Ele não bloqueia a troca (o macOS não oferece API para isso, e até os apps pagos como ToothFairy e SoundSource funcionam do mesmo jeito): ele escuta o evento e reverte em milissegundos.

Instalar

Uma linha, sem dependências (recomendado). Baixa o binário universal pré-compilado (Intel e Apple Silicon, gerado pelo GitHub Actions a partir deste repositório), confere o SHA-256, instala em ~/.local/bin e carrega um LaunchAgent. Não precisa de Xcode nem de Homebrew:

curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/install.sh | bash

Via Homebrew:

brew install marks-zyz/tap/nomicswitch
brew services start nomicswitch

Do código-fonte (precisa do Xcode Command Line Tools, xcode-select --install):

git clone https://github.com/marks-zyz/NoMicSwitch.git
cd NoMicSwitch
make install

Nos três casos ele inicia na hora e volta sozinho depois de reiniciar o Mac.

Ligar e desligar

Ação Instalação de uma linha ou do fonte Homebrew
Iniciar (e ativar no login) o comando de instalação acima brew services start nomicswitch
Pausar (para usar outro mic por um tempo) launchctl bootout gui/$(id -u)/com.nomicswitch brew services stop nomicswitch
Iniciar de novo launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.nomicswitch.plist brew services start nomicswitch
Remover curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/uninstall.sh | bash brew services stop nomicswitch && brew uninstall nomicswitch
Acompanhar o log tail -f ~/Library/Logs/nomicswitch.log tail -f $(brew --prefix)/var/log/nomicswitch.log

Opções

nomicswitch --list lista os dispositivos de entrada com seus UIDs. --uid <UID> trava outro dispositivo (um mic USB, por exemplo), --volume <0-100> escolhe o volume travado (padrão 100) e --no-volume trava só o dispositivo e deixa o volume em paz. O daemon acha os dispositivos pelo UID porque o UID é estável e igual em qualquer idioma, enquanto o macOS traduz os nomes.

Como funciona

Um binário Swift com três listeners do CoreAudio: troca do dispositivo de entrada, mudança do volume de entrada, e conexão ou remoção de dispositivos. Entre eventos o processo dorme. No macOS 26 medi 0,0% de CPU em repouso, reação abaixo de 100 ms e cerca de 14 MB de RAM. Não há dependências em runtime. A primeira versão era um script de shell verificando a cada dois segundos; funcionava, mas cada verificação criava dois processos, e o daemon substituiu isso.

Requisitos e limites

Testado no macOS 26 (Tahoe) 26.6.2 em Apple Silicon. Deve funcionar do macOS 12 em diante, Intel ou Apple Silicon. Enquanto o daemon roda você não consegue escolher outro microfone à mão: pause o serviço antes (um comando, tabela acima). Não existe .pkg nem .app porque, sem um Apple Developer ID pago, o Gatekeeper bloqueia downloads não assinados no macOS recente; o binário é assinado ad-hoc, compilado em CI público, e o checksum é conferido antes de instalar. Alternativas: veja a seção "Alternatives" acima.

Licença

Unlicense: domínio público. Faça o que quiser, sem exigência de atribuição. Os glifos do ícone são do Phosphor Icons (MIT).


Español

nomicswitch es un pequeño daemon para macOS que mantiene la entrada de audio predeterminada en el micrófono integrado del MacBook y el volumen de entrada al 100%. Cuando macOS, una app o tus AirPods mueven la entrada a otro sitio, la devuelve en menos de 100 ms. Entre eventos duerme, así que el uso de CPU en reposo es cero.

El problema

Cuando los AirPods se conectan, macOS cambia la entrada predeterminada a su micrófono. Dos cosas salen mal: (1) el micrófono de los AirPods es mucho peor que el conjunto de micrófonos integrados de un MacBook moderno; (2) en la mayoría de las apps de videollamada, lo que escuchas también empeora, porque al activarse el micrófono Bluetooth los AirPods pasan a un modo auricular de bajo ancho de banda (Apple describe los dos modos en support.apple.com/102217). Los firmwares recientes de AirPods Pro 2 y AirPods 4 en macOS 15 o posterior mitigan el segundo problema en algunas apps, pero no el primero.

No bloquea el cambio (macOS no ofrece ninguna API para eso, e incluso las apps de pago como ToothFairy y SoundSource funcionan igual): escucha el evento y lo revierte en milisegundos.

Instalar

Una línea, sin dependencias (recomendado). Descarga el binario universal precompilado (Intel y Apple Silicon, generado por GitHub Actions a partir de este repositorio), verifica el SHA-256, lo instala en ~/.local/bin y carga un LaunchAgent. No necesitas Xcode ni Homebrew:

curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/install.sh | bash

Con Homebrew:

brew install marks-zyz/tap/nomicswitch
brew services start nomicswitch

Desde el código fuente (necesita Xcode Command Line Tools, xcode-select --install):

git clone https://github.com/marks-zyz/NoMicSwitch.git
cd NoMicSwitch
make install

En los tres casos arranca de inmediato y vuelve solo tras reiniciar el Mac.

Activar y desactivar

Acción Instalación de una línea o desde fuente Homebrew
Iniciar (y activar al iniciar sesión) el comando de instalación de arriba brew services start nomicswitch
Pausar (para usar otro micro un rato) launchctl bootout gui/$(id -u)/com.nomicswitch brew services stop nomicswitch
Iniciar de nuevo launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.nomicswitch.plist brew services start nomicswitch
Eliminar curl -fsSL https://raw.githubusercontent.com/marks-zyz/NoMicSwitch/main/uninstall.sh | bash brew services stop nomicswitch && brew uninstall nomicswitch
Ver el log tail -f ~/Library/Logs/nomicswitch.log tail -f $(brew --prefix)/var/log/nomicswitch.log

Opciones

nomicswitch --list lista los dispositivos de entrada con sus UIDs. --uid <UID> fija otro dispositivo (un micro USB, por ejemplo), --volume <0-100> elige el volumen bloqueado (100 por defecto) y --no-volume fija solo el dispositivo y no toca el volumen. El daemon busca los dispositivos por UID porque el UID es estable e igual en cualquier idioma, mientras que macOS traduce los nombres.

Cómo funciona

Un binario Swift con tres listeners de CoreAudio: cambio del dispositivo de entrada, cambio del volumen de entrada, y conexión o desconexión de dispositivos. Entre eventos el proceso duerme. En macOS 26 medí 0,0% de CPU en reposo, reacción por debajo de 100 ms y unos 14 MB de RAM. No hay dependencias en runtime. La primera versión era un script de shell que comprobaba cada dos segundos; funcionaba, pero cada comprobación creaba dos procesos, y el daemon lo sustituyó.

Requisitos y límites

Probado en macOS 26 (Tahoe) 26.6.2 en Apple Silicon. Debería funcionar desde macOS 12 en adelante, Intel o Apple Silicon. Mientras el daemon corre no puedes elegir otro micrófono a mano: pausa el servicio antes (un comando, tabla de arriba). No hay .pkg ni .app porque, sin un Apple Developer ID de pago, Gatekeeper bloquea las descargas sin firmar en macOS reciente; el binario está firmado ad-hoc, compilado en CI público, y el checksum se verifica antes de instalar. Alternativas: consulta la sección "Alternatives" más arriba.

Licencia

Unlicense: dominio público. Haz lo que quieras, sin atribución obligatoria. Los glifos del icono son de Phosphor Icons (MIT).

About

Never let AirPods hijack your Mac's microphone: pins the default input to the built-in mic and locks input volume. Event-driven CoreAudio daemon, 0% idle CPU.

Topics

Resources

Stars

11 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages