www.pudn.com > TMIDIPlayer_w_MidiSheet.rar > MIDITest.pas, change:2014-02-08,size:30376b


{ UNIT MIDITest.pas 
--------------------------------- MIDITest ----------------------------- 
 Main unit of kMIDIPlayer project and kMIDIPlayer is a Demo program for TMidiPlayer2. 
 
 Copyright 2011 Silhwan Hyun, All Rights Reserved 
 This unit is free. It may be used both in commercial and non-commercial software either in 
  original or in modified form. 
 This unit can be freely distributed in any way and by any means provided this copyright 
  notice is preserved. 
 
 Author : Silhwan Hyun   (e-mail addr : hyunsh@hanafos.com) 
 
 Author's comment : Please let me know if you have any idea to improve or to debug this unit. 
 
 Contibutors 
   Emil Weiss : Test & Advice for update/debug 
 
 Revision History 
 ------------------------------ 
  Ver 0.9.7   21 May 2013 
   - Can change playing position at stopped, paused state  (requires TMidiPlayer2 v0.9.5.2) 
   - Reflects mouse click on music sheet of MidiSheetMusic 
   - Play, pause operation by the buttons of MidiSheetMusic 
 
  Ver 0.9.6   11 May 2013 
   - Added a TEdit which shows Beat information. 
   - Added "Show Music Sheet" button. 
   - Support MidiSheetMusic program which shows music sheet of opened MIDI file. 
 
  Ver 0.9.5    9 Feb 2013 
   - Added "Show Channel Window" button. 
 
  Ver 0.9.4    28 May 2012 
   - The enabled state of VolumeBar is depends on the VolumeAdjustable property of TMIDIPlayer2 
   - Added a sub-form to adjust tempo & pitch. 
   - Added a TMemo to display sync lyrics 
   - Removed a TCheckBox for mute control (=> it may be not effective if we use a synthesizer 
     other than Microsoft MIDI Mapper or Microsoft Wavetable Synth) 
 
  Ver 0.9.3    27 Aug 2011 
   - Added a CheckBox to control the Mute state of sound output. 
   - Support local language using gnugettext 
 
  Ver 0.9.2    Jul 09 2011 
   - Added a EditBox to display the elapsed time at playing MIDI file. 
   - Added a Speed control TrackBar. 
 
  Ver 0.9.1    Jul 01 2011 
   - Added functions to play MIDI file with MidiPlayer2. 
 
  Ver 0.9.0    Jun 04 2011 
   - Initial release 
----------------------------------------------------------------------------} 
 
unit MIDITest; 
 
interface 
 
// If you want to use local language for captions of components and messages then 
//  activate following conditional compiler directive. (also in the project source) 
// You should also prepare your local language file(mo file) to translate captions of 
//  components and messages. 
{$DEFINE MULTI_LANGUAGE} 
 
uses 
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, FileCtrl, ComCtrls, 
  MidiPlayer2, MidiFile2, XPMan, Dialogs, StdCtrls, ActnList, CheckLst, StrUtils, ShowMIDILyrics, 
  {$IFDEF MULTI_LANGUAGE}gnugettext,{$ENDIF} ExtCtrls, AppEvnts; 
 
const 
  WM_InformPlayerStat = WM_USER + 146; 
  WM_WaitingURLCheck  = WM_USER + 147; 
  Set_ChannelStat     = 1; 
  Set_PlayingPos      = 2; 
  Set_Title           = 3; 
  Set_Lyrics          = 4; 
 
