Improving serial performance when echoing input to output

It has been reported that when the MIDI module gets saturated with a lot of data. An example is pitch bend on a synth attached to MIDI IN. In that case, the soft thru code can’t keep up, and the result lags.

I haven’t tested this yet myself, but it makes sense.

In its simplest current form, the way soft thru works is it echos serial in back to serial out. Is there a way (which doesn’t require producing different compiled modules for the different Gadgeteer boards) to improve that echo performance?

I plan to handle this on a companion chip in the future, but I’m curious about possible solutions for what we have today.

Thanks.

Pete

Can you post the code you’re using for the midi thru?

I think it’s just the fact that there’s an event handler and buffer copy going on that really kills it.

        // Loads incoming data from the serial port to the buffer.
        private void OnSerialDataReceived(GTI.Serial sender, SerialData data)
        {
            if (data == SerialData.Chars)
            {
                int count = 0;

                //lock (_buffer)
                //{
                count = _serial.Read(_buffer, 0, _serial.BytesToRead > MaxBufferSize ? MaxBufferSize : sender.BytesToRead);
 #if False
                    for (int i = 0; i < count; i++)
                    {
                        DebugPrint("Buffer[" + i + "] = " + _buffer[i]);
                    }
 #endif

                // performance of this needs to be monitored to see if
                // it holds up with other tasks going on
                    if (SoftThru)
                    {
                        _serial.Write(_buffer, 0, count);
                        _serial.Flush();
                    }

                    ParseBuffer(count);
                //}

            }
            else if (data == SerialData.Eof)
            {
                // TODO
            }
        }

The block with “if SoftThru” is the important part here.

One thing I’ll do for sure is let the developer specify a channel so I can skip parsing data that isn’t for the channel the NETMF code is interested in, with a note that if you go omni (all channels) there’s some perf overhead. That approach may take care of the problem in some situations.

Here’s the report that got me thinking about this:

[quote]This is my whole code - I have only connected the MIDI module and the USB power module:

midi.SoftThru = true;
midi.EnableReceiver();

When you play only notes, the delay is hardly audible. But when you send some more messages (e.g. by moving the pitch bender), the delay gets really long (> 1s).[/quote]

Pete

I think it is the OnSerialDataReceived event handler that kills you. The underlying framework will keep queing events while you’re in the handler, making things stacking up.

I would create a thread that is always waiting in the serial.read operation. (And set a readtimeout to 1 second f.e. if you ever want to terminate your thread).
Also would do binary processing, not ascii.


void ThreadProc()
{
   int bytesRead = 0;
   byte[] buffer = new byte[32];
   serial.ReceiveTimeout = 1000;
   while (!terminate)
   {
      bytesRead = serial.Read(buffer, 0, buffer.Length);
      if (bytesRead < 0) break;
      if (bytesRead == 0) continue;
      if (SoftThru)
      {
         serial.Write(buffer, 0, bytesRead);
         //serial.Flush(); // is flush really needed?
      }
   }
}

The thing you need to check out is that the read operation returns immediatly in case it is waiting for timeout and only one byte arrives. This might differ from the full framework implementation. Otherwise you might want to wait on a single byte and then read the BytesToRead amount, appending them to the buffer.

Also keep an eye on thread safety:

count = _serial.Read(_buffer, 0, _serial.BytesToRead > MaxBufferSize ? MaxBufferSize : sender.BytesToRead);

Say MaxBufferSize is 10 and the first time _serial.BytesToRead also returns 10, then thread is doing the compare and thread switching happens to the serial port code which gets an additional byte in the receive buffer. When you come back to your thread, the second sender.BytesToRead will return 11…