|||||||||||||||||||||

なんぶ電子

- 更新: 

ラズベリーパイをWiFi操作可能な音楽プレーヤーに

音楽プレーヤーコントロール画面

最近では Free Music Archiveや、AudionautiXなどといったサイトでロイヤリティフリーな音源も増えてきました。中には商用利用まで可能なものもあります。たくさんの音楽を管理には音楽サーバーがあると便利です。

Volumio2などのオープンソースで利用できるアプリもありますが、今回はラズベリーパイを使って自作してみようと思います。操作する側で音楽を再生するのではなく、ラズベリーパイをスピーカーにつなげておいて、スマホからWi-Fi経由で曲を選択して再生したり、曲をアップロードしたりできるようにしたいと考えています。

この記事ではラズベリーパイは3B、OSはRaspbian Lite(CUI)を使います。raspberry piにはDAC(Digital Analog Converter)ボードが別売でありますが、ここではオンボードのイヤホンジャックを使います。

使わなくなったPC(目安としてWindowsXP以降の世代のもので、audio出力ジャックがあるもの)があれば、同系列のフリーOS Debianをインストールすることで同じことができます。その場合は、主にラズベリーパイでのコマンドを紹介していきますのでsudoと記述してあるとこころは、suで管理者権限になって実行してください。

サウンドカードの確認

念のためマシンがサウンドカードを認識しているか確認します。おそらくボードについているので、存在の有無の確認はlspciから確認します。

次に表示する例はとあるDebianPCの例です

Audio deviceの行が存在するので認識はされているというのが確認できます。

$ sudo lspci
00:00.0 Host bridge: Intel Corporation 4 Series Chipset DRAM Controller (rev 03)
00:02.0 VGA compatible controller: Intel Corporation 4 Series Chipset Integrated Graphics Controller (rev 03)
00:1b.0 Audio device: Intel Corporation 82801JI (ICH10 Family) HD Audio Controller
00:1c.0 PCI bridge: Intel Corporation 82801JI (ICH10 Family) PCI Express Root Port 1

MPD(Music Player Daemon)

まずは再生用のアプリをインストールします。ここではMPD(Music Player Daemon)を使います。これは音楽プレーヤーですが、操作するためのクライアントは別に用意する必要があります。

$ sudo apt update
...
$ sudo install mpd mpc
...

設定ファイルは/etc/mpd.confにあります。

この中のmusic directoryの行に、音楽ファイルを保存するディレクトリ(フォルダ)を指定します。

MPDの設定はそれだけで終わりです。サービスを再起動します。

$ sudo systemctl restart mpd
...

MPC

MPCはMPDを操作するクライアントのひとつです。MPDにはフリーで使えるGUIのクライアントもありますが、今回はWebブラウザ経由でコマンドを実行する前提なので、コマンドラインで操作できるMPCを利用します。