type 
  PSyncLyrics = ^TRawLyrics; 
  TPlayerMode = (plmStandby, plmReady, plmStopped, plmPlaying, plmPaused); 
 
  TMainForm = class(TForm) 
    btnOpen: TButton; 
    OpenDialog1: TOpenDialog; 
    btnClose: TButton; 
    edTracks: TEdit; 
    Label1: TLabel; 
    Label2: TLabel; 
    edFormat: TEdit; 
    edLength: TEdit; 
    Label4: TLabel; 
    edFile: TEdit; 
    Label7: TLabel; 
    edCopyright: TEdit; 
    Label9: TLabel; 
    btnPlay: TButton; 
    cbSynthesizer: TComboBox; 
    btnStop: TButton; 
    PositionBar: TProgressBar; 
    Label3: TLabel; 
    XPManifest1: TXPManifest; 
    btnPause: TButton; 
    Label6: TLabel; 
    MidiFile: TMidiFile2; 
    edTime: TEdit; 
    MidiPlayer: TMidiPlayer2; 
    tbVolume: TTrackBar; 
    PageControl1: TPageControl; 
    TrackSheet: TTabSheet; 
    TrackList: TCheckListBox; 
    PlainLyricsSheet: TTabSheet; 
    lbl_Synth: TLabel; 
    PlainLyrics: TListBox; 
    Button1: TButton; 
    TabSheet1: TTabSheet; 
    SyncLyricsList: TListBox; 
    btnShowLyricsWindow: TButton; 
    btnViewChannel: TButton; 
    btnShowMusicSheet: TButton; 
    Label5: TLabel; 
    Label8: TLabel; 
    ApplicationEvents1: TApplicationEvents; 
    Label10: TLabel; 
    edBeat: TEdit; 
    procedure MidiPlayerMidiEvent(Track: integer; Event: PMidiEvent); 
    procedure MidiPlayerTrackEnd(Track: Integer); 
    procedure MidiPlayerPlayEnd(Sender: TObject); 
    procedure MidiPlayerTempoChange(Tempo: Cardinal); 
    procedure MidiPlayerPosUpdate(TimePos, TicksPos: Cardinal); 
    procedure btnOpenClick(Sender: TObject); 
    procedure btnCloseClick(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    procedure btnPlayClick(Sender: TObject); 
    procedure btnStopClick(Sender: TObject); 
    procedure PositionBarMouseDown(Sender: TObject; Button: TMouseButton; 
      Shift: TShiftState; X, Y: Integer); 
    procedure tbVolumeChange(Sender: TObject); 
    procedure btnPauseClick(Sender: TObject); 
    procedure cbSynthesizerChange(Sender: TObject); 
    procedure TrackListClickCheck(Sender: TObject); 
    procedure FormShow(Sender: TObject); 
    procedure Button1Click(Sender: TObject); 
    procedure MidiPlayerGotLyrics(Track: Integer; Event: PMidiEvent); 
    procedure btnShowLyricsWindowClick(Sender: TObject); 
    procedure btnViewChannelClick(Sender: TObject); 
    procedure btnShowMusicSheetClick(Sender: TObject); 
    procedure FormClose(Sender: TObject; var Action: TCloseAction); 
    procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean); 
  private 
    { Private declarations } 
    FCanvas: TCanvas; 
    procedure FillMidiOutDevices; 
    function  SendDataW(HServer : THandle; WData: WideString; RequestId : integer): integer; 
  public 
    { Public declarations } 
    procedure WndProc(var Msg : TMessage); override; 
  end; 
 
var 
  MainForm: TMainForm; 
 
implementation 
 
{$R *.dfm} 
 
uses {$IFDEF UNICODE}ViewChannel{$ELSE}ViewChannel_D7{$ENDIF}, TempoControl; 
 
const 
  CMaxVolume = 65535; 
  CStepVolume = 3120; 
 
  WM_Set_PlayerState  = WM_USER + 1;  // message to inform MidiSheetMusic of player's state 
  WM_MIDI_PosUpdate   = WM_USER + 3;  // message to update the position of progress bar on music sheet 
  WM_Mouse_Clicked    = WM_USER + 7;  // message from MidiSheetMusic to inform host program that there was 
                                      // a mouse click event on music sheet 
  WM_Button_Clicked  = WM_USER + 9;   // message from MidiSheetMusic to inform host program that there was 
                                      // a button click event on player panel of MidiSheetMusic 
 
 // Request ID used for controlling MidiSheetMusic 
  rqOpenFile = $0001;    // Open MIDI file 
  rqCloseFile = $0002;   // Close MIDI file 
  rqStop = $0005;        // Stop displaying progress bar 
  rqExit = $0044;        // Terminate program 
  rqSetHandle = $0099;   // Set the handle to host's window 
 
var 
 // MidiFile: TMidiFile2; 
 // MidiPlayer: TMidiPlayer2; 
  Paused: Boolean = false; 
  MIDILyrics: TRawLyrics; 
  PassedActivation: boolean = false; 
  CurMIDIFile: string; 
 
  hMsg_StartUp: Cardinal; 
  hMsg_Terminated: Cardinal; 
  MIDIProcessHandle: THandle = 0; 
  MIDIThreadId: DWORD; 
  MIDIWinHandle: THandle = 0; 
 
  FhServer: cardinal = 0; 
  PrevPosMs: DWORD = 0; 
  PrevPosTicks: DWORD = 0; 
  TicksPerMilSec: single = 0; 
  MilSecPerTick: single = 0; 
 
procedure TMainForm.WndProc(var Msg : TMessage); 
var 
  I : integer; 
  {$IFDEF MULTI_LANGUAGE} 
  S1, S2, S3 : string; 
  {$ENDIF} 
