www.pudn.com > d4psp31.zip > fmMainForm.pas, change:1998-11-01,size:88749b


(*==========================================================================* 
 | unit fmMainForm                                                          | 
 |                                                                          | 
 | Main form for PowerSequencerPlus3.0                                      | 
 |                                                                          | 
 | Copyright (c) Colin Wilson 1996                                          | 
 |                                                                          | 
 | Version  Date      By    Description                                     | 
 | -------  --------  ----  ----------------------------------------------- | 
 | 1.0      14/12/96  CPWW  Original                                        | 
 *==========================================================================*) 
 
unit fmMainForm; 
interface 
 
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  Menus, cmpMidiData, cmpMidiPlayer, Buttons, ExtCtrls, StdCtrls, ComCtrls, fmPower, 
  MMSystem, cmpMidiInput, fmStepMode, unitMidiGlobals, cmpInstrument, 
  cmpTrackOutputs, cmpTimeDisplay, cmpPosDisplay, fmTakes, PlaybackTempoForm, 
  ImgList; 
 
type 
  TBoolVal = class 
    val : boolean; 
  end; 
 
  TMidiOutputPortDetails = class 
     name : string; 
     instrumentName : string; 
     instrument : TInstrument; 
  end; 
 
  TMainForm = class(TForm) 
  //----------------------------------------------------- 
  // File menu items 
    MainMenu: TMainMenu; 
    File1: TMenuItem; 
    FileNewSong: TMenuItem; 
    FileOpenSong: TMenuItem; 
    FileSaveSong: TMenuItem; 
    FileSaveSongAs: TMenuItem; 
    FileN1: TMenuItem; 
    FilePrint: TMenuItem; 
    FileN2: TMenuItem; 
    FileProperties: TMenuItem; 
    FileN3: TMenuItem; 
    FileExit: TMenuItem; 
 
  //----------------------------------------------------- 
  // Edit menu items 
    Edit1: TMenuItem; 
    EditUndelete: TMenuItem; 
    EditRedo: TMenuItem; 
    EditN1: TMenuItem; 
    EditCut: TMenuItem; 
    EditCopy: TMenuItem; 
    EditPaste: TMenuItem; 
    EditDelete: TMenuItem; 
    EditN2: TMenuItem; 
    EditSelectAll: TMenuItem; 
    EditSelecttostart: TMenuItem; 
    EditSelecttoend: TMenuItem; 
 
  //----------------------------------------------------- 
  // View menu items 
    View1: TMenuItem; 
    ViewEventList: TMenuItem; 
    ViewNoteEditor: TMenuItem; 
    ViewTempoMap: TMenuItem; 
    ViewTrackSheet: TMenuItem; 
 
  //----------------------------------------------------- 
  // Track menu items 
    Track1: TMenuItem; 
    TrackSelect: TMenuItem; 
    TrackN1: TMenuItem; 
    TrackSlide: TMenuItem; 
    TrackQuantize: TMenuItem; 
    TrackN2: TMenuItem; 
    TrackEraseAll: TMenuItem; 
    TrackKill: TMenuItem; 
    TrackN3: TMenuItem; 
    TrackStepModeRecord: TMenuItem; 
    TrackProperties: TMenuItem; 
 
  //----------------------------------------------------- 
  // Song menu items 
    Song1: TMenuItem; 
    SongProperties: TMenuItem; 
 
  //----------------------------------------------------- 
  // Window menu items 
    Window1: TMenuItem; 
    WindowCloseAll: TMenuItem; 
    WindowCascade: TMenuItem; 
    WindowTileHorizontal: TMenuItem; 
    WindowTileVertical: TMenuItem; 
    WindowMinimizeAll: TMenuItem; 
 
  //----------------------------------------------------- 
  // Help menu items 
    Help1: TMenuItem; 
    HelpAbout: TMenuItem; 
    HelpHowtoUseHelp: TMenuItem; 
    HelpSearchforHelpOn: TMenuItem; 
    HelpContents: TMenuItem; 
 
  //----------------------------------------------------- 
  // Button Bar items 
    pnlButtonBar: TPanel; 
    stPosition: TLabel;         // Temporary song position label 
 
  //----------------------------------------------------- 
  // Transport bar items 
    pnlTransport: TPanel; 
    btnRewind: TSpeedButton; 
    btnStop: TSpeedButton; 
    btnPlay: TSpeedButton; 
    btnFfwd: TSpeedButton; 
    btnRecord: TSpeedButton; 
 
  //----------------------------------------------------- 
  // Status bar items 
    StatusBar: TStatusBar; 
 
  //----------------------------------------------------- 
  // Dialogs 
    OpenSongDialog: TOpenDialog; 
    SaveSongDialog: TSaveDialog; 
    PrintDialog: TPrintDialog; 
    PatchTypeImages: TImageList; 
 
  //----------------------------------------------------- 
  // MIDI controls 
    UpdateTimer: TTimer; 
    MidiData: TMidiData; 
    MidiPlayer: TMidiPlayer; 
    MidiInput: TMidiInput; 
    TrackOutputs: TTrackOutputs; 
    N1: TMenuItem; 
    GoTo1: TMenuItem; 
    SongGoToStart: TMenuItem; 
    SongGoToEnd: TMenuItem; 
    SongGoToPosition: TMenuItem; 
    pnlPosDisplay: TPanel; 
    PosDisplay: TPosDisplay; 
    TimeDisplay: TTimeDisplay; 
    Bevel1: TBevel; 
    Bevel2: TBevel; 
    Bevel3: TBevel; 
    Panel2: TPanel; 
    btnNew: TSpeedButton; 
    btnSave: TSpeedButton; 
    btnPrint: TSpeedButton; 
    btnOpen: TSpeedButton; 
    mnuViewTakes: TMenuItem; 
    ViewControllerMap: TMenuItem; 
    mnuSongPlaybackTempo: TMenuItem; 
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
    procedure FormShow(Sender: TObject); 
    procedure FormKeyDown(Sender: TObject; var Key: Word; 
      Shift: TShiftState); 
 
  //----------------------------------------------------- 
  // File menu event handlers 
    procedure FileNewSongClick(Sender: TObject); 
    procedure FileOpenSongClick(Sender: TObject); 
    procedure FileSaveSongClick(Sender: TObject); 
    procedure FileSaveSongAsClick(Sender: TObject); 
    procedure FilePrintClick(Sender: TObject); 
    procedure FilePropertiesClick(Sender: TObject); 
    procedure FileExitClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // Edit menu event handlers 
 
    procedure EditSelectAllClick(Sender: TObject); 
    procedure EditCopyClick(Sender: TObject); 
    procedure EditCutClick(Sender: TObject); 
    procedure EditPasteClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // View menu event handlers 
    procedure ViewTrackSheetClick(Sender: TObject); 
    procedure ViewNoteEditorClick(Sender: TObject); 
    procedure ViewEventListClick(Sender: TObject); 
    procedure ViewTempoMapClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // Track menu event handlers 
    procedure TrackSelectClick(Sender: TObject); 
    procedure TrackEraseAllClick(Sender: TObject); 
    procedure TrackKillClick(Sender: TObject); 
    procedure TrackStepModeRecordClick(Sender: TObject); 
    procedure TrackPropertiesClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // Song menu event handlers 
    procedure SongPropertiesClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // Window menu event handlers 
    procedure WindowCascadeClick(Sender: TObject); 
    procedure WindowTileHorizontalClick(Sender: TObject); 
    procedure WindowTileVerticalClick(Sender: TObject); 
    procedure WindowCloseAllClick(Sender: TObject); 
    procedure WindowMinimizeAllClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // Help menu event handlers 
    procedure HelpAboutClick(Sender: TObject); 
    procedure HelpContentsClick(Sender: TObject); 
    procedure HelpHowtoUseHelpClick(Sender: TObject); 
    procedure HelpSearchforHelpOnClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // Transport button event handlers 
    procedure btnRewindClick(Sender: TObject); 
    procedure btnStopClick(Sender: TObject); 
    procedure btnPlayClick(Sender: TObject); 
    procedure btnFfwdClick(Sender: TObject); 
    procedure btnRecordClick(Sender: TObject); 
 
  //----------------------------------------------------- 
  // MIDI controls event handlers 
    procedure UpdateTimerTimer(Sender: TObject); 
    procedure MidiPlayerCallback(Sender: TObject; Event: PMidiEvent); 
    procedure MidiPlayerStop(Sender: TObject); 
    procedure MidiPlayerPlay(Sender: TObject); 
    procedure MidiPlayerFastForward(Sender: TObject); 
    procedure MidiInputStepData(const data: TEventData); 
    procedure PosDisplayBoxClicked(sender: TObject; Button: TMouseButton; 
      Shift: TShiftState; box: Integer); 
    procedure SongGoToStartClick(Sender: TObject); 
    procedure SongGoToEndClick(Sender: TObject); 
    procedure SongGoToPositionClick(Sender: TObject); 
    procedure mnuViewTakesClick(Sender: TObject); 
    procedure EditDeleteClick(Sender: TObject); 
    procedure ViewControllerMapClick(Sender: TObject); 
    procedure mnuSongPlaybackTempoClick(Sender: TObject); 
    procedure Song1Click(Sender: TObject); 
 
  private 
    ActiveForms : TList; 
    fCurrentTrackNo : Integer; 
    MidiDir : string; 
    MidiInputPorts : TStringList; 
    MidiOutputPorts : TList; 
    StepModeForm : TStepModeForm; 
    StepNoteCount : Integer; 
    fRecording : boolean; 
 
    TakesForm : TTakesForm; 
 
  //----------------------------------------------------- 
  // Helper functions for form properties 
    procedure SetCurrentTrackNo (value : Integer); 
    function GetCurrentTime : Integer; 
    function GetCurrentPosition : Integer; 
    procedure WMGetMinMaxInfo (var msg : TWMGetMinMaxInfo); message WM_GETMINMAXINFO; 
    procedure WMTempoChange (var msg : TMessage); message WM_TEMPOCHANGE; 
 
  //----------------------------------------------------- 
  // Misc. private functions 
 
    procedure OpenSong (fileName : string); 
    procedure NewSong; 
    function CloseSong : boolean; 
    function SaveSongAs : boolean; 
    function SaveSong : boolean; 
 
    procedure UpdateCurrentTrackDetails; 
    procedure SetCaption; 
    procedure SetMenuOptions; 
    procedure ReadProperties; 
    procedure ResetPatchNames; 
 
  public 
  //----------------------------------------------------- 
  // Constructor and destructor 
    DragNoteVolume : Integer; 
    SelStartPos, SelEndPos : Integer; 
 
    constructor Create (AOwner : TComponent); override; 
    destructor Destroy; override; 
 
  //----------------------------------------------------- 
  // Active forms list public functions 
    procedure AddActiveForm (frm : TForm); 
    procedure DeleteActiveForm (frm : TForm); 
    procedure NotifyActiveForms (tp : TActiveFormNotify); 
    procedure NotifyAll; 
 
  //----------------------------------------------------- 
  // Dialog public functions 
    function SelectTrackDialog (var trackNo : Integer; ShowTrack0 : boolean; ShowEmpty : boolean) : boolean; 
    procedure GetTrackProperties; 
    procedure DisplayCurrentPosition; 
 
  //----------------------------------------------------- 
  // Step mode public functions 
    procedure SetStepModeState (value : boolean); 
 
    procedure TakesFormRemoved; 
 
  //----------------------------------------------------- 
  // Port & Patch public functions 
    function GetOutputPortInstrument (index : Integer) : TInstrument; 
    function GetOutputPortInstrumentName (index : Integer) : string; 
    procedure SetOutputPortInstrument (index : Integer; fileName : string); 
    function GetPatchForInstrument (portNo, patch, bank : Integer) : TPatch; 
 
  //----------------------------------------------------- 
  // Form properties 
    property CurrentTrackNo : Integer read fCurrentTrackNo write SetCurrentTrackNo; 
    property CurrentPosition : Integer read GetCurrentPosition; 
    property CurrentTime : Integer read GetCurrentTime; 
  end; 
 
