Main Site Documentation

Speeding up MIDI code


#1

Well I was here once before and I’m back again this time with a new problem. Basically what I’m doing if feeding the Panda MIDI data from a computer to COM1. The Panda is then supported to process the data and play a square wave out of DIO2 corresponding to the freq of the MIDI note it was sent.

Basically a MIDI synthesizer/player, very simple at this point.

Now my current code works but it’s slow… If I give it a bunch of notes to process at once it misses notes and trips up. I had a similar peace of software running on an 8 bit 16MHz AVR for a long time and it out performs the 72MHZ 32 bit ARM by about double!

My question is can some one give some pointer on how to speed up the code. currently it only has 2 threads, one for processing the incoming serial data and another for playing square wave.

Any help would be a big help thanks,

My code:

MIDI class:

using System;
using System.IO.Ports;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace FEZ_Panda_MIDI
{
    static public class MIDI
    {
        private static SerialPort UART; //Serial port for incoming MIDI data
        private static byte[] buffer = new byte[200]; //buffer for incomming data
        private static byte[] data1 = new byte[16];
        private static byte[] data2 = new byte[16];
        private static string[] messageType = new string[16];
        private static int newData = 0;
       
        

        static public void Initialize(string port)
        {
            //Initialize the serial port and stuff
            Thread ProcData = new Thread(processData);
            ProcData.Start();
            UART = new SerialPort(port, 31250); 
            UART.ReadTimeout = 0;
            UART.ErrorReceived += new SerialErrorReceivedEventHandler(UART_ErrorReceived);
            UART.Open();
            UART.DataReceived += new SerialDataReceivedEventHandler(UART_DataReceived);
            
        
        }

        static private void UART_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            Debug.Print("COM Error: " + e.EventType.ToString());
        }

        static private void UART_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
           newData = UART.Read(buffer, 0, buffer.Length); //Reda the data from the port
        }

        static private void processData()
        {
            int dataReceived;
            int bufferIndex = 0;
            byte message;
            byte channel;

            while (true)
            {
                dataReceived = newData;
                newData = 0;

                if (dataReceived != 0)
                {
                    //loop through all of the data in the buffer
                    for (bufferIndex = 0; bufferIndex < dataReceived; bufferIndex++)
                    {
                        //check is current byte is a statis byte
                        if (buffer[bufferIndex] >= 128)
                        {
                            message = (byte)(buffer[bufferIndex] >> 4); //get high nibble (message type)
                            channel = (byte)(buffer[bufferIndex] & 15); //get low nibble (MIDI channel);

                            switch (message)
                            {
                                case 8: //Note off event
                                    messageType[channel] = "NoteOff";
                                    data1[channel] = buffer[bufferIndex + 1];
                                    data2[channel] = buffer[bufferIndex + 2];
                                    break;
                                case 9: //Note on event
                                    //if the volocity is 0 treat it as a NoteOff
                                    if (buffer[bufferIndex + 2] > 0)
                                    {
                                        messageType[channel] = "NoteOn";
                                    }
                                    else
                                    {
                                        messageType[channel] = "NoteOff";
                                    }
                                    data1[channel] = buffer[bufferIndex + 1];
                                    data2[channel] = buffer[bufferIndex + 2];
                                    break;
                                case 14: //pitch bend event
                                    messageType[channel] = "PitchBend";
                                    data1[channel] = buffer[bufferIndex + 1];
                                    data2[channel] = buffer[bufferIndex + 2];
                                    break;

                            }
                        }
                    }
                }
            }
        }
        
        static public string getType(byte channel)
        {
            return messageType[channel - 1];
        }

        static public byte getData1(byte channel)
        {
            return data1[channel - 1];
        }

        static public byte getData2(byte channel)
        {
            return data2[channel - 1];
        }

        

    }
}

Square wave playing class:

using System;
using System.Threading;