begin 
  if (Msg.Msg = WM_SyncLyricsConfig) then 
  begin 
    if Msg.WParam = SyncLyrics_Shown then 
    begin 
     //  b_ViewSyncLyrics := true; 
       btnShowLyricsWindow.Enabled := false; 
    end else 
    if Msg.WParam = SyncLyrics_Closed then 
    begin 
     //  b_ViewSyncLyrics := false; 
       btnShowLyricsWindow.Enabled := true; 
    end 
  end else 
  if (Msg.Msg = WM_ViewChannelConfig) then 
  begin 
    if Msg.WParam = ViewChannel_Shown then 
       btnViewChannel.Enabled := false 
    else 
    if Msg.WParam = ViewChannel_Closed then 
       btnViewChannel.Enabled := true; 
  end 
  else if (Msg.Msg = WM_Mouse_Clicked) then  // mouse click on music sheet of MidiSheetMusic 
  begin 
    if (FhServer <> 0) and 
      (Paused or (not MidiPlayer.Playing)) then 
    begin 
      if (not Paused) then 
      begin 
         Paused := true; 
         for I := 0 to MidiPlayer.MidiFile.TrackCount - 1 do 
           MidiPlayer.MidiFile.GetTrack(I).EndOfTrack := false; 
      end; 
 
      PostMessage(FhServer, WM_MIDI_PosUpdate, WPARAM(Msg.WParam), 0{=Move to new position}); 
      PrevPosMs := Midifile.Tick2TimePos(Msg.WParam); 
      MidiPlayer.CurrentTime := PrevPosMs; 
      PrevPosTicks := Msg.WParam; 
      MidiPlayerPosUpdate(PrevPosMs, Msg.WParam); 
    end; 
  end 
  else if (Msg.Msg = WM_Button_Clicked) then  // button click on player panel of MidiSheetMusic 
  begin 
    if (FhServer <> 0) then 
      if Msg.WParam = 1 then   // Play button ? 
      begin 
        if MidiPlayer.Playing  then 
          btnPauseClick(Self) 
        else 
          btnPlayClick(Self); 
      end else 
      if Msg.WParam = 2 then   // Stop button ? 
        btnStopClick(Self) 
      else if Msg.WParam = 9 then   // Mode-Change button ? 
      begin 
        if (FhServer <> 0) then   // Previously connected state ? 
          if Msg.LParam = FhServer then  // The message was from my launched program ? 
          begin 
            FhServer := 0; 
            {$IFDEF MULTI_LANGUAGE} 
            S1 := _('The operation mode of Midi Sheet Music'); 
            S2 := S1 + chr(10) + _('was switched to independent mode.'); 
            S3 := _('Confirm'); 
            MessageBox(Self.Handle, PChar(S2), PChar(S3), MB_OK or MB_ICONEXCLAMATION); 
            {$ELSE} 
            MessageBox(Self.Handle, 'The operation mode of Midi Sheet Music' + chr(10) + 
                        'was switched to independent mode.', 'Confirm', MB_OK or MB_ICONEXCLAMATION); 
            {$ENDIF} 
          end; 
      end; 
  end 
  {else if (Msg.Msg = WM_QueryEndSession) then 
    Msg.Result := 1    // Allow system termination  } 
  else 
    inherited WndProc(Msg); 
end; 
 
procedure TMainForm.FillMidiOutDevices; 
var 
  iDevice: Integer; 
  I: Integer; 
begin 
  for iDevice := 0 to MidiPlayer.MidiOutDevices - 1 do 
    cbSynthesizer.Items.Add(MidiPlayer.MidiOutProductName(iDevice)); 
 
  if MidiPlayer.OpenedMidiOut <> '' then 
    for I := 0 to cbSynthesizer.Items.Count - 1 do 
      if MidiPlayer.OpenedMidiOut = cbSynthesizer.Items[I] then 
        cbSynthesizer.ItemIndex := I; 
end; 
 
function LookForMIDIWindow(aHandle: HWnd; aThreadId: Longint): BOOL; stdcall; 
var 
  ClassName: array[0..255] of Char; 
  ThreadId: DWORD; 
  ProcId: DWORD; 
  s: string; 
begin 
  result := true; 
 
  if GetClassName(aHandle, ClassName, SizeOf(ClassName)) = 0 then 
     exit; 
 
  s := string(ClassName); 
  if copy(s, 1, 14) <> 'WindowsForms10' then 
    exit; 
 
  ThreadId := GetWindowThreadProcessId(aHandle, @ProcId); 
  if aThreadId <> 0 then 
  begin 
     if ThreadId = aThreadId then 
     begin 
        MIDIWinHandle := aHandle; 
        result := false; // exit enumeration loop 
     end; 
  end else  // aThreadId = 0 
  if GetWindowText(aHandle, ClassName, SizeOf(ClassName)) > 0 then 
     if StrComp(ClassName, 'Midi Sheet Music') = 0 then 
     begin 
        MIDIWinHandle := aHandle; 
        result := false; // exit enumeration loop 
     end; 
 
end; 
 
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); 
begin 
 // Check if my launched MidiSheetMusic is running. 
 // ( MIDIWinHandle is not 0 if it is running. ) 
   if MIDIProcessHandle <> 0 then 
   begin 
      MIDIWinHandle := 0; 
      EnumWindows(@LookForMIDIWindow, MIDIThreadId); 
      if MIDIWinHandle <> 0 then 
        SendDataW(FhServer, 'Exit', rqExit);  // Let MidiSheetMusic terminate 
   end; 
end; 
 
