ALSA RawMIDIの問題

ALSA RawMIDIの問題

私は、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() 今後リクエストが送信されました。 (たとえば、次を参照してください。アミディ.)

関連情報