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() 앞으로요청이 전송되었습니다. (예를 들어 다음을 참조하세요.아미디.)

관련 정보