Информационный портал по безопасности » Операционные системы » Android » Хак для поддержки кнопок Android-гарнитуры под Windows

 

Хак для поддержки кнопок Android-гарнитуры под Windows

Автор: admin от 16-07-2018, 14:55, посмотрело: 257

Я почти каждый день слушаю музыку на смартфоне и использую кнопки управления на гарнитуре. Но мне всегда не нравилась одна вещь. Я прихожу домой, продолжая прослушивание, гарнитура подключается к домашнему ПК — и внезапно кнопки перестают работать.



Конечно, я погуглил решение этой проблемы. К сожалению, на Windows эта замечательная функция не слишком поддерживается. Пара минут поиска дали только мутные упоминания на Stack Overflow о звуковых картах и сообщения некоторых людей, что на их ноутбуках всё работает нормально.



Меня это не испугало — и я решил принять проблему как интересный вызов: можно ли создать какую-то программу для активации кнопок управления, если аппаратной поддержки для них вообще нет? Ответ — да, можно. И вот как сделать это за полчаса.

эту спецификацию из документации Android. Там есть диаграмма.



Хак для поддержки кнопок Android-гарнитуры под Windows


Как можно понять, при нажатии кнопки на гарнитуре замыкается цепь на одном из резисторов. Особого внимания заслуживает Кнопка A (Play/Pause/Hook) с сопротивлением 0 Ом, то есть замыканием микрофона. Если мы способны обнаружить короткое замыкание микрофона, то так сможем определить нажатие кнопки Play/Pause.



Проверка гипотезы



Прежде чем начать программировать, хотелось бы проверить разумность наших рассуждений в принципе. То есть того, что по сигналу с микрофона можно определить нажатие кнопки Play/Pause. К счастью, для этого достаточно просто записать звук на компьютере и посмотреть на результат. Я запустил Audacity, нажал во время записи кнопку Play/Pause — и получил такой сигнал.



Хак для поддержки кнопок Android-гарнитуры под Windows
Бинго



Как видим, нажатие кнопки очевидно отражается в форме сигнала: внезапное падение до –1 с последующим внезапным переходом к 1 и постепенным уменьшением до 0. Интуитивно по спецификации я бы предположил, что сигнал подскочит до 1 и останется там, пока кнопку не отпустить, но в реальности выглядит иначе. Тем не менее, такую картинку всё равно легко обнаружить, если захватить аудиопоток с микрофона.



Захват звука средствами Python



Зная способ, как обнаружить нажатие кнопок на гарнитуре, можно подумать о главной цели: как управлять плеером на рабочем столе с помощью кнопок гарнитуры.



Первый шаг — обнаружение нажатия кнопки. Для этого нужно захватить аудиопоток с микрофона и обнаружить отчётливую подпись, которую мы видели ранее. Для простоты реализуем решение на Python. После ещё одного небольшого поиска в интернете я нашёл пакет под названием sounddevice, который позволяет абстрагироваться от самой трудной части — реального аудиозахвата с микрофона.



Немножко кодирования даёт нам следующее:



import sounddevice as sd

SAMPLE_RATE = 1000 # Sample rate for our input stream
BLOCK_SIZE = 100 # Number of samples before we trigger a processing callback

class HeadsetButtonController:
    def process_frames(self, indata, frames, time, status):
        mean = sum([y for x in indata[:] for y in x])/len(indata[:])

        print(mean)

    def __init__(self):
        self.stream = sd.InputStream(
            samplerate=SAMPLE_RATE,
            blocksize=BLOCK_SIZE,
            channels=1,
            callback=self.process_frames
        )
        self.stream.start()

if __name__ == '__main__':
    controller = HeadsetButtonController()

    while True:
        pass


Такой код непрерывно выдаёт среднее значение каждой партии образцов. Мы установили частоту дискретизации 1000, что ужасно мало для обработки звука (обычно используется 44100), но нам в реальности не нужна большая точность. Размер блока определяет, сколько сэмплов в буфере инициируют обратный вызов. Опять же, мы установили очень низкие значения. Размер блока 100 и частота дискретизации 1000 фактически означает срабатывание 10 раз в секунду, где при каждом вызове обрабатывается только 100 сэмплов.



Определение нажатия кнопки: наверное, слишком простой способ



Теперь мы захватываем аудиопоток и можно реализовать реальный механизм для обнаружения нажатия кнопки. Напомним, что сигнал подскакивает до 1 всякий раз при нажатии. Это подсказывает самый простой способ обнаружения: если у N последовательных блоков значения сигнала выше 0,9, то есть нажатие.



Реализуем алгоритм в нашей функции:



import sounddevice as sd

SAMPLE_RATE = 1000 # Sample rate for our input stream
BLOCK_SIZE = 100 # Number of samples before we trigger a processing callback
PRESS_SECONDS = 0.2 # Number of seconds button should be held to register press
PRESS_SAMPLE_THRESHOLD = 0.9 # Signal amplitude to register as a button press

BLOCKS_TO_PRESS = (SAMPLE_RATE/BLOCK_SIZE) * PRESS_SECONDS

...

def process_frames(self, indata, frames, time, status):
    mean = sum([y for x in indata[:] for y in x])/len(indata[:])

    if mean < PRESS_SAMPLE_THRESHOLD:
        self.times_pressed += 1

        if self.times_pressed > BLOCKS_TO_PRESS and not self.is_held:
            # The button was pressed!
            self.is_held = True
    else:
        self.is_held = False
        self.times_pressed = 0

...


По сути мы запустили внутренний счётчик, сколько обработанных блоков отвечают пороговому требованию, которое просто установили на 0,9, предусмотрев неизбежное зашумление образца. Если блок не удовлетворяет требованию, счётчик сбрасывается — и мы начинаем заново. Переменная is_held отслеживает срабатывания, чтобы не регистрировать их многократно, если кнопка не отпускается.



Управление воспроизведением в Windows



Теперь осталось только заменить в реальном коде комментарий “The button was pressed!”, чтобы управлять воспроизведением звука в Windows. Снова погуглим, чтобы разобраться, как это сделать: оказывается, можно управлять воспроизведением, имитируя нажатие клавиш соответствующими кодами виртуальных клавиш.



Оказалось, что имитировать нажатия клавиш очень легко с помощью пакета pywin32, который является просто оболочкой Python для Windows API. Собрав всё вместе, мы можем создать следующую функцию:



import win32api
import win32con

VK_MEDIA_PLAY_PAUSE = 0xB3

def toggle_play():
    win32api.keybd_event(VK_MEDIA_PLAY_PAUSE, 0, 0, 0)


И у нас получилось! Обращение к функции toggle_play в том месте кода, где был комментарий “The button was pressed!”, позволяет управлять любым медиаплеером в Windows с помощью кнопок на гарнитуре Android.



Тесты показали, что код работает на удивление хорошо. Единственное различие между функциональностью на Android и Windows заключается в небольшой задержке при нажатии на кнопку, но с этим можно жить.



Хак для поддержки кнопок Android-гарнитуры под Windows
И вот что получилось



Скрипт Python состоит из 51 строки, которые активируют кнопки гарнитуры Android в Windows. Окончательный исходный код этого проекта лежит на Github.



Погодите, это ещё не всё!



После счастливого использования программы в течение нескольких часов я заметил серьёзную проблему:



Хак для поддержки кнопок Android-гарнитуры под Windows


Программа использует почти 30% CPU! Очевидно, это неприемлемо при длительной работе, что-то нужно делать. Посмотрев на код, я понял, что основной поток находится в состоянии ожидания в основном цикле, хотя там ничего не происходит. Наиболее логичное решение — просто усыпить поток навсегда: поскольку колбэк вызывается автоматически, нам всё равно не нужен цикл.



from time import sleep

if __name__ == '__main__':
    controller = HeadsetButtonController()

    while True:
        sleep(10)


Хак для поддержки кнопок Android-гарнитуры под Windows


Я также не хотел запускать скрипт Python вручную после каждого запуска компьютера. К счастью Python для Windows поставляется с полезной утилитой pythonw.exe, которая запускает процесс «демона» без подключенного терминала. Размещаем ярлык к этому процессу в каталоге MicrosoftWindowsStart MenuProgramsStartup, указав наш скрипт в качестве первого аргумента — тогда приложение автоматически запускается и незаметно работает в фоновом режиме.

Источник: Хабр / Интересные публикации

Категория: Android

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
  • bowtiesmilelaughingblushsmileyrelaxedsmirk
    heart_eyeskissing_heartkissing_closed_eyesflushedrelievedsatisfiedgrin
    winkstuck_out_tongue_winking_eyestuck_out_tongue_closed_eyesgrinningkissingstuck_out_tonguesleeping
    worriedfrowninganguishedopen_mouthgrimacingconfusedhushed
    expressionlessunamusedsweat_smilesweatdisappointed_relievedwearypensive
    disappointedconfoundedfearfulcold_sweatperseverecrysob
    joyastonishedscreamtired_faceangryragetriumph
    sleepyyummasksunglassesdizzy_faceimpsmiling_imp
    neutral_faceno_mouthinnocent