java midi 〜javax.sound.midiを使う〜

今回はJavaでMIDIを鳴らすためのプログラムを改定みようと思います。まずは、PC上で鳴らすことを考えます。
早い話が、JavaでMidiを扱う時の概要を理解しようというところです。

事の経緯

Microbitからメッセージを送信し、ラズパイで受けて適当なメッセージを作成しMIDI音源に送信、音を鳴らす。というものを作成しようとしています。

でわ。。。学習開始!

java.sound.midi

上のリンク先にはJavaDocがあります。そして、チュートリアルがありました。ここのページを学習します。

ちなみに、大元のページはこちらです。java sound APIのドキュメントになります。

Java Sound プログラマーズガイド

このページを学習します。

Midiを使う

シンプルにMIDIとは音源を鳴らすためのデータです。なのでこれを受け取ったMIDI音源はドラムとかピアノとか音を鳴らすわけです。じゃ、Javaでやろうとしたらどうやるか?
扱うデータは目で見えるようにすると下のようなイメージです。

そして、MIDIの概要を大まかに説明しているのはこちらのページです。ここでMIDIはどんなものか記載されています。
そして、Javaではどのような扱いをするのか記載しています。

プログラミング

JavaでMIDIを扱う時にはMidiSystemクラスを使用するようです。そして以下のものを使用できるようです。

  1. シーケンサ
  2. シンセサイザ
  3. トランスミッタ (MIDI 入力ポートに関連付けられたトランスミッタなど)
  4. レシーバ (MIDI 出力ポートに関連付けられたレシーバなど)
  5. 標準 MIDI ファイルのデータ
  6. サウンドバンクファイルのデータ

プログラムの実行手順

  1. デバイスの取得
  2. デバイスのオープン
  3. トランスミッタとレシーバの接続
  4. (デバイスへの)MIDIメッセージの送信

JavaでMIDIを使う場合は

JavaTM Sound API は、MIDI データ用のメッセージルーティングアーキテクチャーを指定します。

そして、トランスミッタ(MIDI入力)、レシーバ(MIDI出力)を使用します。

トランスミッタが MidiMessages を送信するレシーバの設定および問い合わせを行うためのメソッドが含まれます。

のようなので、この仕組みを使用してMIDI再生を行うプログラムを作ります。

Java Midi の仕組みについて

Java Sound API のメッセージ交換システム内の基本モジュール、以下にまとめます。
MidiDevice 、内容は以下に。。。
< シンセサイザ >
< シーケンサ >
< MIDI入力ポート >
< MIDI出力ポート >

サンプルコード

単一のデバイスの接続

    Sequencer seq;
    Transmitter seqTrans;
    Synthesizer synth;
    Receiver synthRcvr;
    try {
          seq = MidiSystem.getSequencer();
          seqTrans = seq.getTransmitter();
          synth = MidiSystem.getSynthesizer();
          synthRcvr = synth.getReceiver(); 
          seqTrans.setReceiver(synthRcvr);  
    } catch (MidiUnavailableException e) {
          // handle or throw exception
    }

複数のデバイスへの接続

    Synthesizer synth;
    Sequencer seq;
    MidiDevice inputPort;
    // [obtain and open the three devices...]
    Transmitter inPortTrans1, inPortTrans2;
    Receiver synthRcvr;
    Receiver seqRcvr;
    try {
          inPortTrans1 = inputPort.getTransmitter();
          synthRcvr = synth.getReceiver(); 
          inPortTrans1.setReceiver(synthRcvr);
          inPortTrans2 = inputPort.getTransmitter();
          seqRcvr = seq.getReceiver(); 
          inPortTrans2.setReceiver(seqRcvr);
    } catch (MidiUnavailableException e) {
          // handle or throw exception
    }

ぶっちゃけて複数のMIDI音源を使用するつもりはないので、単一のデバイスだけで良い気がしますが、もしかしたら複数ならしたい。。。となるかもしれないので記載しておきます。
が、まずは単一のデバイスを鳴らすことから始めます。

