Changeset 173 for trunk


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.
Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Install/deb/debian/control

    r160 r173  
    88Package: c-evo
    99Architecture: any
    10 Depends: ${shlibs:Depends}, ${misc:Depends},
     10Depends: ${shlibs:Depends}, ${misc:Depends}, sox, libsox-fmt-mp3
    1111Description: Empire building game
    1212HomePage: https://app.zdechov.net/c-evo
  • trunk/LocalPlayer/CityScreen.pas

    r170 r173  
    9090
    9191uses
    92   Select, Messg, MessgEx, Help, Tribes, Directories, Math, PixelPointer;
     92  Select, Messg, MessgEx, Help, Tribes, Directories, Math, PixelPointer, Sound;
    9393
    9494{$R *.lfm}
  • trunk/LocalPlayer/MessgEx.pas

    r170 r173  
    7474uses
    7575  ClientTools, BaseWin, Term, Help, UnitStat, Tribes, PixelPointer,
    76   IsoEngine, Diagram;
     76  IsoEngine, Diagram, Sound;
    7777
    7878{$R *.lfm}
  • trunk/LocalPlayer/Term.pas

    r170 r173  
    459459procedure HelpOnTerrain(Loc, NewMode: integer);
    460460
     461
    461462implementation
    462463
    463464uses
    464465  Directories, IsoEngine, CityScreen, Draft, MessgEx, Select, CityType, Help,
    465   UnitStat, Log, Diagram, NatStat, Wonders, Enhance, Nego, PixelPointer,
     466  UnitStat, Log, Diagram, NatStat, Wonders, Enhance, Nego, PixelPointer, Sound,
    466467  Battle, Rates, TechTree, Registry;
    467468
  • trunk/Messg.pas

    r135 r173  
    4242
    4343implementation
     44
     45uses
     46  Sound;
    4447
    4548{$R *.lfm}
  • trunk/Packages/CevoComponents/ScreenTools.pas

    r170 r173  
    2121{$ENDIF}
    2222procedure RestoreResolution;
    23 function Play(Item: string; Index: integer = -1): boolean;
    24 procedure PreparePlay(Item: string; Index: integer = -1);
    2523procedure EmptyMenu(MenuItems: TMenuItem; Keep: Integer = 0);
    2624function TurnToYear(Turn: integer): integer;
     
    121119  hOrna = 26; // ornament
    122120
    123   // sound modes
    124   smOff = 0;
    125   smOn = 1;
    126   smOnAlt = 2;
    127 
    128121  // color matrix
    129122  clkAge0 = 1;
     
    173166
    174167var
    175   Phrases, Phrases2, Sounds: TStringTable;
    176   nGrExt: integer;
     168  Phrases: TStringTable;
     169  Phrases2: TStringTable;
     170  nGrExt: Integer;
    177171  GrExt: array [0 .. nGrExtmax - 1] of ^TGrExtDescr;
    178   HGrSystem, HGrSystem2, ClickFrameColor, SoundMode, MainTextureAge: integer;
     172  HGrSystem, HGrSystem2, ClickFrameColor, MainTextureAge: Integer;
    179173  MainTexture: TTexture;
    180174  Templates, Colors, Paper, BigImp, LogoBuffer: TBitmap;
     
    227221  ResolutionChanged := False;
    228222  {$ENDIF}
    229 end;
    230 
    231 function Play(Item: string; Index: integer = -1): boolean;
    232 {$IFNDEF DEBUG}
    233 var
    234   WavFileName: string;
    235 {$ENDIF}
    236 begin
    237   Result := False;
    238 {$IFNDEF DEBUG}
    239   if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
    240   begin
    241     Result := True;
    242     Exit;
    243   end;
    244   WavFileName := Sounds.Lookup(Item, Index);
    245   Assert(WavFileName[1] <> '[');
    246   Result := (WavFileName <> '') and (WavFileName[1] <> '[') and (WavFileName <> '*');
    247   if Result then
    248     // SndPlaySound(pchar(HomeDir+'Sounds' +DirectorySeparator+WavFileName+'.wav'),SND_ASYNC)
    249     PlaySound(HomeDir + 'Sounds' + DirectorySeparator + WavFileName);
    250 {$ENDIF}
    251 end;
    252 
    253 procedure PreparePlay(Item: string; Index: Integer = -1);
    254 {$IFNDEF DEBUG}
    255 var
    256   WavFileName: string;
    257 {$ENDIF}
    258 begin
    259 {$IFNDEF DEBUG}
    260   if (Sounds = nil) or (SoundMode = smOff) or (Item = '') then
    261     Exit;
    262   WavFileName := Sounds.Lookup(Item, Index);
    263   Assert(WavFileName[1] <> '[');
    264   if (WavFileName <> '') and (WavFileName[1] <> '[') and (WavFileName <> '*') then
    265     PrepareSound(HomeDir + 'Sounds' + DirectorySeparator + WavFileName);
    266 {$ENDIF}
    267223end;
    268224
     
    14031359procedure LoadPhrases;
    14041360begin
    1405   if Phrases = nil then
    1406     Phrases := TStringTable.Create;
    1407   if Phrases2 = nil then
    1408     Phrases2 := TStringTable.Create;
     1361  if Phrases = nil then Phrases := TStringTable.Create;
     1362  if Phrases2 = nil then Phrases2 := TStringTable.Create;
    14091363  Phrases2FallenBackToEnglish := False;
    14101364  if FileExists(LocalizedFilePath('Language.txt')) then
    14111365  begin
    1412     Phrases.loadfromfile(LocalizedFilePath('Language.txt'));
     1366    Phrases.LoadFromFile(LocalizedFilePath('Language.txt'));
    14131367    if FileExists(LocalizedFilePath('Language2.txt')) then
    1414       Phrases2.loadfromfile(LocalizedFilePath('Language2.txt'))
     1368      Phrases2.LoadFromFile(LocalizedFilePath('Language2.txt'))
    14151369    else
    14161370    begin
    1417       Phrases2.loadfromfile(HomeDir + 'Language2.txt');
     1371      Phrases2.LoadFromFile(HomeDir + 'Language2.txt');
    14181372      Phrases2FallenBackToEnglish := True;
    14191373    end;
     
    14211375  else
    14221376  begin
    1423     Phrases.loadfromfile(HomeDir + 'Language.txt');
    1424     Phrases2.loadfromfile(HomeDir + 'Language2.txt');
    1425   end;
    1426 
    1427   if Sounds = nil then
    1428     Sounds := TStringTable.Create;
    1429   if not Sounds.loadfromfile(HomeDir + 'Sounds' + DirectorySeparator + 'sound.txt') then
     1377    Phrases.LoadFromFile(HomeDir + 'Language.txt');
     1378    Phrases2.LoadFromFile(HomeDir + 'Language2.txt');
     1379  end;
     1380
     1381  if Sounds = nil then Sounds := TStringTable.Create;
     1382  if not Sounds.LoadFromFile(HomeDir + 'Sounds' + DirectorySeparator + 'sound.txt') then
    14301383  begin
    14311384    FreeAndNil(Sounds);
     
    14541407        if s[1] = '#' then begin
    14551408          s := TrimRight(s);
    1456           if s = '#SMALL' then
    1457             Section := ftSmall
    1458           else if s = '#TINY' then
    1459             Section := ftTiny
    1460           else if s = '#CAPTION' then
    1461             Section := ftCaption
    1462           else if s = '#BUTTON' then
    1463             Section := ftButton
    1464           else
    1465             Section := ftNormal;
     1409          if s = '#SMALL' then Section := ftSmall
     1410          else if s = '#TINY' then Section := ftTiny
     1411          else if s = '#CAPTION' then Section := ftCaption
     1412          else if s = '#BUTTON' then Section := ftButton
     1413          else Section := ftNormal;
    14661414        end else begin
    14671415          p := Pos(',', s);
     
    15991547  FreeAndNil(Phrases);
    16001548  FreeAndNil(Phrases2);
    1601   if Sounds <> nil then
    1602     FreeAndNil(Sounds);
    16031549  FreeAndNil(LogoBuffer);
    16041550  FreeAndNil(BigImp);
  • 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.