Version:0.9 StartHTML:0000000105 EndHTML:0000302398 StartFragment:0000000141 EndFragment:0000302362
//  still on work - next is form - done
// by Robert Rossmair, June 5 0
// by Robert Rossmair, June 5 2002
// by Max, August 2024 , November 2024

//ref: https://github.com/project-jedi/jcl/blob/master/jcl/source/common/JclMIDI.pas
//https://github.com/project-jedi/jcl/blob/master/jcl/examples/common/multimedia/MidiOutExampleMain.pas
unit MidiOutExampleMain_Form_Tuning_14;
interface
{uses
  SysUtils, Classes, Controls, Forms, Menus, StdCtrls, ComCtrls, Buttons, Spin,
  JclMIDI; }
  
 
const
  MIDI_NOTE_ON = $90;
  MIDI_NOTE_OFF = $80;
  MIDI_CHANGE_INSTRUMENT = $C0;
  MIDI_DEVICE = 0;
  MIDI_VEL = 108;
  MIDI_PERCUSSION_CHANNEL = 9; 
  
  
 
type
  TTuningDialog = {class(}TForm;
  var
    OKBtn: TButton;
    CancelBtn: TButton;
    Bevel1: TBevel;
    MIDIFreq: TEdit;
    FreqHertz: TEdit;
    MIDIFreqLabel: TLabel;
    FreqLabel: TLabel;
    MIDIKey: TSpinEdit;
    MIDIKeyLabel: TLabel;
    NoteLabel: TLabel;
    procedure TTuningDialogMIDIKeyChange(Sender: TObject);
    procedure TTuningDialogMIDIFreqChange(Sender: TObject);
    procedure TTuningDialogFreqHertzChange(Sender: TObject);
    procedure TTuningDialogMIDIFreqExit(Sender: TObject);
    procedure TTuningDialogFreqHertzExit(Sender: TObject);
  //private
  var
    FInMIDIFreqChange: Boolean;
    FInFreqHertzChange: Boolean;
    FChangingFrequency: Boolean;
    FChangingMidiFrequency: Boolean;
    FFrequency: Single;
    FMidiFrequency: Single;
    procedure SetFrequency(Value: Single);
    procedure SetMidiFrequency(Value: Single);
  //public
  var
    {property} Frequency: Single;// read FFrequency write SetFrequency; // Hertz
    {property} MidiFrequency: Single; // read FMidiFrequency write SetMidiFrequency;
 //end;
var
  TuningDialog: TTuningDialog;

type
  TKeyboard = {class(}TForm;
  var
    Key48: TSpeedButton;
    Key49: TSpeedButton;
    Key51: TSpeedButton;
    Key50: TSpeedButton;
    Key55: TSpeedButton;
    Key54: TSpeedButton;
    Key53: TSpeedButton;
    Key52: TSpeedButton;
    Key58: TSpeedButton;
    Key56: TSpeedButton;
    Key59: TSpeedButton;
    Key57: TSpeedButton;
    MidiProgramNum: TSpinEdit;
    Label1: TLabel;
    KeyMenu: TPopupMenu;
    TuningItem: TMenuItem;
    Key72: TSpeedButton;
    Key74: TSpeedButton;
    Key76: TSpeedButton;
    Key77: TSpeedButton;
    Key73: TSpeedButton;
    Key75: TSpeedButton;
    Key79: TSpeedButton;
    Key81: TSpeedButton;
    Key83: TSpeedButton;
    Key78: TSpeedButton;
    Key80: TSpeedButton;
    Key82: TSpeedButton;
    Key60: TSpeedButton;
    Key62: TSpeedButton;
    Key64: TSpeedButton;
    Key65: TSpeedButton;
    Key61: TSpeedButton;
    Key63: TSpeedButton;
    Key67: TSpeedButton;
    Key69: TSpeedButton;
    Key71: TSpeedButton;
    Key66: TSpeedButton;
    Key68: TSpeedButton;
    Key70: TSpeedButton;
    PitchBender: TTrackBar;
    btnAllNotesOff: TButton;
    cbMidiOutSelect: TComboBox;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    ModWheel: TTrackBar;
    procedure TKeyboardFormCreate(Sender: TObject);
    procedure TKeyboardKeyMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure TKeyboardKeyMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure TKeyboardMidiProgramNumChange(Sender: TObject);
    procedure TKeyboardTuningItemClick(Sender: TObject);
    procedure TKeyboardPitchBenderChange(Sender: TObject);
    procedure TKeyboardKeyClick(Sender: TObject);
    procedure TKeyboardbtnAllNotesOffClick(Sender: TObject);
    procedure TKeyboardcbMidiOutSelectChange(Sender: TObject);
    procedure TKeyboardModWheelChange(Sender: TObject);
  //private
  var
    FMidiOut: IJclMidiOut;
    FChannel: TMidiChannel;
    //Keys: array[TMidiNote] of TSpeedButton;
      Keys: array[48..83] of TSpeedButton;
    procedure InitKeyboard;
    procedure AllNotesOff;
 // end;
var
  Keyboard: TKeyboard;
implementation
//uses MidiOutExampleTuningDlg;

//{$R *.dfm}
const
  HalftonesPerOctave = 12;
  MiddleA            = 440.0; // Hertz
  MidiMiddleA        = 69;    // A3 = 440 Hertz
  Digits = 6;
  MIDIFreqMax = 127.99993896;
  FreqHertzMin = 8.17579892;
  FreqHertzMax = 13289.70346552;
function Hertz(MIDINote: Extended): Extended;
begin
  result := TwoToY((MIDINote - MidiMiddleA) / HalftonesPerOctave) * MiddleA;
end;
function MIDINote(Hertz: Extended): Extended;
begin
  if Hertz < 1.0 then
    result := mininteger //minint; //Low(Integer)
  else
    result := LogBase2(Hertz / MiddleA) * HalftonesPerOctave + MidiMiddleA;
end;
procedure TTuningDialogMIDIKeyChange(Sender: TObject);
begin
  MIDIFrequency := MIDIKey.Value;
  NoteLabel.Caption := MidiNoteToStr(MIDIKey.Value);
end;
{$DEFINE COMPILER6_UP}
procedure TTuningDialogMIDIFreqChange(Sender: TObject);
var
  F: Double;
begin
  if FInFreqHertzChange or (MIDIFreq.Text = '') then
    Exit;
  FInMIDIFreqChange := True;
  try
    {$IFDEF COMPILER6_UP}
    if TryStrToFloat(MidiFreq.Text, F) then
    {$ELSE}
    if TextToFloat(PChar(MidiFreq.Text), F, fvExtended) then
    {$ENDIF COMPILER6_UP}
      FMidiFrequency := F;
      SetMidiFrequency(F);
  finally
    FInMIDIFreqChange := False;
  end;
end;
procedure TTuningDialogFreqHertzChange(Sender: TObject);
var
  F: Double; //Extended;
begin
  if FInMIDIFreqChange or (FreqHertz.Text = '') then
    Exit;
  FInFreqHertzChange := True;
  try
    {$IFDEF COMPILER6_UP}
    if TryStrToFloat(FreqHertz.Text, F) then
    {$ELSE}
    if TextToFloat(PChar(FreqHertz.Text), F, fvExtended) then
    {$ENDIF COMPILER6_UP}
      FFrequency := F;
      SetFrequency(F);
  finally
    FInFreqHertzChange := False;
  end;
end;
procedure SetFrequency(Value: Single);
begin
  if FChangingFrequency or (Value = Frequency) then
    Exit;
  FChangingFrequency := True;
  try
    if Value < FreqHertzMin then
      Value := FreqHertzMin
    
else
    if Value > FreqHertzMax then
      Value := FreqHertzMax;
    FFrequency := Value;
    if not FInFreqHertzChange then
      FreqHertz.Text := FloatToStrF(Value, ffFixed, 9, 6);
    MidiFrequency := MIDINote(Value);
  finally
    FChangingFrequency := False;
  end;
end;
procedure SetMidiFrequency(Value: Single);
begin
  if FChangingMidiFrequency then
  // or (Value = MidiFrequency) then
    Exit;
  if Value < 0 then
    Value := 0
  else
  if Value > MidiFreqMax then
    Value := MidiFreqMax;
  FChangingMidiFrequency := True;
  try
    FMidiFrequency := Value;
    if not FInMidiFreqChange then
      MIDIFreq.Text := FloatToStrF(Value, ffFixed, 9, 6);
    Frequency := Hertz(Value);
  finally
    FChangingMidiFrequency := False;
  end;
end;
procedure TTuningDialogMIDIFreqExit(Sender: TObject);
begin
  MIDIFreq.Text := FloatToStrF(MidiFrequency, ffFixed, 9, 6);
end;
procedure TTuningDialogFreqHertzExit(Sender: TObject);   //2012   D
begin
  FreqHertz.Text := FloatToStrF(Frequency, ffFixed, 9,6);
end;
//----------------------------TKeyboard----------------------------------//
procedure TKeyboardFormCreate(Sender: TObject);
begin
  FChannel := 1;
  GetMidiOutputs(cbMidiOutSelect.Items);
  cbMidiOutSelect.ItemIndex := 0;
  TKeyboardcbMidiOutSelectChange(Self);
  InitKeyboard;
end;
procedure InitKeyboard;
var
  Note: TMidiNote;
begin
  for Note := Low(Keys) to High(Keys) do begin
    //writ('debug '+itoa(low(keys))+ '  '+itoa(high(keys)));  
    //writ('debug: '+itoa(note)); 
    //Keys[Note] := FindComponent2(Format('Key%d', [Note])) as TSpeedButton;
    try
      //Keys[Note]:= TSpeedButton(FindComponent2('Key69'));
      Keys[Note]:= keyboard.FindComponent(Format('Key%d', [Note])) as TSpeedButton;
    except
      writeln(ExceptionToString(ExceptionType, ExceptionParam));  
    
end;  
    
// writ(objtostr(keys[note]));
    if Keys[Note] <> nil then
      with Keys[Note] do begin
        //objtostr(keys[note]);
        //PopupMenu := KeyMenu;
        showhint:= true;
        Hint := Format('MIDI Note #%d'#13#10'%s', [tag, MidiNoteToStr(tag)]);
      end;
  end;
end;
procedure {TKeyboard}AllNotesOff;
var
  Note: TMidiNote;
begin
  if Assigned(FMidiOut) then
    FMidiOut.SwitchAllNotesOff(FChannel);
  //for Note := Low(Note) to High(Note) do
   for Note := 48 to 83 do
    if Assigned(Keys[Note]) then
      Keys[Note].Down := False;
end;
procedure TKeyboardKeyMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    if (Sender as TSpeedButton).Down then
      FMidiOut.SendNoteOff(FChannel, TComponent(Sender).Tag, 127)
    else
      FMidiOut.SendNoteOn(FChannel, TComponent(Sender).Tag, 127);
    writ('note trace: '+MidiNoteToStr(TComponent(Sender).Tag));  
end;
procedure TKeyboardKeyMouseUp(
  Sender: TObject;
  Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    FMidiOut.SendNoteOff(FChannel, TComponent(Sender).Tag, 127);
end;
procedure TKeyboardMidiProgramNumChange(Sender: TObject);
begin
  FMidiOut.SendProgramChange(FChannel, MidiProgramNum.Value);
end;
procedure TKeyboardTuningItemClick(Sender: TObject);
begin
  {with TuningDialog do
  begin
    MIDIKey.Value := KeyMenu.PopupComponent.Tag;
    if ShowModal = mrOK then
      FMidiOut.SendSingleNoteTuningChange(0, 0, [MidiSingleNoteTuningData(MIDIKey.Value, MIDIFrequency)]);
  end; }
end;
const MIDIPitchWheelCenter  = 1 shl (BitsPerMIDIDataWord - 1);
procedure TKeyboardPitchBenderChange(Sender: TObject);
begin
  FMidiOut.SendPitchWheelChange(FChannel, PitchBender.Position + MidiPitchWheelCenter);
end;
procedure TKeyboardModWheelChange(Sender: TObject);
begin
  FMidiOut.SendModulationWheelChangeHR(FChannel, ModWheel.Position);
end;
procedure TKeyboardKeyClick(Sender: TObject);
begin
  //with Sender as TSpeedButton do
   with TSpeedButton(sender) do begin
    if Down then
      FMidiOut.SendNoteOn(FChannel, TComponent(Sender).Tag, 127)
    else
      FMidiOut.SendNoteOff(FChannel, TComponent(Sender).Tag, 127);
  end;
end;
procedure TKeyboardbtnAllNotesOffClick(Sender: TObject);
begin
  AllNotesOff;
end;
procedure TKeyboardcbMidiOutSelectChange(Sender: TObject);
begin
  AllNotesOff;
  FMidiOut := MidiOut(cbMidiOutSelect.ItemIndex);
  FMidiOut.SendProgramChange(FChannel, MidiProgramNum.Value);
end;
{const
  HalftonesPerOctave = 12;
  MiddleA            = 440.0; // Hertz
  MidiMiddleA        = 69;    // A3 = 440 Hertz
  Digits = 6;
  MIDIFreqMax = 127.99993896;
  FreqHertzMin = 8.17579892;
  FreqHertzMax = 13289.70346552;   }
function Hertz2(MIDINote: Extended): Extended;
begin
  result:= TwoToY((MIDINote - MidiMiddleA) / HalftonesPerOctave) * MiddleA;
end;
procedure loadtuningform;
begin
 TuningDialog:= TTuningDialog.create(self)
 with tuningdialog do begin
  Left:= 245
  Top:= 108
  BorderStyle:= bsDialog
  Caption
:= 'Tuning'
  ClientHeight:= 177
  ClientWidth:= 313
  Color:= clBtnFace
  Font
.Charset:= DEFAULT_CHARSET
  Font
.Color:= clWindowText
  Font
.Height:= -11
  Font.Name:= 'MS Sans Serif'
  Font.Style:= []
  OldCreateOrder:= True
  Position
:= poScreenCenter
  PixelsPerInch
:= 96
  //TextHeight:= 13
  //Showmodal;
  Bevel1:= TBevel.create(tuningdialog)
  //end;
  with bevel1 do begin
   parent:= tuningdialog;
    Left:= 8
    Top:= 8
    Width:= 297
    Height:= 117
    Shape:= bsFrame
  
end;
   MIDIFreqLabel:= TLabel.create(tuningdialog);
   with MIDIFreqLabel do begin
   parent:= tuningdialog;
    Left:= 36
    Top:= 56
    Width:= 110
    Height:= 13
    Caption:= '&MIDI relative frequency'
  end;
  FreqLabel:= TLabel.create(tuningdialog);
   with FreqLabel do begin
   parent:= tuningdialog;
    Left:= 36
    Top:= 84
    Width:= 84
    Height:= 13
    Caption:= 'Frequency [Hertz]'
  end;
  MIDIKeyLabel:= TLabel.create(tuningdialog);
   with MIDIKeyLabel do begin
   parent:= tuningdialog;
    Left:= 36
    Top:= 28
    Width:= 81
    Height:= 13
    Caption:= 'MIDI key number'
  end;
  noteLabel:= TLabel.create(tuningdialog);
   with noteLabel do begin
   parent:= tuningdialog;
    Left:= 224
    Top:= 28
    Width:= 49
    Height:= 13
    Caption:= 'NoteLabel'
  end;
  OKBtn:= TButton.create(tuningdialog)
  with okbtn do begin
   parent:= tuningdialog
    Left
:= 79
    Top:= 140
    Width:= 75
    Height:= 25
    Caption:= 'OK'
    Default:= True
    ModalResult
:= 1
    TabOrder:= 0
  end;
  cancelBtn:= TButton.create(tuningdialog)
  with cancelbtn do begin
   parent:= tuningdialog
    Left
:= 159
    Top:= 140
    Width:= 75
    Height:= 25
    Cancel:= True
    Caption
:= 'Cancel'
    ModalResult:= 2
    TabOrder:= 1
  end;
  MIDIFreq:= TEdit.create(tuningdialog)
  with midifreq do begin
   parent:= tuningdialog;
    Left:= 156
    Top:= 52
    Width:= 121
    Height:= 21
    TabOrder:= 2
    OnChange:= @TTuningDialogMIDIFreqChange;
    OnExit:= @TTuningDialogMIDIFreqExit;
  end ;
  //object FreqHertz: TEdit
  FreqHertz:= TEdit.create(tuningdialog)
  with FreqHertz do begin
   parent:= tuningdialog;
    Left:= 156
    Top:= 80
    Width:= 121
    Height:= 21
    TabOrder:= 3
    OnChange:= @TTuningDialogFreqHertzChange
    OnExit
:= @TTuningDialogFreqHertzExit
  
end ;
  MIDIKey:= TSpinEdit.create(tuningdialog)
  with midikey do begin
   parent:= tuningdialog;
    Left:= 156
    Top:= 24
    Width:= 53
    Height:= 22
    MaxValue:= 127
    MinValue:= 0
    TabOrder:= 4
    Value:= 0
    OnChange:= @TTuningDialogMIDIKeyChange;
  end;
  showmodal;
 end;
end; 
procedure loadKeyboardform;
begin
Keyboard:= TKeyboard.create(self)
with keyboard do begin
  Left:= 145
  Top:= 347
  ClientWidth:= 520
  ClientHeight:= 223
  HorzScrollBar.Range:= 517
  VertScrollBar.Range:= 209
  ActiveControl:= MidiProgramNum
  AutoScroll
:= False
  icon
.loadfromresourcename(hinstance, 'Piano');
  Caption:= 'MIDI Keys Example 0.92/ maXbox5'
  Color:= clBtnFace
  Font
.Charset:= DEFAULT_CHARSET
  Font
.Color:= clWindowText
  Font
.Height:= 11
  Font.Name:= 'MS Sans Serif'
  //Font.Pitch:= fpVariable
  Font.Style:= []
  OldCreateOrder:= True
  ShowHint
:= True
  OnCreate
:= @TKeyboardFormCreate;
  PixelsPerInch:= 96
  //TextHeight:= 13
  Show;
  Key48:= TSpeedButton.create(keyboard)
  with key48 do begin
    parent:= keyboard;
    Tag:= 48
    Left:= 4
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 48
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key50:= TSpeedButton.create(keyboard)
  with key50 do begin
    parent:= keyboard;
    Tag:= 50
    Left:= 24
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 50
     OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key52:= TSpeedButton.create(keyboard)
  with key52 do begin
    parent:= keyboard;
    Tag:= 52
    Left:= 44
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 52
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key53:= TSpeedButton.create(keyboard)
  with key53 do begin
    parent:= keyboard;
    Tag:= 53
    Left:= 64
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 53
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end ;
  Key49:= TSpeedButton.create(keyboard)
  with key49 do begin
    parent:= keyboard;
    Tag:= 49
    Left:= 18
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 49
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key51:= TSpeedButton.create(keyboard)
  with key51 do begin
    parent:= keyboard;
    Tag:= 51
    Left:= 38
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 51
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key55:= TSpeedButton.create(keyboard)
  with key55 do begin
    parent:= keyboard;
    Tag:= 55
    Left:= 84
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 55
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
 Key57:= TSpeedButton.create(keyboard)
  with key57 do begin
    parent:= keyboard;
    Tag:= 57
    Left:= 104
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 57
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key59:= TSpeedButton.create(keyboard)
  with key59 do begin
    parent:= keyboard;
    Tag:= 59
    Left:= 124
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 59
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key54:= TSpeedButton.create(keyboard)
  with key54 do begin
    parent:= keyboard;
    Tag:= 54
    Left:= 78
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 54
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key56:= TSpeedButton.create(keyboard)
  with key56 do begin
    parent:= keyboard;
    Tag:= 56
    Left:= 98
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 56
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key58:= TSpeedButton.create(keyboard)
  with key58 do begin
    parent:= keyboard;
    Tag:= 58
    Left:= 118
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 58
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key60:= TSpeedButton.create(keyboard)
  with key60 do begin
    parent:= keyboard;
    Tag:= 60
    Left:= 144
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 60
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key62:= TSpeedButton.create(keyboard)
  with key62 do begin
    parent:= keyboard;
    Tag:= 62
    Left:= 164
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 62
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end;
  Key64:= TSpeedButton.create(keyboard)
  with key64 do begin
    parent:= keyboard;
    Tag:= 64
    Left:= 184
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 64
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end;
  Key65:= TSpeedButton.create(keyboard)
  with key65 do begin
    parent:= keyboard;
    Tag:= 65
    Left:= 204
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 65
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end;
  Key61:= TSpeedButton.create(keyboard)
  with key61 do begin
    parent:= keyboard;
    Tag:= 61
    Left:= 158
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 61
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end;
  Key63:= TSpeedButton.create(keyboard)
  with key63 do begin
    parent:= keyboard;
    Tag:= 63
    Left:= 178
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 63
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end ;
  Key67:= TSpeedButton.create(keyboard)
  with key67 do begin
    parent:= keyboard;
    name:= 'key67';
    //glyph.loadfromresourcename (hinstance, 'C10');
    Tag:= 67
    Left:= 224
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 67
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end;
  Key69:= TSpeedButton.create(keyboard)
  with key69 do begin
    parent:= keyboard;
    name:= 'Key69';
    Tag:= 69
    Left:= 244
    Top:= 36
    Width:= 21
    Height:= 97
    color:= clblack;
    //glyph.loadfromresourcename (hinstance, 'citymax');
    AllowAllUp:= True
    GroupIndex
:= 69
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end ;
  Key71:= TSpeedButton.create(keyboard)
  with key71 do begin
    parent:= keyboard;
    //glyph.loadfromresourcename (hinstance, 'C10');
    name:= 'key71';
    Tag:= 71
    Left:= 264
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 71
    OnMouseDown:=  @TKeyboardKeyMouseDown;
  end;
  Key66:= TSpeedButton.create(keyboard)
  with key66 do begin
    parent:= keyboard;
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 66
    Left:= 218
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 66
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key68:= TSpeedButton.create(keyboard)
  with key68 do begin
    parent:= keyboard;
    name:= 'key68';
    glyph.loadfromresourcename (hinstance, 'citymax');
   // glyph.loadfromresourcename (hinstance, 'C10');
    Tag:= 68
    Left:= 238
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 68
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key70:= TSpeedButton.create(keyboard)
  with key70 do begin
    parent:= keyboard;
    name:= 'key70';
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 70
    Left:= 258
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 70
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key72:= TSpeedButton.create(keyboard)
  with key72 do begin
    parent:= keyboard;
    //glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 72
    Left:= 284
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 72
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key74:= TSpeedButton.create(keyboard)
  with key74 do begin
    parent:= keyboard;
    Tag:= 74
    Left:= 304
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 74
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key76:= TSpeedButton.create(keyboard)
  with key76 do begin
    parent:= keyboard;
    Tag:= 76
    Left:= 324
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 76
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key77:= TSpeedButton.create(keyboard)
  with key77 do begin
    parent:= keyboard;
    Tag:= 77
    Left:= 344
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 77
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key73:= TSpeedButton.create(keyboard)
  with key73 do begin
    parent:= keyboard;
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 73
    Left:= 298
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 73
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key75:= TSpeedButton.create(keyboard)
  with key75 do begin
    parent:= keyboard;
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 75
    Left:= 318
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 75
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key79:= TSpeedButton.create(keyboard)
  with key79 do begin
    parent:= keyboard;
    //glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 79
    Left:= 364
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 79
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key81:= TSpeedButton.create(keyboard)
  with key81 do begin
    parent:= keyboard;
    Tag:= 81
    Left:= 384
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 81
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key83:= TSpeedButton.create(keyboard)
  with key83 do begin
    parent:= keyboard;
    Tag:= 83
    Left:= 404
    Top:= 36
    Width:= 21
    Height:= 97
    AllowAllUp:= True
    GroupIndex
:= 83
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key78:= TSpeedButton.create(keyboard)
  with key78 do begin
    parent:= keyboard;
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 78
    Left:= 358
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 78
   OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
 Key80:= TSpeedButton.create(keyboard)
  with key80 do begin
    parent:= keyboard;
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 80
    Left:= 378
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 80
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  Key82:= TSpeedButton.create(keyboard)
  with key82 do begin
    parent:= keyboard;
    glyph.loadfromresourcename (hinstance, 'citymax');
    Tag:= 82
    Left:= 398
    Top:= 36
    Width:= 13
    Height:= 61
    AllowAllUp:= True
    GroupIndex
:= 82
    OnMouseDown:= @TKeyboardKeyMouseDown;
  end;
  with TLabel.create(keyboard) do begin
  parent:= keyboard;
    Left:= 440
    Top:= 9
    Width:= 75
    Height:= 13
    font.color:= clPurple;
    font.size:= 8;
    font.style2:= fsbold2
    Caption
:= 'minimax1'
  end ;
  
  Label1
:= TLabel.create(keyboard)
  with label1 do begin
   parent:= keyboard;
    Left:= 440
    Top:= 36
    Width:= 75
    Height:= 13
    Caption:= 'MIDI Program #'
  end ;
  Label2:= TLabel.create(keyboard)
  with label2 do begin
   parent:= keyboard;
    Left:= 8
    Top:= 12
    Width:= 43
    Height:= 13
    Caption:= 'MIDI Out'
  end ;
  Label3:= TLabel.create(keyboard)
  with label3 do begin
   parent:= keyboard;
    Left:= 8
    Top:= 144
    Width:= 58
    Height:= 13
    Caption:= 'Pitch Wheel'
    FocusControl:= PitchBender
  
end;
  Label4:= TLabel.create(keyboard)
  with label4 do begin
   parent:= keyboard;
    Left:= 8
    Top:= 184
    Width:= 58
    Height:= 13
    Caption:= 'Mod. Wheel'
    FocusControl:= ModWheel
  
end;
  MidiProgramNum:= TSpinEdit.create(keyboard)
  with MidiProgramNum do begin
   parent:= keyboard;
    Left:= 440
    Top:= 60
    Width:= 77
    Height:= 22
    MaxValue:= 127
    MinValue:= 0
    TabOrder:= 0
    Value:= 0
    OnChange:= @TKeyboardMidiProgramNumChange
  
end ;
  PitchBender:= TTrackBar.create(keyboard)
  with pitchbender do begin
   parent:= keyboard;
    Left:= 72
    Top:= 136
    Width:= 441
    Height:= 33
    Hint:= 'Pitch bender'
    Max:= 8191
    Min:= -8192
    Orientation:= trHorizontal
    PageSize
:= 256
    Frequency:= 2048
    Position:= 0
    SelEnd:= 0
    SelStart:= 0
    TabOrder:= 1
    TickMarks:= tmBottomRight
    TickStyle
:= tsAuto
    OnChange
:= @TKeyboardPitchBenderChange
  
end;
  btnAllNotesOff:= TButton.create(keyboard)
  with btnallnotesoff do  begin
   parent:= keyboard;
    Left:= 440
    Top:= 104
    Width:= 75
    Height:= 25
    Caption:= '&All Notes Off'
    TabOrder:= 2
    OnClick:= @TkeyboardbtnAllNotesOffClick
  
end ;
  cbMidiOutSelect:= TComboBox.create(keyboard)
  with  cbMidiOutSelect do begin
   parent:= keyboard;
    Left:= 68
    Top:= 8
    Width:= 253
    Height:= 21
    Style:= csDropDownList
    ItemHeight
:= 13
    TabOrder:= 3
    OnChange:= @TKeyboardcbMidiOutSelectChange
  
end ;
  modwheel:= TTrackBar.create(keyboard)
  with modwheel do begin
   parent:= keyboard;
    Left:= 73
    Top:= 176
    Width:= 440
    Height:= 33
    Hint:= 'Pitch bender'
    Max:= 16383
    Orientation:= trHorizontal
    PageSize
:= 256
    Frequency:= 2048
    Position:= 0
    SelEnd:= 0
    SelStart:= 0
    TabOrder:= 4
    TickMarks:= tmBottomRight
    TickStyle
:= tsAuto
    OnChange
:= @TkeyboardModWheelChange
  
end ;
  {object KeyMenu: TPopupMenu
    Left:= 336
    Top:= 8
    object TuningItem: TMenuItem
      Caption:= 'Tuning...'
      OnClick:= TuningItemClick
    end;
  end }
 end ;
  TKeyboardFormCreate(self);
end;
procedure TMyFormInitialisePanels(RowCount, ColCount: Integer);
var
  Row, Col: Integer;
  aLeft, aTop, aWidth, aHeight: Integer;
  Panel: TPanel;
   FPanels: array of array of TPanel;
begin
  //SetLength2(FPanels, RowCount, ColCount);
  aTop := 0;
  for Row := 0 to RowCount-1 do begin
    aLeft := 0;
    aHeight := (keyboard.ClientHeight-aTop) div (RowCount-Row);
    for Col := 0 to ColCount-1 do begin
      Panel := TPanel.Create(Self);
      FPanels[Row][ Col] := Panel;
      Panel.Parent := Self;
      aWidth := (keyboard.ClientWidth-aLeft) div (ColCount-Col);
      Panel.SetBounds(aLeft, aTop, aWidth, aHeight);
      inc2(aLeft, aWidth);
    end;
    inc2(aTop, aHeight);
  end;
end;
procedure basetone(note: TMidinote; fmidi: IJclMidiOut);
begin
   writ(MIDINoteToStr(note+69));
      writ(flots(Hertz(69)));
    writ(MIDINoteToStr(note+64));
      writ(flots(Hertz(64)));   
   writ
(MIDINoteToStr(note+60));
      writ(flots(Hertz(60))); 
    write
(MIDINoteToStr(note+72));
    write(MIDINoteToStr(note+76));
    write(MIDINoteToStr(note+79)+CRLF);
     // writ(flots(Hertz(72)));  
     //offset - 1 Octave
   note:= note-12;    
   fmidi
.SendNoteOn(2, note+72, $7f);   
   fmidi
.SendNoteOn(2, note+76, $7f);   
   fmidi
.SendNoteOn(2, note+79, $7f);   
   sleep
(800);  
   
//1. Inversion
    write(MIDINoteToStr(note+76));
    write(MIDINoteToStr(note+79));
    write(MIDINoteToStr(note+84)+CRLF);
   //fmidi.SendNoteOn(2, note+72, $7f);   
   fmidi.SendNoteOn(2, note+76, $7f);  //E4 
   fmidi.SendNoteOn(2, note+79, $7f);  //G 
   fmidi.SendNoteOn(2, note+84,$7f);   //C
   //writ(MIDINoteToStr(note+84)); 
   sleep(1000);  
   
//2. Inversion
    write(MIDINoteToStr(note+79));
    write(MIDINoteToStr(note+84));
    write(MIDINoteToStr(note+88)+CRLF);
   //fmidi.SendNoteOn(2, note+72, $7f);   
   fmidi.SendNoteOn(2, note+79, $7f);   
   fmidi
.SendNoteOn(2, note+84, $7f);   
   fmidi
.SendNoteOn(2, note+88,$7f); 
   
//writ(MIDINoteToStr(note+84)); 
   sleep(2000);  
end;
var
  mNotes: array of string;
function StrtoMIDINote(Notename: string; octave: byte): TMIDINote;
var i, fn: integer;
   aNoteName: string;
  aOctave, NoteIndex: Integer;  note: TMidinote;
begin
  mnotes:= ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
  
  
// Find note index
  NoteIndex := -1;
  //octave:= octave;
  fn:= pos('+',notename)
  if fn> 0 then begin inc(octave); delete(notename,fn,1); end;
  fn:= pos('-',notename)
  if fn> 0 then begin dec(octave); delete(notename,fn,1); end;
  //writ(notename)
  //if pos(notename, '-') > 0 then dec(octave); //}
   // Extract note name and octave
  //NoteName := UpperCase(Copy(Notename, 1, Length(Notename) - 1));
  NoteName := UpperCase(Notename);
  //Octave := StrToIntDef(Copy(NoteStr, Length(NoteStr), 1), 4);
  for  I := 0 to High(mNotes) do 
    
if mNotes[I] = NoteName then begin
      NoteIndex:= I;
      write(mnotes[i]+' ')
      Break;
    end;
   // writeln(CRLF);
  // writeln('basenote octave '+itoa(octave));
  // Calculate MIDI note number
  if NoteIndex <> -1 then
    Result := (Octave + 1) * 12 + NoteIndex
  
else
    Result := -1; // Invalid note
end;
function StrtoMIDINote2(Notestr: string; octave: byte): TMIDINote;
var i, fn: integer;
   NoteName,tmp: string;
  aOctave, NoteIndex: Integer;  note: TMidinote;
begin
  mnotes:= ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
  //writ('sr'+notestr)
  //tmp:= Copy(NoteStr, 1, Length(NoteStr) - 1);
  //notename:= uppercase(tmp);
  NoteName := AnsiUpperCase(Copy(NoteStr, 1, Length(NoteStr) - 1));
   //deletestr(NoteStr, 1, Length(NoteStr));
  //writ('dd'+notename)
  Octave := StrToIntDef(Copy(NoteStr, Length(NoteStr), 1), 4);
  //writ('oc'+itoa(octave))
  // Find note index
  NoteIndex := -1;
   // Extract note name and octave
  //NoteName := UpperCase(Copy(Notename, 1, Length(Notename) - 1));
  //NoteName := UpperCase(Notename);
  //Octave := StrToIntDef(Copy(NoteStr, Length(NoteStr), 1), 4);
  for  I := 0 to High(mNotes) do 
    
if mNotes[I] = NoteName then begin
      NoteIndex:= I;
      write(mnotes[i]+' ')
      Break;
    end;
   // writeln(CRLF);
  // writeln('basenote octave '+itoa(octave));
  // Calculate MIDI note number
  if NoteIndex <> -1 then
    Result := (Octave + 1) * 12 + NoteIndex
  
else
    Result := -1; // Invalid note
end;
procedure playtune(tune: array of string; pause: integer; octave: byte; fin: boolean; fmidi: IJclMidiOut);
var i, anote: integer;
begin
  for i:= 0 to High(tune) do begin
     anote:= StrtoMIDINote2(tune[i],octave);
     fmidi.SendNoteOn(2, anote, $7f); 
     delay
(pause)  
     fmidi
.SendNoteOff(2, anote, $7f); 
  
end;
  if fin then sleep(1500);  
  
//writeln(CRLF);
end;
procedure playchord(tune: array of string; pause: integer; fin: boolean; fmidi: IJclMidiOut);
var i, anote: integer;
begin
  //for i:= 0 to High(tune) do begin
     anote:= StrtoMIDINote2(tune[0],2);
     fmidi.SendNoteOn(2, anote, $7f); 
     anote
:= StrtoMIDINote2(tune[1],2);
     fmidi.SendNoteOn(2, anote, $7f);
     anote:= StrtoMIDINote2(tune[2],2);
     fmidi.SendNoteOn(2, anote, $7f);
     anote:= StrtoMIDINote2(tune[3],2);
     fmidi.SendNoteOn(2, anote, $7f);
     delay(pause)  
     fmidi
.SendNoteOff(2, anote, $7f); 
  
//end;
  if fin then sleep(1300);  
  
//writeln(CRLF);
end;
procedure basetune(note: TMidinote; fmidi: IJclMidiOut);
begin
  //StrToMidiNote('C4');
  writeln('strtomidi: '+itoa(StrToMidiNote('C',5)));
  // writeln('strtomidi: '+itoa(StrToMidiNote('a')));
   writ(MIDINoteToStr(note+72));
      writ(flots(Hertz(72))); 
    write
(MIDINoteToStr(note+72));      //ceg
    write(MIDINoteToStr(note+76));
    write(MIDINoteToStr(note+79)+CRLF);
     // writ(flots(Hertz(72)));  
     //offset - 1 Octave
   note:= note-12;    
   fmidi
.SendNoteOn(2, note+72, $7f); 
   delay
(200)  
   fmidi
.SendNoteOn(2, note+76, $7f);   
   delay
(200)
   fmidi.SendNoteOn(2, note+79, $7f);   
   delay
(700);  
   
//1. Inversion
    write(MIDINoteToStr(note+74));       //dfa
    write(MIDINoteToStr(note+77));
    write(MIDINoteToStr(note+81)+CRLF);
   //fmidi.SendNoteOn(2, note+72, $7f);   
   fmidi.SendNoteOn(2, note+74, $7f); delay(200);  //d4 
   fmidi.SendNoteOn(2, note+77, $7f); delay(200);  //f 
   fmidi.SendNoteOn(2, note+81,$7f);   //a
   //writ(MIDINoteToStr(note+84)); 
   delay(700);  
   
//2. Inversion
    write(MIDINoteToStr(note+76));   //egh
    write(MIDINoteToStr(note+79));  
    write
(MIDINoteToStr(note+83)+CRLF);
   //fmidi.SendNoteOn(2, note+72, $7f);   
   fmidi.SendNoteOn(2, note+76, $7f); delay(200);  
   fmidi
.SendNoteOn(2, note+79, $7f); delay(200);  
   fmidi
.SendNoteOn(2, note+83,$7f); 
   sleep
(700); 
   
//writ(MIDINoteToStr(note+84)); 
     write(MIDINoteToStr(note+77));   //fac
    write(MIDINoteToStr(note+81));  
    write
(MIDINoteToStr(note+84)+CRLF);
   //fmidi.SendNoteOn(2, note+72, $7f);   
   fmidi.SendNoteOn(2, note+77, $7f); delay(200);  
   fmidi
.SendNoteOn(2, note+81, $7f); delay(200);  
   fmidi
.SendNoteOn(2, note+84,$7f); 
   sleep
(1500);  
end;
procedure setChordProgression(note : integer);
begin
  fmidiout.SendNoteOff(2, note+40, $7f);
  fmidiout.SendNoteOFF(2, note+42, $7f);
   fmidiout.SendNoteOn(2, note+60, $7f);
end;
function MIDIEncodeMessage(Channel, Msg, Param1, Param2: integer): integer;
begin
  result:= Channel or Msg + (Param1 shl 8) + (Param2 shl 16);
end;
var  mlist: TStringList;
     note: Tmidinote;   tmidi: TJclMIDIOut;
begin //@main
  //-init
  maxform1.setconsole;
  setdebugcheck(false)
  writeln('cpuspeed '+cpuspeed);
  //application.icon.loadfromresourcename(hinstance,'XJICON');
  writ(MIDINoteToStr(note)); 
   writ
(MIDINoteToStr(note+12));
   writ(MIDINoteToStr(note+24));
   writ(MIDINoteToStr(note+36));
   writ(MIDINoteToStr(note+69));
    writ(flots(Hertz(69)));
   mlist:= THashedStringList.create;
   GetMidiOutputs(mlist);
   writeln(mlist.text)
   mlist.free;
   fmidiout:= MIDIOut(0);
   //fmidiout.SendMessage(const Data: array of Byte);
    fmidiout.SwitchPolyModeOn(16);
    //midiOutShortMsg
    //MIDIMsgProgramChange = $C0;
    //fmidiout.SendProgramChange(2, 7);
    //fmidiout.SendProgramChange(2, 1);
    //procedure SelectProgram(Channel: TMIDIChannel; BankNum: TMIDIDataWord; ProgramNum: TMIDIDataByte);
   //fmidiout.SelectProgram(0,0, 4);
    //tmidi:= fmidiout as TJclMIDIOut;
    //with tmidi  do begin
    {  fmidiout.SendNoteOn(2, note+36, $7f);
      fmidiout.SendNoteOn(2, note+40, $7f);
      fmidiout.SendNoteOn(2, note+42, $7f);
      sleep(800) }
   {   fmidiout.SendNoteOff(2, note+36, $7f);
      fmidiout.SendNoteOff(2, note+40, $7f);
      fmidiout.SendNoteOFF(2, note+42, $7f);
      fmidiout.SendNoteOn(2, note+60, $7f);
      fmidiout.SendNoteOn(2, note+64, $7f);
      fmidiout.SendNoteOn(2, note+68, $7f);
      sleep(900)
    // mid.SendNoteoff(2, note+68, $7f);
      fmidiout.SendNoteOn(2, note+72, $7f);
      sleep(700);
     fmidiout.SendNoteOn(2, note+69, $7f);
      fmidiout.SendNoteOn(2, note+79, $7f);
     fmidiout.SendNoteOn(2, note+61, $7f);
     fmidiout.SendNoteOn(2, note+59, $7f);
     fmidiout.SendNoteOn(2, note+55, $7f); 
     sleep(1900);     }
    writ(fmidiout.getname);
    
    
//basetone(note, fmidiout);
    
     
//basetune(note, fmidiout);
     
     
{   C   Dm   Em   F   G   Am   Hm-
      Step   I   II   III;   IV   V   VI   VII
     Tunes   c e g   d f a   e g h   f a c   }
      writ(flots(Hertz(72)));
       writ(flots(Hertz(84)));
    //playTune(['c','e','g','d','f','a','e','g','b','f','a','c+'], 200, 5, true, fmidiout);
     // playtune(['c','e','g','d','f','a','e','g','b'],fmidiout);
     //F-G-A-B-C-D-E F_Lydisch  F Lydischer Modus: F-G-A-B-C-D-E
     {for it:= 1 to 5 do
       playTune(['f','g','a','b','c','d','e','f'], 200, 5, false, fmidiout);}
     //C-Lydisch-Tonleiter c d e f# g a 
  //  playTune(['c','d','e','f#','g','a','b','c+'], 400, 4, true, fmidiout);
   //D-Lydisch-Tonleiter d e f# g# a b c# d   W-W-W-H-W-W-H
   //https://www.hooktheory.com/cheat-sheet/key/d/lydian
    //playTune(['d','e','f#','g#','a','b','c#+','d+'], 500, 4, true, fmidiout);
    
    
//playTune(['d4','e4','f#4','g#4','a4','b4','c#5','d5'], 500, 4, true, fmidiout);
    
    
//E Phrygischer Modus: E-F-G-A-B-C-D    H W W W H W W
    //https://emastered.com/de/blog/phrygian-mode
    //https://www.hooktheory.com/cheat-sheet/key/g/phrygian
    // playTune(['g4','ab4','bb4','c5','d5','eb5','f','g'], 500, 4, true, fmidiout);
   
   
// playTune(['g4','g#4','a#4','c5','d5','d#5','f5','g5'], 500, 4, true, fmidiout);
   
   
//https://www.hooktheory.com/cheat-sheet/key/g/phrygian
  {  playChord(['g2','a#3','d4','g4'], 1100, false, fmidiout);
    playChord(['g#2','c4','d#4','g#4'], 1000, false, fmidiout);
     playChord(['g2','a#3','d4','g4'], 1100, false, fmidiout);
     playChord(['f2','c4','f4','g#4'], 1000, true, fmidiout);      }
     
   
//https://www.hooktheory.com/cheat-sheet/key/g/phrygian
   for it:= 1 to 3 do begin
    playChord(['g2','a#3','d4','g4'], 1100, false, fmidiout);   //gm
    playChord(['c3','c4','d#4','g4'], 1100, false, fmidiout);  //cm
     playChord(['a#2','a#3','d4','f4'], 1000, false, fmidiout); //bflat
     playChord(['g#2','c4','d#4','g#4'], 900, true, fmidiout); //aflat 
   end; 
    
//g a b c d e f# g  as g major scale
  //  playTune(['g','a','b','c+','d+','e+','f#+','g+'], 400, 4, true, fmidiout);
    //end of waveframe;  
 {   loadKeyboardform();
    TKeyboardKeyMouseDown(key69, mbleft, [], 0,0);
     TKeyboardKeyMouseDown(key79, mbleft, [], 0,0);
    TKeyboardKeyMouseDown(key61, mbleft, [], 0,0);  
     TKeyboardKeyMouseDown(key59, mbleft, [], 0,0);  
     TKeyboardKeyMouseDown(key55, mbleft, [], 0,0);  }
   // Key69.click();
   //TKeyboardKeyClick(key50);
   //loadtuningform();
  //PlayMidiFile2('C:\maxbox\maxbox51\examples\schww20240810_104146_f#_hexdorian.mid');
   // PlayMidiFile2('C:\maxbox\maxbox51\examples\schww20240810_104146_f#_hexdorian.mid');
   // PlayMidiFile2('C:\maxbox\maxbox51\examples\wurm20240818_114553_f#_harmonicminor.mid');
  // TJclMIDIOut(fmidiout).free;
  fmidiout:= NIL;
   
  
//maxform1.Showmidiform(self);
  {  sr:= 'abc+df'
    it:= pos('+',sr)
    writ(itoa(it));
  delete(sr,it,1)
  writ(sr) //}
  
   
//MCISendString('PLAY ' +'"C:\maxbox\maxbox51\examples\schww20240810_104146_f#_hexdorian.mid"', 'nil', 0, 0);
   //PlaySound1('C:\maxbox\maxbox51\examples\schww20240810_104146_f#_hexdorian.mid');
   //writ(botostr(SndPlaySound('C:\maxbox\maxbox51\examples\schww20240810_104146_f#_hexdorian.mid',1)));
   //MCISendString(('play '+'C:\maxbox\maxbox51\examples\schww20240810_104146_f#_hexdorian.mid'), 'nil', 0, 0);
  end. 
end.
ref: https://de.wikipedia.org/wiki/Modale_Tonleitern#Modi
     https://github.com/project-jedi/jcl/blob/master/jcl/source/common/JclMIDI.pas
     https://stackoverflow.com/questions/27213327/how-to-access-midi-percussion-on-delphi
     https://www.hochweber.ch/theorie/akkordaufbau/akkordaufbau.htm
     https://stackoverflow.com/questions/66416799/sending-midi-control-message-via-64-bit-delphi
   https://unison.audio/phrygian-scale/
//  Registered Parameter Numbers [CC# 65H,64H]
// -----------------------------------------------------------
//  CC#65 (MSB) | CC#64 (LSB) | Function
//  Hex|Dec|    |  Hex|Dec|   |
//  - - - - - - | - - - - - - |- - - - - - - - - - - - - - - -
//   00:= 0     |  00:= 0     | Pitch Bend Sensitivity
//   00:= 0     |  01:= 1     | Channel Fine Tuning
//   00:= 0     |  02:= 2     | Channel Coarse Tuning
//   00 = 0     |  03 = 3     | Tuning Program Change
//   00 = 0     |  04 = 4     | Tuning Bank Select
Reisedatum 12. bis 26. September

The MIDI standard itself does 
not define any instruments or percussion sounds.
The General MIDI specification defines 128 instruments, and 47 percussion sounds. All channels except 
channel 
9 (counting from zero) play instruments, channel 9 plays percussion sounds, 
with different note numbers resulting in different sounds.
Other specifications (General MIDI 2, GS, XG etc.) define more sounds, 
and have mechanisms to select which channel(s) to use for percussion sounds.
To code the StrToMidiNote function in Delphi, you can create a function that converts 
a string representation 
of a musical note to its corresponding MIDI note number. 
Here
's an implementation:  This function does the following:
It defines a constant array of note names.
It extracts the note name and octave from the input string.
It finds the index of the note in the Notes array.
It calculates the MIDI note number using the formula: (Octave + 1) * 12 + NoteIndex.
This implementation assumes that the input string is in the format 'NoteOctave' (e.g., 'C4', 'F#3', 'B5'). 
It handles both sharp notes 
(with '#') and natural notes. If an invalid note is provided, the function returns -1.

G9_zeitraum mp3 127 C# Major EbM camelot 3B     
EbM Db
@
G Mixolydischer Modus: G-A-B-C-D-E-F

Die mixolydische Tonleiter folgt einer bestimmten Formel von Ganz
- und Halbtonschritten:
W-W-H-W-W-H-W

Die phrygische Tonleiter folgt einem spezifischen Muster von Ganz
- und Halbtonschritten:
H W W W H W W

E Phrygischer Modus
: E-F-G-A-B-C-D

code c
-lydisch
Lydisch sind alle wei
ßen Tasten auf dem Klavier von F bis zum nächst höheren F. 
Die Halbtonschritte liegen zwischen 
4. und 5. Stufe sowie zwischen 7. und 8. Stufe.
F Lydischer Modus: F-G-A-B-C-D-E
                   W
-W-W-H-W-W-H

C
-lydisch geheimnisvoll und tiefsinnig. Eine kleine Anpassung mit grosser Wirkung.   
Wollen wir zum C
-Akkord improvisieren, müssen wir die Noten für C-lydisch ermitteln. 
C
-lydisch ist die 4. Tonstufe der gesuchten Basis-Dur-Tonleiter. Wir zählen nun 
von der 
4. Tonstufe der Dur-Tonleiter zurück bis zur Tonika.
Name: C-Lydisch-Tonleiter
Intervalle
: 1 2+ 3+ #4 5 6+ 7+
Töne: c d e f# g a b c

C
-lydisch basiert also auf der G-Dur Tonleiter. Du kannst somit die G-Dur Tonleiter spielen, 
allerdings mit C als Ausgangsnote
.
     g a b c d e f# g


 CL
.AddTypeS('TExprTokenJ', '( etEof, etNumber, etIdentifier, etUser0, etUser1,'
   +' etUser2, etUser3, etUser4, etUser5, etUser6, etUser7, etUser8, etUser9, e'
   +'tUser10, etUser11, etUser12, etUser13, etUser14, etUser15, etUser16, etUse'
   +'r17, etUser18, etUser19, etUser20, etUser21, etUser22, etUser23, etUser24,'
   +' etUser25, etUser26, etUser27, etUser28, etUser29, etUser30, etUser31, etN'
   +'otEqual, etLessEqual, etGreaterEqual, etBang, etDoubleQuote, etHash, etDol'
   +'lar, etPercent, etAmpersand, etSingleQuote, etLParen, etRParen, etAsterisk'
   +', etPlus, etComma, etMinus, etDot, etForwardSlash, etColon, etSemiColon, e'
   +'tLessThan, etEqualTo, etGreaterThan, etQuestion, etAt, etLBracket, etBackS'
   +'lash, etRBracket, etArrow, etBackTick, etLBrace, etPipe, etRBrace, etTilde'
   +', et127, etEuro, et129, et130, et131, et132, et133, et134, et135, et136, e'
   +'t137, et138, et139, et140, et141, et142, et143, et144, et145, et146, et147'
   +', et148, et149, et150, et151, et152, et153, et154, et155, et156, et157, et'
   +'158, et159, et160, et161, et162, et163, et164, et165, et166, et167, et168,'
   +' et169, et170, et171, et172, et173, et174, et175, et176, et177, et178, et1'
   +'79, et180, et181, et182, et183, et184, et185, et186, et187, et188, et189, '
   +'et190, et191, et192, et193, et194, et195, et196, et197, et198, et199, et20'
   +'0, et201, et202, et203, et204, et205, et206, et207, et208, et209, et210, e'
   +'t211, et212, et213, et214, et215, et216, et217, et218, et219, et220, et221'
   +', et222, et223, et224, et225, et226, et227, et228, et229, et230, et231, et'
   +'232, et233, et234, et235, et236, et237, et238, et239, et240, et241, et242,'
   +' et243, et244, et245, et246, et247, et248, et249, et250, et251, et252, et2'
   +'53, et254, et255, etInvalid )');
   
   This means that Our intelligence 
is shaped by the selection of billions of years of life on Earthin which it has been 
   
and continues to be put to the test by the dangers present on our planet. The strategy of our genes and our 
   development has been 
to select a flexible organwhich can adapt to most contexts to be able to challenge any problem 
   that arises
. Artificial intelligence is not able to do this for now, and resigns itself to performing (very well) 
   only the tasks 
for which it was designed.
 

 https
://de.slideshare.net/slideshow/ekon28_modernregex_12_regular_expressions-pdf/271055222
      <iframe src="https://www.slideshare.net/slideshow/embed_code/key/2aLvlPkWAtlfcR?hostedIn=slideshare&page=upload" 
      width="476" height="400" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
      https://de.slideshare.net/slideshow/ekon28_maps_api_12_google_openstreetmaps-pdf/271055221
      <iframe src="https://www.slideshare.net/slideshow/embed_code/key/6es0FlJNIh47sv?hostedIn=slideshare&page=upload" 
      width="476" height="400" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>