Changeset 210 for branches/highdpi/Packages/CevoComponents/Sound.pas
- Timestamp:
- May 9, 2020, 4:02:07 PM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/highdpi/Packages/CevoComponents/Sound.pas
r178 r210 4 4 5 5 uses 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}; 8 10 9 11 type 10 TSoundPlayer = class(TForm) 12 TPlayStyle = (psAsync, psSync); 13 14 { TSoundPlayer } 15 16 TSoundPlayer = class(TDpiForm) 11 17 private 12 18 {$IFDEF WINDOWS} 19 PrevWndProc: WNDPROC; 13 20 procedure OnMCI(var m: TMessage); message MM_MCINOTIFY; 21 public 22 constructor Create(AOwner: TComponent); override; 14 23 {$ENDIF} 15 24 end; 16 25 17 function PrepareSound(FileName: string): integer; 18 procedure PlaySound(FileName: string); 19 20 implementation 21 22 {$R *.lfm} 23 24 type 26 { TSound } 27 25 28 TSound = class 29 private 30 {$IFDEF LINUX} 31 PlayCommand: string; 32 SoundPlayerAsyncProcess: TAsyncProcess; 33 SoundPlayerSyncProcess: TProcess; 34 {$ENDIF} 35 function GetNonWindowsPlayCommand: string; 26 36 public 27 FDeviceID: word;37 FDeviceID: Word; 28 38 FFileName: string; 39 PlayStyle: TPlayStyle; 29 40 constructor Create(const FileName: string); 30 41 destructor Destroy; override; … … 34 45 end; 35 46 47 function PrepareSound(FileName: string): Integer; 48 procedure PlaySound(FileName: string); 49 function Play(Item: string; Index: Integer = -1): Boolean; 50 procedure PreparePlay(Item: string; Index: Integer = -1); 51 52 const 53 // sound modes 54 smOff = 0; 55 smOn = 1; 56 smOnAlt = 2; 57 58 var 59 Sounds: TStringTable; 60 SoundMode: Integer; 61 SoundPlayer: TSoundPlayer; 62 SoundList: TFPGObjectList<TSound>; 63 PlayingSound: TSound; 64 65 66 implementation 67 68 {$R *.lfm} 69 70 resourcestring 71 SUnableToPlay = 'PlayStyle=%s: Unable to play %s Message:%s'; 72 SPlayCommandNotWork = 'The play command %s does not work on your system'; 36 73 37 74 constructor TSound.Create(const FileName: string); … … 41 78 {$ENDIF} 42 79 begin 80 PlayStyle := psAsync; 81 FFileName := FileName; 43 82 {$IFDEF WINDOWS} 44 83 FDeviceID := 0; 45 FFileName := FileName; 46 if FileExists(FFileName) then 47 begin 84 if FileExists(FFileName) then begin 48 85 OpenParm.dwCallback := 0; 49 86 OpenParm.lpstrDeviceType := 'WaveAudio'; 50 87 OpenParm.lpstrElementName := PChar(FFileName); 51 88 mciSendCommand(0, MCI_Open, MCI_WAIT or MCI_OPEN_ELEMENT or 52 MCI_OPEN_SHAREABLE, integer(@OpenParm));89 MCI_OPEN_SHAREABLE, DWORD_PTR(@OpenParm)); 53 90 FDeviceID := OpenParm.wDeviceID; 54 91 end 55 92 {$ENDIF} 93 {$IFDEF LINUX} 94 PlayCommand := GetNonWindowsPlayCommand; 95 FDeviceID := 1; 96 {$ENDIF} 56 97 end; 57 98 … … 62 103 mciSendCommand(FDeviceID, MCI_CLOSE, MCI_WAIT, 0); 63 104 {$ENDIF} 64 inherited Destroy; 65 end; 105 {$IFDEF LINUX} 106 FreeAndNil(SoundPlayerSyncProcess); 107 FreeAndNil(SoundPlayerAsyncProcess); 108 {$ENDIF} 109 inherited; 110 end; 111 112 function TSound.GetNonWindowsPlayCommand: string; 113 begin 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'; 154 end; 155 66 156 67 157 procedure TSound.Play(HWND: DWORD); … … 69 159 var 70 160 PlayParm: TMCI_Play_Parms; 161 {$ENDIF} 162 {$IFDEF LINUX} 163 var 164 L: TStringList; 165 I: Integer; 71 166 {$ENDIF} 72 167 begin … … 78 173 end 79 174 {$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} 80 225 end; 81 226 … … 85 230 mciSendCommand(FDeviceID, MCI_STOP, 0, 0); 86 231 {$ENDIF} 232 {$IFDEF LINUX} 233 if SoundPlayerSyncProcess <> nil then SoundPlayerSyncProcess.Terminate(1); 234 if SoundPlayerAsyncProcess <> nil then SoundPlayerAsyncProcess.Terminate(1); 235 {$ENDIF} 87 236 end; 88 237 … … 94 243 end; 95 244 96 97 var98 SoundPlayer: TSoundPlayer;99 SoundList: TFPGObjectList<TSound>;100 PlayingSound: TSound;101 102 245 {$IFDEF WINDOWS} 246 function WndCallback(Ahwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam):LRESULT; stdcall; 247 var 248 Message: TMessage; 249 begin 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); 257 end; 258 103 259 procedure TSoundPlayer.OnMCI(var m: TMessage); 104 260 begin 105 if (m.wParam = MCI_N otify_Successful) and (PlayingSound <> nil) then261 if (m.wParam = MCI_NOTIFY_SUCCESSFUL) and (PlayingSound <> nil) then 106 262 begin 107 263 PlayingSound.Reset; … … 109 265 end; 110 266 end; 267 268 constructor TSoundPlayer.Create(AOwner: TComponent); 269 begin 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))); 274 end; 111 275 {$ENDIF} 112 276 113 function PrepareSound(FileName: string): integer;277 function PrepareSound(FileName: string): Integer; 114 278 begin 115 279 Result := 0; 116 while ( result < SoundList.Count) and (SoundList[result].FFileName <> FileName) do117 inc(result);118 if result = SoundList.Count then begin119 // first time this sound is played280 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 120 284 SoundList.Add(TSound.Create(FileName)); 121 285 Result := SoundList.Count - 1; … … 125 289 procedure PlaySound(FileName: string); 126 290 begin 127 if PlayingSound <> nil then 128 exit; 291 if PlayingSound <> nil then Exit; 129 292 if SoundPlayer = nil then 130 Application.CreateForm(TSoundPlayer, SoundPlayer);293 DpiApplication.CreateForm(TSoundPlayer, SoundPlayer); 131 294 PlayingSound := SoundList[PrepareSound(FileName)]; 132 295 if PlayingSound.FDeviceID = 0 then … … 136 299 end; 137 300 301 function Play(Item: string; Index: Integer = -1): Boolean; 302 var 303 WavFileName: string; 304 begin 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); 317 end; 318 319 procedure PreparePlay(Item: string; Index: Integer = -1); 320 var 321 WavFileName: string; 322 begin 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); 329 end; 330 138 331 procedure UnitInit; 139 332 begin … … 150 343 end; 151 344 FreeAndNil(SoundList); 345 if Sounds <> nil then 346 FreeAndNil(Sounds); 152 347 end; 153 348
Note:
See TracChangeset
for help on using the changeset viewer.