|
#!/usr/bin/env python3 |
|
# |
|
# Detects the built-in play/pause button on wired earbuds/headsets by |
|
# monitoring the microphone. |
|
# |
|
# The script outputs a line containing either "press" or "release" whenever |
|
# a press or release is detected. |
|
# |
|
# This script works by detecting a period of high (near maximum) sample values |
|
# followed quickly by a period of low (near minimum) sample values, or vice |
|
# versa. Low -> high corresponds to a press, high -> low corresponds to a |
|
# release. At least according to my testing, YMMV. |
|
|
|
|
|
CHUNK = 1024 |
|
HIST_SIZE = 64 |
|
|
|
import pyaudio; |
|
|
|
audio = pyaudio.PyAudio(); |
|
info = audio.get_default_input_device_info() |
|
stream = audio.open( |
|
input=True, |
|
rate=int(info['defaultSampleRate']), |
|
channels=1, |
|
format=pyaudio.paInt16) |
|
|
|
hi_lo_history = [0] * HIST_SIZE # -1 = low, 1 = high |
|
hist_idx = 0 |
|
|
|
try: |
|
while True: |
|
|
|
frames = stream.read(CHUNK); |
|
|
|
all_high = True # All samples exceed 32000 |
|
all_low = True # All samples exceed -32000 |
|
for frame_idx in range(CHUNK): |
|
sample = 0 |
|
if frames[2*frame_idx + 1]>>7 == 1: |
|
sample = -1 ^ 65535 |
|
|
|
sample |= frames[2*frame_idx] | (frames[2*frame_idx + 1] << 8) |
|
|
|
if sample < 32000: |
|
all_high = False |
|
if sample > -32000: |
|
all_low = False |
|
|
|
if -1 in hi_lo_history and all_high: |
|
print("press", flush=True) |
|
hi_lo_history = [0] * HIST_SIZE |
|
|
|
if 1 in hi_lo_history and all_low: |
|
print("release", flush=True) |
|
hi_lo_history = [0] * HIST_SIZE |
|
|
|
if all_high: |
|
hilo = 1 |
|
elif all_low: |
|
hilo = -1 |
|
else: |
|
hilo = 0 |
|
|
|
hi_lo_history[hist_idx] = hilo |
|
hist_idx = (hist_idx + 1) % HIST_SIZE; |
|
|
|
except KeyboardInterrupt: |
|
pass |