var 
  MainForm: TMainForm; 
 
implementation 
 
{$R *.DFM} 
 
uses Reg, Globals, unitMidiTrackStream, fmAboutBox,fmTracksheet, fmNoteEditor, fmEventList, fmTempoMap, fmSelectTrack, fmTrackProperties, fmProgramProperties, fmSongProperties, fmInstruments, cmpMidiOutput, fmGotoPosition, cmpMidiIterator, Clipbrd, fmController; 
 
(*-------------------------------------------------------------------------* 
 | constructor TMainForm.Create                                            | 
 |                                                                         | 
 | Constructor for main form                                               | 
 |                                                                         | 
 | Parameters:                                                             | 
 | AOwner : TComponent    // The form's owner                              | 
 *-------------------------------------------------------------------------*) 
 
constructor TMainForm.Create (AOwner : TComponent); 
var 
  i : Integer; 
  numDevs : Integer; 
  inCaps : TMidiInCaps; 
begin 
  ShowInitialTracksheet := True; 
  ActiveForms := TList.Create;       // Create the active forms list 
  fCurrentTrackNo := 1; 
  DragNoteVolume := 64; 
 
                                     // Create the input port list 
  midiInputPorts := TStringList.Create; 
  numDevs := MidiInGetNumDevs; 
  for i := 0 to numDevs - 1 do 
  begin 
    midiInGetDevCaps (i, @inCaps, sizeof (inCaps)); 
    midiInputPorts.AddObject (inCaps.szPName, TBoolVal.Create); 
  end; 
 
                                     // Create the output port list 
  midiOutputPorts := TList.Create; 
                                     // Read properties 
 
  inherited Create (AOwner); 
end; 
 
(*-------------------------------------------------------------------------* 
 | destructor TMainForm.Destroy                                            | 
 |                                                                         | 
 | Free the input ports list, output ports list and associated instruments | 
 | and the active form list.                                               | 
 *-------------------------------------------------------------------------*) 
 
destructor TMainForm.Destroy; 
var 
  i : Integer; 
  port : TMidiOutputPortDetails; 
begin 
  for i := 0 to MidiInputPorts.Count-1 do 
    TBoolVal (MidiInputPorts.objects [i]).Free; 
  MidiInputPorts.Free;          // Free the input ports list 
 
                                // Free the output ports details list 
                                // and associated instruments 
  for i := 0 to MidiOutputPorts.Count - 1 do 
  begin 
    port := TMidiOutputPortDetails (MidiOutputPorts.Items [i]); 
    if Assigned (port.Instrument) then 
      port.Instrument.Free; 
    port.Free 
  end; 
  MidiOutputPorts.Free; 
 
  ActiveForms.Free;             // Free the active forms list 
  inherited 
end; 
 
//========================================================================== 
// Form event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FormShow ()                                         | 
 |                                                                         | 
 |                                                                         | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 |                                                                         | 
 | The function returns                                                    | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FormShow(Sender: TObject); 
var i : Integer; 
begin 
  ReadProperties; 
 
  for i := 0 to MidiInputPorts.Count - 1 do 
    if TBoolVal (MidiInputPorts.Objects [i]).val then 
      MidiInput.OpenPorts [i] := True; 
 
                                     // Open the MIDI input ports. 
 
  if ParamCount > 0 then             // Open the 'command line' file 
  try 
    OpenSong (ParamStr (1))          // ... supports drag 'n' drop 
  except 
    MessageDlg ('Unable to open ' + ParamStr (1), mtWarning, [mbOk], 0); 
    NewSong; 
  end 
  else NewSong;                      // nb. Both Open Song & New Song display 
                                     // the initial track sheet if required, 
                                     // and initialise the whole dang thing. 
 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FormCloseQuery                                      | 
 |                                                                         | 
 | Try to close the song.  If there've been changes, but the save is       | 
 | cancelled, don't terminate.                                             | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
begin 
  CanClose := CloseSong 
end; 
 