Javaコードを書く

とりあえずは、上のコードを色々と使用できるように「MidiCreator」という名前でクラスを作成しました。

public class MidiCreator {
    private Sequencer seq;
    private Transmitter seqTrans;
    private Synthesizer synth;
    private Receiver synthRcvr;

    /** コンストラクタ */
    public MidiCreator() {
        try {
              seq = MidiSystem.getSequencer();
              seqTrans = seq.getTransmitter();
              synth = MidiSystem.getSynthesizer();
              synthRcvr = synth.getReceiver(); 
              seqTrans.setReceiver(synthRcvr);  
        } catch (MidiUnavailableException e) {
              // handle or throw exception
        }
    }
}

コンストラクタで、使用するクラス群のセットアップを行なっています。ほとんどサンプルコードのコピーです。

今回は、ここまでにします。

でわでわ。。。

Microbit ラズパイ Bluetooth〜マイクロビットとラズパイ間をSDPで通信する〜

今回は、Microbitとラズパイの間をBluetooth経由で通信するためのサーバーを作成し始めようと思います。
参考にするページは本家Oracleのページです

元々のやりたいこと

一応この実装の目的は、Microbitからのbluetooth経由メッセージをラズパイで受信して、Midiメッセージを音源モジュールに送信、音を鳴らすというのが目的です。下はイメージになります。

SDPで通信

SDP(Sockets Direct Protocol)はMicrobitとラズパイの通信で使用できるという情報を得て、現在に至りますが、早い話が、Bluetoothでの通信時にこのSDPが使用できるということです。このテクノロジーを使用するためにサポートされて(JDKに入って)いるクラスは下に記載しました。(参考サイト参照)

  • java.net package
    • Socket
    • ServerSocket
  • java.nio.channels package:
    • SocketChannel
    • ServerSocketChannel
    • AsynchronousSocketChannel
    • AsynchronousServerSocketChannel

必要な作業

  1. SDP構成ファイルの作成
  2. SDPプロトコルの有効化
  3. SDPのデバッグ

こんな感じの作業が必要になると思いますが、如何せんやってみないことには始まらないので。。。

SDPサーバーを作る

SDP構成ファイルの作成このページを読んでみると、構成ファイル(設定ファイル)を作る必要がある、と記載しているのでその通りにやってみます。。。
しかし、記載ルールが書いてあるだけなのでよくわからない状態です。
わからないので調べます。そして、ちょうど良いのがありました。設定ファイルのテンプレートのようなものらしいです。早速失敬して。。。
ご丁寧に、全部コメントアウトされています。必要な部分を修正、コメントを外して使用します。

設定ファイル(Conf File)

#
# Configuration file to enable InfiniBand Sockets Direct Protocol.
#
# Each line that does not start with a comment (#) is a rule to indicate when
# the SDP transport protocol should be used. The format of a rule is as follows:
#   ("bind"|"connect") 1*LWSP-char (hostname|ipaddress["/"prefix]) 1*LWSP-char ("*"|port)["-"("*"|port)]
#
# A "bind" rule indicates that the SDP protocol transport should be used when
# a TCP socket binds to an address/port that matches the rule. A "connect" rule
# indicates that the SDP protocol transport should be used when an unbound
# TCP socket attempts to connect to an address/port that matches the rule.
# Addresses may be specified as hostnames or literal Internet Protocol (IP)
# addresses. When a literal IP address is used then a prefix length may be used
# to indicate the number of bits for matching (useful when a block of addresses
# or subnet is allocated to the InfiniBand fabric). 

# Use SDP for all sockets that bind to specific local addresses
#bind    192.168.4.1    *
#bind    fe80::21b:24ff:fe3d:7896    *

# Use SDP for all sockets that bind to the wildcard address in a port range
#bind    0.0.0.0    5000-5999
#bind    ::0        5000-5999

# Use SDP when connecting to all application services on 192.168.1.*
#connect 192.168.1.0/24       1024-*

