Search code examples
c#wpfnaudio

MIDI sound only in one channel (left or right)


I have an WPF application which need to play MIDI sound but only in one channel - on left or right headset channel.

I have a piece of a code that plays a MIDI note (in both channels), however I don't know how to modify it to play the MIDI sound ONLY on the RIGHT or ONLY on the LEFT channel.

This is my code:

public static class MidiNote
{
    public static MidiOut MidiOut = new MidiOut(0);
    public static void PlayNote(int key, int duration)
    {
        MidiOut.Volume = 65535;
        MidiOut.Send(MidiMessage.StartNote(key, 127, 1).RawData);
        Thread.Sleep(duration);
        MidiOut.Send(MidiMessage.StopNote(key, 0, 1).RawData);
    }
}

I found some partial answers but I do not know how can I use them. I read these two articles:

Play sound on specific channel with NAudio

How to play sound only on the left channel of the headphone and only on the right channel of the headphone in c#?


Solution

  • When you are using MIDI you have to send an appropriate MIDI message (a Control- Change or CC message) to the MIDI OUT device to tell it what PAN setting you want to use.

    The PAN setting value should be in the range of 0-127 where:

    • 0 = PAN HARD LEFT
    • 127 = PAN HARD RIGHT
    • 64 = PAN CENTER

    I used your code as starting point and added a few methods to allow adjusting the PAN setting of the MIDI OUT device.

    My example calls to demonstrate the PANNING are written as if it were a CONSOLE app but the new methods should work fine in your existing class / WPF application.

    public static class MidiNote
    {
        public static MidiOut MidiOut = new MidiOut(0);
    
        public static void PlayNote(int key, int duration)
        {
            MidiOut.Volume = 65535;
            MidiOut.Send(MidiMessage.StartNote(key, 127, 1).RawData);
            Thread.Sleep(duration);
            MidiOut.Send(MidiMessage.StopNote(key, 0, 1).RawData);
        }
    
        public static void SetPanHardLeft()
        {
            var panSettingHardLeft = 0;
            var cce = new ControlChangeEvent(0L, 1, MidiController.Pan, panSettingHardLeft);
            MidiOut.Send( cce.GetAsShortMessage() );
        }
    
        public static void SetPanHardRight()
        {
            var panSettingHardRight = 127;
            var cce = new ControlChangeEvent(0L, 1, MidiController.Pan, panSettingHardRight);
            MidiOut.Send(cce.GetAsShortMessage());
        }
    
        public static void SetPanCenter()
        {
            var panSettingCenter = 64;
            var cce = new ControlChangeEvent(0L, 1, MidiController.Pan, panSettingCenter);
            MidiOut.Send(cce.GetAsShortMessage());
        }
    
        public static void PlayNoteRightChannel(int key, int duration)
        {
            var panSettingHardRight = 127;
            var cce = new ControlChangeEvent(0L, 1, MidiController.Pan, panSettingHardRight);
            MidiOut.Send(cce.GetAsShortMessage());
    
            PlayNote(key, duration);
        }
    
        static void Main(string[] args)
        {
            // Plays using current setting (probably CENTER)
            Console.WriteLine( "Pan setting unchanged (should be CENTER)");
            PlayNote( 50, 2000 );
    
            // Set the PAN for the MIDI device to HARD LEFT...
            Console.WriteLine("Pan setting HARD LEFT");
            SetPanHardLeft();
            // ...and play the note again
            PlayNote( 50, 2000);
    
            // Set the PAN for the MIDI device to HARD RIGHT...
            Console.WriteLine("Pan setting HARD RIGHT");
            SetPanHardRight();
            // ...and play the note again
            PlayNote(50, 2000);
    
            // Set the PAN for the MIDI device back to CENTER...
            Console.WriteLine("Pan setting CENTER");
            SetPanCenter();
            // ...and play the note one last time
            PlayNote(50, 2000);
        }
    
    }