/// <summary>
        /// Create a MPTK Midi event from a midi input message
        /// </summary>
        /// <param name="data"></param>
        public MPTKEvent(ulong data)
        {
            Source  = (uint)(data & 0xffffffffUL);
            Command = (MPTKCommand)((data >> 32) & 0xFF);
            if (Command < MPTKCommand.Sysex)
            {
                Channel = (int)Command & 0xF;
                Command = (MPTKCommand)((int)Command & 0xF0);
            }
            byte data1 = (byte)((data >> 40) & 0xff);
            byte data2 = (byte)((data >> 48) & 0xff);

            if (Command == MPTKCommand.NoteOn && data2 == 0)
            {
                Command = MPTKCommand.NoteOff;
            }

            //if ((int)Command != 0xFE)
            //    Debug.Log($"{data >> 32:X}");

            switch (Command)
            {
            case MPTKCommand.NoteOn:
                Value    = data1;  // Key
                Velocity = data2;
                Duration = -1;     // no duration are defined in Midi flux
                break;

            case MPTKCommand.NoteOff:
                Value    = data1;  // Key
                Velocity = data2;
                break;

            case MPTKCommand.KeyAfterTouch:
                Value    = data1;  // Key
                Velocity = data2;
                break;

            case MPTKCommand.ControlChange:
                Controller = (MPTKController)data1;
                Value      = data2;
                break;

            case MPTKCommand.PatchChange:
                Value = data1;
                break;

            case MPTKCommand.ChannelAfterTouch:
                Value = data1;
                break;

            case MPTKCommand.PitchWheelChange:
                Value = data2 << 7 | data1;     // Pitch-bend is transmitted with 14-bit precision.
                break;
            }
        }
        /*
         * fluid_channel_cc
         */
        public void fluid_channel_cc(MPTKController numController, int valueController)
        {
            cc[(int)numController] = (short)valueController;

            if (synth.VerboseController)
            {
                Debug.LogFormat("ChangeController\tChannel:{0}\tControl:{1}\tValue:{2}", channum, numController, valueController);
            }

            switch (numController)
            {
            case MPTKController.Sustain:
            {
                if (valueController < 64)
                {
                    /*      printf("** sustain off\n"); */
                    synth.fluid_synth_damp_voices(channum);
                }
                else
                {
                    /*      printf("** sustain on\n"); */
                }
            }
            break;

            case MPTKController.BankSelect:
                banknum = valueController;
                break;

            case MPTKController.BankSelectLsb:
            {
                // Not implemented
                // FIXME: according to the Downloadable Sounds II specification, bit 31 should be set when we receive the message on channel 10 (drum channel)
                //TBC fluid_channel_set_banknum(chan, (((unsigned int)value & 0x7f) + ((unsigned int)chan->bank_msb << 7)));
            }
            break;

            case MPTKController.AllNotesOff:
                synth.fluid_synth_noteoff(channum, -1);
                break;

            case MPTKController.AllSoundOff:
                synth.fluid_synth_soundoff(channum);
                break;

            case MPTKController.ResetAllControllers:
                fluid_channel_init_ctrl();
                synth.fluid_synth_modulate_voices_all(channum);
                break;

            //case MPTKController.DATA_ENTRY_MSB:
            //    {
            //        //int data = (value << 7) + chan->cc[DATA_ENTRY_LSB];

            ///* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74)  */
            //if ((chan->cc[NRPN_MSB] == 120) && (chan->cc[NRPN_LSB] < 100))
            //{
            //    float val = fluid_gen_scale_nrpn(chan->nrpn_select, data);
            //    FLUID_LOG(FLUID_WARN, "%s: %d: Data = %d, value = %f", __FILE__, __LINE__, data, val);
            //    fluid_synth_set_gen(chan->synth, chan->channum, chan->nrpn_select, val);
            //}
            //    break;
            //}

            //case MPTKController.NRPN_MSB:
            //    cc[(int)MPTKController.NRPN_LSB] = 0;
            //    nrpn_select = 0;
            //    break;

            //case MPTKController.NRPN_LSB:
            //    /* SontFont 2.01 NRPN Message (Sect. 9.6, p. 74)  */
            //    if (cc[(int)MPTKController.NRPN_MSB] == 120)
            //    {
            //        if (value == 100)
            //        {
            //            nrpn_select += 100;
            //        }
            //        else if (value == 101)
            //        {
            //            nrpn_select += 1000;
            //        }
            //        else if (value == 102)
            //        {
            //            nrpn_select += 10000;
            //        }
            //        else if (value < 100)
            //        {
            //            nrpn_select += value;
            //            Debug.LogWarning(string.Format("NRPN Select = {0}", nrpn_select));
            //        }
            //    }
            //    break;

            //case MPTKController.RPN_MSB:
            //    break;

            //case MPTKController.RPN_LSB:
            //    // erase any previously received NRPN message
            //    cc[(int)MPTKController.NRPN_MSB] = 0;
            //    cc[(int)MPTKController.NRPN_LSB] = 0;
            //    nrpn_select = 0;
            //    break;

            default:
                if (synth.MPTK_ApplyRealTimeModulator)
                {
                    synth.fluid_synth_modulate_voices(channum, 1, (int)numController);
                }
                break;
            }
        }