私はどのようにLinuxのハードウェアミュートボタンを作成しました


私は、パンデミックのために3月中旬から家で働いています.(私はこれを可能にする雇い主を持つのに十分な特権を与えられており、誰にでもできるだけリーンにスイッチを入れました)私は始めに苦労しました、しかし、突然、私が持っていたすべての会議は、ビデオ通話でした.初めに、私のカメラはLinuxでさえ動作しませんでした(私は以前にそれを使わなかったので、私は気にかけませんでした).私は以来ずっと家で私のセットアップを改善しました、そして、私は現在私の人生をこれまでに少しより便利にするために、より多くの仕掛けと道具を導入している点にいます.
このポストでは、私のセットアップへの最新の追加を説明します.

なぜですか。


いくつかの理由!まず第一に、それが楽しいから.話す前にボタンを押すと、このゲームショーを感じさせる.建物とテストも楽しい、私はティッカーと物事を作るのが大好きです.さらに:利便性.を目指して、目的と画面上のミュートボタンを押すだけではなく、単にハードウェアボタンを押すと、より便利な感じです.

いくつかの前提条件


私は次のことをインストールします.

  • PulseAudio(マイクを制御する)

  • bash ( pulseaudioコマンドの実行)

  • (デバイスドライバを書く)

  • システム(サービスとしてそれを可能にして、アップスタートまたは類似しているかもしれないトリックもする)
  • Linuxを実行している場合は、すでにインストールされています.

    ハードウェアの入手


    ハードウェアのミュートボタンでは、ハードウェアが必要です.数年前、私は夢の頬でいくつかの「大きな赤いボタン」を注文しました

    ( Amazon . comからのイメージ)
    (私はちょっと技術的な……)しかし、明らかに会社は存在しません.そして、それは少し彼らを命じます.しかし、使用されるものを見つけることができます.そして、それがUSBであるので、基本的にどんなボタンもします.ちょうどそれが確認可能であり、USBコネクタを持っていることを確認します.“ビッグレッドボタンUSB”のインターネットを検索し、オプションの無数を見つけることができます.
    準備ができたので、私は進んだ.

    CLIでマイクを切り替える


    私はpulseaudioで非常に味わっていませんでした.私がこのコマンドをコピーして、mictoggle.shと呼ばれるファイルにそれを置くところからの非常にLinuxの味わった友人pointed me to a post on AskUbuntu
    #!/bin/bash
    pacmd list-sources | \
            grep -oP 'index: \d+' | \
            awk '{ print $2 }' | \
            xargs -I{} pactl set-source-mute {} toggle
    
    すべてのオーディオソースをリストアップし、インデックスを抽出し、pactlをコマンドset-source-muteで呼び出して実行すると、これはマイクのミュート/ミュート状態を切り替えます.今、私はUSBボタンにそれをフックする必要がありました.

    デバイスドライバの書き込み


    JavaScriptで書くことができるすべては最終的にJavaScriptで書かれるので、なぜ、そのボタンのためにデバイス・ドライバーをノードを使用して書くか?
    私はlibrary that more or less did what I wantedを見つけました、しかし、それが後ろで州の機械を使用したので、若干の欠点がありました(1つのプレスだけは認められました、そして、私はそれを閉じて、次のプレスを認めるために、ボタンのカバーを開けなければなりませんでした)、ボタンが切り離されたとき、衝突して、スクリプトが走っている間、新しく接続されるとき、ボタンを認識しませんでした.それで、私はこれから若干のインスピレーションとUSBインターフェース取扱いを描きました.
    まず、usbというパッケージをインストールしました.
    npm i usb
    
    今、私は正しいインターフェイスに接続するためにボタンのvendoridとproductidを把握する必要がありました.通常、既存のLIBSとチュートリアルを通じて十分な掘削を使用すると、あなたの製品のためにそれらを見つけることができますが、接続時にUSBダンプはまた、必要な情報をもたらすことができます.夢の生意気なボタンのために、それらは0x1d34(ベンダー)と0x000d(製品)です.
    最初に、これらの2つのIDでボタンをフェッチする関数を書きました.
    const usb = require('usb')
    
    const getButton = (idVendor, idProduct) => {
      return usb.findByIds(idVendor, idProduct)
    }
    
    次に、ボタンのインターフェイスを取得し、必要に応じてカーネルドライバから切り離してこのプロセスを要求します.これはgetInterfaceという関数で行います.
    const getInterface = button => {
      button.open()
    
      const buttonInterface = button.interface(0)
    
      if (button.interfaces.length !== 1 || buttonInterface.endpoints.length !== 1) {
        // Maybe try to figure out which interface we care about?
        throw new Error('Expected a single USB interface, but found: ' + buttonInterface.endpoints.length)
      }
    
      if (buttonInterface.isKernelDriverActive()) {
        buttonInterface.detachKernelDriver()
      }
    
      buttonInterface.claim()
    
      return buttonInterface
    }
    
    正しく状態を取得するには、いくつかのマジックナンバーが必要でした.
    const bmRequestType = 0x21
    const bRequest = 0x9
    const wValue = 0x0200
    const wIndex = 0x0
    const transferBytes = 8
    
    それらのマジックナンバーはparameters for the underlying libusb_control_transfer callです.十分に便利だったので、以前に述べたライブラリには、USBダンプを使って既に考え出したものがありました.
    私は今、これらの関数を使ってボタンで何が起こっているかを聞くことができました.
    const poll = button => {
      const buttonInterface = getInterface(button)
    
      const stateDict = {
        21: 'close',
        22: 'press',
        23: 'open',
      }
    
      const endpointAddress = buttonInterface.endpoints[0].address
      const endpoint = buttonInterface.endpoint(endpointAddress)
    
      endpoint.timeout = 300
    
      return new Promise((resolve, reject) => {
        const buffer = new Buffer([0, 0, 0, 0, 0, 0, 0, 2])
        button.controlTransfer(bmRequestType, bRequest, wValue, wIndex, buffer, (error, data) => {
          if (error) {
            reject(error)
          }
    
          endpoint.transfer(transferBytes, (error, data) )> {
            if (error) {
              reject(error)
            }
    
            resolve(stateDict[data[0]])
          })
        })
      })
    }
    
    私はこのコードを使用して、まったく動作しているかどうかをテストします.
    setInterval(() => {
      const button = getButton(idVendor, idProduct)
    
      if (!button) {
        return
      }
    
      poll(button).then(state => {
        console.log(state)
      }).catch(() => {})
    }, 15)
    
    それで、15 msごとに、ボタンは、それからこのようにstdoutに印刷されるその状態を求められます(短縮版).
    node ./bigRedButton.js
    close
    close
    close
    open
    open
    open
    press
    press
    press
    press
    open
    open
    open
    # ...
    
    そして、問題があります:ボタンが押される限り、「プレス」状態は活発です.今、私はライブラリがステートマシンを使用している理由を理解しました:ボタンが押されるのではなく、ボタンが押されると、コールバックは実行されるべきです.これは私が働くことができました.私もコードをいくつかのコールバックを取る関数に詰め込みました.
    const listenToButton = (openCallback, pressCallback, closeCallback) => {
      var isPressed = false
    
      setInterval(() => {
        const button = getButton(idVendor, idProduct)
    
        if (!button) {
          return
        }
    
        poll(button).then(state => {
          if (isPressed && state !== 'press') {
            // Not pressing anymore
            isPressed = false
          }
    
          if (!isPressed && state === 'press') {
            isPressed = true
            // Executes the callback at the beginning of a button press
            pressCallback()
          }
    
          if (state === 'open') {
            openCallback()
          }
    
          if (state === 'close') {
            closeCallback()
          }
        }).catch(() => {})
      }, 15)
    }
    
    module.exports = listenToButton
    
    今、私は、マイクトグルスクリプトと一緒に使用するには、インポート可能なlibを持っていた.以来、それはボタンを請求するたびに、任意のエラーを飲み込む、ボタンを切断し、再接続すると、魅力のように動作します.
    今、私は一緒に作品を接着する必要がありました
    const bigRedButton = require('./bigRedButton')
    const { exec } = require('child_process')
    
    const openCallback = () => {}
    const pushCallback = () => {
      exec('XDG_RUNTIME_DIR=/run/user/1000 ./mictoggle.sh')
    }
    const closeCallback = () => {}
    
    bigRedButton(openCallback, pushCallback, closeCallback)
    
    ( XDG_RUNTIME_DIR ENV変数はone of two kinds of data echanges USB can do (the other being a a functional data exchange)に必要です.テスト中、これを考え出すまでは動作しませんでした)
    このスクリプトを実行すると、ハードウェアのミュートボタンに大きな赤いボタンを回す!

    非対話的シェルでPulseAudioコマンドを実行する サービスにする


    起動時にミュートボタンを作成するには、このコンテンツで/lib/systemd/systemの下にサービスファイルを作成しました.
    [Unit]
    Description=Hardware mute button
    After=multi-user.target
    
    [Service]
    Type=simple
    User=USER
    ExecStart=/home/USER/.nvm/versions/node/v14.15.0/bin/node /home/USER/projects/mutebutton/index.js
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    
    (単にExecStart経路を調整し、ユーザ名でUSERに置き換えます).
    それから、私はサービス(sudo systemctl start mutebutton)を始めました.そして、何度かボタンをためしました.そして、喜びでくすくすしました.そして、スタートアップ(sudo systemctl enable mutebutton)の上でサービスを可能にしました.

    テイクアウト思考


    私はこの小さい側プロジェクトの前にUSBとLibusbについてあまり知りませんでした、しかし、私はその過程でたくさん学びました.このことは、“インターネットを検索”と“ちょうどそれが働くまで物事をしようとする”いくつかの偉大な教師のために作ることを証明した.
    私はこのボタンをインストールして以来、私は今実際に多くのビデオ通話を楽しみにしているボタンを押すと、ビデオの呼び出しは、もっと楽しくなった.ゲームのショーと同じように!
    この記事を読んで楽しんでください!もしそうならば❤️ または🦄! 私はフリータイムでハイテク記事を書き、毎回コーヒーを飲みたいです.
    私の努力をサポートする場合は、 またはを考慮してください!
    buying me a coffee