using System.IO.Ports;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace FEZ_Panda_MIDI
{
    class DRMIDI
    {
        OutputPort led;
        private OutputCompare OC;
        private Thread DRthread;
        private byte Channel = 1;
        private byte maxLimit = 127;
        private byte minLimit = 0;
        private string type = "";
        private byte data1 = 0;
        private byte data2 = 0;
        private uint[] timing = new uint[2];
       


        public void Initialize(FEZ_Pin.Digital pinNumber, byte Channel)
        {
            //Setup output compare and new thread
            OC = new OutputCompare((Cpu.Pin)pinNumber, false, 2);
            led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false);
            DRthread = new Thread(run);
           
        
        }
        
        public void start()
        {
            //Start the thread for first time
            if(DRthread.ThreadState == ThreadState.Unstarted)
                DRthread.Start();

            //Restart thread 
            if (DRthread.ThreadState == ThreadState.Suspended)
                DRthread.Resume();
        }
        
        public void stop()
        {
            //disable the thread 
            if (DRthread.ThreadState == ThreadState.Running)
            {
                DRthread.Suspend();
                OC.Set(false);
            }
        }

        public void kill()
        {
            //kill the thread and take out the trash
            DRthread.Abort();
            OC.Set(false);
            OC.Dispose();
        }
        
        public void run()
        {
            while (true)
            {
                //if the message is exatly the same as the previos then ignore it.
                if (type == MIDI.getType(Channel) && data1 == MIDI.getData1(Channel) && data2 == MIDI.getData2(Channel))
                {
                    //do nothing
                }

                else
                {
                    type = MIDI.getType(Channel);
                    data1 = MIDI.getData1(Channel);
                    data2 = MIDI.getData2(Channel);


                    //Check which event has happened and take action 
                    switch (type)
                    {
                        case "NoteOn":
                            playNote(data1);
                            break;
                        case "NoteOff":
                            stopNote(data1);
                            break;
                        case "PitchBend":
                            pitchBend(data1, data2);
                            break;

                    }
                }
            }
        }

        private void playNote(byte note)
        {
            int frequancy; //nore frequancy in Hz
            int period; //period in uS
            int onTime = 500; //on time in uS
            int offTime;
            
            //calculate the period of the note
            frequancy = (int)(System.Math.Pow(2.0, ((float)(note - 57) / 12.0)) * 440.0);
            period = (int)(1000000.0 * (1.0 / (float)frequancy));

            offTime = period - onTime;

            timing[0] = (uint)offTime;
            timing[1] = (uint)onTime;

            OC.Set(false, timing, 0, 2, true);
            
            led.Write(true);
        }

        private void stopNote(byte note)
        {
            OC.Set(false);
            led.Write(false);
        }

        private void pitchBend(byte MSB, byte LSB)
        {
            Debug.Print("Bending");
        }
    
    
    
    
    
   
    }
}

Main thread

using System;
using System.Threading;
using System.IO.Ports;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace FEZ_Panda_MIDI
{
    public class Program
    {
        
        
        public static void Main()
        {
            
            DRMIDI DRMIDI1 = new DRMIDI();
           
           
            
            MIDI.Initialize("COM1");
            DRMIDI1.Initialize(FEZ_Pin.Digital.Di2, 1);
            DRMIDI1.start();
            
            
            while (true)
            {
                
            }
                
         }
    
        public static void play(byte note)
        {


        
        
        }

        public static void stop(byte note)
        {


        }
    
    
    
    }
}

#2

First thing I’ve noticed.

Change in your Main method:


            while (true)
            {
 
            }

to


Thread.Sleep(Timeout.Infinite);

As a general suggestion, have you looked at RLP?

Cheers,
V


#3

Looks like there are several areas where you could clean up the code. Common sense should help.
Assigning or comparing strings in a loop costs a lot of time (stuff like: “NoteOff”).
Use numbers instead.
Addressing array members is also time intensive. There may be better ways to do that.
And, as Architect mentioned, if all that doesn’t help, you can use RLP. But 32500 Baud midi isn’t that wild, as long as you don’t have to do too much processing.


#4

Thanks for the suggestions, I will implement them and see how that helps.

I kinda understand what RPL it seems like it’s a native language for ARM?

I looked at the RPL/C/C++ forum but it didn’t help much on explaining the topic. Is there a web page or something I can used to learn more about this and possible some command reference? This makes a lot of sense in that assembler and basic C always runs faster.

Thanks again,
Eric


#5

http://www.tinyclr.com/support/


#6

I didn’t see anything on the support page about RPL/C/C++

Maybe I’m just crazy and it’s staring me in the face…


#7

Did you search the page for “RLP”? :slight_smile:


#8

oh gosh it was staring me in the face! :-[

Thanks a bunch.