Eu estou tentando escrever um aplicativo usando a interface ALSA RawMIDI para trocar dados sysex com um sintetizador de hardware via USB em um PI de framboesa.
a biblioteca RawMIDI vem com libasound2-dev.
No meu aplicativo de teste, estou enviando uma solicitação de sysex para o dispositivo da seguinte forma:
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);
o dispositivo então responde com uma estrutura de dados sysex.
Funciona, mas às vezes parece haver um problema de inicialização do driver MIDI. Uma em cada dez vezes quando inicio o aplicativo, a solicitação / resposta não é bem-sucedida. Eu então reinicio o aplicativo, e ele às vezes funciona e às vezes não funciona.
Se funcionar, funciona bem. Se a primeira solicitação for bem-sucedida, posso enviar milhares de solicitações e a comunicação com o sintetizador é confiável.
Então, acho que isso tem a ver com a inicialização / desmontagem da biblioteca MIDI.
A biblioteca não relata erros durante a inicialização ou o envio de dados.
Provavelmente estou perdendo alguma coisa durante a inicialização ou desmontagem? Existe uma maneira de redefinir o driver MIDI quando eu iniciar o aplicativo?
aqui está meu código de inicialização:
if ((status = snd_rawmidi_open(&midiin, &midiout, portname, mode)) < 0)
{
errormessage("Problem opening MIDI connection: %s", snd_strerror(status));
exit(1);
}
e aqui está o meu código de desmontagem:
snd_rawmidi_close(midiin);
snd_rawmidi_close(midiout);
midiin = NULL;
midiout = NULL;
parece fácil o suficiente, certo?
edit: aqui está meu 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;
}
e aqui está o método 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;
}
}
}
para esclarecer:
se eu apenas lançar o processo, eu posso enviar sysex ou outros dados MIDI para o PI manualmente da máquina, isso geralmente funciona.
se, diretamente após o lançamento do processo, eu enviar uma solicitação para a máquina, às vezes ela responde, às vezes não. Se eu esperar um pouco, é mais provável que o mecanismo de solicitação / resposta funcione.
Atualmente, acho que o motivo da falha é que eu tenho que esperar a inicialização MIDI terminar. Parece que quando snd_rawmidi_open
retorna, ele não está totalmente inicializado. Eu não sei quanto tempo devo esperar embora?
mais edição:
parece que a questão não se limita ao sysex.
Se eu iniciar o processo e começar a ler e, em seguida, enviar eventos de nota MIDI do sintetizador para o processo, às vezes, a primeira nota não será lida. a seguinte anotação e todos os eventos seguintes são lidos corretamente.
por exemplo, se eu colocar printf("reading...\n");
no topo da função de leitura e lançar o processo, a saída de log será semelhante a:
Init MIDI...
reading...
Normalmente, se eu enviar uma nota seguida de uma nota do sintetizador, será assim:
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...
mas, às vezes, a primeira nota não é recebida:
Init MIDI...
reading...
reading...
reading...
note off chn: 0 note: 36 vel: 0
reading...