# Use SDP when connecting to the http server or MySQL database on hpccluster.
#connect hpccluster.foo.com   80
#connect hpccluster.foo.com   3306

Socketの実装

以前作成したものですが、これに設定ファイルを適用すればいけるみたいです。下のものはSocketサーバーを起動するコードです。
クライアントから送信されたメッセージ(テキストなど)をそのまま返す処理になっています。

public void startServer() {
    try {
        System.out.println("*** SocketServer start " + server.isBound() + "***");
        Socket recieve = server.accept();
        System.out.println("*** Server Get Request start***");
        // 受信データの読み込み
        StringBuilder responseTxt = new StringBuilder("");
        // 受信状態
        System.out.println("クライアント: " + recieve.getRemoteSocketAddress());
        System.out.println("接続: " + recieve.isConnected());
        System.out.println("入力ストリームが閉じている: " + recieve.isInputShutdown());

        // 受信したリクエスト
        InputStream in = recieve.getInputStream();
        // 返却するレスポンス
        OutputStream writer = recieve.getOutputStream();
        System.out.print("Server recieve is ...");
        int c = 0;
        // CRとCRLFの場合で入力が終了している時がある
        while((c = in.read()) != -1) {
            char ch = (char)c;
            responseTxt.append(ch);
            // 空の場合
            if (c == 10 || c == 13) {
                break;
            }
        }
        System.out.println(responseTxt.toString());
        System.out.println("*** Server Send Response start***");
        // レスポンス送信
        writer.write((responseTxt.toString() + System.getProperty("file.separator")).getBytes());
        writer.flush();
        in.close();
        writer.close();
        System.out.println("*** SocketServer end ***");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 開いたストリームを閉じる
        try {
            closeServer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
一度書けばどこでも動くの精神

ということです(笑)
あとは動かしてみてうまくいくかどうか?を確認します。
そして、これをベースにして「〜を受信したときに〜する」というような処理を実装していきます。
ソースはGithubにコミットしてありますので、よかったら全体のソースも見てみてください、

でわでわ。。。



Java Socket Server〜ラズパイで動くサーバーを作る4: javaサーバー2〜

今回はJavaサーバーの設計に着手します。
MicrobitとラズパイのBluetooth接続ができることは確認したので、その次にMicrobitのBluetoothボタンサービスから送信されるデータを受け取ることを考えます。

Bluetoothサービス調査

ラズパイとMicrobitを接続した時には、blutoothctlコマンドで接続しました。なので、JavaからBluetoothに接続したいと思いますので、そこらへんの調査をします。
Googleで「java bluetooth api」などと検索するとオラクルのページを見つけることができました。
やっぱり英語なので、翻訳機能で日本語にして読みます。が大雑把な概要(イメージ)歯科理解できず。。。
仕方ないので他に探します。

ちょっとアプローチを変えて

調べてみると接続して、通信するにはSDPというプロトコルを使用する様です。
上記のリンクをまとめると、ラズパイでSDPサーバーを起動してMIcrobitからの通信を受信するためのアプリケーションを起動、待機させておくということをやった様です。

プロトコルとは

余談ですが、プロトコルという言葉について記載しておきます。プロトコルは送信側と受信側「この様にデータを投げるし、受け取るよ?」というルールを決めたものなので作ればいくらでも存在するものです。有名なものしか耳に入ってこないので、それしかないというわけではありませんということを記載しておきたかった次第です。
具体的には「http」とか「ftp」などのURLの頭につくもののことです。

SDPプロトコル

参考URL:
シリアル通信のQA

結局のところはMicrobitからシリアル通信を行います、具体的には以下の様なケースが考えられます。

  1. ボタンを押下した時に文字をシリアル通信
  2. 温度をシリアル通信
  3. そのほかのセンサーなどをシリアル通信

こんな感じでデータをラズパイに送信します。

これらを実現するためにはとりあえず、ラズパイでMicrobitからの通信を受信するためのサーバーが必要です。
上の参考URLではSDPサーバーを立ち上げて動かしている様でした。

JavaサーバーSDP

もしかしたらSDPを使用しなくても良いのかもしれません。
現状、持っている情報から行くとSDPでMicrobitからデータが送信されてくるという認識なのでSDPを使用しようとしています。がここも調査が必要です。
今回はJavaでBluetooth通信をしたいのでここについて詳しく調べます。
Java Bluetooth APIを使用するのも手段の1つです。というかこれを使用します。

JSR 82

これがJavaでBluetoothを使用するときの仕様(JSR 82)になる様です。
とりあえずは、必要なライブラリをインストール(ダウンロード)します。つまりPOMファイルの出番です。
POMファイルはXMLに必要な情報を記述して「ビルド!」ってやるとMavenリポジトリから対象のライブラリをダウンロードしてくれる優れものです。
ここのリンクにある内容をPOMファイルに追記します。
このページに追記する内容がそのまま書いてあります。

ここの<!-- http://XXXXXXX -->とある部分はリポジトリのURLになります。なので手順は以下の様になります。

  1. 青色で囲っている部分をコピー
  2. POMファイルを開き<dependncies>で囲まれている部分にペースト

最終的に以下の様になりました。

  <repositories>
    <repository>
      <name>jsr82</name>
     <id>microemu-extensions</id> <url>https://mvnrepository.com/artifact/org.microemu/microemu-jsr-82</url>
    </repository>
  </repositories>
 ・
   ・
   ・

nameは適当につけました。。。。
これを作成したら、Eclipseのプロジェクトを右クリック→Maven→Maven installをクリックすると下の様にインストールされました。

次回は、このライブラリを使用してみます。

でわでわ。。。



Java Socket Server〜ラズパイで動くサーバーを作る3: javaサーバー〜

今回は、Javaで作るAPサーバーを作り始めます。
ちなみに、APサーバーというのは画面を表示しないサーバーのことだと思ってくれればOKです。

現状

前回までにやった事は以下になります。

ここまでで、ラズパイに作成したソースをGit hubにアップしておけばあとはラズパイで起動できるようになりました。

目的の確認

今度は、核になるサーバーを作ります。確認のため全体の処理フローを以下に示します。

雑な絵ですが、つまるところは

  1. マイクロビットから何かしらのメッセージをラズパイに送る
  2. ラズパイで受けたメッセージからMidiメッセージを生成
  3. 生成したメッセージをラズパイに接続したMidi音源に送信

この様な形で、マイクロビットをコントローラーにした、Midiプレーヤーを作ろうと考えてます。イメージは下の様な感じです。

![](http://zenryokuservice.com/wp/wp-content/uploads/2019/10/スクリーンショット-2019-10-08-20.19.29-300x200.png)

Javaサーバー

今回作成するものは、上記の「2」にある様に、ラズパイで起動して、メッセージの受信と送信を行うものにします。

リクエストとレスポンス

余談ではありますが、今回作成するものは受信と送信を行うのですが、この仕組みは身近にあるものなので記載しておきます。

ホームページ開くのと同じ

余りに身近になったこのテクノロジーは、TCP/IPなどと呼ばれるネットワーク技術をベースにしています。送受信するメッセージがhttpメッセージです。

httpリクエストの例

httpの詳細には触れませんが、大雑把に、リクエストとレスポンスがありサーバーへ送信するメッセージをリクエスト、サーバーが返すメッセージをレスポンスと呼びます。

スマホで検索したページを開く時
  1. スマホを開いてブラウザアプリを起動します
  2. http://www.google.co.jpにアクセスします。
  3. この時にスマホから上のURLへGETリクエストを送信
  4. レスポンスとして、スマホで見ているHTML(であろう)ファイルをダウンロード
  5. ブラウザで表示

というような手順を踏みます。

これと同じ様にJavaサーバーも動く
  1. マイクロビットからBluetooth経由でhttpでないがメッセージ(データ)を送信
  2. ラズパイのJavaサーバーがそのデータを受け取る
  3. 何かしらの処理をしてMidi音源にUSB経由でMidiメッセージを送信
  4. Midi音源が鳴る

こんな風に動かそうと思いこの計画を立てました。

JavaServerの仕様

全体の流れとしては上記の通りです。これを実現するために、どーしたら良いか?考えます。

前提

  1. マイクロビットは、シリアルデータ通信を行う事が出来る事を確認済み

今後は、以下の事を確かめていきます

  1. ラズパイでマイクロビットとBluetooth通信を行う為のペアリングが出来る事を確認
  2. ラズパイからUSBでMidi音源にMidiメッセージを送信出来る事を確認

この部分は、後々に確かめようと思います。

理由

  1. 早くJavaサーバーを作り、ラズパイで動かしたいから
  2. もし前提が通らなくても、他の事に使えるので、作り損はない

こんな感じです。

プログラム設計

お待ちかね、プログラム設計です。今回はJavaのSocketを使用したサーバーを作ります。ソケットは昔からある低レベAPIで、送信データの内容全てをコントロール出来ます。逆に言うと全部自分で作る…という事です。

[ソケットの扱い](https://docs.oracle.com/javase/jp/8/docs/api/java/net/Socket.html)に関しては次回以降やります。

でわでわ…

## 関連ページ
1. [Javaで通信処理を行う](https://zenryokuservice.com/wp/2019/07/20/java-socket-%e3%80%9c%e6%a5%b5%e5%b0%8f%e3%82%b5%e3%83%bc%e3%83%90%e3%83%bc%e3%81%a7%e9%80%9a%e4%bf%a1%e3%81%ae%e5%9f%ba%e6%9c%ac%e3%82%92%e5%ad%a6%e3%81%b6%e3%80%9c/)
2. [極小サーバーを作る](https://zenryokuservice.com/wp/2018/09/14/java-network-socket%e3%80%9c%e6%a5%b5%e5%b0%8f%e3%82%b5%e3%83%bc%e3%83%90%e3%83%bc%e3%80%9c/)




Java Socket Server 〜ラズパイで動かすサーバーを作る2: mavenでjarファイル出力〜

Mavenビルドをかけると下の様なエラーが出ました。

解決方法に関して、結局のところはマニフェストファイルを作成してビルドし直すというところです。

できた方法

  1. pom.xmlファイルにプラグインを追加する
    「maven-dependency-plugin」
    「maven-assembly-plugin」
  2. ビルドする。ゴールはとりあえずで、「install」を設定して起動した。
  3. 出力したXXXX-jar-with-dependenciesを実行する

こんな感じでできました。
ちなみに、実行するコマンドは下のものです。
java 対象のjarファイル.jar ./対象のファイルまでのパス/出力した.jar という形でコマンドを叩きます。
自分の叩いたものはこちらです。
java -jar ./target/socket.server-0.0.1-SNAPSHOT-jar-with-dependencies.jar

下のものは調べたけどうまくいかなかったので、一応の形で記載しています。でも、初めからjar-with-dependenciesのついているjarファイルを実行していれば問題なかったかも?

マフェストファイルを出力する方法

作成したプロジェクトをMaven化するなどしてMavenプロジェクトを作成します。

そして、プロジェクトを右クリックしてエクスポート(Export)をクリックします。そうすると下の様なダイヤログが見れます。

ここで、Java/Jar Fileを選択します。
そして出力するプロジェクトを選択します。

下のものはそのまま次へ

次のところで、出力するマニフェストファイル名を指定します。

そして、出力した結果が以下になります。

packageコマンドを実行する場合

色々と調べてみましたが、「コマンドを実行したほうが早い」ということになり、以下のコマンドを実行します。
ターミナル(コマンドプロンプト)を開き、プロジェクトのディレクトリまで移動します。
そして、「mvn package」と入力します。

でわでわ。。。