procedure TMainForm.FormCreate(Sender: TObject); 
begin 
 {$IFDEF MULTI_LANGUAGE} 
  if GetOEMCP = 949 then   // Korean language ? 
  begin 
    UseLanguage('ko'); 
    Font.Name := '굴림'; 
    Font.Size := 9; 
    lbl_Synth.Font.Name := 'Tahoma'; 
    cbSynthesizer.Font.Name := 'Tahoma'; 
    lbl_Synth.Font.Size := 9; 
    cbSynthesizer.Font.Size := 9; 
    edTime.Font.Name := 'Arial'; 
  end; 
  if GetOEMCP = 1141 then  // *** Please check and modify if it's wrong. 
  begin 
    UseLanguage('de'); 
  end; 
 
  TranslateComponent(self); 
 {$ENDIF} 
 
  FCanvas := TControlCanvas.Create; 
  TControlCanvas(FCanvas).Control := edFile; 
  FCanvas.Font := edFile.Font; 
 
 // You can use following sentences if you want to create the instances of TMidiFile & 
 //  TMidiPlayer by code. (i.e., not by placing the components on form at design time.) 
 // MidiFile := TMidiFile2.Create(Self); 
 // MidiPlayer := TMidiPlayer2.Create(Self); 
 // MidiPlayer.MidiFile := MidiFile; 
 // MidiPlayer.OnMidiEvent := MidiPlayerMidiEvent; 
 // MidiPlayer.OnEndOfTrack := MidiPlayerTrackEnd; 
 // MidiPlayer.OnPlayEnd := MidiPlayerPlayEnd; 
 // MidiPlayer.OnTempoChange := MidiPlayerTempoChange; 
 // MidiPlayer.OnPosUpdate := MidiPlayerPosUpdate; 
 // MidiPlayer.OnGotLyrics := MidiPlayerGotLyrics; 
 
  tbVolume.Max := CMaxVolume div CStepVolume; 
  FillMidiOutDevices; 
  if not MidiPlayer.VolumeAdjustable then 
    tbVolume.Enabled := false; 
 
  hMsg_StartUp := RegisterWindowMessage('MIDI Sheet Music Started'); 
  hMsg_Terminated := RegisterWindowMessage('MIDI Sheet Music Terminated'); 
  Self.Position := poDesigned; 
end; 
 
procedure TMainForm.FormDestroy(Sender: TObject); 
begin 
  FCanvas.Free; 
  if MidiPlayer.Playing then 
    MidiPlayer.StopPlaying; 
 
 // You should activate following sentences if the instances of TMidiFile & TMidiPlayer 
 //  are created by code. 
 // MidiPlayer.Free; 
 // MidiFile.Free; 
end; 
 
 
procedure TMainForm.tbVolumeChange(Sender: TObject); 
var 
  Volume: TVolume; 
begin 
  Volume.Left := tbVolume.Position * CStepVolume;   // Volume of left side 
  Volume.Right := tbVolume.Position * CStepVolume;  // Volume of right side 
  MidiPlayer.Volume := Volume; 
end; 
 
procedure TMainForm.MidiPlayerGotLyrics(Track: Integer; Event: PMidiEvent); 
var 
  MidiEvent: TMidiEvent; 
  s: string; 
  N: integer; 
begin 
  MidiEvent := Event^; 
  s := MidiEvent.Msg; 
  if S = '' then 
    exit; 
 
  N := SyncLyricsList.Items.Count; 
  if (s[Length(s)] = chr(10)) or (s[Length(s)] = chr(13)) then 
  begin 
    if Length(s) = 1 then 
      SyncLyricsList.Items.Add('') 
    else begin 
      if N > 0 then 
      begin 
        SyncLyricsList.Items[N-1] := SyncLyricsList.Items[N-1] + copy(s, 1, length(s) - 1); 
        SyncLyricsList.Items.Add(''); 
      end else 
        SyncLyricsList.Items.Add(''); 
    end; 
  end else 
  if (s[1] = chr(10)) or (s[1] = chr(13)) then  // for MIDI Karaoke files 
  begin 
    SyncLyricsList.Items.Add(''); 
    if Length(s) > 1 then 
      SyncLyricsList.Items[N] := copy(s, 2, length(s) - 1); 
  end else 
  begin 
    if N > 0 then 
      SyncLyricsList.Items[N-1] := SyncLyricsList.Items[N-1] + s 
    else 
      SyncLyricsList.Items.Add(s); 
  end; 
 
// Scroll up the sync lyrics window to show the newly added contents. 
  N := SyncLyricsList.Items.Count - (SyncLyricsList.ClientHeight div SyncLyricsList.ItemHeight); 
  if N < 0 then N := 0; 
  if SyncLyricsList.TopIndex <> N then 
    SyncLyricsList.TopIndex := N; 
end; 
 
procedure TMainForm.MidiPlayerMidiEvent(Track: integer; Event: PMidiEvent); 
var 
  MidiEvent: TMidiEvent; 
 // s: string; 
