Controlling MIDI Output with Delphi

I found it difficult when I first started trying MIDI programming to get any decent documentation or a simple snippet of code to turn MIDI notes on and off. This Delphi source code allows you to control the Windows MIDI notes from within a Delphi application. Create a new unit _midi.pas and copy and paste the code contents below into it.

This allows single or multiple notes to be played. Using these simple commands (all based on the mmsystem.pas unit that talks to the windows MMSYSTEM.DLL) is how I handled all the MIDI in GBK.

Hopefully this helps anyone who is looking for a simple way to get MIDI support into their Delphi application.


{
Instructions:
1) Call OpenMidiOut before playing any notes
2) Call TurnOnMidiNote to start playing a note
   This can be called multiple times for "simultaneous" notes
3) Add some sort of delay for the notes. ie sleep() etc
4) Call TurnOffMidiNote to stop playing all playing note(s)
5) When done, call CloseMidiOut to allow other apps to access the midi port
}

unit _midi;

interface

uses Windows,Messages,SysUtils,Classes,Graphics,Controls,
     Forms,Dialogs,StdCtrls,mmsystem,registry;

function MidiAvailable:boolean;
procedure OpenMidiOut(ParentForm:TForm);
procedure TurnOnMidiNote(Note,Instrument,Volume,Channel,Panning:byte);
procedure TurnOffSingleMidiNote(Note,Channel:byte);
procedure TurnOffAllMidiNotes;
procedure CloseMidiOut;

var MidiOutDev:HMIDIOUT; //global handle to midi device
    MidiOpen:boolean; //refer to this variable to see if a midi device is currently open

implementation

function MidiAvailable:boolean;
begin
     If (midiOutGetNumDevs()=0) then MidiAvailable:=false
     else MidiAvailable:=true;
end;

procedure OpenMidiOut(ParentForm:TForm);
begin
     midiOutOpen(@MidiOutDev,MIDI_MAPPER,ParentForm.Handle,0,CALLBACK_WINDOW);
     MidiOpen:=true;
end;

//note = 0-127 - see table at bottom for values
//inst = 0-127 - see table at bottom for values
//vol  = 0-127
//chan = 0-15
//pan  = 0-127 0=only left 63=both 127=only right
procedure TurnOnMidiNote(Note,Instrument,Volume,Channel,Panning:byte);
begin
     //tell midi device which instrument or patch to use
     midiOutShortMsg(MidiOutDev,MAKEWORD($C0 OR Channel,Instrument));

     //panning
     midiOutShortMsg(midioutdev,strtoint('$'+inttohex(panning,2)+'0AB'+inttohex(channel,1)));

     //tell midi device what note to play
     midiOutShortMsg(MidiOutDev,MAKELONG(MAKEWORD($90 or Channel,Note),MAKEWORD(Volume,0)));
end;

procedure TurnOffSingleMidiNote(Note,Channel:byte);
begin
     //tell midi device what note to play
     midiOutShortMsg(MidiOutDev,MAKELONG(MAKEWORD($80 or Channel,Note),MAKEWORD(0,0)));
end;

procedure TurnOffAllMidiNotes;
begin
     {Reset the midi device}
     midiOutReset(MidiOutDev);
end;

procedure CloseMidiOut;
begin
     {Close the midi device so anyone else can use it.}
     midiOutClose(MidiOutDev);
     MidiOpen:=false;
end;

{
Oct#  C   C#  D   D#  E   F   F#  G   G#  A   A#  B
 -1   0   1   2   3   4   5   6   7   8   9   10  11
  0   12  13  14  15  16  17  18  19  20  21  22  23
  1   24  25  26  27  28  29  30  31  32  33  34  35
  2   36  37  38  39  40  41  42  43  44  45  46  47
  3   48  49  50  51  52  53  54  55  56  57  58  59
  4   60  61  62  63  64  65  66  67  68  69  70  71
  5   72  73  74  75  76  77  78  79  80  81  82  83
  6   84  85  86  87  88  89  90  91  92  93  94  95
  7   96  97  98  99  100 101 102 103 104 105 106 107
  8   108 109 110 111 112 113 114 115 116 117 118 119
  9   120 121 122 123 124 125 126 127
}

{
Acoustic Grand Piano
Bright Acoustic Piano
Electric Grand Piano
Honky-tonk Piano
Electric Piano 1
Electric Piano 2
Harpsichord
Clavi
Celesta
Glockenspiel
Music Box
Vibraphone
Marimba
Xylophone
Tubular Bells
Dulcimer
Drawbar Organ
Percussive Organ
Rock Organ
Church Organ
Reed Organ
Accordion
Harmonica
Tango Accordion
Acoustic Guitar (nylon)
Acoustic Guitar (steel)
Electric Guitar (jazz)
Electric Guitar (clean)
Electric Guitar (muted)
Overdriven Guitar
Distortion Guitar
Guitar harmonics
Acoustic Bass
Electric Bass (finger)
Electric Bass (pick)
Fretless Bass
Slap Bass 1
Slap Bass 2
Synth Bass 1
Synth Bass 2
Violin
Viola
Cello
Contrabass
Tremolo Strings
Pizzicato Strings
Orchestral Harp
Timpani
String Ensemble 1
String Ensemble 2
SynthStrings 1
SynthStrings 2
Choir Aahs
Voice Oohs
Synth Voice
Orchestra Hit
Trumpet
Trombone
Tuba
Muted Trumpet
French Horn
Brass Section
SynthBrass 1
SynthBrass 2
Soprano Sax
Alto Sax
Tenor Sax
Baritone Sax
Oboe
English Horn
Bassoon
Clarinet
Piccolo
Flute
Recorder
Pan Flute
Blown Bottle
Shakuhachi
Whistle
Ocarina
Lead 1 (square)
Lead 2 (sawtooth)
Lead 3 (calliope)
Lead 4 (chiff)
Lead 5 (charang)
Lead 6 (voice)
Lead 7 (fifths)
Lead 8 (bass + lead)
Pad 1 (new age)
Pad 2 (warm)
Pad 3 (polysynth)
Pad 4 (choir)
Pad 5 (bowed)
Pad 6 (metallic)
Pad 7 (halo)
Pad 8 (sweep)
FX 1 (rain)
FX 2 (soundtrack)
FX 3 (crystal)
FX 4 (atmosphere)
FX 5 (brightness)
FX 6 (goblins)
FX 7 (echoes)
FX 8 (sci-fi)
Sitar
Banjo
Shamisen
Koto
Kalimba
Bag pipe
Fiddle
Shanai
Tinkle Bell
Agogo
Steel Drums
Woodblock
Taiko Drum
Melodic Tom
Synth Drum
Reverse Cymbal
Guitar Fret Noise
Breath Noise
Seashore
Bird Tweet
Telephone Ring
Helicopter
Applause
Gunshot
}

