اخترق لدعم أزرار سماعة Windows Android

في كل يوم تقريباً أستمع إلى الموسيقى على هاتفي الذكي وأستخدم أزرار التحكم في سماعة الرأس. لكنني لم يعجبني دائمًا شيء واحد. أعود إلى المنزل ، واستمر في الاستماع ، تتصل سماعة الرأس بجهاز الكمبيوتر في المنزل - وفجأة تتوقف الأزرار عن العمل.

بالطبع ، لقد بحثت في Google عن حل هذه المشكلة. لسوء الحظ ، في Windows لا يتم دعم هذه الميزة الرائعة للغاية. بضع دقائق من البحث أعطت فقط إشارات موحلة على Stack Overflow حول بطاقات الصوت والرسائل من بعض الأشخاص بأن كل شيء يعمل بشكل جيد على أجهزة الكمبيوتر المحمولة الخاصة بهم.

هذا لم يخيفني - وقررت قبول المشكلة كتحدي مثير للاهتمام: هل من الممكن إنشاء نوع من البرامج لتنشيط أزرار التحكم ، إذا لم يكن هناك دعم أجهزة لهم على الإطلاق؟ الجواب نعم ، يمكنك ذلك. وإليك كيفية القيام بذلك في غضون نصف ساعة.

كيف تعمل أزرار سماعة Android


أول شيء يجب فهمه هو كيفية عمل أزرار سماعة الرأس. وجد بحث سريع على الإنترنت هذه المواصفات من وثائق Android. هناك مخطط هناك.



كما يمكنك أن تفهم ، عند الضغط على زر في سماعة الرأس ، يتم إغلاق الدائرة الموجودة على أحد المقاومات. من الجدير بالذكر أن الزر A (تشغيل / إيقاف مؤقت / خطاف) بمقاومة 0 أوم ، أي دائرة قصر ميكروفون. إذا تمكنا من اكتشاف دائرة قصر في الميكروفون ، فيمكننا تحديد الضغط على زر التشغيل / الإيقاف المؤقت.

اختبار الفرضيات


قبل البدء في البرمجة ، أود التحقق من معقولية منطقنا من حيث المبدأ. أي أنه يمكن تحديد الإشارة من الميكروفون بالضغط على زر التشغيل / الإيقاف المؤقت. لحسن الحظ ، يكفي هذا لتسجيل الصوت على الكمبيوتر والنظر إلى النتيجة. لقد أطلقت Audacity ، واضغطت على زر التشغيل / الإيقاف المؤقت أثناء التسجيل - وتلقيت مثل هذه الإشارة.


البنغو

كما ترى ، من الواضح أن الضغط على الزر ينعكس في الشكل الموجي: انخفاض مفاجئ إلى −1 متبوعًا بانتقال مفاجئ إلى 1 وانخفاض تدريجي إلى 0. في الواقع تبدو مختلفة. ومع ذلك ، لا يزال من السهل اكتشاف مثل هذه الصورة إذا التقطت دفق الصوت من الميكروفون.

التقاط الصوت مع 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 المتغيرات is_held حتى لا يتم تسجيلها بشكل متكرر إذا لم يتم تحرير الزر.

التحكم في تشغيل Windows


الآن يبقى فقط لتحل محل التعليق "تم الضغط على الزر!" في رمز حقيقي للتحكم في تشغيل الصوت في Windows. جوجل مرة أخرى لمعرفة كيفية القيام بذلك: اتضح أنه يمكنك التحكم في التشغيل عن طريق محاكاة ضربات المفاتيح مع رموز المفاتيح الافتراضية المقابلة.

اتضح أن محاكاة ضربات المفاتيح أمر سهل للغاية مع حزمة pywin32 ، وهي مجرد قذيفة Python لواجهة برمجة تطبيقات Windows. من خلال وضع كل ذلك معًا ، يمكننا إنشاء الوظيفة التالية:

 import win32api import win32con VK_MEDIA_PLAY_PAUSE = 0xB3 def toggle_play(): win32api.keybd_event(VK_MEDIA_PLAY_PAUSE, 0, 0, 0) 

وفعلنا ذلك! toggle_play وظيفة toggle_play في مكان الرمز حيث التعليق "تم الضغط على الزر!" ، يسمح لك بالتحكم في أي مشغل وسائط في Windows باستخدام الأزرار الموجودة في سماعة رأس Android.

أظهرت الاختبارات أن الشفرة تعمل بشكل جيد بشكل مدهش. الفرق الوحيد بين الوظائف على Android و Windows هو تأخير بسيط عند الضغط على الزر ، ولكن يمكنك التعايش معه.


وهكذا ما حدث

يتكون نص Python النصي من 51 سطرًا تعمل على تنشيط الأزرار الموجودة على سماعة رأس Android على Windows. كود المصدر النهائي لهذا المشروع موجود على جيثب .

انتظر ، هذا ليس كل شيء!


بعد استخدام البرنامج لحسن الحظ لعدة ساعات ، لاحظت مشكلة خطيرة:



يستخدم البرنامج ما يقرب من 30 ٪ من وحدة المعالجة المركزية! من الواضح أن هذا أمر غير مقبول أثناء العمل الطويل ، ويجب القيام بشيء ما. بالنظر إلى الشفرة ، أدركت أن الخيط الرئيسي في حالة الخمول في الحلقة الرئيسية ، على الرغم من عدم حدوث أي شيء هناك. الحل الأكثر منطقية هو ببساطة موت الخيط إلى الأبد: نظرًا لأن الاستدعاء يُسمى تلقائيًا ، ما زلنا لا نحتاج إلى حلقة.

 from time import sleep if __name__ == '__main__': controller = HeadsetButtonController() while True: sleep(10) 



كما أنني لم أرغب في تشغيل نص Python النصي يدويًا بعد كل بدء تشغيل للكمبيوتر. لحسن الحظ ، يأتي Python for Windows مع أداة مساعدة مفيدة تسمى pythonw.exe تبدأ عملية الخفي دون توصيل محطة طرفية. نضع اختصارًا لهذه العملية في دليل Microsoft \ Windows \ Start Menu \ Programs \ Startup ، ونحدد نصنا البرمجي كوسيطة أولى - ثم يبدأ التطبيق تلقائيًا ويعمل بهدوء في الخلفية.

Source: https://habr.com/ru/post/ar417303/


All Articles