begin 
 // Played a Midi Event 
 
 MidiEvent := Event^; 
 
 // Following code is an example to display Midi Event 
 { case (MidiEvent.Event div 16) of 
    8: s := 'Note Off'; 
    9: s := 'Note On'; 
    $A: s := 'After touch'; 
    $B: s := 'Control change'; 
    $C: s := 'Program change'; 
    $D: s := 'Channel after touch'; 
    $E: s := 'Pitch wheel change'; 
    $F: if MidiEvent.Event = $FF then 
          s := 'Meta Event' 
        else 
          s := 'System Event'; 
   end; 
  EventDisplayForm.ListBox1.Items.Add('Position:' + intToStr(MidiEvent.Positon) + ' Track:' 
                          + intToStr(Track) + ' ' + s + ' $' + intToHex(MidiEvent.Data1, 2)); } 
  MidiEvent := Event^; 
  case (MidiEvent.Event div 16) of 
    8..$E : ViewChannelForm.GetChannelEvent(MidiEvent); 
   { $F: if MidiEvent.Event = $FF then 
          s := 'Meta Event' 
        else 
          s := 'System Event'; } 
  end; 
end; 
 
procedure TMainForm.MidiPlayerTrackEnd(Track: Integer); 
begin 
 // A track is ended playing 
end; 
 
procedure TMainForm.MidiPlayerPlayEnd(Sender: TObject); 
begin 
 // Play ended  ( = all tracks are ended playing) 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_ChannelStat, ord(plmStopped)); 
end; 
 
procedure TMainForm.MidiPlayerTempoChange(Tempo: Cardinal); 
begin 
 // Tempo changed 
 // Tempo : microseconds per quarter note 
end; 
 
procedure TMainForm.MidiPlayerPosUpdate(TimePos, TicksPos: Cardinal); 
var 
  PlaySec: double; 
 // PlayMilSec: double; 
begin 
  PlaySec := TimePos / (1000 * 24 * 3600); 
 // PlaySec := PlayMilSec / (1000 * 24 * 3600); 
  edTime.Text := FormatDateTime ('nn:ss', PlaySec); 
  edTime.Update; 
 // PositionBar.Position := TicksPos; 
  PositionBar.Position := round(TimePos / 100.0); 
  PositionBar.Update; 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_PlayingPos, LPARAM(TicksPos)); 
 
  if FhServer <> 0 then 
    if TimePos > PrevPosMs then 
      if (TimePos - PrevPosMs) >= 100 then 
      begin 
        PostMessage(FhServer, WM_MIDI_PosUpdate, WPARAM(TicksPos), 1{=progressive update}); 
        PrevPosMs := TimePos; 
        PrevPosTicks := TicksPos; 
      end; 
end; 
 
procedure TMainForm.PositionBarMouseDown(Sender: TObject; Button: TMouseButton; 
  Shift: TShiftState; X, Y: Integer); 
var 
   SongPos : DWORD; 
   SongTicksPos : DWORD; 
   I : integer; 
begin 
  if not MidiPlayer.ReadyToPlay then 
    exit; 
 
  if (not MidiPlayer.Playing) and (not Paused) then 
  begin 
    Paused := true; 
    for I := 0 to MidiPlayer.MidiFile.TrackCount - 1 do 
       MidiPlayer.MidiFile.GetTrack(I).EndOfTrack := false; 
  end; 
 
  if MidiPlayer.Playing or Paused then 
  begin 
    SongPos := round(PositionBar.Max * (X / PositionBar.Width) * 100.0); 
    PositionBar.Position := round(PositionBar.Max * SongPos / MidiPlayer.Midifile.Duration); 
    MidiPlayer.CurrentTime := SongPos; 
 
    if FhServer <> 0 then 
    begin 
      SongTicksPos := MidiPlayer.CurrentPos; 
      PostMessage(FhServer, WM_MIDI_PosUpdate, WPARAM(SongTicksPos), 0{=Move to new position}); 
      PrevPosMs := SongPos; 
      PrevPosTicks := SongTicksPos; 
    end; 
  end; 
end; 
 
procedure TMainForm.btnOpenClick(Sender: TObject); 
var 
  I: integer; 
  p: integer; 
  S: string; 
  S1, S2 : string; 
  CurFileName: string; 
  {$IFNDEF UNICODE} 
  S3: WideString; 
  {$ENDIF} 
  MidiTrack: TMidiTrack; 
  PlayTime: TDateTime; 
  pTitle: pWideChar; 
