Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wisp
GitHub Repository: wisp/impinj-reader-app
Path: blob/master/MidiDemo/MIDI_control.cs
179 views
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Sanford.Multimedia.Midi;
using Sanford.Multimedia.Midi.UI;

namespace MIDI_Control_Demo
{
    public partial class MIDI_control : Form
    {
        // This class helps store info about the note that is playing.
        private class NoteInfo
        {
            public int instr;
            public int val;
            public int vol;
            public int pitch;
            public int mod;
        }

        private OutputDevice outDevice;

        // Is this always the proper device ID??
        private int outDeviceID = 0;

        private OutputDeviceDialog outDialog;

        private NoteInfo curNote;

        private bool midiStarted = false;

        private bool noteStarted = false;

        static int VELOCITY = 127;

        // Local acceleration data: current value
        private int xac_Scaled;
        private int yac_Scaled;
        private int zac_Scaled;

        // Local accel scaling: min and max recorded values
        private double xac_min, xac_max;
        private double yac_min, yac_max;
        private double zac_min, zac_max;

        // Signals that this is the first run after a load or MIDI reset
        private bool firstRunFlag = true;

        // The little XY plot for testing
        XY_panel myXYPanel;
        //

        public MIDI_control()
        {
            InitializeComponent();
            myXYPanel = new XY_panel(this);
        }

        protected override void OnLoad(EventArgs e)
        {
            updateParamDisplay();

            base.OnLoad(e);

            curNote = new NoteInfo();
        }

        private void updateNote(bool forceRestart)
        {

            if (!midiStarted)
                return;

            if (!noteStarted)
            {
                killNote();
                curNote = new NoteInfo(); // Reset note info
                return;
            }

            // Build up new note candidate
            NoteInfo newNote = new NoteInfo();

            if(opt_modeMan.Checked)
            {
                // All settings manual, no accel control
                newNote.instr = (int)(instrumentSelect.Value);
                newNote.val = (int)(noteSelect.Value);
                newNote.vol = (int)(volumeSelect.Value);
                newNote.pitch = (int)(pitchBendSelect.Value);
                newNote.mod = (int)(modSelect.Value);
            }

            else if(opt_mode1.Checked)
            {
                // X accel controls note value, Y accel controls modulation
                newNote.instr = (int)(instrumentSelect.Value);

                // Use WHOLE TONE SCALE for creepy sounds, and rescale for mid-high range notes
                newNote.val = ((int)((xac_Scaled + 240) / 8)) * 2;

                newNote.vol = yac_Scaled;
                newNote.pitch = (int)(pitchBendSelect.Value);
                newNote.mod = (int)(modSelect.Value);
            }
            else if(opt_mode2.Checked)
            {
                // X accel controls pitch bend, Y accel controls modulation
                newNote.instr = (int)(instrumentSelect.Value);
                newNote.val = (int)(noteSelect.Value);
                newNote.vol = (int)(volumeSelect.Value);
                newNote.pitch = xac_Scaled;
                newNote.mod = yac_Scaled;
            }
            else if(opt_mode3.Checked)
            {
                // X accel controls pitch bend, Y accel controls volume
                newNote.instr = (int)(instrumentSelect.Value);
                newNote.val = (int)(noteSelect.Value);
                newNote.vol = yac_Scaled;
                newNote.pitch = xac_Scaled;
                newNote.mod = (int)(modSelect.Value);
            }
            else
            {
                // None of the option boxes are checked?!
                return;
            }

            // Keep track; should note be restarted after all channel commands
            bool restartNote = forceRestart;

            if (newNote.instr != curNote.instr || forceRestart)
            {
                // Change instrument patch (aka program)
                outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, 0, newNote.instr, 0));
                restartNote = true;
            }

            if (newNote.val != curNote.val)
            {
                // Restarting note will update note value
                restartNote = true;
            }

            if (newNote.vol != curNote.vol || forceRestart)
            {
                // Change master volume (controller # 0x07)
                outDevice.Send(new ChannelMessage(ChannelCommand.Controller, 0, 0x07, newNote.vol));
            }

            if (newNote.pitch != curNote.pitch || forceRestart)
            {
                // Change pitch wheel setting
                outDevice.Send(new ChannelMessage(ChannelCommand.PitchWheel, 0, 0, newNote.pitch));
            }

            if (newNote.mod != curNote.mod || forceRestart)
            {
                // Change Modulation controller (0x01) setting
                outDevice.Send(new ChannelMessage(ChannelCommand.Controller, 0, 0x01, newNote.mod));
            }

            // Restart the note based on what was updated...
            if (restartNote)
            {
                outDevice.Send(new ChannelMessage(ChannelCommand.NoteOff, 0, curNote.val, 0));
                outDevice.Send(new ChannelMessage(ChannelCommand.NoteOn, 0, newNote.val, VELOCITY));
            }

