ALSA RawMIDI issue

1

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...
    
por spinalwrap 07.03.2016 / 12:06

1 resposta

0

Um dispositivo MIDI bruto ALSA na verdade não começa a ler dados do hardware até que snd_rawmidi_read() seja chamado. Isso significa que você deve chamar snd_rawmidi_read() com antecedência suficiente, ou o primeiro byte (s) da repsonse pode se perder.

A maneira mais segura de fazer isso é chamar snd_rawmidi_read() antes de a solicitação ser enviada. (Veja, por exemplo, amidi .)

    
por 07.03.2016 / 16:18

Tags