begin 
  if MidiPlayer.Playing then 
  begin 
   {$IFDEF MULTI_LANGUAGE} 
    S1 := _('Cannot open a MIDI file during playing.'); 
    S2 := _('Confirm'); 
    MessageBox(Self.Handle, PChar(S1), PChar(S2), MB_OK); 
   {$ELSE} 
    MessageBox(Self.Handle, 'Cannot open a MIDI file during playing.', 'Confirm', MB_OK); 
   {$ENDIF} 
    exit; 
  end; 
  if not OpenDialog1.Execute then 
    exit; 
 
  MidiPlayer.OpenFile(OpenDialog1.FileName); 
  if not MidiFile.Valid then 
  begin 
   {$IFDEF MULTI_LANGUAGE} 
    S1 := _('Not a valid MIDI file.'); 
    S2 := _('Confirm'); 
    MessageBox(Self.Handle, PChar(S1), PChar(S2), MB_OK); 
   {$ELSE} 
    MessageBox(Self.Handle, 'Not a valid MIDI file.', 'Confirm', MB_OK); 
   {$ENDIF} 
    exit; 
  end; 
 
  CurMIDIFile := OpenDialog1.FileName; 
  if FhServer <> 0 then 
    SendDataW(FhServer, CurMIDIFile, rqOpenFile); 
 
  edFile.Text := MinimizeName(OpenDialog1.FileName, FCanvas, edFile.Width); 
  case MidiFile.Format of 
    0: edFormat.Text := 'single-track, '; 
    1: edFormat.Text := 'multiple tracks, synchronous, '; 
    2: edFormat.Text := 'multiple tracks, asynchronous, '; 
  end; 
  case MidiFile.TickUnit of 
    0: edFormat.Text := edFormat.Text + intToStr(MidiFile.TicksPerQuarter) + ' Ticks per quarter note'; 
    1: edFormat.Text := edFormat.Text + intToStr(MidiFile.TicksPerQuarter) + ' Ticks per second'; 
  end; 
 
//  edCopyright.Text := MidiFileInfo.GetTrack(0); 
  MidiTrack := TMidiTrack(MidiFile.GetTrack(0)); 
  edCopyright.Text := MidiTrack.Copyright; 
  PlayTime := MidiFile.Duration / (1000 * 24 * 3600); 
  edLength.Text := FormatFloat('#,##0', MidiFile.PlayTicks) + ' Ticks / ' 
                 + FormatFloat('#,##0', MidiFile.Duration) + ' ms' 
                 + ' (' + FormatDateTime('nn:ss', PlayTime) + ')'; 
  if GetOEMCP = 949 then   // Korean language ? 
    edBeat.Text := intToStr(MidiFile.TimeSigature.Denominator) + '분의 ' + 
                   intToStr(MidiFile.TimeSigature.Numerator) + '박자' 
  else 
    edBeat.Text := intToStr(MidiFile.TimeSigature.Numerator) + '-' + 
                   intToStr(MidiFile.TimeSigature.Denominator) + ' time'; 
  edTracks.Text := intToStr(MidiFile.TrackCount); 
  TrackList.Clear; 
  for I := 0 to (MidiFile.TrackCount - 1) do 
  begin 
    MidiTrack := TMidiTrack(MidiFile.GetTrack(I)); 
    TrackList.Items.Add('Track ' + intToStr(I) + ' : ' + MidiTrack.TrackName); 
    TrackList.Checked[I] := true; 
  end; 
  TrackList.ItemEnabled[0] := false; 
 
 // ReloadLyrics(MidiFile.SyncLyrics); 
  CurFileName := ExtractFileName(OpenDialog1.FileName); 
  {$IFDEF UNICODE} 
  pTitle := pChar(CurFileName); 
  {$ELSE} 
  S3 := CurFileName; 
  pTitle := pWideChar(S3); 
  {$ENDIF} 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_Title, LPARAM(pTitle)); 
  MIDILyrics := MidiFile.SyncLyrics; 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_Lyrics, LPARAM(@MidiLyrics)); 
 
  PlainLyrics.Clear; 
  SyncLyricsList.Clear; 
  if MidiFile.Lyrics <> '' then 
  begin 
    S := MidiFile.Lyrics; 
  // Replace Line Feed Code to ' ' + Carriage return code 
   {$IFDEF UNICODE} 
    S := ReplaceStr(S, chr($0A), ' ' + chr($0D)); 
   {$ELSE} 
    S := StringReplace(S, chr($0A), ' ' + chr($0D), [rfReplaceAll]); 
   {$ENDIF} 
    repeat 
      p := pos(chr($0D), S);    // Carriage return code ? 
      if P > 0 then 
      begin 
        PlainLyrics.Items.Add(copy(S, 1, p-1)); 
        S := copy(S, p + 1, Length(S) - p); 
      end else 
      begin 
        PlainLyrics.Items.Add(S); 
        S := ''; 
      end; 
    until S = ''; 
  end; 
 
 // PositionBar.Max := MidiFile.PlayTicks; 
  PositionBar.Max := round(MidiFile.Duration / 100.0); 
  PositionBar.Position := 0; 
  edTime.Text := '00:00'; 
  Paused := false; 
 
  TicksPerMilSec := MidiFile.PlayTicks / MidiFile.Duration; 
  PrevPosMs := 0; 
  PrevPosTicks := 0; 
