Ignore:
Timestamp:
Jun 16, 2019, 10:57:17 AM (5 years ago)
Author:
chronos
Message:
  • Added: Sound support for Linux. By default it should execute asynchronously 'play' command for playing mp3 files.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Packages/CevoComponents/Sound.pas

    r135 r173  
    44
    55uses
    6   Messages, SysUtils, Classes, Graphics, Controls, Forms, fgl
    7   {$IFDEF WINDOWS}, MMSystem, Windows{$ENDIF};
     6  Messages, SysUtils, Classes, Graphics, Controls, Forms, fgl, FileUtil,
     7  StringTables, Directories
     8  {$IFDEF WINDOWS}, MMSystem, Windows{$ENDIF}
     9  {$IFDEF LINUX}, Process, AsyncProcess{$ENDIF};
    810
    911type
     12  TPlayStyle = (psAsync, psSync);
     13
    1014  TSoundPlayer = class(TForm)
    1115  private
     
    1519  end;
    1620
    17 function PrepareSound(FileName: string): integer;
    18 procedure PlaySound(FileName: string);
    19 
    20 implementation
    21 
    22 {$R *.lfm}
    23 
    24 type
     21  { TSound }
     22
    2523  TSound = class
     24  private
     25    PlayCommand: string;
     26    {$IFDEF LINUX}
     27    SoundPlayerAsyncProcess: TAsyncProcess;
     28    SoundPlayerSyncProcess: TProcess;
     29    {$ENDIF}
     30    function GetNonWindowsPlayCommand: string;
    2631  public
    27     FDeviceID: word;
     32    FDeviceID: Word;
    2833    FFileName: string;
     34    PlayStyle: TPlayStyle;
    2935    constructor Create(const FileName: string);
    3036    destructor Destroy; override;
     
    3440  end;
    3541
     42function PrepareSound(FileName: string): Integer;
     43procedure PlaySound(FileName: string);
     44function Play(Item: string; Index: Integer = -1): Boolean;
     45procedure PreparePlay(Item: string; Index: Integer = -1);
     46
     47const
     48  // sound modes
     49  smOff = 0;
     50  smOn = 1;
     51  smOnAlt = 2;
     52
     53var
     54  Sounds: TStringTable;
     55  SoundMode: Integer;
     56  SoundPlayer: TSoundPlayer;
     57  SoundList: TFPGObjectList<TSound>;
     58  PlayingSound: TSound;
     59
     60
     61implementation
     62
     63{$R *.lfm}
     64
     65resourcestring
     66  SUnableToPlay = 'PlayStyle=%s: Unable to play %s Message:%s';
     67  SPlayCommandNotWork = 'The play command %s does not work on your system';
    3668
    3769constructor TSound.Create(const FileName: string);
     
    4173{$ENDIF}
    4274begin
     75  PlayStyle := psAsync;
     76  FFileName := FileName;
    4377  {$IFDEF WINDOWS}
    4478  FDeviceID := 0;
    45   FFileName := FileName;
    46   if FileExists(FFileName) then
    47   begin
     79  if FileExists(FFileName) then begin
    4880    OpenParm.dwCallback := 0;
    4981    OpenParm.lpstrDeviceType := 'WaveAudio';
     
    5486  end
    5587  {$ENDIF}
     88  {$IFDEF LINUX}
     89  PlayCommand := GetNonWindowsPlayCommand;
     90  FDeviceID := 1;
     91  {$ENDIF}
    5692end;
    5793
     
    6298    mciSendCommand(FDeviceID, MCI_CLOSE, MCI_WAIT, 0);
    6399  {$ENDIF}
     100  {$IFDEF LINUX}
     101  {$IFNDEF WINDOWS}
     102  FreeAndNil(SoundPlayerSyncProcess);
     103  FreeAndNil(SoundPlayerAsyncProcess);
     104  {$ENDIF}
     105  {$ENDIF}
    64106  inherited Destroy;
    65107end;
     108
     109function TSound.GetNonWindowsPlayCommand: string;
     110begin
     111  Result := '';
     112  // Try play
     113  if (FindDefaultExecutablePath('play') <> '') then
     114    Result := 'play';
     115  // Try aplay
     116  if (result = '') then
     117    if (FindDefaultExecutablePath('aplay') <> '') then
     118      Result := 'aplay -q';
     119  // Try paplay
     120  if (Result = '') then
     121    if (FindDefaultExecutablePath('paplay') <> '') then
     122      Result := 'paplay';
     123  // Try mplayer
     124  if (Result = '') then
     125    if (FindDefaultExecutablePath('mplayer') <> '') then
     126      Result := 'mplayer -really-quiet';
     127  // Try CMus
     128  if (Result = '') then
     129    if (FindDefaultExecutablePath('CMus') <> '') then
     130      Result := 'CMus';
     131  // Try pacat
     132  if (Result = '') then
     133    if (FindDefaultExecutablePath('pacat') <> '') then
     134      Result := 'pacat -p';
     135  // Try ffplay
     136  if (Result = '') then
     137    if (FindDefaultExecutablePath('ffplay') <> '') then
     138      result := 'ffplay -autoexit -nodisp';
     139  // Try cvlc
     140  if (Result = '') then
     141    if (FindDefaultExecutablePath('cvlc') <> '') then
     142      result := 'cvlc -q --play-and-exit';
     143  // Try canberra-gtk-play
     144  if (Result = '') then
     145    if (FindDefaultExecutablePath('canberra-gtk-play') <> '') then
     146      Result := 'canberra-gtk-play -c never -f';
     147  // Try Macintosh command?
     148  if (Result = '') then
     149    if (FindDefaultExecutablePath('afplay') <> '') then
     150      Result := 'afplay';
     151end;
     152
    66153
    67154procedure TSound.Play(HWND: DWORD);
     
    69156var
    70157  PlayParm: TMCI_Play_Parms;
     158{$ENDIF}
     159{$IFDEF LINUX}
     160var
     161  L: TStringList;
     162  I: Integer;
    71163{$ENDIF}
    72164begin
     
    78170  end
    79171  {$ENDIF}
     172  {$IFDEF LINUX}
     173  // How to play in Linux? Use generic Linux commands
     174  // Use asyncprocess to play sound as SND_ASYNC
     175  // proceed if we managed to find a valid command
     176  if PlayCommand <> '' then begin
     177    L := TStringList.Create;
     178    try
     179      L.Delimiter := ' ';
     180      L.DelimitedText := PlayCommand;
     181      if PlayStyle = psASync then begin
     182        if SoundPlayerAsyncProcess = nil then
     183          SoundPlayerAsyncProcess := TAsyncProcess.Create(nil);
     184        SoundPlayerAsyncProcess.CurrentDirectory := ExtractFileDir(FFilename);
     185        SoundPlayerAsyncProcess.Executable := FindDefaultExecutablePath(L[0]);
     186        SoundPlayerAsyncProcess.Parameters.Clear;
     187        for I := 1 to L.Count - 1 do
     188          SoundPlayerAsyncProcess.Parameters.Add(L[I]);
     189        SoundPlayerAsyncProcess.Parameters.Add(FFilename);
     190        try
     191          SoundPlayerAsyncProcess.Execute;
     192        except
     193          On E: Exception do
     194            E.CreateFmt(SUnableToPlay, ['paASync', FFilename, E.Message]);
     195        end;
     196        PlayingSound := nil;
     197      end else begin
     198        if SoundPlayerSyncProcess = nil then
     199          SoundPlayerSyncProcess := TProcess.Create(nil);
     200        SoundPlayerSyncProcess.CurrentDirectory := ExtractFileDir(FFilename);
     201        SoundPlayerSyncProcess.Executable := FindDefaultExecutablePath(L[0]);
     202        SoundPlayersyncProcess.Parameters.Clear;
     203        for I := 1 to L.Count - 1 do
     204          SoundPlayerSyncProcess.Parameters.Add(L[I]);
     205        SoundPlayerSyncProcess.Parameters.Add(FFilename);
     206        try
     207          SoundPlayerSyncProcess.Execute;
     208          SoundPlayersyncProcess.WaitOnExit;
     209        except
     210          On E: Exception do
     211            E.CreateFmt(SUnableToPlay, ['paSync', FFilename, E.Message]);
     212        end;
     213        PlayingSound := nil;
     214      end;
     215    finally
     216      L.Free;
     217    end;
     218  end
     219  else
     220    raise Exception.CreateFmt(SPlayCommandNotWork, [PlayCommand]);
     221  {$ENDIF}
    80222end;
    81223
     
    85227  mciSendCommand(FDeviceID, MCI_STOP, 0, 0);
    86228  {$ENDIF}
     229  {$IFDEF LINUX}
     230  if SoundPlayerSyncProcess <> nil then SoundPlayerSyncProcess.Terminate(1);
     231  if SoundPlayerAsyncProcess <> nil then SoundPlayerAsyncProcess.Terminate(1);
     232  {$ENDIF}
    87233end;
    88234
     
    93239  {$ENDIF}
    94240end;
    95 
    96 
    97 var
    98   SoundPlayer: TSoundPlayer;
    99   SoundList: TFPGObjectList<TSound>;
    100   PlayingSound: TSound;
    101241
    102242{$IFDEF WINDOWS}
     
    111251{$ENDIF}
    112252
    113 function PrepareSound(FileName: string): integer;
     253function PrepareSound(FileName: string): Integer;
    114254begin
    115255  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
     256  while (Result < SoundList.Count) and (SoundList[result].FFileName <> FileName) do
     257    Inc(Result);
     258  if Result = SoundList.Count then begin
     259    // First time this sound is played
    120260    SoundList.Add(TSound.Create(FileName));
    121261    Result := SoundList.Count - 1;
     
    125265procedure PlaySound(FileName: string);
    126266begin
    127   if PlayingSound <> nil then
    128     exit;
     267  if PlayingSound <> nil then Exit;
    129268  if SoundPlayer = nil then
    130269    Application.CreateForm(TSoundPlayer, SoundPlayer);
     
    136275end;
    137276
     277function Play(Item: string; Index: Integer = -1): Boolean;
     278{$IFNDEF DEBUG}
     279var
     280  WavFileName: string;
     281{$ENDIF}
     282begin
     283  Result := False;
     284{$IFNDEF DEBUG}
     285  if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
     286  begin
     287    Result := True;
     288    Exit;
     289  end;
     290  WavFileName := Sounds.Lookup(Item, Index);
     291  Assert(WavFileName[1] <> '[');
     292  Result := (WavFileName <> '') and (WavFileName[1] <> '[') and (WavFileName <> '*');
     293  if Result then
     294    // SndPlaySound(pchar(HomeDir+'Sounds' +DirectorySeparator+WavFileName+'.wav'),SND_ASYNC)
     295    PlaySound(HomeDir + 'Sounds' + DirectorySeparator + WavFileName);
     296{$ENDIF}
     297end;
     298
     299procedure PreparePlay(Item: string; Index: Integer = -1);
     300{$IFNDEF DEBUG}
     301var
     302  WavFileName: string;
     303{$ENDIF}
     304begin
     305{$IFNDEF DEBUG}
     306  if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
     307    Exit;
     308  WavFileName := Sounds.Lookup(Item, Index);
     309  Assert(WavFileName[1] <> '[');
     310  if (WavFileName <> '') and (WavFileName[1] <> '[') and (WavFileName <> '*') then
     311    PrepareSound(HomeDir + 'Sounds' + DirectorySeparator + WavFileName);
     312{$ENDIF}
     313end;
     314
    138315procedure UnitInit;
    139316begin
     
    150327  end;
    151328  FreeAndNil(SoundList);
     329  if Sounds <> nil then
     330    FreeAndNil(Sounds);
    152331end;
    153332
Note: See TracChangeset for help on using the changeset viewer.