質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Raspbian

Raspbianは、DebianベースのRaspberry Pi用ディストリビューション。ハードウェア浮動小数点演算を有効にすることが可能で、Webブラウズなどの速度を向上できます。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Q&A

解決済

1回答

2298閲覧

RPi 3 で 'amidi' の MIDI ダンプをパイプで受け取るとMIDIシーケンスが1つ前のものになる

KEINOS

総合スコア15

Raspbian

Raspbianは、DebianベースのRaspberry Pi用ディストリビューション。ハードウェア浮動小数点演算を有効にすることが可能で、Webブラウズなどの速度を向上できます。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

0グッド

0クリップ

投稿2018/03/30 07:47

編集2018/05/08 09:12

TL;DR(概要)

RaspberryPi 3 に MIDI キーボードをつなげ、受け取った MIDI シーケンスデータが、 $ amidi --port="hw:1,0,0" --dump で表示される内容と、$ amidi --port="hw:1,0,0" --dump | php sample.php でパイプでプログラムに渡された内容が異なる。

押下/押上の結果

続けて「ド・レ・レ」と弾いた場合の信号の違いは以下の通りです。
「Dump RAW」は $ amidi --port="hw:1,0,0" --dump の値、「パイプ経由」は $ amidi --port="hw:1,0,0" --dump | php sample.php で標準入力をおうむ返しした値です。

ノート番号操作Dump RAWパイプ経由
3C押下↓90 3C 3380 3C 00
3C押上↑80 3C 0090 3C 74
3E押下↓90 3E 4280 3C 00
3E押上↑80 3E 0090 3E 24
3E押下↓90 3E 2C80 3E 00
3E押上↑80 3E 0090 3E 64

