私は、ALSA RawMIDIインターフェースを使用してRaspberry PIのUSB経由でハードウェアシンセサイザーとsysexデータを交換するアプリケーションを作成しようとしています。 RawMIDIライブラリはlibasound2-devに付属しています。
私のテストアプリケーションは、次のようにデバイスにsysexリクエストを送信します。
uint8_t req[] = {0xF0, 0x00, 0x20, 0x3C, 0x07, 0x00, type, 0x01, 0x01, position, 0x00, 0x00, 0x00, 0x05, 0xF7};
if ((status = snd_rawmidi_write(midiout, req, 15)) < 0)
{
errormessage("Problem sending request: %s", snd_strerror(status));
exit(1);
}
snd_rawmidi_drain(midiout);
その後、デバイスはsysexデータ構造で応答します。
うまくいきますが、時にはMIDIドライバに初期化の問題があるようです。アプリケーションを起動すると、10個の要求/応答のうち約1個がまったく成功しません。その後、アプリを再起動しましたが、うまくいくことも、うまくいかない場合もあります。
うまくいけばとても良いです。最初の要求が成功すると、何千もの追加の要求を送信でき、シンセサイザとの通信は安定しています。
だから私はこれがMIDIライブラリの初期化/解体に関連していると思います。ライブラリーは、初期化中またはデータ転送中にエラーを報告しません。
たぶん初期化や分解中に何かが落ちたのでしょうか?アプリケーションの起動時にMIDIドライバをリセットする方法はありますか?
これは私の初期化コードです。
if ((status = snd_rawmidi_open(&midiin, &midiout, portname, mode)) < 0)
{
errormessage("Problem opening MIDI connection: %s", snd_strerror(status));
exit(1);
}
私の分解コードは次のとおりです。
snd_rawmidi_close(midiin);
snd_rawmidi_close(midiout);
midiin = NULL;
midiout = NULL;
簡単だと思いますか?
編集:これは私のmain.cppです。
#include <signal.h>
#include <thread>
#include "MIDI.hpp"
using namespace std;
static MIDI midi;
void sighandler(int dum)
{
midi.quit();
exit(0);
}
void midiRead()
{
while(1)
midi.read();
}
int main()
{
signal(SIGINT,sighandler);
thread midiReadThread(midiRead);
midiReadThread.join();
return 0;
}
これはmidiReadメソッドです:
void MIDI::read()
{
uint8_t readByte;
if ((status = snd_rawmidi_read(midiin, &readByte, 1)) < 0) {
errormessage("Problem reading MIDI input: %s", snd_strerror(status));
}
// if status byte other than sysex end, reset read buffer:
if(readByte & 0x80 && readByte != 0xF7)
{
if(readByte == 0xF0)
printf("syx");
argsLeft = getArgsExpected(readByte);
bufIdx = 0;
currentCommand = readByte;
inBuffer[bufIdx++] = readByte;
}
// if it's a data byte or sysex end:
else if(argsLeft || currentCommand == 0xF0)
{
inBuffer[bufIdx++] = readByte;
argsLeft--;
// handle the sysex message:
if(readByte == 0xF7)
{
printf(" done\n");
handleSysex(inBuffer, bufIdx);
}
}
// if we don't expect any more data bytes from non-sysex:
if(!argsLeft && currentCommand != 0xF0)
{
switch (currentCommand & 0xF0) {
case 0x90:
{
handleNoteOn(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
case 0x80:
{
handleNoteOff(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
case 0xA0:
{
handleAftertouch(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
case 0xB0:
{
handleController(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
default:
break;
}
}
}
明確にするには:プロセスを開始すると、システムからPIにsysexまたは他のMIDIデータを手動で送信できます。これは通常機能します。
プロセスが開始された直後にコンピュータに要求を送信すると、応答することもあり、応答しないこともあります。少し待つと、要求/応答メカニズムが機能する可能性が高くなります。
現在の失敗の原因は、MIDI初期化が完了するのを待たなければならないと思います。snd_rawmidi_open
返されると、実際に完全に初期化されていないようです。どのくらい待つべきかわかりませんか?
追加の修正:
問題はsysexに限定されないようです。プロセスを開始して読み取りを開始し、シンセサイザーからプロセスにMIDIノートイベントを送信すると、最初のノートが読み取られないことがあります。以下の説明とその後のすべてのイベントを正しく読みました。
printf("reading...\n");
たとえば、これを読み取り関数の先頭に置いてプロセスを開始すると、ログ出力は次のようになります。
Init MIDI...
reading...
通常、シンセサイザーで起動するノートを送信し、終了するノートを送信する場合は次のようになります。
Init MIDI...
reading...
reading...
reading...
note on chn: 0 note: 36 vel: 100
reading...
reading...
reading...
note off chn: 0 note: 36 vel: 0
reading...
しかし、時には最初のメモを受け取らないことがあります。
Init MIDI...
reading...
reading...
reading...
note off chn: 0 note: 36 vel: 0
reading...
答え1
snd_rawmidi_read()
ALSAネイティブMIDIデバイスは、実際に呼び出されるまでハードウェアからデータの読み取りを開始しません。これはsnd_rawmidi_read()
できるだけ早く呼び出す必要があることを意味します。そうしないと、応答の最初のバイトが失われる可能性があります。
最も安全な方法は電話をすることです。snd_rawmidi_read()
今後リクエストが送信されました。 (たとえば、次を参照してください。アミディ.)