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