Dear All,
I am developing a patch librarian for a synthesizer. Because the communication happens with Midi sysex and timing is not relevant I chose RawMIDI interface and not the alsa sequencer. The problem I have encountered is that writing to the midi port with snd_rawmidi_write() seems to broken. The function accepts any size of data block and claims to have sent everything. In reality only a part of the message is sent. For a large block size I can see the activity light blinking for fraction of a second. This happens both in blocking and non-blocking mode. I would expect the snd_rawmidi_write() to accept only a part of the message.
My hardware is Miditech Midiface 4x4, a USB midi interface: $ cat /proc/asound/cards 0 [PCH ]: HDA-Intel - HDA Intel PCH HDA Intel PCH at 0xe0610000 irq 45 1 [MIDI4x4 ]: USB-Audio - MIDI4x4 MIDIPLUS MIDI4x4 at usb-0000:00:14.0-1, full speed usbid: 1acc:1a0b
My alsa is quite recent (aplay shows version 1.0.27.2), I am running Ubuntu with 3.13.0-39-generic kernel.
I have tested the system by connecting the output port to an input port on the hardware and having another program output the received bytes on stdout. My observations: - As long as the buffer size is 276 bytes or less it gets sent ok - Larger buffer sizes cause corruption and roughly 300 bytes of received. - Alsa reports midi ring buffer size of 4096. - snd_rawmidi_status_get_xruns() on both ends reports 0 errors. - If I write N=128 byte blocks and usleep for 352*N after each write, everything works ok.
Is this due to hardware incompatibility with alsa driver or what is going on? Is there a way to connect two programs via a virtual RawMIDI port so that I could test without using the hardware? Any other diagnostics that I could do?
My test programs:
write.cpp: int main( void ) { int intstatus; snd_rawmidi_t *midiout;
if( (intstatus = snd_rawmidi_open(NULL, &midiout, "hw:1,0,1", 0)) ) { std::cerr << "unable to open MIDI out port: " << snd_strerror(intstatus) << "\n"; exit( 1 ); }
int N = 3000; // 276 bytes gets through ok. unsigned char msg[N]; for( int a = 0; a < N; a++ ) { msg[a] = a % 0x80; } msg[0] = 0xF0; msg[N-1] = 0xF7;
ssize_t wrstatus; if( (wrstatus = snd_rawmidi_write(midiout, msg, N)) < 0 ) { std::cerr << "unable to write MIDI port: " << snd_strerror(wrstatus) << "\n"; return( -1 ); } std::cout << "write status = " << wrstatus << "\n"; if( (intstatus = snd_rawmidi_drain( midiout )) ) { std::cerr << "unable to drain: " << snd_strerror(intstatus) << "\n"; exit( 1 ); }
// Sleep for the amount of time needed for transfer + 10 % extra + 100 ms usleep( 352*N + 100000 );
snd_rawmidi_status_t *stat; snd_rawmidi_status_malloc( &stat ); snd_rawmidi_status( midiout, stat ); int err = snd_rawmidi_status_get_xruns( stat ); std::cout << "xruns = " << err << "\n";
snd_rawmidi_close( midiout );
return( 0 ); }
-------------------- read.cpp: bool quit = false;
void signal_int( int ) { quit = true; }
int main( void ) { int mode = SND_RAWMIDI_NONBLOCK; int intstat; std::string portname = "hw:1,0,0"; //std::string portname = "virtual"; snd_rawmidi_t* midiin;
if( (intstat = snd_rawmidi_open(&midiin, NULL, portname.c_str(), mode)) < 0 ) { std::cerr << "unable to open MIDI port " << portname << ": " << snd_strerror(intstat) << "\n"; exit( 1 ); }
int count = 0; unsigned char buf[1];
std::cout << std::dec << std::setfill(' ') << std::setw(6) << count << " ";
signal( SIGINT, signal_int );
while( !quit ) { int read = snd_rawmidi_read( midiin, buf, 1 ); if( read == 0 || read == -EBUSY || read == -EAGAIN ) { usleep( 320 ); continue; } else if( read < 0 ) { std::cerr << "unable to read MIDI port" << portname << ": " << snd_strerror(read) << "\n"; exit( 1 ); }
std::cout << std::hex << std::setfill('0') << "0x" << std::setw(2) << (int)buf[0] << " " << std::flush; count++; if( count % 8 == 0 ) { std::cout << "\n"; std::cout << std::dec << std::setfill(' ') << std::setw(6) << count << " " << std::flush; } }
snd_rawmidi_status_t *stat; snd_rawmidi_status_malloc( &stat ); snd_rawmidi_status( midiin, stat ); int err = snd_rawmidi_status_get_xruns( stat ); std::cout << "\nxruns = " << err << "\n"; snd_rawmidi_close( midiin );
return( 0 ); }
Thanks. Taneli