見出し画像

SAM2695のNRPNの深掘り


  • SAM2695でNRPNするとき、特殊なコントロールチェンジに対応しているようだが、midoでは送信できないような気がする

    • (同等なメッセージを通常のNRPNで送信できる)

  • 需要はないだろうし、同等なメッセージがNRPNで送信できるので、それほど困っていないが、探求心から詳細を調べてみた(完全に自己満足のポエム記事である)

  • 以下のつづき


  • SAM2695は、NRPNを使って、CutoffやResonanceの設定ができる

  • NRPNは、雑に解説すると、1つの項目を設定するのに3回のコントロールチェンジを必要とする

  • 以下のような命令(Cutoffの周波数を最大の0x7Fにする設定)

[0xb0, 0x63, 0x01]
[0xb0, 0x62, 0x20]
[0xb0, 0x06, 0x7F]
  • しかしながら、M5Stackのuiflow MicroPythonライブラリの実装では、

  • 以下のように、1回のコントロールチェンジのデータを送信している

    • (以下、本記事では特殊CCと呼ぶことにする)

  • SAM2695は、これを許容しているようで、CutoffやResonanceが効いていることを実際に音を聞いて確認した

[0xb0, 0x63, 0x01, 0x62, 0x20, 0x06, 0x7F]


  • しかし、SAM2695のデータシートには、3回のコントロールチェンジをする旨が書かれている気がする

https://docs.dream.fr/pdf/Serie2000/SAM_Datasheets/SAM2695.pdf


  • コードを読み間違えている、理解が間違っているかもしれない、と思い、

  • 念のため、Windows側からもMIDIメッセージを送信して、サクッと確認して終わりにしようと思った

  • そのちょっとした興味本位のせいで、膨大な時間を消費することになってしまったのであった


midoで特殊CCを送信したい

  • Windowsのmidoを使って、特殊CCを送信しようと思った

  • しかしながら、mido.Message.from_bytes()関数を使って、3バイト以上のコントロールチェンジメッセージを作ると、

msg = mido.Message.from_bytes([0xb0, 0x63, 0x01, 0x62, 0x20, 0x06, 0x00]) # cutoff frequency
  • コントロールチェンジのメッセージサイズと異なるという以下のエラーが出る(3バイトである必要がある)

site-packages\mido\messages\decode.py", line 52, in _decode_data_bytes
   raise ValueError(
ValueError: wrong number of bytes for control_change message

https://github.com/mido/mido/blob/7313549206d95ab8e14d24f91b502fac0264a8e2/mido/messages/decode.py#L52-L53

  • しかたないので、コントロールチェンジの長さチェックしている条件判定において、

  • and spec['type'] != 'control_change': などの条件を付与して

  • コントロールチェンジのときだけ、バリデーションを回避してみる

  • これで特殊CCが送信できるといいのだが…


midoが扱うのはMessage型

  • midoでデータを送信するとき、Message型のデータを指定する必要がある

  • 例え、バリデーションを回避しても、

  • mido.Message.from_bytes()関数で、「bytes型」でMIDIメッセージを作成した場合、「Message型」に変換される

  • そうすると、コントロールチェンジは必ず3バイトに強制的に変換されてしまう

  • 特殊CCは、3バイトの通常のコントロールチェンジに変換されてしまう

    • 後半が欠落する

  • 不正なデータが作られないようにする、親切設計なのだが、

  • 今回のケースでは仇となってしまっている


bytes型で送信するメソッドを作ってみる

  • port.send()するとき(MIDIメッセージを送信するとき)、送信するデータは「Message型」である必要があるが、

  • 「bytes型」で送信するメソッドを作ってみる

def send(self, bytes):
"""Send bytes on the port."""
    with self._send_lock:
        self._rt.send_message(bytes)

https://github.com/mido/mido/blob/7313549206d95ab8e14d24f91b502fac0264a8e2/mido/backends/rtmidi.py#L209-L212

  • 今度は、python-rtmidi側のバリデーションでエラーが出る

File "_rtmidi.pyx", line 1103, in _rtmidi.MidiOut.send_messageValueError: 'message' longer than 3 bytes but does not start with 0xF0.


RtMidiで試すことに

  • 方向性を変えて、RtMidiライブラリを使って、C++で実装してみる

  • 何も問題なく、特殊CCが送信できて、Cutoff、Resonanceを確認できた

RtMidiOut *midiout = 0;
unsigned char message[32];

// RtMidiOut constructor procedures are omitted because it isn't main topic.
//  :
//

// Reset
message[0] = 0xFF;
midiout->sendMessage(message, 1);
SLEEP(100);

// Note On: 144, 64, 90
message[0] = 144;
message[1] = 60;
message[2] = 90;
midiout->sendMessage(message, 3);
SLEEP(3000);

// Note Off: 128, 64, 0
message[0] = 128;
message[1] = 60;
message[2] = 0;
midiout->sendMessage(message, 3);

// CC: cutoff (NRPN for SAM2695)
message[0] = 0xb0;
message[1] = 0x63;
message[2] = 0x01;
message[3] = 0x62;
message[4] = 0x20;
message[5] = 0x06;
message[6] = 0x7f;
midiout->sendMessage(message, 7);

// CC: resonance (NRPN for SAM2695)
message[0] = 0xb0;
message[1] = 0x63;
message[2] = 0x01;
message[3] = 0x62;
message[4] = 0x21;
message[5] = 0x06;
message[6] = 0x7f;
midiout->sendMessage(message, 7);

// Note On: 144, 64, 90
message[0] = 144;
message[1] = 60;
message[2] = 90;
midiout->sendMessage(message, 3);
SLEEP(3000);

// Note Off: 128, 64, 0
message[0] = 128;
message[1] = 60;
message[2] = 0;
midiout->sendMessage(message, 3);



まとめ

  • SAM2695では、特殊CCで、NRPNを送信することができる(3つのメッセージが1つになるので実装が楽になる)

  • midoでは、特殊CCは、送信できないと思われる

    • どこかに回避用の策が用意されているかも

  • midoにおいても、通常のNRPN(3回のコントロールチェンジ)を使えば、SAM2695のCutoffなどを変更できる



最後に

  • かなり無意味な調査に膨大な時間を使ってしまったが

  • 調査技術、情報の整理技術が向上したといいかせる

  • 不便なので、python-rtmidiのビルドして、midoで特殊CCを送信できるようにしたい(が、時間が足りなすぎる)



python-rtmidiのビルドもしてみた


いいなと思ったら応援しよう!