end; 
 
 
procedure TMainForm.btnPlayClick(Sender: TObject); 
begin 
  if MidiPlayer.Playing then exit; 
  if not MidiPlayer.ReadyToPlay then exit; 
 
  SyncLyricsList.Clear; 
  if Paused then 
    MidiPlayer.ResumePlaying 
  else 
    MidiPlayer.StartPlaying; 
 
  if MidiPlayer.Playing then 
  begin 
    Paused := false; 
    PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_ChannelStat, ord(plmPlaying)); 
    if FhServer <> 0 then 
      PostMessage(FhServer, WM_Set_PlayerState, ord(plmPlaying), 0); 
  end; 
end; 
 
procedure TMainForm.btnPauseClick(Sender: TObject); 
begin 
  if not MidiPlayer.Playing then 
    exit; 
 
  MidiPlayer.StopPlaying; 
  Paused := true; 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_ChannelStat, ord(plmPaused)); 
  if FhServer <> 0 then 
    PostMessage(FhServer, WM_Set_PlayerState, ord(plmPaused), 0); 
end; 
 
procedure TMainForm.btnShowLyricsWindowClick(Sender: TObject); 
begin 
  ShowLyricsForm.Show; 
end; 
 
procedure TMainForm.btnShowMusicSheetClick(Sender: TObject); 
var 
  {$IFNDEF UNICODE} 
   StartUpInfo: TStartUpInfo; 
  {$ELSE} 
   StartUpInfo: TStartUpInfoA; 
  {$ENDIF} 
   s: AnsiString; 
   ProcessInfo: TProcessInformation; 
   ProgDir: string; 
 