MPCの操作においては特に設定ファイルを記述する必要はありません。基本的な使い方は次のようにします。コマンドはすべてユーザー権限で動かせます。

  • MPDのデータベースの更新

    mpc updateとすることで、ファイルは先にMPDでmusic directoryに指定したディレクトリ以下の音楽ファイルがすべてデータベースに取り込まれます。この時、音楽ファイルの権限がMDPからアクセスできるようになっていなければいけません。

    $ mpc update
    Updating DB(#1)
  • プレイリストの作成

    プレイリストはMPDデータベース内から選ぶ必要があります。先にMPDでmusic directoryに指定したディレクトリからの相対パスで指定します。

    直接ファイルを指定することもできますが、/で終わらせることでディレクトリ単位で指定できます。そのため/とだけ指定すれば、ディレクトリ内のすべてのファイルをリストに加えます。

    同じファイルを2回addすればその分繰り返されます。

    削除したい場合はdelを使えますが、こちらは曲順を指定して削除します。数値は1から始まりますが、再生中は0を指定することで再生中の曲を指定することもできます。プレイリストをすべて消す場合はclearを使います。

    $ mpc add /
    $ mpc add subdir/sample.mp3
    ...
    $ mpc del 1
    $ mpc clear
  • 再生

    リストができたらplayで再生できるようになります。

    正常に再生が始まると1曲目のタイトルと、トラック番号、再生時間等の情報が表示されます。volume:100%...から始まる行だけが表示されている場合はおそらくプレイリストが作成できていません。プレイリストの確認は、「mpc playlist」で可能です。

    $ mpc play
  • シーク

    シークはseekコマンドで実行できます。HH:MM:SSの形式で時間を指定する方法と、0-100の間で%を指定する方法があります。ともに先頭に+-を加えることで、相対的な指定もできます。

    $ mpc seek 50%
    ...
    $ mpc seek -00:00:30
    ...
  • ボリュームの調整

    volumeでボリュームの調整ができます。0-100までで指定します。こちらもseek時と同じように値を直接指定する方法と、+-をつけて相対的に指定する方法があります。

    $ mpc volume 50
    ...
  • 移動

    リストの移動はprevで戻り、nextで進みます。

    $ mpc next
    ...
    $ mpc prev
    ...
  • 停止

    停止はstopです

    $ mpc stop
    ...
  • リピート

    repeatを実行すると、リピート機能のon/offが切り替わります。repeatの後に、「on」または「off」という形で値の指定も可能です。

    リピートのほかに音楽プレーヤーで同じみの機能として同じ曲の繰り返しであるsingleや、ランダム再生のrandomも存在します。

    $ mpc repeat on
    ...
    $ mpc single
    ...
    $ mpc random off
    ...
  • consume

    リピート等と同じようにトグル属性を持った機能としてconsumeがあります。これは再生の終わった曲をリストから削除するものです。

  • ステータス

    statusを実行すると、現在の曲名とリストのインデックス、再生時間が表示されます。

他のコマンドに関しては「man mpc」を確認してください。

AlsaMixerとPulseAudio

ラズベリーパイでもDebianでもインストールしただけでは再生はできても音は出ないと思います。

ラズベリーパイではイヤホンジャックから音をだす設定が必要になります。「raspi-config」コマンドから「1:System Options」→「S2 Audio」→「0 Headphones」を指定します。ラズベリーパイのバージョンが違うとメニューの構成も変わることがありますので、存在しなかった場合はAutio設定を探してください。

$ sudo raspi-config
ラズベリーパイでイヤホンジャックから音を出す設定に

Debianではアプリのインストールが必要です。

ラズベリーパイやDebianが所属するLinuxの世界では、サウンド系の管理はalsa-utils(AlsaMixer)とPulseAudioで管理されるのが一般的なようです。 みつきんのメモ:「ざっくりとALSAとPulseAudioの関係」によると、AlsaMixerはサウンド系のデバイスドライバとやりとりをします。PulseAudioは各アプリケーションからの音声を取りまとめてAlsaMixerに送るるサウンドサーバです。

筆者のDebian環境(buster CUI)では、どちらもインストールされていませんでした。そのため先の「lspci」コマンドでaudioコントローラーは見つかる状態でもイヤホンジャックから音は流れませんでした。

ふたつをインストールするには次のようにします。AlsaMixerはalsa-utilsの中に含まれています。ラズベリーパイの場合はAlsaMixerがデフォルトでインストールされているのでこれらの作業は不要だと思います。

$ sudo apt update
...
$ sudo apt install alsa-utils pulseaudio
...

ここからはラズベリーパイの場合も含みます。ボリューム調整の仕方です。これはAlsaMixerで行います。

$ alsamixer

AlsaMixerを実行すると、ミキサーのGUIが表示されます。ラズベリーパイの場合は「Headphone」、Debainの場合は「Master」の項目にカーソルがある状態で、キーボードの↑↓キーを押すとボリュームを上下させることができます。また項目名の上が「MM」となっているとミュートを意味します。その場合はMキーを押して「00」に変えてください。

alsamixer

ボリューム調整をしても音が大きくならなかったり、他音声に関する問題が発生する場合は、Debian系の記事ではありませんがarchlinux:「PulseAudio/トラブルシューティング」が詳しいので参考にしてみてください。

ちなみにラズベリーパイではデフォルトでPulseAudioはインストールされていません。先の参考ページにあった通り、アプリから直接AlsaMixerを制御することもできるのでPulseAudioは必須ではないようです。

構成

ここまでで音楽プレーヤーができました。あとはサーバーに仕立てていきたいと思いますが、構想が2通りあります。ひとつは音楽サーバを無線LANの親機として設定し操作はその子機から行う方法。もうひとつは音楽サーバを既存のLANの中に入れる方法です。

ここでは無線LANの親機として設定する方法を紹介します。手間がかかりますがケーブルや電波など既存のネットワークの有無に左右されません。無線LANアダプタのないDebian環境で同じことをしたい場合はDebianでAPを構築を参考にUSB Wi-Fiアンテナを設定してください。有線LANや無線子機として音楽サーバを既存のLANの中に入れる場合は次の項目は読み飛ばしてください。

hostapdとdhcp

hostapdは無線親機のアプリです。これはIPアドレスを配布するDHCPが含まれていませんので、あわせてそれもインストールします。さらにラズベリーパイの場合は、DHCPの稼働を安定化させるためにネットワークの設定も変更します。詳しくは「ラズベリーパイでゲストWi-Fiを構築」の記事を確認してください。ここでは設定方法だけを紹介していきます。

まず必要なアプリをインストールします。hostapdが無線親機用のアプリです。isc-dhcp-serverは接続してきた端末にIPアドレスを配布するアプリです。

$ sudo apt update
...
$ sudo apt install hostapd isc-dhcp-server
...

hostapdの設定ファイルを/etc/hostapdディレクトリにhostapd.confという名前で作成します。ここでは無線の種類をg,チャンネルは1、wpa_passphraseはpassword、ssidはmusic-serverに、していますが。好みにより変更してください。

/etc/hostapd/hostapd.conf

interface=wlan0
driver=nl80211
ssid=music-server
auth_algs=1
wpa=2
wpa_passphrase=password
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP TKIP
rsn_pairwise=CCMP
country_code=JP
ieee80211d=1
ieee80211h=0
hw_mode=g
channel=1

dhcpサーバーの設定です。/etc/dafault/isc-dhcp-serverを次のように修正します。

/etc/default/isc-dhcp-server

INTERFACESv4="wlan0"
INTERFACESv6=""

/etc/dhcp/dhcp.confを次のようにします。外側のネットーワークとは通信しない前提なので、ドメインやDNSは適当でいいと思います。配布アドレスはサーバーとなるマシンのIPと重ならいない値で同じネットワークになるようにします。

subnetでネットワークを設定し、rangeで配布範囲を設定します。

/etc/dhcp/dhcp.conf

...
#ドメイン
option domain-name "my.domain";
#DNS
option domain-name-servers 8.8.8.8;
...
#コメントアウトを外します
authoritative;
...
#配布アドレスの設定
subnet 192.168.2.0 netmask 255.255.255.0 {
  range 192.168.2.10 192.168.2.30;
}

ラズベリーパイの場合は、isc-dhcp-serverを自動起動するために次のようにしてdhcpdの機能を止めます。

$sudo systemctl disable dhcpcd
$sudo systemctl enable networking

dhcpdの代わりに固定IPアドレスを設定します。eth0(有線)側はつかいませんが、後からメンテナンスする際の利便性を考えて普段使うネットワークにしておくといいかもしれません。

/etc/network/interfaces

# コメントアウト
# source-directory /etc/network/interfaces.d

allow-hotplug eth0
iface eth0 inet static
address 192.168.1.251/24
gateway 192.168.1.1
dns-nameservers 8.8.8.8

allow-hotplug wlan0
iface wlan0 inet static
address 192.168.2.1/24

UIの作成

UIはApacheとPHPを使ったウェブアプリにします。画面は次のように3分割で構成しました。アップロード画面、管理画面、再生画面。加えて、それとmpcのコマンドを実行するバックボーン用と、共通設定を記憶しておくphpファイルの2つを作成しました。

わかりやすくするために、書くページのコードにはCSSをつけていません。

まずPHPの共通設定の部分です。php.iniに次の設定をします。

  • post_max_size

    POSTするファイルの最大値です。筆者は1Gを設定しました。

  • upload_max_filesize

    ファイル単位での最大サイズです。100Mを設定しました。

  • max_file_uploads

    同時アップロードのファイル数の上限です。100を設定しました。

  • max_execution_time

    実行時間のタイムアウトです。HTML経由で処理するのでデフォルトだと30秒でタイムアウトしてしまいます。ここでは0を指定して無制限にしました。

    phpファイル内でset_time_limit(0);やini_set("max_execution_time","0");を指定することも可能です。

また、音楽保存ディレクトリのパスを次のようにphpファイルに定数として規定しました。このディレクトリはMPDの設定ファイルに音楽ファイルのディレクトリとして記述します。またMPDからアクセス可能にしておかなければいけません。

mcommon.php

define('UPLOAD_DIR','./uploads');

アップロード画面は次のようになります。

mupload.php

<?php
  //アップロード
  
  //共通設定読み込み
  require_once('./mcommon.php');
  
  //メッセージ初期化
  $strMsgs=array();

  //ファイルの確認
  if(isset($_FILES["uploadfiles"])){
    //保存先
    $strDir="";

    if (isset($_POST['dir'])) {
      $strDir = basename($_POST['dir'])."/";
      mkdir(UPLOAD_DIR."/".$strDir);
    }
    
    for ($i=0; $i < count($_FILES["uploadfiles"]["name"]); $i++){
      if(is_uploaded_file($_FILES["uploadfiles"]["tmp_name"][$i])){
        if (file_exists(UPLOAD_DIR."/". $_FILES["uploadfiles"]["name"][$i])) {
          $strMsgs[]="すでに同名のファイルが存在します:".$_FILES["uploadfiles"]["name"][$i];
        } else {
          if (move_uploaded_file($_FILES["uploadfiles"]["tmp_name"][$i], UPLOAD_DIR."/".$strDir.$_FILES["uploadfiles"]["name"][$i])) {
            $strMsgs[]="アップロードしました:".$_FILES["uploadfiles"]["name"][$i];
          } else {
            $strMsgs[]="ファイル移動に失敗しました:".$_FILES["uploadfiles"]["name"][$i];
          }
        }
      }
    }
  }
?>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
    <title>アップロード</title>
</head>
<body>
  <form aciton="./" method="post" enctype="multipart/form-data">
    <p>アップロード</p>
    <div>
      保存フォルダ<input type="text" name="dir" list="dirs" autocomplete="off" />
      <?php
        $dirs=array();
        echo '<datalist id="dirs">';
        $files = scandir(UPLOAD_DIR);

        foreach($files as $f) {
          if (!in_array($f,array(".",".."),true)) {
            if (is_dir(UPLOAD_DIR.DIRECTORY_SEPARATOR.$f)) {
              echo '<option value="'.$f.'">';
            }
          }
        }
        echo '</datalist>';
      ?>
    </div>
    <div>
      <p><input type="file" multiple name="uploadfiles[]" /></p>
      <p><input type="submit" value=" 送  信 " /></p>
    </div>
    <div>
      <ul>
        <?php 
          for($i = 0; $i < count($strMsgs); $i++) {
            echo "<li>".$strMsgs[$i]."</li>"; 
          }
        ?>
      </ul>
    </div>
  </form>
</body></html>

音楽管理画面では、フォルダ間の移動や音楽の削除を管理します。加えてダウンロードやコントローラー側での再生機能も保持しています。

mmanage.php

<?php
  //音楽管理  
  //共通設定読み込み
  require_once('./mcommon.php');

  //メッセージ初期化
  $strMsgs=array();

  $strTargetDir="";

  if(isset($_POST['mode'])) {
    $strDir = basename($_POST['dir']);
    $mFiles = array();
    //選択していないとセットされていない
    if (isset($_POST['mfile'])) {
      for ($i = 0; $i < count($_POST['mfile']); $i++) {
        $mFiles[]=basename($_POST['mfile'][$i]);
      }
    }

    if (is_dir(UPLOAD_DIR."/".$strDir) == false) {
      $strMsgs[]="指定したフォルダが存在しません";
    } else {
      $childFiles = scandir(UPLOAD_DIR."/".$strDir);
      $strNewDir = $_POST['newdir'];
      
      switch($_POST['mode']) {
      case 'deldir':
        if(count($childFiles)===2) {
          if(rmdir(UPLOAD_DIR."/".$strDir)) {
            $strMsgs[]="フォルダを削除しました";
          } else {
            $strMsgs[]="フォルダ削除に失敗しました";
          }
        } else {
          $strMsgs[]="フォルダ内にファイルがあるので削除できません";
        }
        break;
      case 'del':
        if (count($mFiles)===0) {
          $strMsgs[]="削除対象ファイルが指定されていません";
        } else {
          for($i=0; $i<count($mFiles);$i++) {
            if(unlink(UPLOAD_DIR."/".$strDir."/".$mFiles[$i])) {
              $strMsgs[]="削除しました:".$mFiles[$i];
            } else {
              $strMsgs[]="削除に失敗しました:".$mFiles[$i];
            }
          }
        }
        break;
      case 'move':
        if (count($mFiles)===0) {
          $strMsgs[]="移動対象ファイルが指定されていません";
        } else {
          $blnSkip = false;
          if (is_dir(UPLOAD_DIR."/".$strNewDir)===false) {
            if (mkdir(UPLOAD_DIR."/".$strNewDir)===false) {
              $strMsgs[]="移動先フォルダ作成に失敗しました";
              $blnSkip = true;
            }
          }
          if ($blnSkip === false){
            for($i=0; $i<count($mFiles);$i++) {
              if (is_file(UPLOAD_DIR."/".$strDir."/".$mFiles[$i])) {
                if(rename(UPLOAD_DIR."/".$strDir."/".$mFiles[$i],UPLOAD_DIR."/".$strNewDir."/".$mFiles[$i])) {
                  $strMsgs[]="移動しました:".$mFiles[$i];
                } else {
                  $strMsgs[]="移動に失敗しました:".$mFiles[$i];
                }
              } else {
                $strMsgs[]="ファイルが存在しません:".$mFiles[$i];
              }
            }
          }
        }

        break;
      default:
        $strMsgs[]="モードが不正です";
      }
    }
  }

  if(isset($_GET['dir'])) {
    if(is_dir(UPLOAD_DIR.DIRECTORY_SEPARATOR.basename($_GET['dir']))){
      $strTargetDir=basename($_GET['dir']);
    }
  }
?>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
  <title>ファイル・フォルダ管理</title>
</head>
<body>
  <form name="form1" aciton="./" method="post" enctype="multipart/form-data">
    <p>ファイル・フォルダ管理</p>
  <div>
      <p>フォルダ:<select name="dir" onchange="changeDir(this.value);"></p>
      <?php
        $files = scandir(UPLOAD_DIR);
        $dirs[]=array();
        echo '<div><option value=""></option>';
        foreach($files as $f) {
          if (!in_array($f,array(".",".."),true)) {
            if (is_dir(UPLOAD_DIR."/".$f)) {
               $dirs[]=$f;
               if ($f===$strTargetDir) {
                 echo '<option value="'.$f.'" selected>'.$f.'</option>';
               } else {
                 echo '<option value="'.$f.'">'.$f.'</option>';
               }
            }
          }
        }
        echo '</select></div><div>';
        echo 'ファイル<br><select id="mfile" name="mfile[]" size="10" onchange="changeFile();"multiple>';
        $files = scandir(UPLOAD_DIR.DIRECTORY_SEPARATOR.$strTargetDir);
        foreach($files as $f) {
          if (!in_array($f,array(".",".."),true)) {
            if (is_file(UPLOAD_DIR.DIRECTORY_SEPARATOR.$strTargetDir.DIRECTORY_SEPARATOR.$f)) {
               echo '<option value="'.$f.'">'.$f.'</option>';
            }
          }
        }
        echo '</select></div>';
      ?>
    </div>
    <input type="hidden" name="mode" id="mode" value="" />
    <div>
      <p><input type="button" value="  ダウンロード  " onclick="download();"/></p>
      <p><input type="button" value=" 選択ファイル削除 " onclick="toSubmit('del','選択ファイルを削除します。よろしいですか?');"/></p>
      <p><input type="button" value="  フォルダ移動  " onclick="toSubmit('move','選択ファイルを移動します。よろしいですか?');"/>
        <?php
          echo '<input type="text" name="newdir" list="dirs" autocomplete="off" />';
          echo '<datalist id="dirs">';
      
          foreach($dirs as $d) {
            if (is_dir(UPLOAD_DIR."/".$d)) {
              echo '<option value="'.$d.'">';
            }
          }
          echo '</datalist>';
        ?>
      </p>
      <p><input type="button" value=" 表示フォルダ削除 " onclick="toSubmit('deldir','表示中のフォルダを削除します。よろしいですか?');"/><br>中身が空でないと削除できません</p>
    </div>  
    <div>
      <ul>
        <?php 
          for($i = 0; $i < count($strMsgs); $i++) {
            echo "<li>".$strMsgs[$i]."</li>"; 
          }
        ?>
      </ul>
    </div>
    <div>
      ローカルで再生<br>
          <audio src="" controls>
    </div>  
  </form>
  <script>
    let strPlay="";
    <?php
       echo 'let strMusicdir="'.UPLOAD_DIR.'/'.$strTargetDir.'";';
    ?>
    function changeDir(strDir) {
      let strUrl = location.href;
      let intFind =strUrl.indexOf('?');
      
      if (0 < intFind) {
        strUrl = strUrl.substr(0,intFind);
      }
      location.href=strUrl+"?dir="+strDir;
    }
    function changeFile() {
      let elSel = document.getElementById('mfile');  
      let strMFiles=[];
      let mp = document.getElementsByTagName('audio')[0];

      for (let i = 0; i < elSel.length; i++) {
        if (elSel[i].selected) {
          strMFiles.push(elSel[i].value);
          break;
        }
      }
      if (strMFiles.length==0) {
        mp.src="";
      }
      mp.src=location.protocol+"//"+location.host+"/"+strMusicdir+"/"+strMFiles[0];
    }
    function download() {
      let elSel = document.getElementById('mfile');
      
      for (let i = 0; i < elSel.length; i++) {
        if (elSel[i].selected) {
          let elA = document.createElement('a');
          elA.href=strMusicdir+"/"+elSel[i].value;
          elA.download=elSel[i].value;
          elA.click();
        }
      }
    }
    function toSubmit(strMode,strMsg) {
      if (confirm(strMsg)) {
        document.getElementById('mode').value=String(strMode);
        document.form1.submit();
      } 
    }
  </script>
</body></html>

次に再生画面です。主にmpcコマンドを実行するphpファイルにデータを送信します。曲名やボリューム、リピート状況などはmpcのstatusコマンドで帰ってくる値を、XMLHttpRequestで定期的に取得してそのまま表示しています。

mplayer.php


<?php
  //共通設定
  require_once('./mcommon.php');
?>

<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>再生画面</title>
  </head>
<body>
<div>
<p>再生画面</p>
<div>
  <div>
    <p>再生リスト作成</p>
    <form name="form1" aciton="./mcommand.php" method="post" enctype="multipart/form-data">
      <p>再生フォルダ:<select name="dir"></p>
      <?php
        $files = scandir(UPLOAD_DIR);
        $dirs[]=array();
        echo '<div><option value="">全曲</option>';
        foreach($files as $f) {
          if (!in_array($f,array(".",".."),true)) {
            if (is_dir(UPLOAD_DIR."/".$f)) {
              $dirs[]=$f;
              if ($f===$strTargetDir) {
                echo '<option value="'.$f.'" selected>'.$f.'</option>';
              } else {
                echo '<option value="'.$f.'">'.$f.'</option>';
              }
            }
          }
        }
        echo '</select></div>';
      ?>
      <button onclick="">再生リスト作成</button>
    </form>
  </div>
  &nbsp;<br>
  <div>
    <button onclick="getCom('play');">再 生</button>
    <button onclick="getCom('stop');">停 止</button>
    <button onclick="getCom('next');">次 曲</button>
    <button onclick="getCom('prev');">前 曲</button>
  </div>
  &nbsp;<br>
  <div>
    <button onclick="getCom('volu');">ボリューム▼</button>
    <button onclick="getCom('vold');">ボリューム▲</button>
    <button onclick="getCom('mute');">ミュート</button>
  </div>
  &nbsp;<br>
  <div>
    <button onclick="getCom('repeat');">リピート</button>
    <button onclick="getCom('single');">1曲</button>
    <button onclick="getCom('random');">ランダム</button>
    <button onclick="reloadDisplay();">ステータス</button>
  </div>
  &nbsp;<br>
  <div id="display">

  </div>
  &nbsp;<br>
  <select id="interval">
    <option value="0">停止</option>
    <option value="200">200ms</option>
    <option value="500">500ms</option>
    <option value="1000">1000ms</option>
    <option value="3000">3000ms</option>
    <option value="5000">5000ms</option>
  </select>
  <button onclick="setRefreshRate();">更新頻度設定</button>
</div>
<script>
  //ステータス画面
  let divDisplay = document.getElementById('display');
  //ステータス画面更新頻度
  let selInterval = document.getElementById('interval');

  let issueIndex = 0;
  let displayIndex = 0;
  let displayInterval =500;
  let interval;
  const strTargetUrl="mcommand.php";

  const getCom = (strCommand) => {
    //コマンド送信
    let req = new XMLHttpRequest();
    req.open('GET',strTargetUrl+"?com="+strCommand,true);
    req.send(null);
  }

  const reloadDisplay = () => {
    //画面更新
    let req = new XMLHttpRequest();
  
    const thisIndex = ++issueIndex;
  
    req.onreadystatechange=() => {
      if (req.readyState == 4 && req.status == 200) {
        if (displayIndex < thisIndex) {
          displayIndex=thisIndex;
          divDisplay.innerHTML=req.responseText;
        }
      }
    }
    req.open('GET',strTargetUrl,true);
    req.send(null);
  }

  const startDisplay=()=> {
    //ステータス更新開始
    interval = setInterval(reloadDisplay,displayInterval);
  }

  const stopDisplay=()=> {
    //ステータス更新停止
    clearInterval(interval);
  }
  
  const setRefreshRate=()=> {
    //ステータス更新頻度設定
    stopDisplay();
    displayInterval = selInterval.value;
    if (0 < displayInterval){
      startDisplay();
    } else {
      divDisplay.innerHTML="";
    }
  }
  startDisplay();
</script>
</body>
</html>

最後にコマンド管理用のファイルです。基本的にshell_execでmpcコマンドを実行しているだけです。

mcommand.php


<?php
  //リスト再作成
  if (isset($_POST['dir'])) {
    shell_exec("mpc stop");
    shell_exec("mpc clear");
    shell_exec("mpc update");
    if($_POST['dir']==="") {
      shell_exec("mpc add /");
    } else {
      shell_exec("mpc add ".basename($_POST['dir'])."/");
    }
    
    shell_exec("mpc play");
    header('Location: ./player.php');
  }

  //コマンド群
  if (isset($_GET['com'])===false) {
    echo shell_exec("mpc status");
    echo time();
    exit;
  }
 
  switch($_GET['com']) {
  case "next":
  case "prev":
  case "play":
  case "stop":
  case "repeat":
  case "single":
  case "random":
    echo shell_exec("mpc ".$_GET['com']);
    break;
  case "mute":
    echo shell_exec("mpc volume 0");
    break;
  case "volu":
    echo shell_exec("mpc volume +10");
    break;
  case "vold":
    echo shell_exec("mpc volume -10");
    break;
  default:
    echo shell_exec("mpc status");
  }
  exit;
?>

コードは以上になりますが、これらはセキュリティ的な部分は考慮していません。あらかじめご了承ください。

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告