            curNote = newNote;

        }

        private void killNote()
        {
            if (!midiStarted)
                return;

            outDevice.Send(new ChannelMessage(ChannelCommand.NoteOff, 0, curNote.val, 0));

            noteStarted = false;

        }

        private void btn_startMIDI_Click(object sender, EventArgs e)
        {
            if (midiStarted)
            {
                closeMidiOutput();
            }
            else
            {
                // Try to open midi out. If successful, initialize other stuff
                if (!openMidiOutput())
                    return;

                updateNote(true);
                killNote();

                firstRunFlag = true;

            }
           
        }

        private void btn_noteOn_Click(object sender, EventArgs e)
        {
            noteStarted = true;
            updateNote(true);
        }

        private void btn_noteOff_Click(object sender, EventArgs e)
        {
            killNote();
        }


        public void updateAccelValues(double xac, double yac, double zac)
        {
            if (firstRunFlag)
            {
                // Don't use initial values sent in by accel demo, as they are garbage.
                xac_max = yac_max = zac_max = 51;
                xac_min = yac_min = zac_min = 49;
                xac = yac = zac = 50;
                firstRunFlag = false;
            }
            else
            {
                if (xac > xac_max)
                    xac_max = xac;
                if (xac < xac_min)
                    xac_min = xac;

                if (yac > yac_max)
                    yac_max = yac;
                if (yac < yac_min)
                    yac_min = yac;

                if (zac > zac_max)
                    zac_max = zac;
                if (zac < zac_min)
                    zac_min = zac;

                /*
                // High pass filter the max/min vals
                xac_max = 0.9 * xac_max + 0.1 * (xac_max + xac_min) / 2;
                xac_min = 0.9 * xac_min + 0.1 * (xac_max + xac_min) / 2;
                yac_max = 0.9 * yac_max + 0.1 * (xac_max + xac_min) / 2;
                yac_min = 0.9 * yac_min + 0.1 * (xac_max + xac_min) / 2;
                zac_max = 0.9 * zac_max + 0.1 * (xac_max + xac_min) / 2;
                zac_min = 0.9 * zac_min + 0.1 * (xac_max + xac_min) / 2;
                */
                
                // Autoscale based on historical min/max values
                xac_Scaled = (int)Math.Round(((xac - xac_min) * 127) / (xac_max - xac_min));
                yac_Scaled = (int)Math.Round(((yac - yac_min) * 127) / (yac_max - yac_min));
                zac_Scaled = (int)Math.Round(((zac - zac_min) * 127) / (zac_max - zac_min));
            }
            this.updateNote(false);
        }

        private void openTestPanel_CheckedChanged(object sender, EventArgs e)
        {
            if (openTestPanel.Checked)
                myXYPanel.Show();
            else
                myXYPanel.Hide();
        }

        public void testPanelHidden()
        {
            openTestPanel.Checked = false;
        }

        private void MIDI_control_FormClosing(object sender, FormClosingEventArgs e)
        {
            closeMidiOutput();

            if (!myXYPanel.IsDisposed)
                myXYPanel.Close();
        }


        private bool openMidiOutput()
        {

            outDialog = new OutputDeviceDialog();

            if (OutputDevice.DeviceCount == 0)
            {
                MessageBox.Show("No MIDI output devices available.", "Error!",
                    MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return false;
            }
            else
            {
                try
                {
                    outDevice = new OutputDevice(outDeviceID);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error!",
                        MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    return false;
                }

            }

            // Flag that MIDI output has successfully initialized
            midiStarted = true;
            btn_startMIDI.Text = "Stop MIDI";
            btn_startMIDI.BackColor = Color.LightCoral;

            return true;
        }


        private void closeMidiOutput()
        {
            // Flag that MIDI out will/has stop(ped)
            midiStarted = false;
            btn_startMIDI.Text = "Start MIDI";
            btn_startMIDI.BackColor = Color.DarkSeaGreen;

            try
            {
                if (outDevice != null && !outDevice.IsDisposed)
                    outDevice.Dispose();
            }
            catch (Exception e)
            {

            }

        }

        private void updateParamDisplay()
        {
            if (opt_modeMan.Checked)
            {
                // All settings manual, no accel control
                instrumentSelect.Enabled = true;
                noteSelect.Enabled = true;
                volumeSelect.Enabled = true;
                pitchBendSelect.Enabled = true;
                modSelect.Enabled = true;
            }

            else if (opt_mode1.Checked)
            {

                // X accel controls note value, Y accel controls modulation
                instrumentSelect.Enabled = true;
                noteSelect.Enabled = false;
                volumeSelect.Enabled = false;
                pitchBendSelect.Enabled = true;
                modSelect.Enabled = true;
            }
            else if (opt_mode2.Checked)
            {
                // X accel controls pitch bend, Y accel controls modulation
                instrumentSelect.Enabled = true;
                noteSelect.Enabled = true;
                volumeSelect.Enabled = true;
                pitchBendSelect.Enabled = false;
                modSelect.Enabled = false;
            }
            else if (opt_mode3.Checked)
            {
                // X accel controls pitch bend, Y accel controls volume
                instrumentSelect.Enabled = true;
                noteSelect.Enabled = true;
                volumeSelect.Enabled = false;
                pitchBendSelect.Enabled = false;
                modSelect.Enabled = true;
            }
            else
            {
                // None of the option boxes are checked?!
                return;
            }
        }

        private void opt_modeMan_CheckedChanged(object sender, EventArgs e)
        {
            updateParamDisplay();
        }

        private void opt_mode1_CheckedChanged(object sender, EventArgs e)
        {
            updateParamDisplay();
        }

        private void opt_mode2_CheckedChanged(object sender, EventArgs e)
        {
            updateParamDisplay();
        }

        private void opt_mode3_CheckedChanged(object sender, EventArgs e)
        {
            updateParamDisplay();
        }




    }
}