end.

I have been using Delphi for years for programming all the applications on Softology. I highly recommend it to anyone interested in programming. If they would only release a cheap/free entry level version for newcomers to play with.

Jason.

11 responses to “Controlling MIDI Output with Delphi

    • OK, simple steps…

      1. Save the _midi.pas (copy the code from the blog post and save as _midi.pas).
      2. Create a new project with a single form (Form1) and a single button (Button1).
      3. This is the code for form1

      
      
      unit Unit1;
      
      interface
      
      uses
        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
        Dialogs, StdCtrls, _midi;
      
      type
        TForm1 = class(TForm)
          Button1: TButton;
          procedure FormCreate(Sender: TObject);
          procedure FormDestroy(Sender: TObject);
          procedure Button1Click(Sender: TObject);
        private
          { Private declarations }
        public
          { Public declarations }
        end;
      
      var
        Form1: TForm1;
      
      implementation
      
      {$R *.dfm}
      
      procedure TForm1.Button1Click(Sender: TObject);
      begin
           TurnOnMidiNote( 60, //C in octave 4
                           14, //tubular bells instrument
                           127, //full volume
                           0, //channel 0
                           63 //middle panning - centered
                           );
           //wait 1 second
           sleep(1000);
           //turn off note
           TurnOffSingleMidiNote( 60, //note to turn off
                                  0 //channel 0
                                  );
      end;
      
      procedure TForm1.FormCreate(Sender: TObject);
      begin
           OpenMidiOut(form1);
      end;
      
      procedure TForm1.FormDestroy(Sender: TObject);
      begin
           CloseMidiOut;
      end;
      
      end.
      
      
      

      OpenMidiOut is called once when the form is first created.
      CloseMidiOut is called when the form is destroyed.
      TurnOnMidiNote plays the note.

      Hope that helps.

      Jason.

      • Hi Jack,

        For panning you pass the panning amount (63 in the above sample code). 0 is full left, 63 is both/centered and 127 is full right.

        So if you have another edit box (eg panedit) with a spinner attached (set the spinner to min 0 and max 127 – default 63), then change the code to

        
        procedure TForm1.Button1Click(Sender: TObject);
        begin
             TurnOnMidiNote( 60, //C in octave 4
                             14, //tubular bells instrument
                             127, //full volume
                             0, //channel 0
                             strtoint(panedit.text) //panning
                             );
             //wait 1 second
             sleep(1000);
             //turn off note
             TurnOffSingleMidiNote( 60, //note to turn off
                                    0 //channel 0
                                    );
        end;
        

        Jason.

  1. Hi Jason
    Im using Churchers component for my midi project. I need to made a programchange and Controller change for my Midi preamp. The programchange Works fine with Midioutput.programchange(2,1,1) command. Now i need to change the gain setting or the Master volume setting. I think i have to use the Midioutput.putshort (); command, but i cant get i to Work? Can you help ?
    Kind regards
    JCA

    • I can’t find any Midi info about Gain, but Volume is supported by midiOutShortMsg

      midiOutShortMsg(MidiOutDev,MAKELONG(MAKEWORD($90 or Channel,Note),MAKEWORD(Volume,0)));

      I haven’t used midi beyond my own simple code above and never for controlling external devices. You would need to contact the author for more help with his components.

      Jason.

  2. Do you have a routine for Delphi2 for MidiIn? The MidiOut works fine and I can control 8 different hardware synths. Now I want to control them via a midi controller.
    Thanks for any suggestion

  3. Hi there,

    i am able to catch midiinput events in Delphi (with the MidiIO Class based on mmssystem) from an USB Midi Controller Akai (LPD8) and to do all what i want with data1, data2, Channel etc..
    In fact i would ilke to send a feedback to the controler (via midiout) to switch on/off the lights of the pads (leds). Is there someone who could help me there ?
    Thanks in advance.

    • I self reply my post because i have just found myself how to light a specific pad, just by playing a note to the midi output interface (where the LPD8 is plugged). I send the channel, the pad number (= the note assigned to this) and the “dynamic” data (example, “1” is working).
      When i want it to be lighted i send a “noteon” with the parameters above, and when i want to light it off, i play a noteOff with the same parameters.

      It works properly.

      Here is a sample of my code, in Delphi (2010):

      FMidiOut.NoteON( FCurrentMapping.FCHPlay, CurrentMapping.FNumPLay, 1);
      FMidiOut.NoteOFF( FCurrentMapping.FCHPlay, FCurrentMapping.FNumPLay, 1);

      //FMidiOut is the object implemented from the class TMidiOutput, derived from TMidiIO.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s