//========================================================================== 
// File menu event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FileNewClick                                        | 
 |                                                                         | 
 | Respond to a 'new song' menu event.                                     | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FileNewSongClick(Sender: TObject); 
begin 
  if CloseSong then          // First, try to close the old song. 
    NewSong 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FileOpenSongClick                                   | 
 |                                                                         | 
 | Respond to a 'FileOpen' event.                                          | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FileOpenSongClick(Sender: TObject); 
begin 
  OpenSongDialog.FileName := MidiData.FileName; 
  OpenSongDialog.InitialDir := MidiDir; 
  if OpenSongDialog.Execute then       // Do the 'open file' dialog 
    if CloseSong then                  // Try to close the old song 
      OpenSong (OpenSongDialog.FileName); 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FileSaveSongClick                                   | 
 |                                                                         | 
 | Respond to 'save song' messages.                                        | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FileSaveSongClick(Sender: TObject); 
begin 
  SaveSong 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FileSaveSongAsClick                                 | 
 |                                                                         | 
 | Respond to 'save song as' messages                                      | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FileSaveSongAsClick(Sender: TObject); 
begin 
  SaveSongAs 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FilePrintClick                                      | 
 |                                                                         | 
 | Respond to 'File print' events                                          | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FilePrintClick(Sender: TObject); 
begin 
  if PrintDialog.Execute then 
end; 
 
procedure TMainForm.ResetPatchNames; 
var 
  i : Integer; 
  PatchRec : TPatch; 
begin 
  for i := 0 to MidiData.NoTracks - 1 do 
    with MidiData.Tracks [i] do 
    begin 
      PatchRec := GetPatchForInstrument (trackOutputs.GetTrackPortID (i), Patch, Bank); 
      if Assigned (patchRec) then 
        InstrumentName := PatchRec.PatchName 
      else 
        InstrumentName := '' 
    end 
end; 
 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FilePropertiesClick                                 | 
 |                                                                         | 
 | Respond to 'FileProperties' events.                                     | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FilePropertiesClick(Sender: TObject); 
