Also available as GitHub Repository: https://github.com/bulletinmybeard/py-macos-mic-control/
A Python script that automatically maintains consistent built-in microphone input volume during calls/meetings on macOS. It detects active audio sessions and adjusts the microphone level accordingly, ensuring the mic volume stays at an optimal level during calls.
- Automatic call/meeting detection through audio sampling
- Smart microphone volume management
- Continuous background monitoring
- Detailed logging of volume adjustments and call status
Install the required Python packages using pip:
pip install sounddevice numpy
#!/usr/bin/env python3
import subprocess
import time
import logging
from datetime import datetime
import sounddevice as sd
import numpy as np
# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(message)s',
handlers=[
logging.FileHandler('mic_control.log'),
logging.StreamHandler()
]
)
def set_mic_volume(volume):
"""Set the microphone input volume (0-100)."""
try:
cmd = f"osascript -e 'set volume input volume {volume}'"
subprocess.run(cmd, shell=True, check=True)
return True
except subprocess.CalledProcessError as e:
logging.error(f"Failed to set volume: {e}")
return False
def get_mic_volume():
"""Get current microphone input volume."""
try:
cmd = "osascript -e 'input volume of (get volume settings)'"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True)
return int(result.stdout.strip())
except subprocess.CalledProcessError as e:
logging.error(f"Failed to get volume: {e}")
return None
def is_audio_active(threshold=0.01, duration=1.0):
"""
Check if there's significant audio activity on the microphone.
Args:
threshold: RMS threshold for considering audio as active
duration: Duration in seconds to sample audio
"""
try:
# Get default input device
device_info = sd.query_devices(kind='input')
sample_rate = int(device_info['default_samplerate'])
# Record audio for specified duration
recording = sd.rec(
int(duration * sample_rate),
channels=1,
dtype='float32',
samplerate=sample_rate
)
sd.wait()
# Calculate RMS value
rms = np.sqrt(np.mean(recording**2))
return rms > threshold
except Exception as e:
logging.error(f"Error detecting audio: {e}")
return False
def detect_call_activity(audio_check_duration=5):
"""
Detect if we're likely in a call by monitoring audio activity over time.
Returns True if consistent audio activity is detected.
"""
audio_samples = []
# Take several samples over a period
for _ in range(audio_check_duration):
audio_samples.append(is_audio_active())
time.sleep(1)
# If we detect audio activity in at least 40% of samples,
# we're probably in a call
return sum(audio_samples) / len(audio_samples) >= 0.4
def main():
target_volume = 80
active_check_interval = 3 # seconds when audio activity detected
idle_check_interval = 15 # seconds when no audio activity
call_check_interval = 30 # seconds between full call detection checks
logging.info("Starting microphone level controller with audio detection")
last_call_check = 0
in_call = False
while True:
current_time = time.time()
# Periodically do a full call detection check
if current_time - last_call_check > call_check_interval:
in_call = detect_call_activity()
last_call_check = current_time
logging.info(f"Call status check: {'in call' if in_call else 'not in call'}")
if in_call:
current_volume = get_mic_volume()
if current_volume is not None and current_volume != target_volume:
logging.info(f"In call: Adjusting volume from {current_volume} to {target_volume}")
set_mic_volume(target_volume)
time.sleep(active_check_interval)
else:
time.sleep(idle_check_interval)
if __name__ == "__main__":
main()
2024-10-24 13:15:52,295 - Starting microphone level controller with audio detection
2024-10-24 13:16:03,483 - Call status check: in call
2024-10-24 13:16:07,231 - In call: Adjusting volume from 74 to 80
2024-10-24 13:16:13,948 - In call: Adjusting volume from 61 to 80
2024-10-24 13:16:20,709 - In call: Adjusting volume from 65 to 80
2024-10-24 13:16:35,104 - Call status check: not in call
2024-10-24 13:17:16,222 - Call status check: in call
2024-10-24 13:17:16,449 - In call: Adjusting volume from 38 to 80
2024-10-24 13:17:47,492 - Call status check: in call
2024-10-24 13:17:47,741 - In call: Adjusting volume from 67 to 80
2024-10-24 13:18:18,348 - Call status check: in call
2024-10-24 13:18:18,585 - In call: Adjusting volume from 39 to 80
2024-10-24 13:18:49,137 - Call status check: in call
2024-10-24 13:18:49,368 - In call: Adjusting volume from 65 to 80
2024-10-24 13:18:56,058 - In call: Adjusting volume from 54 to 80
2024-10-24 13:19:02,783 - In call: Adjusting volume from 52 to 80
2024-10-24 13:19:20,354 - Call status check: in call
2024-10-24 13:19:20,590 - In call: Adjusting volume from 42 to 80
2024-10-24 13:19:24,030 - In call: Adjusting volume from 67 to 80
2024-10-24 13:19:51,336 - Call status check: not in call
2024-10-24 13:20:32,464 - Call status check: not in call
2024-10-24 13:21:13,591 - Call status check: not in call
2024-10-24 13:21:54,726 - Call status check: not in call
2024-10-24 13:22:35,877 - Call status check: not in call
2024-10-24 13:23:17,015 - Call status check: not in call
2024-10-24 13:23:58,146 - Call status check: not in call
2024-10-24 13:24:39,274 - Call status check: not in call
2024-10-24 13:25:20,390 - Call status check: not in call
2024-10-24 13:26:01,510 - Call status check: in call
2024-10-24 13:26:01,756 - In call: Adjusting volume from 30 to 80
2024-10-24 13:26:05,225 - In call: Adjusting volume from 61 to 80
2024-10-24 13:26:08,693 - In call: Adjusting volume from 49 to 80
2024-10-24 13:26:18,638 - In call: Adjusting volume from 55 to 80
2024-10-24 13:26:32,981 - Call status check: in call
2024-10-24 13:26:33,217 - In call: Adjusting volume from 26 to 80
2024-10-24 13:26:36,666 - In call: Adjusting volume from 24 to 80
2024-10-24 13:26:40,113 - In call: Adjusting volume from 64 to 80
2024-10-24 13:26:43,556 - In call: Adjusting volume from 65 to 80
2024-10-24 13:26:50,238 - In call: Adjusting volume from 55 to 80
2024-10-24 13:27:04,636 - Call status check: in call
2024-10-24 13:27:04,887 - In call: Adjusting volume from 36 to 80
2024-10-24 13:27:08,326 - In call: Adjusting volume from 64 to 80