begin 
 // Check if my launched MidiSheetMusic is running 
 // ( MIDIProcessHandle is not 0 if it is running ) 
   if MIDIProcessHandle <> 0 then 
   begin 
      MIDIWinHandle := 0; 
      EnumWindows(@LookForMIDIWindow, MIDIThreadId); 
      if MIDIWinHandle <> 0 then 
      begin 
        BringWindowToTop(MIDIWinHandle); 
        exit; 
      end; 
   end; 
 
   ProgDir := ExtractFilePath(ParamStr(0)); 
   if not FileExists(ProgDir + 'MidiSheetMusic.exe') then 
   begin 
      Application.MessageBox('No MidiSheetMusic.exe in program directory.', 
                             'Error', MB_OK or MB_ICONINFORMATION); 
      exit; 
   end; 
 
   FillChar(StartUpInfo, SizeOf(TStartupInfo), #0); 
   FillChar(ProcessInfo, SizeOf(TProcessInformation), #0); 
   StartUpInfo.cb := SizeOf(TStartupInfo); 
   if CurMIDIFile = '' then 
     s := '"' + ProgDir + 'MidiSheetMusic.exe"' 
   else 
     s := '"' + ProgDir + 'MidiSheetMusic.exe" "' + CurMIDIFile + '"'; 
  {$IFNDEF UNICODE} 
   if CreateProcess(nil, pChar(s), nil, nil, false, 
                    NORMAL_PRIORITY_CLASS, nil, nil, StartUpInfo, ProcessInfo) then 
  {$ELSE} 
   if CreateProcessA(nil, pAnsiChar(s), nil, nil, false, 
                    NORMAL_PRIORITY_CLASS, nil, nil, StartUpInfo, ProcessInfo) then 
  {$ENDIF} 
   begin 
      MIDIProcessHandle := ProcessInfo.hProcess; 
      MIDIThreadId := ProcessInfo.dwThreadId; 
   end; 
 
end; 
 
procedure TMainForm.btnStopClick(Sender: TObject); 
begin 
  if not MidiPlayer.Playing then 
    exit; 
 
  MidiPlayer.StopPlaying; 
  PositionBar.Position := 0; 
  edTime.Text := '00:00'; 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_ChannelStat, ord(plmStopped)); 
 
  // Remove any highlighted notes 
  if FhServer <> 0 then 
   // SendDataW(FhServer, 'Stop', rqStop); 
    PostMessage(FhServer, WM_Set_PlayerState, ord(plmStopped), 0); 
 
  PrevPosMs := 0; 
  PrevPosTicks := 0; 
end; 
 
procedure TMainForm.btnViewChannelClick(Sender: TObject); 
begin 
  ViewChannelForm.Show; 
end; 
 
procedure TMainForm.Button1Click(Sender: TObject); 
begin 
  TempoControlForm.Show; 
end; 
 
procedure TMainForm.cbSynthesizerChange(Sender: TObject); 
begin 
  MidiPlayer.SelectMidiOut(cbSynthesizer.Text); 
  if not MidiPlayer.VolumeAdjustable then 
    tbVolume.Enabled := false 
  else begin 
    tbVolume.Enabled := true; 
    tbVolume.Position := (MidiPlayer.Volume.Left) div CStepVolume; // take left volume 
  end; 
end; 
 
procedure TMainForm.TrackListClickCheck(Sender: TObject); 
var 
  MidiTrack: TMidiTrack; 
  tmpPaused: boolean; 
 
begin 
  tmpPaused := false; 
  MidiTrack := TMidiTrack(MidiFile.GetTrack(TrackList.ItemIndex)); 
  if MidiPlayer.Playing then 
    if not TrackList.Checked[TrackList.ItemIndex] then  // Inactivate a track ? 
    begin 
      MidiPlayer.StopPlaying;                           // To avoid a noisy sound. 
      tmpPaused := true; 
    end; 
 
  MidiTrack.Active := TrackList.Checked[TrackList.ItemIndex]; 
  if tmpPaused then 
    MidiPlayer.ResumePlaying; 
end; 
 
 
procedure TMainForm.FormShow(Sender: TObject); 
begin 
  if PassedActivation then 
      exit; 
 
  ViewChannelForm.ResetFields(true{Reset Device});  // ** Added at 2013-02-12 
  if tbVolume.Enabled then 
    tbVolume.Position := 16; 
 
 // ViewChannelForm.Show; 
  ShowLyricsForm.Show; 
  PostMessage(ShowLyricsForm.Handle, WM_InformPlayerStat, Set_ChannelStat, ord(plmStandby)); 
  PassedActivation := true; 
 
 // TrackList.Items.Assign(Screen.Fonts); 
end; 
 
function TMainForm.SendDataW(HServer : THandle; WData: WideString; RequestId : integer): integer; 
var 
   CopyData : TCopyDataStruct; 
   s1 : WideString; 
   nLen: integer; 
 //  {$IF CompilerVersion < 22} 
   dummy : dword; 
 //  {$IFEND} 
begin 
  result := 0; 
 
  if HServer = 0 then 
     exit; 
 
  CopyData.dwData := RequestId; 
 
  s1 := WData; 
  nLen := (Length(s1) + 1) * SizeOf(WideChar); 
  s1 := s1 + chr(0); 
  CopyData.cbData := nLen; 
  CopyData.lpData := pWideChar(s1); 
 
  {$IF CompilerVersion >= 22} 
  result := SendMessageTimeout(HServer, WM_COPYDATA, Self.Handle, LONG_PTR(@CopyData), SMTO_ABORTIFHUNG + SMTO_NORMAL, 
                           5000, @dummy); 
  {$ELSE} 
  result := SendMessageTimeout(HServer, WM_COPYDATA, Self.Handle, LONG_PTR(@CopyData), SMTO_ABORTIFHUNG + SMTO_NORMAL, 
                           5000, dummy); 
  {$IFEND} 
end; 
 
procedure TMainForm.ApplicationEvents1Message(var Msg: tagMSG; 
  var Handled: Boolean); 
var 
  ThreadId : DWORD; 
  ProcId : DWORD; 
  r : TRect; 
  W : integer; 
  LeftSided : boolean; 
 
begin 
  if (Msg.message = hMsg_StartUp) then 
  begin 
    Handled := True; 
 
    if (FhServer = 0) then 
    begin 
      if Msg.lParam <> 1 then  // message comes from MidiSheetMusic ?  (Msg.lParam = 1) 
        exit; 
 
      ThreadId := GetWindowThreadProcessId(Msg.wParam, @ProcId); 
      if ThreadId = MIDIThreadId then  // It's my launched MidiSheetMusic ? 
      begin 
        FhServer := Msg.wParam; 
       // Reset FhServer if the request to set main window handle is not accepted by MidiSheetMusic. 
        if SendDataW(FhServer, intToStr(Self.Handle), rqSetHandle) = 0 then 
          FhServer := 0 
      end; 
      if FhServer <> 0 then 
        if MidiPlayer.Playing then 
          PostMessage(FhServer, WM_Set_PlayerState, ord(plmPlaying), 0) 
        else begin 
          if MidiPlayer.ReadyToPlay then 
          begin 
            PostMessage(FhServer, WM_Set_PlayerState, ord(plmPaused), 0); 
            PostMessage(FhServer, WM_MIDI_PosUpdate, WPARAM(MidiPlayer.CurrentPos), 0{=Move to new position}); 
          end else 
            PostMessage(FhServer, WM_Set_PlayerState, ord(plmStandby), 0); 
        end; 
    end; 
  end else 
  if (Msg.message = hMsg_Terminated) then 
  begin 
    Handled := True; 
 
    if (FhServer <> 0) then   // Previously connected state ? 
    begin 
      if Msg.lParam <> 1 then  // message comes from MidiSheetMusic ? 
        exit; 
 
      if Msg.wParam = FhServer then  // it's my launched MidiSheetMusic ? 
        FhServer := 0; 
    end; 
  end; 
 
end; 
 
procedure TMainForm.btnCloseClick(Sender: TObject); 
begin 
   Close; 
end; 
 
end.