var 
  dialog : TProgramProperties; 
  registry : TReg; 
 
  i, idx : Integer; 
  Item : TListItem; 
  extItem : TListItem; 
  OutputPort : TMidiOutputPortDetails; 
 
  begin                          // Create and fill in the dialog... 
    dialog := TProgramProperties.Create (Self); 
    try 
      dialog.cbShowHints.Checked := ShowHint; 
      dialog.cbInitialTracksheet.Checked := ShowInitialTracksheet; 
      dialog.cbAutoSynchronize.Checked := AutoSynchronize; 
      dialog.udDragNoteVolume.Position := DragNoteVolume; 
      dialog.cbBoostPlaybackPriority.Checked := MidiPlayer.BoostPriority; 
 
                                 // Fill in 'output ports' list view 
      for i := 0 to MidiOutputPorts.Count - 1 do 
      begin 
        extItem := dialog.elvOutputPorts.Items.Add; 
        extItem.caption := IntToStr (i); 
        extItem.SubItems.Add (TMidiOutputPortDetails (MidiOutputPorts.Items [i]).Name); 
        extItem.SubItems.Add (ExtractInstrumentName (TMidiOutputPortDetails (MidiOutputPorts.Items [i]).InstrumentName)); 
      end; 
 
                                 // Fill in 'input ports' list views 
      for i := 0 to MidiInputPorts.Count - 1 do 
      begin 
        if TBoolVal (MidiInputPorts.Objects [i]).val then 
          item := dialog.lvSelectedPorts.items.Add 
        else 
          item := dialog.lvUnSelectedPorts.items.Add; 
        item.caption := MidiInputPorts.Strings [i] 
      end; 
 
      Dialog.DefaultPortNo := TrackOutputs.DefaultOutputPort; 
 
                                // Do the dialog... 
      if dialog.ShowModal = mrOk then 
      begin 
        registry := TReg.Create (HKEY_CURRENT_USER, ProgramKey + '\' + PropertiesKey, True); 
        try                     // Save the new 'ShowHints' value 
          if dialog.cbShowHints.Checked <> ShowHint then 
          begin 
                                // Set 'ShowHint' form property here... 
            ShowHint := dialog.cbShowHints.Checked; 
            Registry.WriteBoolean (ShowHintsValue, ShowHint); 
          end; 
 
                                // Save the new 'InitialTracksheet' value 
          if dialog.cbInitialTracksheet.Checked <> ShowInitialTracksheet then 
          begin 
            ShowInitialTracksheet := dialog.cbInitialTracksheet.Checked; 
            Registry.WriteBoolean (InitialTracksheetValue, ShowInitialTracksheet); 
          end; 
 
                               // Save the new 'AutoSynchronize' value 
          if dialog.cbAutoSynchronize.Checked <> AutoSynchronize then 
          begin 
            AutoSynchronize := dialog.cbAutoSynchronize.Checked; 
            Registry.WriteBoolean (AutoSynchronizeValue, AutoSynchronize); 
          end; 
 
          if dialog.cbBoostPlaybackPriority.Checked <> MidiPlayer.BoostPriority then 
          begin 
            MidiPlayer.BoostPriority := dialog.cbBoostPlaybackPriority.Checked; 
            Registry.WriteBoolean (BoostPriorityValue, MidiPlayer.BoostPriority); 
          end; 
 
          if dialog.udDragNoteVolume.Position <> DragNoteVolume then 
          begin 
            DragNoteVolume := dialog.udDragNoteVolume.Position; 
            Registry.WriteInteger (DragNoteVolumeValue, DragNoteVolume) 
          end; 
 
        finally 
          registry.Free 
        end; 
                             // Save the 'unselected' value in the input ports list 
        for i := 0 to dialog.lvUnselectedPorts.items.Count - 1 do 
        begin 
          idx := MidiInputPorts.IndexOf (dialog.lvUnselectedPorts.items.item [i].Caption); 
          if idx > -1 then 
            TBoolVal (MidiInputPorts.Objects [idx]).val := False 
        end; 
 
                             // Save the 'selected' value in the input ports list 
        for i := 0 to dialog.lvSelectedPorts.items.Count - 1 do 
        begin 
          idx := MidiInputPorts.IndexOf (dialog.lvSelectedPorts.items.item [i].Caption); 
          if idx > -1 then 
            TBoolVal (MidiInputPorts.Objects [idx]).val := True 
        end; 
 
                             // Save the active input ports registry keys. 
        registry := TReg.Create (HKEY_CURRENT_USER, ProgramKey + '\' + PropertiesKey + '\' + InputPortsKey, True); 
        try 
          for i := 0 to MidiInputPorts.Count - 1 do 
          begin 
            if TBoolVal (MidiInputPorts.Objects [i]).val then 
              Registry.WriteBoolean (MidiInputPorts.Strings [i], True) 
            else 
              try 
                Registry.DeleteValue (MidiInputPorts.Strings [i]) 
              except 
              end 
          end 
        finally 
          Registry.free 
        end; 
 
        TrackOutputs.DefaultOutputPort := Dialog.DefaultPortNo ; 
        					// Save the default output port. 
        with TReg.Create (HKEY_CURRENT_USER, ProgramKey + '\' + PropertiesKey + '\' + OutputPortsKey, True) do 
	    try 
  	      WriteInteger (DefaultOutputPortValue, TrackOutputs.DefaultOutputPort); 
  	    finally 
	      Free 
	    end; 
 
			    // Reload the instruments for each port, as these 
                            // might have changed in the dialog... 
        for i := 0 to MidiOutputPorts.Count - 1 do 
        begin 
          OutputPort := TMidiOutputPortDetails (MidiOutputPorts.Items [i]); 
          if Assigned (outputPort.Instrument) then 
          begin 
            OutputPort.Instrument.Free; 
            OutputPort.Instrument := Nil; 
            SetOutputPrtInstrument (i, Nil) 
          end; 
 
          if OutputPort.InstrumentName <> '' then 
          try 
            with TFileStream.Create (OutputPort.InstrumentName, fmOpenRead) do 
            try 
              try 
                OutputPort.Instrument := TInstrument.Create (Nil); 
                ReadComponent (OutputPort.Instrument); 
 
                	    // Set the instrument cache (TMidiOutput. 
                            //  ** Todo - change the way instrument caching works 
                            // - it's too messy 
                SetOutputPrtInstrument (i, OutputPort.Instrument); 
              except 
              end 
            finally 
              free 
            end 
          except 
          end 
        end; 
 
        ResetPatchNames; 
        		    // Replace the instrument (patch) names for all the 
                            // tracks, in case we changed them above; 
 
        UpdateCurrentTrackDetails; 
        if Assigned (TracksheetForm) then TrackSheetForm.Notify; 
      end 
    finally 
      dialog.Free 
    end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.FileExitClick                                       | 
 |                                                                         | 
 | Respond to 'Exit' events.                                               | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.FileExitClick(Sender: TObject); 
begin 
  Close 
end; 
 
//========================================================================== 
// View menu event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.ViewTrackSheetClick                                 | 
 |                                                                         | 
 | Respond to 'View Tracksheet' events.                                    | 
 | nb.  Only allow one tracksheet, so toggle the form.  The menu item gets | 
 | checked/unchecked in the tracksheet form's Show and Close event         | 
 | handlers.  The TracksheetForm variable gets set to nil in the           | 
 | tracksheet form's Destroy handler.                                      | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.ViewTrackSheetClick(Sender: TObject); 
begin 
  if Assigned (TracksheetForm) then // Already displayed 
    TracksheetForm.Close            // .. Close it 
  else 
  begin                             // Not displayed.  Display it. 
    TracksheetForm := TTracksheetForm.Create (self); 
    TracksheetForm.Show 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.ViewNoteEditorClick                                 | 
 |                                                                         | 
 | Respond to a ViewNoteEditor event.                                      | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.ViewNoteEditorClick(Sender: TObject); 
begin 
  if Assigned (MidiData.Tracks [CurrentTrackNo]) then 
    TNoteEditorForm.Create (self).Show 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.ViewEventListClick                                  | 
 |                                                                         | 
 | Respond to a ViewEventList event.                                       | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.ViewEventListClick(Sender: TObject); 
begin 
  if Assigned (MidiData.Tracks [CurrentTrackNo]) then 
    TEventListForm.Create (self).Show 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.ViewTempoMapClick                                   | 
 |                                                                         | 
 | Respond to a ViewTempoMap event.                                        | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.ViewTempoMapClick(Sender: TObject); 
begin 
  if Assigned (MidiData.Tracks [0]) then 
    TTempoMapForm.Create (self).Show 
end; 
 
//========================================================================== 
// Track menu event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.TrackSelectClick                                    | 
 |                                                                         | 
 | Respond to a 'TrackSelec' event.  This comes into it's own when there's | 
 | no track sheet displayed...                                             | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.TrackSelectClick(Sender: TObject); 
var trackNo : Integer; 
begin 
  trackNo := currentTrackNo; 
  if SelectTrackDialog (trackNo, False, True) then 
  begin 
    CurrentTrackNo := trackNo; 
    if Assigned (TracksheetForm) then 
      TracksheetForm.SelectTrack (trackNo); 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.TrackEraseAllClick                                  | 
 |                                                                         | 
 | Respond to a TrackEraseAll event                                        | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.TrackEraseAllClick(Sender: TObject); 
begin 
  if Application.MessageBox ('Are you sure?', 'Erase All Events', mb_IconQuestion or mb_YesNo or mb_DefButton2) = IDYes then 
  begin 
    MidiData.EraseTrack (CurrentTrackNo); 
                                  // Update everything to display the new track info 
    NotifyAll 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.TrackKillClick                                      | 
 |                                                                         | 
 | Respond to a TrackKill event.                                           | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.TrackKillClick(Sender: TObject); 
begin 
  if Application.MessageBox ('Are you sure?', 'Kill Track', mb_IconQuestion or mb_YesNo or mb_DefButton2) = IDYes then 
  begin 
    TrackOutputs [CurrentTrackNo].Free; 
    MidiData.RemoveTrack (CurrentTrackNo); 
    UpdateCurrentTrackDetails; 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.TrackStepModeRecordClick                            | 
 |                                                                         | 
 | Respond to a Track/StepMode event                                       | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.TrackStepModeRecordClick(Sender: TObject); 
begin 
  SetStepModeState (not TrackStepModeRecord.Checked); 
  if TrackStepModeRecord.Checked then 
  begin 
    StepModeForm := TStepModeForm.Create (self); 
    StepModeForm.Show 
  end 
  else StepModeForm.Close 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.TrackPropertiesClick                                | 
 |                                                                         | 
 | Respond to a Track/Properties event                                     | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.TrackPropertiesClick(Sender: TObject); 
begin 
  GetTrackProperties 
end; 
 
//========================================================================== 
// Song menu event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.SongPropertiesClick                                 | 
 |                                                                         | 
 | Respond to a Song/Properties event                                      | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.SongPropertiesClick(Sender: TObject); 
var 
  dialog : TSongPropertiesForm; 
  iterator : TMidiPosition; 
  c, i : Integer; 
  lowerDiv : Integer; 
  s : string; 
  key : ShortInt; 
begin 
  dialog := TSongPropertiesForm.Create (self); 
  iterator := TMidiIterator.Create (self); 
  iterator.MidiData := MidiData; 
  try 
    dialog.edResolution.Text := IntToStr (MidiData.ppqn); 
    dialog.edFormat.Text := Format ('Type %d', [MidiData.FileType]); 
    dialog.edNumberOfTracks.Text := IntToStr (MidiData.NoTracks); 
 
    with MidiData do 
      if Assigned (Tracks [0]) then with Tracks [0] do 
      begin 
        c := EventCount; 
        for i := 0 to c - 1 do 
          with Event [i]^ do 
          begin 
            iterator.position := pos; 
          	if data.Status = midiMeta then with data do 
              case byte (sysex [0]) of 
                metaTimesig : 
                  with dialog.lvTimeSignatures.Items.Add do 
                  begin 
                    if Integer (sysex [2]) < 2 then 
                      lowerDiv := 4 shr (2 - Integer (sysex [2])) 
                    else 
                      lowerDiv := 4 shl (Integer (sysex [2]) - 2); 
                    with Iterator do Caption := Format ('%d-%02.2d-%03.3d', [bar + 1, beat + 1, tick]); 
                    SubItems.Add (Format ('%d/%d', [Integer (sysex [1]), LowerDiv])); 
                  end; 
 
                metaKeySig : 
                  with dialog.lvKeySignatures.Items.Add do 
                  begin 
                    ImageIndex := 1; 
                    with Iterator do Caption := Format ('%d-%02.2d-%03.3d', [bar + 1, beat + 1, tick]); 
                    key := ShortInt (sysex [1]); 
                    if key < 0 then 
                      s := Char (Ord ('A') + (7 + (key + 1)) mod 7) + ' Min' 
                    else 
                      s := Char (Ord ('A') + key ) + ' Maj'; 
                    SubItems.Add (s); 
                  end; 
              end 
          end 
      end; 
 
    if dialog.ShowModal = mrOk then 
    begin 
      MidiData.ppqn := StrToInt (dialog.edResolution.Text); 
      NotifyActiveForms (ntFullUpdate) 
    end 
  finally 
    iterator.Free; 
    dialog.Free 
  end 
 
end; 
 
//========================================================================== 
// Window menu event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.WindowCascadeClick                                  | 
 |                                                                         | 
 | Respond to a Window/Cascade event                                       | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.WindowCascadeClick(Sender: TObject); 
begin 
  Cascade 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.WindowTileHorizontalClick                           | 
 |                                                                         | 
 | Respond to a Window/TileHorizontal event                                | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.WindowTileHorizontalClick(Sender: TObject); 
begin 
  TileMode := tbHorizontal; 
  Tile 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.WindowTileVerticalClick                             | 
 |                                                                         | 
 | Respond to a Window/TileVertical event                                  | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.WindowTileVerticalClick(Sender: TObject); 
begin 
  TileMode := tbVertical; 
  Tile 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.WindowCloseAllClick                                 | 
 |                                                                         | 
 | Respond to a Window/CloseAll event                                      | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.WindowCloseAllClick(Sender: TObject); 
var i : Integer; 
begin 
  for i := 0 to ComponentCount - 1 do 
    if Components [i] is TForm then 
      TForm (Components [i]).Close; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.WindowMinimizeAllClick                              | 
 |                                                                         | 
 | Respond to a Window/MinimizeAll event                                   | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.WindowMinimizeAllClick(Sender: TObject); 
var i : Integer; 
begin 
  for i := 0 to ComponentCount - 1 do 
    if Components [i] is TForm then 
      TForm (Components [i]).WindowState := wsMinimized 
end; 
 
//========================================================================== 
// Help menu event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.HelpAboutClick                                      | 
 |                                                                         | 
 | Respond to a Help/About event                                           | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.HelpAboutClick(Sender: TObject); 
begin 
  with TfmStdAboutBox.Create (self) do 
  try 
    ShowModal; 
  finally 
    Free 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.HelpContentsClick                                   | 
 |                                                                         | 
 | Respond to a Help/ContentsClick event                                   | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.HelpContentsClick(Sender: TObject); 
begin 
  Application.HelpJump ('Introduction'); 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.HelpHowtoUseHelpClick                               | 
 |                                                                         | 
 | Respond to a Help/HowToUseHelp event                                    | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.HelpHowtoUseHelpClick(Sender: TObject); 
begin 
  Application.HelpCommand (HELP_HELPONHELP, 0); 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.HelpSearchforHelpOnClick                            | 
 |                                                                         | 
 | Respond to a Help/SearchForHelpOn event                                 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.HelpSearchforHelpOnClick(Sender: TObject); 
begin 
  Application.HelpCommand (HELP_FINDER, 0); 
end; 
 
//========================================================================== 
// Transport panel event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.btnRewindClick                                      | 
 |                                                                         | 
 | Respond to a Rewind click                                               | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.btnRewindClick(Sender: TObject); 
begin 
  MidiInput.SetRecording (False, Nil); 
  MidiPlayer.Rewind; 
  DisplayCurrentPosition; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.btnStopClick                                        | 
 |                                                                         | 
 | Respond to a Stop click                                                 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.btnStopClick(Sender: TObject); 
begin 
  if MidiInput.Recording then 
    MidiInput.SetRecording (False, Nil); 
  MidiPlayer.Stop; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.btnPlayClick                                        | 
 |                                                                         | 
 | Respond to a 'Play' button click.                                       | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.btnPlayClick(Sender: TObject); 
begin 
  try 
    MidiInput.SetRecording (False, Nil); 
    MidiPlayer.Play := True; 
  except 
    btnStop.Down := True 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.btnFfwdClick                                        | 
 |                                                                         | 
 | Respond to a 'Fast Forward' button click                                | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.btnFfwdClick(Sender: TObject); 
begin 
  try 
    MidiInput.SetRecording (False, Nil); 
    MidiPlayer.FastForward := True; 
  except 
    btnStop.Down := True 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.btnRecordClick                                      | 
 |                                                                         | 
 | Respond to the 'record' button.                                         | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.btnRecordClick(Sender: TObject); 
begin 
  try 
    MidiPlayer.AutoStop := False; 
    MidiInput.RecordStartTime := MidiPlayer.Time; 
    MidiPlayer.Play := True; 
    MidiInput.SetRecording (True, MidiData); 
    fRecording := True; 
  except 
    BtnStop.Down := True 
  end 
end; 
 
//========================================================================== 
// MIDI controls event handlers 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.UpdateTimerTimer                                    | 
 |                                                                         | 
 | Respond to an Update Timer event.  Get the current position from the    | 
 | MIDI player, and update the display.                                    | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.UpdateTimerTimer(Sender: TObject); 
begin 
  DisplayCurrentPosition; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.MidiPlayerCallback                                  | 
 |                                                                         | 
 | Respond to a Midi Player callback.  Typically when a sysex event is     | 
 | sent, etc.                                                              | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 | Event  : PMidiEvent        // The callback event 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.MidiPlayerCallback(Sender: TObject; Event: PMidiEvent); 
begin 
  MessageBeep ($ffff); 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.MidiPlayerStop                                      | 
 |                                                                         | 
 | Respond to the Midi Player saying it's stopped.                         | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.MidiPlayerStop(Sender: TObject); 
begin 
  MidiPlayer.AutoStop := True; 
  if fRecording then 
  begin 
    with MidiInput do TrackOutputs.OpenTrack (Take [TakeCount - 1], Take [TakeCount - 1].TempPort); 
    if Assigned (TakesForm) then 
      TakesForm.Refresh 
    else 
      mnuViewTakesClick (self); 
    fRecording := False 
  end; 
 
  BtnStop.Down := True; 
  UpdateTimer.Enabled := False; 
  DisplayCurrentPosition 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.MidiPlayerPlay                                      | 
 |                                                                         | 
 | Respond to the MidiPlayer saying it's started.                          | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.MidiPlayerPlay(Sender: TObject); 
begin 
  BtnPlay.Down := True; 
  UpdateTimer.Enabled := True; 
  if MidiInput.Recording then 
    TimeDisplay.Color := clRed; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.MidiPlayerFastForward                               | 
 |                                                                         | 
 | Respond to the midi player saying it's started to fast forward.         | 
 |                                                                         | 
 | Parameters:                                                             | 
 | Sender : TObject           // The event sender                          | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.MidiPlayerFastForward(Sender: TObject); 
begin 
  BtnFfwd.Down := True; 
  UpdateTimer.Enabled := True; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.MidiInputStepData                                   | 
 |                                                                         | 
 | Respond to MidiInput data when in step mode.                            | 
 |                                                                         | 
 | Parameters:                                                             | 
 | const data : TEventData    // The step data received                    | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.MidiInputStepData(const data: TEventData); 
var 
   stepNoteLen : Integer; 
   e : TEventData; 
   dotted, quantize : boolean; 
   endPos : Integer; 
   startEvent, endEvent : PMidiEventData; 
begin 
  stepNoteLen := MidiData.PPQN; 
  if Assigned (StepModeForm) then 
    stepNoteLen := StepModeForm.AdjustNoteLen (stepNoteLen, dotted, quantize); 
  if Assigned (MidiData.Tracks [CurrentTrackNo]) then 
    case data.status and MidiStatusMask of 
      midiNoteOn : 
      begin 
        MidiData.Tracks [CurrentTrackNo].BeginUpdate; 
        try 
          e.status := (data.status and MidiStatusMask) or MidiData.Tracks [CurrentTrackNo].Channel; 
          e.b2 := data.b2; 
          e.b3 := data.b3; 
          startEvent := MidiData.Tracks [CurrentTrackNo].InsertEvent (MidiPlayer.Position, e, 0); 
 
          e.status := (e.status and not MidiStatusMask) or midiNoteOff; 
          e.b3 := 0; 
          endPos := stepNoteLen; 
          if dotted then endPos := endPos div 2; 
          endPos := MidiPlayer.Position + endPos; 
 
          endEvent := MidiData.Tracks [CurrentTrackNo].InsertEvent (endPos, e, 0); 
          startEvent^.OnOffEvent := endEvent; 
          endEvent^.OnOffEvent := startEvent; 
        finally 
          MidiData.Tracks [CurrentTrackNo].CancelUpdate 
        end; 
        Inc (stepNoteCount); 
        NotifyAll; 
      end; 
 
      midiNoteOff : 
        if stepNoteCount > 0 then 
        begin 
          Dec (stepNoteCount); 
          if stepNoteCount = 0 then 
          begin 
            MidiPlayer.Position := MidiPlayer.Position + StepNoteLen; 
            NotifyActiveForms (ntPositionUpdate); 
            DisplayCurrentPosition 
          end 
        end 
    end 
end; 
 
//========================================================================== 
// Helper functions for Form properties 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.SetCurrentTrackNo                                   | 
 |                                                                         | 
 | Helper function for the 'CurrentTrackNo' property.  Update the display  | 
 | etc. when the current track changes.                                    | 
 |                                                                         | 
 | Parameters:                                                             | 
 | value : Integer       // The new track no.                              | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.SetCurrentTrackNo (value : Integer); 
begin 
  if value <> fCurrentTrackNo then 
  begin 
    fCurrentTrackNo := value; 
    UpdateCurrentTrackDetails 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.DisplayCurrentPosition                              | 
 |                                                                         | 
 | Helper function form the CurrentPosition property.  Update the          | 
 | displayed position, and notify the active forms that the position has   | 
 | changed.                                                                | 
 |                                                                         | 
 | Parameters:                                                             | 
 | value : Integer       // The new position                               | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.DisplayCurrentPosition; 
var bar, beat, tick, tempo, beatDiv : Integer; 
begin 
  MidiPlayer.GetBarPos (Bar, Beat, Tick); 
  MidiPlayer.GetCurrentTempo (tempo, beatDiv); 
  PosDisplay.SetPosition (bar, beat, tick); 
  TimeDisplay.Time := MidiPlayer.Time; 
  StatusBar.Panels [1].Text := Format ('Tempo %d', [GetBPM (tempo, beatDiv)]); 
  NotifyActiveForms (ntPositionUpdate); 
end; 
 
//========================================================================== 
// Private functions 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.NewSong                                             | 
 |                                                                         | 
 | Start a new song.  This is called at the very beginning, and also when  | 
 | 'New Song' is selected.                                                 | 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.NewSong; 
begin 
  MidiData.New;            // Re-initialise the midi data 
  TrackOutputs.Active := True; 
  MidiPlayer.Reset; 
 
  if ShowInitialTracksheet then 
    PostMessage (Handle, WM_COMMAND, ViewTrackSheet.Command, 0); 
 
  SetCaption;              // Show the 'untitled' caption 
  UpdateCurrentTrackDetails; 
 // DisplayCurrentPosition; 
end; 
 
(*-------------------------------------------------------------------------* 
 | procedure TMainForm.OpenSong                                            | 
 |                                                                         | 
 | Open a song.  This is called a the beginning if there was anything on   | 
 | the command line.  Also when the OpenSong dialog returns.               | 
 |                                                                         | 
 | Parameters:                                                             | 
 | fileName : string         The song file to open.                        | 
 *-------------------------------------------------------------------------*) 
procedure TMainForm.OpenSong (fileName : string); 
begin 
  MidiData.FileName := fileName; 
  MidiDir := ExtractFilePath (ExpandFileName (fileName)); 
                             // Save the path in the registry... 
  with TReg.Create (HKEY_CURRENT_USER, ProgramKey + '\' + DirectoriesKey, True) do 
  try 
    WriteString (MidiFilePathValue, MidiDir); 
  finally 
    Free 
  end; 
 
  MidiData.Active := True;     // Load's the song 
 
  TrackOutputs.Active := True; 
  MidiPlayer.Reset; 
  ResetPatchNames; 
 
                               // Open an output port for each active track 
  if ShowInitialTracksheet and (MidiData.FileType >= 1) then 
    PostMessage (Handle, WM_COMMAND, ViewTrackSheet.Command, 0); 
 
  if MidiData.FileType = 0 then 
    CurrentTrackNo := 0; 
     
  SetCaption; 
  UpdateCurrentTrackDetails; 
  DisplayCurrentPosition; 
end; 
 
(*-------------------------------------------------------------------------* 
 | function TMainForm.CloseSong : boolean;                                 | 
 |                                                                         | 
 | Close a song.  This is called before a new song is opened, to close     | 
 | the song.  If changes have been made, it prompts for saving.            | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.CloseSong : boolean; 
begin 
  result := False; 
  if MidiData.Changes then 
    case MessageDlg (Format ('Save changes to %s', [MidiData.ShortFileName]), mtInformation, mbYesNoCancel, 0) of 
      mrYes    : result := SaveSong; 
      mrNo     : result := True; 
      mrCancel : result := False 
    end 
  else result := True; 
 
  if Result then        // Ok to close ? 
  begin 
    MidiPlayer.Play := False; 
    MidiPlayer.Position := 0; 
    TrackOutputs.Active := False; 
    MidiPlayer.Reset; 
    MidiData.Active := False; 
    WindowCloseAllClick (Nil); 
    Application.ProcessMessages; 
    CurrentTrackNo := 1 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | function TMainForm.SaveSongAs : boolean; 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.SaveSongAs : boolean; 
begin 
  SaveSongDialog.FileName := MidiData.FileName; 
  if SaveSongDialog.Execute then 
  begin 
    MidiData.FileName := SaveSongDialog.FileName; 
    result := SaveSong 
  end 
  else 
    result := False; 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.SaveSong : boolean; 
begin 
  result := False; 
  if MidiData.FileName = '' then 
    result := SaveSongAs 
  else 
  try 
    MidiData.Save; 
    result := True 
  except 
    MessageDlg (Format ('Unable to save file %s', [MidiData.ShortFileName]), mtError, [mbOk], 0); 
  end 
end; 
 
procedure TMainForm.UpdateCurrentTrackDetails; 
var st : string; 
begin 
  st := Format ('Track %d', [CurrentTrackNo]); 
  with MidiData do if Assigned (Tracks [CurrentTrackNo]) then 
  begin 
    st := st + ' - ' + Tracks [CurrentTrackNo].TrackName; 
    MidiInput.EchoPort := TrackOutputs.GetTrackPortID (CurrentTrackNo); 
    MidiInput.ChannelOverride := Tracks [CurrentTrackNo].Channel; 
    TrackOutputs [CurrentTrackNo].SetPatchForPosition 
  end 
  else MidiInput.EchoPort := -2; 
  StatusBar.Panels [0].Text := st; 
  SetMenuOptions 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.SetCaption; 
begin 
  Caption := MidiData.ShortFileName + ' - Power Sequencer Plus 3.0'; 
  Application.Title := Caption 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.SetMenuOptions; 
var Enable : boolean; 
begin 
  if MidiData.FileType = 0 then 
  begin 
    Enable := Assigned (MidiData.tracks [CurrentTrackNo]); 
    ViewTrackSheet.Enabled := False; 
    TrackSelect.Enabled := False; 
    TrackProperties.Enabled := False 
  end 
  else 
  begin 
    Enable := (CurrentTrackNo > 0) and Assigned (MidiData.tracks [CurrentTrackNo]); 
    ViewTrackSheet.Enabled := True; 
    TrackProperties.Enabled := True; 
    TrackSelect.Enabled := True 
  end; 
 
 
  ViewEventList.Enabled := Enable; 
  ViewNoteEditor.Enabled := Enable; 
  ViewTempoMap.Enabled := Enable; 
  ViewControllerMap.Enabled := Enable; 
 
  TrackQuantize.Enabled := Enable; 
  TrackSlide.Enabled := Enable; 
  TrackEraseAll.Enabled := Enable; 
  TrackKill.Enabled := Enable; 
 
  if Assigned (TracksheetForm) and (CurrentTrackNo > 0) then 
    TracksheetForm.SelectTrack (CurrentTrackNo) 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.ReadProperties; 
 
  procedure ReadProgramProperties (const key : HKey); 
 
    procedure ReadPortProperties (const Key : HKey); 
    var 
      i : Integer; 
      outCaps : TMidiOutCaps; 
      numDevs : Integer; 
      port : TMidiOutputPortDetails; 
      nm : string; 
 
    begin 
      with TReg.Create (Key, OutputPortsKey, True) do 
      try 
        TrackOutputs.DefaultOutputPort := ReadIntegerDef (DefaultOutputPortValue, 0); 
        numDevs := MidiOutGetNumDevs; 
        for i := 0 to numDevs - 1 do 
        begin 
          midiOutGetDevCaps (i, @outCaps, sizeof (outCaps)); 
          port := TMidiOutputPortDetails.Create; 
          port.name := outCaps.szPName; 
          port.InstrumentName := ReadStringDef (port.name, ''); 
          if port.InstrumentName = '' then 
          begin 
            nm := ExtractFilePath (Application.ExeName); 
            port.InstrumentName := nm + 'General Midi.ppi' 
          end; 
          try 
            with TFileStream.Create (port.InstrumentName, fmOpenRead) do 
            try 
              try 
                port.Instrument := TInstrument.Create (Nil); 
                ReadComponent (port.Instrument); 
                SetOutputPrtInstrument (i, port.Instrument); 
              except 
              end 
            finally 
              free 
            end 
          except 
          end; 
          MidiOutputPorts.Add (port); 
        end; 
 
      finally // Finished with OutputPortsKey 
        Free 
      end; 
 
      with TReg.Create (Key, InputPortsKey, True) do 
      try 
        for i := 0 to midiInputPorts.Count - 1 do 
          TBoolVal (midiInputPorts.Objects [i]).val := ReadBooleanDef (MidiInputPorts.strings [i], False); 
      finally 
        free 
      end 
    end;  // End of ReadPortProperties 
 
  begin  // ReadProgramProperties 
    with TReg.Create (Key, PropertiesKey, True) do 
    try 
      ReadPortProperties (key); 
      ShowHint := ReadBooleanDef (ShowHintsValue, True); 
      ShowInitialTracksheet := ReadBooleanDef (InitialTracksheetValue, True); 
      AutoSynchronize := ReadBooleanDef (AutoSynchronizeValue, True); 
      DragNoteVolume := ReadIntegerDef (DragNoteVolumeValue, 64); 
      MidiPlayer.BoostPriority := ReadBooleanDef (BoostPriorityValue, MidiPlayer.BoostPriority); 
 
    finally 
      Free 
    end; 
 
    with TReg.Create (Key, DirectoriesKey, True) do 
    try 
      MidiDir := ReadStringDef (MidiFilePathValue, ''); 
    finally 
      Free 
    end 
  end; 
 
begin // ReadProperties 
  with TReg.Create (HKEY_CURRENT_USER, ProgramKey, True) do 
  try 
    ReadProgramProperties (Key); 
  finally 
    free 
  end 
end; 
 
//========================================================================== 
// Active forms list public functions 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.AddActiveForm (frm : TForm); 
begin 
  ActiveForms.Add (frm) 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.DeleteActiveForm (frm : TForm); 
begin 
  ActiveForms.Remove (frm) 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.NotifyActiveForms (tp : TActiveFormNotify); 
var i : Integer; 
begin 
  for i := 0 to ActiveForms.Count - 1 do 
    with TPowerForm (ActiveForms.Items [i]) do 
      if Active or (tp <> ntPositionUpdate) then 
        Notify (tp); 
end; 
 
procedure TMainForm.NotifyAll; 
begin 
  NotifyActiveForms (ntFullUpdate); 
  if Assigned (TracksheetForm) then TracksheetForm.Notify 
end; 
 
//========================================================================== 
// Dialog public functions 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.SelectTrackDialog (var trackNo : Integer; ShowTrack0 : boolean; ShowEmpty : boolean) : boolean; 
var 
  dialog : TSelectTrackForm; 
  i : Integer; 
  trackName, st : string; 
begin 
  result := False; 
 
  dialog := TSelectTrackForm.Create (self); 
  try 
    if ShowTrack0 then 
        dialog.lbTrackNames.Items.Add ('0 - Control'); 
 
    with MidiData do 
      for i := Succ (Low (TTrack)) to High (TTrack) do 
        if Assigned (Tracks [i]) then 
          dialog.lbTrackNames.Items.Add (Format ('%d - %s', [i, Tracks [i].TrackName])) 
        else 
          if ShowEmpty then 
            dialog.lbTrackNames.Items.Add (Format ('%d - <Empty>', [i])); 
 
    dialog.lbTrackNames.ItemIndex := trackNo - Ord (not ShowTrack0); 
//    if MidiData.NoTracks > 0 then dialog.edTrackName.Text := dialog.lbTrackNames.Items [0]; 
 
    if dialog.ShowModal = mrOk then 
    begin 
      trackName := dialog.edTrackName.Text; 
      if (Length (trackName) > 0) and (trackName [1] in ['0'..'9']) then 
      begin 
        i := 1; 
        st := ''; 
        repeat 
          st := st + trackName [i]; 
          Inc (i) 
        until not (trackName [i] in ['0'..'9']); 
 
        i := StrToInt (st); 
        trackNo := i; 
        result := True 
      end 
    end 
  finally 
    dialog.Free 
  end 
end; 
 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.GetTrackProperties; 
var 
  dialog : TTrackPropertiesForm; 
  i : Integer; 
  track : TMidiTrackStream; 
  portNo : Integer; 
  patchRec : TPatch; 
begin 
  dialog := TTrackPropertiesForm.Create (self); 
  try 
    dialog.PageControl1.ActivePage := dialog.pgGeneral; 
    for i := 0 to MidiOutputPorts.Count - 1 do 
      dialog.cbPort.Items.Add (TMidiOutputPortDetails (MidiOutputPorts.Items [i]).name); 
    if Assigned (MidiData.Tracks [CurrentTrackNo]) then 
    begin 
      track := MidiData.Tracks [CurrentTrackNo]; 
      dialog.edTitle.Text := track.TrackName; 
      dialog.udChannel.Position := track.Channel + 1; 
      dialog.udBank.Position := track.Bank; 
      dialog.udPatch.Position := track.Patch; 
    end; 
 
    portNo := trackOutputs.GetTrackPortID (CurrentTrackNo); 
    dialog.cbPort.ItemIndex := portNo; 
    dialog.Instrument := GetOutputPortInstrument (portNo); 
 
    if dialog.ShowModal = mrOk then 
    begin 
      if not Assigned (MidiData.Tracks [CurrentTrackNo]) then 
      begin 
        if MidiData.AddNewTrack (CurrentTrackNo) then 
          trackOutputs.OpenTrack (MidiData.Tracks [0], trackOutputs.DefaultOutputPort); 
        trackOutputs.OpenTrack (MidiData.Tracks [CurrentTrackNo], dialog.cbPort.ItemIndex) 
      end 
      else 
        trackOutputs [CurrentTrackNo].PortID := dialog.cbPort.ItemIndex; 
 
      MidiData.Tracks [CurrentTrackNo].TrackName := dialog.edTitle.Text; 
      MidiData.Tracks [CurrentTrackNo].Channel := dialog.udChannel.Position - 1; 
      MidiData.Tracks [CurrentTrackNo].Patch := dialog.udPatch.Position; 
// ** Not yet implemented  MidiData.Tracks [CurrentTrackNo].Bank := dialog.udBank.Position; 
 
      with MidiData.Tracks [CurrentTrackNo] do 
      begin 
        patchRec := GetPatchForInstrument (trackOutputs [CurrentTrackNo].PortID, Patch, Bank); 
        if Assigned (patchRec) then 
          instrumentName := patchRec.PatchName 
        else 
          instrumentName := '' 
      end; 
 
      UpdateCurrentTrackDetails; 
      NotifyAll 
    end 
  finally 
    dialog.Free 
  end 
end; 
 
//========================================================================== 
// Step mode public functions 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.SetStepModeState (value : boolean); 
var 
  i : Integer; 
begin 
  StepNoteCount := 0; 
  TrackStepModeRecord.Checked := value; 
  with pnlTransport do 
    for i := 0 to ControlCount - 1 do 
      Controls [i].Enabled := not TrackStepModeRecord.Checked; 
  MidiInput.StepMode := TrackStepModeRecord.Checked; 
end; 
 
procedure TMainForm.TakesFormRemoved; 
begin 
  mnuViewTakes.Checked := False; 
  TakesForm := Nil 
end; 
 
//========================================================================== 
// Port & Patch list public functions 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.GetOutputPortInstrument (index : Integer) : TInstrument; 
begin 
  if index < MidiOutputPorts.Count then 
    result := TMidiOutputPortDetails (MidiOutputPorts.Items [index]).Instrument 
  else 
    result := Nil 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.GetOutputPortInstrumentName (index : Integer) : string; 
begin 
  if index < MidiOutputPorts.Count then 
    result := TMidiOutputPortDetails (MidiOutputPorts.Items [index]).InstrumentName 
  else 
    result := '' 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
procedure TMainForm.SetOutputPortInstrument (index : Integer; fileName : string); 
begin 
  if index < MidiOutputPorts.Count then 
  begin 
    TMidiOutputPortDetails (MidiOutputPorts.Items [index]).InstrumentName := fileName; 
      with TReg.Create (HKEY_CURRENT_USER, ProgramKey + '\' + PropertiesKey + '\' + OutputPortsKey, True) do 
      try 
        WriteString (TMidiOutputPortDetails (MidiOutputPorts.Items [index]).name, fileName); 
      finally 
        Free 
      end; 
 
  end 
end; 
 
(*-------------------------------------------------------------------------* 
 | 
 |                                                                         | 
 | 
 |                                                                         | 
 | Parameters:                                                             | 
 | 
 |                                                                         | 
 | The function returns 
 *-------------------------------------------------------------------------*) 
 
function TMainForm.GetPatchForInstrument (portNo, patch, bank : Integer) : TPatch; 
var 
  prt : TMidiOutputPortDetails; 
  i : Integer; 
begin 
  if portNo < MidiOutputPorts.Count then 
  begin 
    prt := TMidiOutputPortDetails (MidioutputPorts [portNo]); 
    if Assigned (prt.Instrument) then 
      with prt.Instrument do 
        for i := 0 to ComponentCount - 1 do 
          with TPatch (Components [i]) do 
            if (PatchNo = patch) and (bankNo = bank) then 
            begin 
              result := TPatch (prt.Instrument.Components [i]); 
              exit 
            end; 
  end; 
  result := Nil; 
end; 
 
function TMainForm.GetCurrentTime : Integer; 
begin 
  result := MidiPlayer.Time; 
end; 
 
function TMainForm.GetCurrentPosition : Integer; 
begin 
  result := MidiPlayer.Position; 
end; 
 
procedure TMainForm.WMGetMinMaxInfo (var msg : TWMGetMinMaxInfo); 
begin 
  inherited; 
  msg.MinMaxInfo^.ptMinTrackSize.x := pnlTransport.Left + pnlTransport.Width + pnlPosDisplay.Width + 10; 
end; 
 
procedure TMainForm.PosDisplayBoxClicked(sender: TObject; 
  Button: TMouseButton; Shift: TShiftState; box: Integer); 
var 
  bar, beat, tick, delta : Integer; 
begin 
  MidiPlayer.GetBarPos (bar, beat, tick); 
  if Button = mbLeft then Delta := 1 else if Button = mbRight then delta := -1 else delta := 0; 
  case box of 
    0 : MidiPlayer.SetBarPos (bar + delta, beat, tick); 
    1 : MidiPlayer.SetBarPos (bar, beat + delta, tick); 
    2 : MidiPlayer.SetBarPos (bar, beat, tick + delta) 
  end; 
 
  DisplayCurrentPosition; 
end; 
 
procedure TMainForm.SongGoToStartClick(Sender: TObject); 
begin 
  MidiPlayer.Position := 0; 
  DisplayCurrentPosition 
end; 
 
procedure TMainForm.SongGoToEndClick(Sender: TObject); 
begin 
  MidiPlayer.SetEndPosition; 
  DisplayCurrentPosition 
end; 
 
procedure TMainForm.SongGoToPositionClick(Sender: TObject); 
var 
  bar, beat, tick : Integer; 
begin 
  MidiPlayer.GetbarPos (bar, beat, tick); 
  with TGotoPositionForm.Create (self) do 
  try 
    edBar.Text := IntToStr (bar + 1); 
    edBeat.Text := IntToStr (beat + 1); 
    edtick.Text := IntToStr (tick); 
    if ShowModal = mrok then 
    begin 
      MidiPlayer.SetBarPos (StrToInt (edBar.Text) - 1, strToInt (edBeat.Text) - 1, StrToInt (edTick.Text)); 
      DisplayCurrentPosition 
    end 
  finally 
    free 
  end 
 
end; 
 
procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; 
  Shift: TShiftState); 
begin 
  case Key of 
    Ord ('P') : btnPlayClick (Sender); 
    Ord ('F') : btnFfwdClick (Sender); 
    Ord ('S') : btnStopClick (Sender); 
    Ord ('R') : btnRewindClick (Sender); 
    Ord ('D') : btnRecordClick (Sender); 
  end 
end; 
 
procedure TMainForm.EditSelectAllClick(Sender: TObject); 
var i : Integer; 
begin 
  SelStartPos := 0; 
  SelEndPos := 0; 
  with MidiData do 
    for i := 0 to NoTracks - 1 do 
      with Tracks [i] do 
        if Event [EventCount - 1].pos > selEndPos then 
          selEndPos := Event [EventCount - 1].pos; 
  NotifyActiveForms (ntFullUpdate); 
end; 
 
procedure TMainForm.EditCopyClick(Sender: TObject); 
begin 
 if ActiveMDIChild is TPowerForm then 
   TPowerForm (ActiveMDIChild).CopyToClipboard 
 else 
   if ActiveMDIChild is TTracksheetForm then 
     TTracksheetForm (ActiveMDIChild).CopyToClipboard; 
 NotifyAll; 
end; 
 
procedure TMainForm.EditCutClick(Sender: TObject); 
begin 
 if ActiveMDIChild is TPowerForm then 
   TPowerForm (ActiveMDIChild).CutToClipboard 
 else 
   if ActiveMDIChild is TTracksheetForm then 
     TTracksheetForm (ActiveMDIChild).CutToClipboard; 
 NotifyAll; 
end; 
 
procedure TMainForm.EditPasteClick(Sender: TObject); 
begin 
 if ActiveMDIChild is TPowerForm then 
   TPowerForm (ActiveMDIChild).PasteFromClipboard 
 else 
   if ActiveMDIChild is TTracksheetForm then 
     TTracksheetForm (ActiveMDIChild).PasteFromClipboard; 
 NotifyAll; 
end; 
 
procedure TMainForm.mnuViewTakesClick(Sender: TObject); 
begin 
  if Assigned (TakesForm) then 
    TakesForm.Free 
  else 
  begin 
    TakesForm := TTakesForm.Create (self); 
    TakesForm.MidiData := MidiData; 
    TakesForm.MidiInput := MidiInput; 
    TakesForm.TrackOutputs := TrackOutputs; 
    TakesForm.Show; 
    mnuViewTakes.Checked := True 
  end 
end; 
 
procedure TMainForm.EditDeleteClick(Sender: TObject); 
begin 
 if ActiveMDIChild is TPowerForm then 
   TPowerForm (ActiveMDIChild).DeleteToClipboard 
 else 
   if ActiveMDIChild is TTracksheetForm then 
     TTracksheetForm (ActiveMDIChild).DeleteToClipboard; 
 NotifyAll; 
end; 
 
procedure TMainForm.ViewControllerMapClick(Sender: TObject); 
begin 
  if Assigned (MidiData.Tracks [CurrentTrackNo]) then 
    TControllerForm.Create (self).Show 
end; 
 
procedure TMainForm.mnuSongPlaybackTempoClick(Sender: TObject); 
begin 
  if not Assigned (fmPlaybackTempo) then 
  begin 
    fmPlaybackTempo := TfmPlaybackTempo.Create (self); 
    with fmPlaybackTempo do 
    begin 
      Trackbar1.Position := MidiPlayer.TempoPercent; 
      Show 
    end 
  end 
  else 
    fmPlaybackTempo.Close 
end; 
 
procedure TMainForm.WMTempoChange (var msg : TMessage); 
begin 
  MidiPlayer.TempoPercent := msg.wParam 
end; 
 
procedure TMainForm.Song1Click(Sender: TObject); 
begin 
  mnuSongPlaybackTempo.Checked := Assigned (fmPlaybackTempo); 
end; 
 
end.