備考

  • MIDIチャンネル「0
  • 押下 = 9x xx xx(ノート・オン)
  • 押上 = 8x xx xx(ノート・オフ)
  • ド = 真ん中のC(3C
  • レ = 真ん中のD(3E
  • 3バイト目の値はベロシティなので00以外は毎回ランダム

パイプで受け取るプログラム例(PHP)

lang

1<?php 2/* sample.php */ 3while (true) { 4 $input = trim(fgets(STDIN)); 5 echo $input . PHP_EOL; 6}

問題点

上記「Dump RAW」($ amidi --port="hw:1,0,0" --dump でダンプした値)は正常に表示されているのですが、それをパイプでプログラムに渡すとズレが発生します。

他に試したこと

$ mkfifo pipemidi で名前付きパイプを作成し、$ amidi --port="hw:1,0,0" --dump > pipemidi と別ターミナルから $ cat pipemidi で正常に受け取れることを確認後、sample.phpを以下のように変更しましたが、同じズレが発生しました。

lang

1<?php 2 3$path_file_pipe_midi = './pipemidi'; 4 5if(! file_exists($path_file_pipe_midi)){ 6 die('Can not read file.'); 7} 8 9$file = new NoRewindIterator( new SplFileObject($path_file_pipe_midi, 'r') ); 10 11$file->setFlags(SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE); 12 13foreach ($file as $n => $line) { 14 if (false === $line) continue; 15 echo "$n $line", PHP_EOL; 16}

環境/条件

ビット長

  • 1 アクションで受け取るMIDIデータ = 3バイト FF FF FF

ビットマスク

項目ビットマスク
キー Down/UpF0 00 00
MIDI チャンネル0F 00 00
ノート番号00 FF 00
ベロシティー00 00 FF

MIDIデータの値

項目
MIDI チャンネル0Ch0
ノートC3 = 真ん中のド(60=0xC3
ノート3E = 真ん中のレ(62=0x3E
押下アクション8x xx xx9x xx xx
押上アクション9x xx xx8x xx xx

検証環境

項目内容
本体RaspberryPi3 B
MIDI キーボードAKAI MPKmini MK2
MIDI 接続USB ケーブル直結
SSH 接続WiFi 経由
PHPv7.0.27-0+deb9u1 (cli)
MIDI ポート($ amidi -lIO hw:1,0,0 MPKmini2 MIDI 1
$amidi --versionv1.0.28
OSJessie, ヘッドレス
$ lsb_release -a(以下を参照)
No LSB modules are available.
Distributor ID:Raspbian
Description:Raspbian GNU/Linux 8.0 (jessie)
Release:8.0
Codename:jessie

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miiika

2018/04/05 06:51

的外れでしたらごめんなさい。一つ目のコードについてですが、trimを外してみてください。 あるいは、echoしている個所で`.`ではなく`,`にしてみてください。おそらくphpの問題だと思いますので、phpのタグをつけたらよろしいかと思います。
KEINOS

2018/04/05 09:40

ご指摘の2点と PHP 以外で シェル・スクリプトでも試してみましたが、いずれもズレは発生しました。ただ、「trimを外して」みたところ、意図しない改行が入っていることに気づきました!もしかすると制御コード系の問題かもしれないので、もう少し検証して原因をしぼったら質問を修正したいと思います!ありがとうございます!
miiika

2018/04/05 10:31

進展があったようで何よりです。
guest

回答1

0

自己解決

解決しました。

信号が1つズレた原因は amidi の仕様で改行コードが出力されるタイミングでした。

TL;DR

amidi のダンプデータは、1 アクションごとに出力する MIDI シーケンスの先頭に改行コードが付いてきます。そのため、1 アクションを検知(判断)するのは EOL\n などの改行コードではなく読み込んだバイト数(9バイト)で判断する必要があります。

TS;DR

MIDI キーボードのキーやパッドを押すと「90 3C 16」といった3バイトの MIDI 信号(シーケンス・データ)が流れてきますが、厳密には「\n90 3c 16」という9バイトの文字列データです。ここで注意すべきは**「\n」の改行コードが先頭に付いている**ことです。

つまり「[データ1]\n [データ2]\n ...」と1アクションごとに改行が後に付いていると思い込んでいたことが原因です。実際には「\n[データ1] \n[データ2] ...」ということです。

このことから、「パイプ渡しによる標準入力」だけでなく「名前付きパイプによるファイル読み込み」であっても、データを読み込む際に1行ごとに読み込みながら処理する場合は注意が必要です。

単純に改行ごとに処理をしてしまうと、1発目のアクションは先頭の改行コードのせいで「空」になり、2発目のアクションで1発目のデータが流れてくることになります。これが1アクションズレる原因でした。

そのため、プログラム言語を問わず、「標準入力」もしくは(名前付きパイプ経由の)「ファイル読み込み」であっても、データを読み込む時は9バイトを境に読み込みを区切る必要があります。

区切りを9バイトにしたのは、私の MIDI 機器(AKAI MPKmini MK2)がどのキーやパッドを操作しても9バイトの固定長だったためです。

\n90 3c 16\n 9 0 3 C 1 6 = 9 バイト長

改行区切りで読み込み、一時的に変数に入れるなどバッファをさせる方法も考えました。しかし、9バイト以上のデータ(シーケンスのダンプなど)が流れる可能性がない限り、リアルタイム処理の場合はバイト長で区切った方が処理が速そうです。

パイプ渡しで確認する例

php

1<?php 2// sample.php 3while(true){ 4 $input = trim(fgets(STDIN, 9)); //9バイト区切りで読み込む 5 echo $input ? $input . PHP_EOL : ''; 6}

bash

1$ #実行 2$ $ amidi --port="hw:1,0,0" --dump | php sample.php

名前付きパイプで確認する例

2つのターミナル・ウィンドウを開いて試してください。

各々のウィンドウで下記コマンドを打ったのち、MIDI キーボードで操作すると、ウィンドウ2にもダンプデータが流れてきます。

この時、一発目の操作で空行が入ることと、カーソルが行末に来たままであることに注目してください。

bash

1$ # ウィンドウ1の作業内奥 2$ # MIDI ポートの確認 3$ amidi -l 4Dir Device Name 5IO hw:1,0,0 MPKmini2 MIDI 1 6$ # 名前付きパイプの作成 7$ mkfifo dump 8$ # MIDI信号のダンプを作成した名前付きパイプに書き込みつつ表示 9$ amidi --port="hw:1,0,0" --dump | tee dump

bash

1$ # ウィンドウ2の作業内容 2$ # dump ファイルの中身を表示する 3$ cat dump

MIDI シーケンスの誤記

なお、質問時の MIDI キーボード押下時および押上時の MIDI シーケンスが間違っていました。正しくは以下です。

操作備考
押下9x xx xxシグナル・オン(9nH
押上8x xx xxシグナル・オフ(8nH

投稿2018/05/07 18:18

編集2019/08/02 10:52
KEINOS

総合スコア15

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問