source: tags/1.2.0/Packages/CevoComponents/Sound.pas

Last change on this file was 207, checked in by chronos, 4 years ago
  • Fixed: Removed more compiler hints and warnings.
File size: 9.6 KB
Line 
1unit Sound;
2
3interface
4
5uses
6 SysUtils, Classes, Graphics, Controls, Forms, fgl, FileUtil,
7 StringTables, Directories
8 {$IFDEF WINDOWS}, MMSystem, Windows{$ENDIF}
9 {$IFDEF LINUX}, Process, AsyncProcess{$ENDIF};
10
11type
12 TPlayStyle = (psAsync, psSync);
13
14 { TSoundPlayer }
15
16 TSoundPlayer = class(TForm)
17 private
18 {$IFDEF WINDOWS}
19 PrevWndProc: WNDPROC;
20 procedure OnMCI(var m: TMessage); message MM_MCINOTIFY;
21 public
22 constructor Create(AOwner: TComponent); override;
23 {$ENDIF}
24 end;
25
26 { TSound }
27
28 TSound = class
29 private
30 {$IFDEF LINUX}
31 PlayCommand: string;
32 SoundPlayerAsyncProcess: TAsyncProcess;
33 SoundPlayerSyncProcess: TProcess;
34 {$ENDIF}
35 function GetNonWindowsPlayCommand: string;
36 public
37 FDeviceID: Word;
38 FFileName: string;
39 PlayStyle: TPlayStyle;
40 constructor Create(const FileName: string);
41 destructor Destroy; override;
42 procedure Play(HWND: DWORD);
43 procedure Stop;
44 procedure Reset;
45 end;
46
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';
73
74constructor TSound.Create(const FileName: string);
75{$IFDEF WINDOWS}
76var
77 OpenParm: TMCI_Open_Parms;
78{$ENDIF}
79begin
80 PlayStyle := psAsync;
81 FFileName := FileName;
82 {$IFDEF WINDOWS}
83 FDeviceID := 0;
84 if FileExists(FFileName) then begin
85 OpenParm.dwCallback := 0;
86 OpenParm.lpstrDeviceType := 'WaveAudio';
87 OpenParm.lpstrElementName := PChar(FFileName);
88 mciSendCommand(0, MCI_Open, MCI_WAIT or MCI_OPEN_ELEMENT or
89 MCI_OPEN_SHAREABLE, DWORD_PTR(@OpenParm));
90 FDeviceID := OpenParm.wDeviceID;
91 end
92 {$ENDIF}
93 {$IFDEF LINUX}
94 PlayCommand := GetNonWindowsPlayCommand;
95 FDeviceID := 1;
96 {$ENDIF}
97end;
98
99destructor TSound.Destroy;
100begin
101 {$IFDEF WINDOWS}
102 if FDeviceID <> 0 then
103 mciSendCommand(FDeviceID, MCI_CLOSE, MCI_WAIT, 0);
104 {$ENDIF}
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
156
157procedure TSound.Play(HWND: DWORD);
158{$IFDEF WINDOWS}
159var
160 PlayParm: TMCI_Play_Parms;
161{$ENDIF}
162{$IFDEF LINUX}
163var
164 L: TStringList;
165 I: Integer;
166{$ENDIF}
167begin
168 {$IFDEF WINDOWS}
169 if FDeviceID <> 0 then
170 begin
171 PlayParm.dwCallback := HWND;
172 mciSendCommand(FDeviceID, MCI_PLAY, MCI_NOTIFY, DWORD_PTR(@PlayParm));
173 end
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}
225end;
226
227procedure TSound.Stop;
228begin
229 {$IFDEF WINDOWS}
230 mciSendCommand(FDeviceID, MCI_STOP, 0, 0);
231 {$ENDIF}
232 {$IFDEF LINUX}
233 if SoundPlayerSyncProcess <> nil then SoundPlayerSyncProcess.Terminate(1);
234 if SoundPlayerAsyncProcess <> nil then SoundPlayerAsyncProcess.Terminate(1);
235 {$ENDIF}
236end;
237
238procedure TSound.Reset;
239begin
240 {$IFDEF WINDOWS}
241 mciSendCommand(FDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
242 {$ENDIF}
243end;
244
245{$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
259procedure TSoundPlayer.OnMCI(var m: TMessage);
260begin
261 if (m.wParam = MCI_NOTIFY_SUCCESSFUL) and (PlayingSound <> nil) then
262 begin
263 PlayingSound.Reset;
264 PlayingSound := nil;
265 end;
266end;
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;
275{$ENDIF}
276
277function PrepareSound(FileName: string): Integer;
278begin
279 Result := 0;
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
284 SoundList.Add(TSound.Create(FileName));
285 Result := SoundList.Count - 1;
286 end;
287end;
288
289procedure PlaySound(FileName: string);
290begin
291 if PlayingSound <> nil then Exit;
292 if SoundPlayer = nil then
293 Application.CreateForm(TSoundPlayer, SoundPlayer);
294 PlayingSound := SoundList[PrepareSound(FileName)];
295 if PlayingSound.FDeviceID = 0 then
296 PlayingSound := nil
297 else
298 PlayingSound.Play(SoundPlayer.Handle);
299end;
300
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
331procedure UnitInit;
332begin
333 SoundList := TFPGObjectList<TSound>.Create;
334 PlayingSound := nil;
335 SoundPlayer := nil;
336end;
337
338procedure UnitDone;
339begin
340 if PlayingSound <> nil then begin
341 PlayingSound.Stop;
342 Sleep(222);
343 end;
344 FreeAndNil(SoundList);
345 if Sounds <> nil then
346 FreeAndNil(Sounds);
347end;
348
349initialization
350
351UnitInit;
352
353finalization
354
355UnitDone;
356
357end.
Note: See TracBrowser for help on using the repository browser.