Ignore:
Timestamp:
May 9, 2020, 4:02:07 PM (4 years ago)
Author:
chronos
Message:
  • Modified: Improved HighDPI branch. Imported new changes from trunk branch.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/highdpi/Packages/CevoComponents/Sound.pas

    r178 r210  
    44
    55uses
    6   Messages, SysUtils, Classes, Graphics, Controls, Forms, fgl
    7   {$IFDEF WINDOWS}, MMSystem, Windows{$ENDIF};
     6  UDpiControls, SysUtils, Classes, Graphics, Controls, Forms, fgl, FileUtil,
     7  StringTables, Directories
     8  {$IFDEF WINDOWS}, MMSystem, Windows{$ENDIF}
     9  {$IFDEF LINUX}, Process, AsyncProcess{$ENDIF};
    810
    911type
    10   TSoundPlayer = class(TForm)
     12  TPlayStyle = (psAsync, psSync);
     13
     14  { TSoundPlayer }
     15
     16  TSoundPlayer = class(TDpiForm)
    1117  private
    1218    {$IFDEF WINDOWS}
     19    PrevWndProc: WNDPROC;
    1320    procedure OnMCI(var m: TMessage); message MM_MCINOTIFY;
     21  public
     22    constructor Create(AOwner: TComponent); override;
    1423    {$ENDIF}
    1524  end;
    1625
    17 function PrepareSound(FileName: string): integer;
    18 procedure PlaySound(FileName: string);
    19 
    20 implementation
    21 
    22 {$R *.lfm}
    23 
    24 type
     26  { TSound }
     27
    2528  TSound = class
     29  private
     30    {$IFDEF LINUX}
     31    PlayCommand: string;
     32    SoundPlayerAsyncProcess: TAsyncProcess;
     33    SoundPlayerSyncProcess: TProcess;
     34    {$ENDIF}
     35    function GetNonWindowsPlayCommand: string;
    2636  public
    27     FDeviceID: word;
     37    FDeviceID: Word;
    2838    FFileName: string;
     39    PlayStyle: TPlayStyle;
    2940    constructor Create(const FileName: string);
    3041    destructor Destroy; override;
     
    3445  end;
    3546
     47function PrepareSound(FileName: string): Integer;
     48procedure PlaySound(FileName: string);
     49function Play(Item: string; Index: Integer = -1): Boolean;
     50procedure PreparePlay(Item: string; Index: Integer = -1);
     51
     52const
     53  // sound modes
     54  smOff = 0;
     55  smOn = 1;
     56  smOnAlt = 2;
     57
     58var
     59  Sounds: TStringTable;
     60  SoundMode: Integer;
     61  SoundPlayer: TSoundPlayer;
     62  SoundList: TFPGObjectList<TSound>;
     63  PlayingSound: TSound;
     64
     65
     66implementation
     67
     68{$R *.lfm}
     69
     70resourcestring
     71  SUnableToPlay = 'PlayStyle=%s: Unable to play %s Message:%s';
     72  SPlayCommandNotWork = 'The play command %s does not work on your system';
    3673
    3774constructor TSound.Create(const FileName: string);
     
    4178{$ENDIF}
    4279begin
     80  PlayStyle := psAsync;
     81  FFileName := FileName;
    4382  {$IFDEF WINDOWS}
    4483  FDeviceID := 0;
    45   FFileName := FileName;
    46   if FileExists(FFileName) then
    47   begin
     84  if FileExists(FFileName) then begin
    4885    OpenParm.dwCallback := 0;
    4986    OpenParm.lpstrDeviceType := 'WaveAudio';
    5087    OpenParm.lpstrElementName := PChar(FFileName);
    5188    mciSendCommand(0, MCI_Open, MCI_WAIT or MCI_OPEN_ELEMENT or
    52       MCI_OPEN_SHAREABLE, integer(@OpenParm));
     89      MCI_OPEN_SHAREABLE, DWORD_PTR(@OpenParm));
    5390    FDeviceID := OpenParm.wDeviceID;
    5491  end
    5592  {$ENDIF}
     93  {$IFDEF LINUX}
     94  PlayCommand := GetNonWindowsPlayCommand;
     95  FDeviceID := 1;
     96  {$ENDIF}
    5697end;
    5798
     
    62103    mciSendCommand(FDeviceID, MCI_CLOSE, MCI_WAIT, 0);
    63104  {$ENDIF}
    64   inherited Destroy;
    65 end;
     105  {$IFDEF LINUX}
     106  FreeAndNil(SoundPlayerSyncProcess);
     107  FreeAndNil(SoundPlayerAsyncProcess);
     108  {$ENDIF}
     109  inherited;
     110end;
     111
     112function TSound.GetNonWindowsPlayCommand: string;
     113begin
     114  Result := '';
     115  // Try play
     116  if (FindDefaultExecutablePath('play') <> '') then
     117    Result := 'play';
     118  // Try aplay
     119  if (result = '') then
     120    if (FindDefaultExecutablePath('aplay') <> '') then
     121      Result := 'aplay -q';
     122  // Try paplay
     123  if (Result = '') then
     124    if (FindDefaultExecutablePath('paplay') <> '') then
     125      Result := 'paplay';
     126  // Try mplayer
     127  if (Result = '') then
     128    if (FindDefaultExecutablePath('mplayer') <> '') then
     129      Result := 'mplayer -really-quiet';
     130  // Try CMus
     131  if (Result = '') then
     132    if (FindDefaultExecutablePath('CMus') <> '') then
     133      Result := 'CMus';
     134  // Try pacat
     135  if (Result = '') then
     136    if (FindDefaultExecutablePath('pacat') <> '') then
     137      Result := 'pacat -p';
     138  // Try ffplay
     139  if (Result = '') then
     140    if (FindDefaultExecutablePath('ffplay') <> '') then
     141      result := 'ffplay -autoexit -nodisp';
     142  // Try cvlc
     143  if (Result = '') then
     144    if (FindDefaultExecutablePath('cvlc') <> '') then
     145      result := 'cvlc -q --play-and-exit';
     146  // Try canberra-gtk-play
     147  if (Result = '') then
     148    if (FindDefaultExecutablePath('canberra-gtk-play') <> '') then
     149      Result := 'canberra-gtk-play -c never -f';
     150  // Try Macintosh command?
     151  if (Result = '') then
     152    if (FindDefaultExecutablePath('afplay') <> '') then
     153      Result := 'afplay';
     154end;
     155
    66156
    67157procedure TSound.Play(HWND: DWORD);
     
    69159var
    70160  PlayParm: TMCI_Play_Parms;
     161{$ENDIF}
     162{$IFDEF LINUX}
     163var
     164  L: TStringList;
     165  I: Integer;
    71166{$ENDIF}
    72167begin
     
    78173  end
    79174  {$ENDIF}
     175  {$IFDEF LINUX}
     176  // How to play in Linux? Use generic Linux commands
     177  // Use asyncprocess to play sound as SND_ASYNC
     178  // proceed if we managed to find a valid command
     179  if PlayCommand <> '' then begin
     180    L := TStringList.Create;
     181    try
     182      L.Delimiter := ' ';
     183      L.DelimitedText := PlayCommand;
     184      if PlayStyle = psASync then begin
     185        if SoundPlayerAsyncProcess = nil then
     186          SoundPlayerAsyncProcess := TAsyncProcess.Create(nil);
     187        SoundPlayerAsyncProcess.CurrentDirectory := ExtractFileDir(FFilename);
     188        SoundPlayerAsyncProcess.Executable := FindDefaultExecutablePath(L[0]);
     189        SoundPlayerAsyncProcess.Parameters.Clear;
     190        for I := 1 to L.Count - 1 do
     191          SoundPlayerAsyncProcess.Parameters.Add(L[I]);
     192        SoundPlayerAsyncProcess.Parameters.Add(FFilename);
     193        try
     194          SoundPlayerAsyncProcess.Execute;
     195        except
     196          On E: Exception do
     197            E.CreateFmt(SUnableToPlay, ['paASync', FFilename, E.Message]);
     198        end;
     199        PlayingSound := nil;
     200      end else begin
     201        if SoundPlayerSyncProcess = nil then
     202          SoundPlayerSyncProcess := TProcess.Create(nil);
     203        SoundPlayerSyncProcess.CurrentDirectory := ExtractFileDir(FFilename);
     204        SoundPlayerSyncProcess.Executable := FindDefaultExecutablePath(L[0]);
     205        SoundPlayersyncProcess.Parameters.Clear;
     206        for I := 1 to L.Count - 1 do
     207          SoundPlayerSyncProcess.Parameters.Add(L[I]);
     208        SoundPlayerSyncProcess.Parameters.Add(FFilename);
     209        try
     210          SoundPlayerSyncProcess.Execute;
     211          SoundPlayersyncProcess.WaitOnExit;
     212        except
     213          On E: Exception do
     214            E.CreateFmt(SUnableToPlay, ['paSync', FFilename, E.Message]);
     215        end;
     216        PlayingSound := nil;
     217      end;
     218    finally
     219      L.Free;
     220    end;
     221  end
     222  else
     223    raise Exception.CreateFmt(SPlayCommandNotWork, [PlayCommand]);
     224  {$ENDIF}
    80225end;
    81226
     
    85230  mciSendCommand(FDeviceID, MCI_STOP, 0, 0);
    86231  {$ENDIF}
     232  {$IFDEF LINUX}
     233  if SoundPlayerSyncProcess <> nil then SoundPlayerSyncProcess.Terminate(1);
     234  if SoundPlayerAsyncProcess <> nil then SoundPlayerAsyncProcess.Terminate(1);
     235  {$ENDIF}
    87236end;
    88237
     
    94243end;
    95244
    96 
    97 var
    98   SoundPlayer: TSoundPlayer;
    99   SoundList: TFPGObjectList<TSound>;
    100   PlayingSound: TSound;
    101 
    102245{$IFDEF WINDOWS}
     246function WndCallback(Ahwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam):LRESULT; stdcall;
     247var
     248  Message: TMessage;
     249begin
     250  if (uMsg = MM_MCINOTIFY) then begin
     251    Message.msg := uMsg;
     252    Message.wParam := wParam;
     253    Message.lParam := lParam;
     254    SoundPlayer.OnMCI(Message);
     255  end;
     256  Result := CallWindowProc(SoundPlayer.PrevWndProc, Ahwnd, uMsg, WParam, LParam);
     257end;
     258
    103259procedure TSoundPlayer.OnMCI(var m: TMessage);
    104260begin
    105   if (m.wParam = MCI_Notify_Successful) and (PlayingSound <> nil) then
     261  if (m.wParam = MCI_NOTIFY_SUCCESSFUL) and (PlayingSound <> nil) then
    106262  begin
    107263    PlayingSound.Reset;
     
    109265  end;
    110266end;
     267
     268constructor TSoundPlayer.Create(AOwner: TComponent);
     269begin
     270  inherited;
     271  // MM_MCINOTIFY is not handled by LCL, fallback to low lever handling
     272  // https://wiki.lazarus.freepascal.org/Win32/64_Interface#Processing_non-user_messages_in_your_window
     273  PrevWndProc := Windows.WNDPROC(SetWindowLongPtr(Self.Handle, GWL_WNDPROC, PtrInt(@WndCallback)));
     274end;
    111275{$ENDIF}
    112276
    113 function PrepareSound(FileName: string): integer;
     277function PrepareSound(FileName: string): Integer;
    114278begin
    115279  Result := 0;
    116   while (result < SoundList.Count) and (SoundList[result].FFileName <> FileName) do
    117     inc(result);
    118   if result = SoundList.Count then begin
    119     // first time this sound is played
     280  while (Result < SoundList.Count) and (SoundList[result].FFileName <> FileName) do
     281    Inc(Result);
     282  if Result = SoundList.Count then begin
     283    // First time this sound is played
    120284    SoundList.Add(TSound.Create(FileName));
    121285    Result := SoundList.Count - 1;
     
    125289procedure PlaySound(FileName: string);
    126290begin
    127   if PlayingSound <> nil then
    128     exit;
     291  if PlayingSound <> nil then Exit;
    129292  if SoundPlayer = nil then
    130     Application.CreateForm(TSoundPlayer, SoundPlayer);
     293    DpiApplication.CreateForm(TSoundPlayer, SoundPlayer);
    131294  PlayingSound := SoundList[PrepareSound(FileName)];
    132295  if PlayingSound.FDeviceID = 0 then
     
    136299end;
    137300
     301function Play(Item: string; Index: Integer = -1): Boolean;
     302var
     303  WavFileName: string;
     304begin
     305  Result := False;
     306  if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
     307  begin
     308    Result := True;
     309    Exit;
     310  end;
     311  WavFileName := Sounds.Lookup(Item, Index);
     312  Assert(WavFileName[1] <> '[');
     313  Result := (WavFileName <> '') and (WavFileName[1] <> '[') and (WavFileName <> '*');
     314  if Result then
     315    // SndPlaySound(pchar(GetSoundsDir + DirectorySeparator + WavFileName + '.wav'), SND_ASYNC)
     316    PlaySound(GetSoundsDir + DirectorySeparator + WavFileName);
     317end;
     318
     319procedure PreparePlay(Item: string; Index: Integer = -1);
     320var
     321  WavFileName: string;
     322begin
     323  if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
     324    Exit;
     325  WavFileName := Sounds.Lookup(Item, Index);
     326  Assert(WavFileName[1] <> '[');
     327  if (WavFileName <> '') and (WavFileName[1] <> '[') and (WavFileName <> '*') then
     328    PrepareSound(GetSoundsDir + DirectorySeparator + WavFileName);
     329end;
     330
    138331procedure UnitInit;
    139332begin
     
    150343  end;
    151344  FreeAndNil(SoundList);
     345  if Sounds <> nil then
     346    FreeAndNil(Sounds);
    152347end;
    153348
Note: See TracChangeset for help on using the changeset viewer.