2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Basic sound manager based on WinMM
8
17/11/09 - DaStr - Improved Unix compatibility
9
(thanks Predator) (BugtrackerID = 2893580)
10
25/07/09 - DaStr - Added $I GLScene.inc
11
30/05/09 - DanB - Fixes for AV when sound finishes, and was repeating the same code more than necessary.
12
24/04/09 - DanB - Creation, split from GLSound.pas, to remove windows dependency
20
{$IFDEF UNIX}{$Message Error 'Unit not supported'}{$ENDIF}
22
uses Classes, GLSound, MMSystem, GLSoundFileObjects;
28
{ Basic sound manager based on WinMM <i>waveOut</i> function.
29
This manager has NO 3D miximing capacity, this is merely a default manager
30
that should work on any windows based system, and help showcasing/testing
31
basic GLSS core functionality.
32
Apart from 3D, mute, pause, priority and volume are ignored too, and only
33
sampling conversions supported by the windows ACM driver are supported
34
(ie. no 4bits samples playback etc.). }
35
TGLSMWaveOut = class (TGLSoundManager)
41
function DoActivate : Boolean; override;
42
procedure DoDeActivate; override;
44
procedure KillSource(aSource : TGLBaseSoundSource); override;
48
constructor Create(AOwner : TComponent); override;
49
destructor Destroy; override;
51
procedure UpdateSources; override;
54
property MaxChannels default 4;
57
procedure PlayOnWaveOut(pcmData : Pointer; lengthInBytes : Integer;
58
sampling : TGLSoundSampling); overload;
59
function PlayOnWaveOut(pcmData : Pointer; lengthInBytes : Integer;
60
waveFormat : TWaveFormatEx) : HWaveOut; overload;
67
TSoundState = (ssPlaying, ssFinished);
69
TWaveOutPlayingRec = record
70
CurrentState: TSoundState;
71
WaveOutDevice: hwaveout;
74
PWaveOutPlayingRec = ^TWaveOutPlayingRec;
76
procedure _waveOutCallBack2(hwo : HWAVEOUT; uMsg : Cardinal;
77
dwInstance, dwParam1, dwParam2 : Integer); stdcall;
83
// PlayOnWaveOut (waveformat)
85
function PlayOnWaveOut(pcmData : Pointer; lengthInBytes : Integer;
86
waveFormat : TWaveFormatEx) : HWaveOut;
92
mmres:=waveOutOpen(@hwo, WAVE_MAPPER, @waveFormat, Cardinal(@_waveOutCallBack2),
93
0, CALLBACK_FUNCTION);
94
Assert(mmres=MMSYSERR_NOERROR, IntToStr(mmres));
95
wh.dwBufferLength:=lengthInBytes;
100
mmres:=waveOutPrepareHeader(hwo, @wh, SizeOf(wavehdr));
101
Assert(mmres=MMSYSERR_NOERROR, IntToStr(mmres));
102
mmres:=waveOutWrite(hwo, @wh, SizeOf(wavehdr));
103
Assert(mmres=MMSYSERR_NOERROR, IntToStr(mmres));
107
// PlayOnWaveOut (sampling)
109
procedure PlayOnWaveOut(pcmData : Pointer; lengthInBytes : Integer;
110
sampling : TGLSoundSampling);
114
wfx:=sampling.WaveFormat;
115
PlayOnWaveOut(pcmData, lengthInBytes, wfx);
119
// ------------------ TGLSMWaveOut ------------------
124
constructor TGLSMWaveOut.Create(AOwner : TComponent);
126
inherited Create(AOwner);
132
destructor TGLSMWaveOut.Destroy;
139
function TGLSMWaveOut.DoActivate : Boolean;
146
procedure TGLSMWaveOut.DoDeActivate;
150
for i:=Sources.Count-1 downto 0 do
151
KillSource(Sources[i]);
156
procedure TGLSMWaveOut.KillSource(aSource : TGLBaseSoundSource);
158
pRec: PWaveOutPlayingRec;
160
if aSource.ManagerTag<>0 then begin
161
pRec := PWaveOutPlayingRec(aSource.ManagerTag);
162
if pRec.CurrentState=ssPlaying then
163
waveOutReset(pRec.WaveOutDevice);
164
waveOutUnprepareHeader(pRec.WaveOutDevice, @pRec.WaveHeader, sizeof(wavehdr));
165
waveOutClose(pRec.WaveOutDevice);
167
aSource.ManagerTag:=0;
171
// Note: This callback function is called from another thread, from MSDN docs:
172
// "Applications should not call any system-defined functions from inside a
173
// callback function, except for EnterCriticalSection, LeaveCriticalSection,
174
// midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage,
175
// PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent,
176
// and timeSetEvent. Calling other wave functions will cause deadlock."
177
procedure _waveOutCallBack(hwo : HWAVEOUT; uMsg : Cardinal;
178
dwInstance, dwParam1, dwParam2 : Integer); stdcall;
180
if uMsg=WOM_DONE then begin
181
PWaveOutPlayingRec(TGLSoundSource(dwInstance).ManagerTag).CurrentState:=ssFinished;
187
procedure TGLSMWaveOut.UpdateSources;
191
smp : TGLSoundSample;
195
pRec: PWaveOutPlayingRec;
197
// count nb of playing sources and delete done ones
199
for i:=Sources.Count-1 downto 0 do
200
if Sources[i].ManagerTag<>0 then
201
if PWaveOutPlayingRec(Sources[i].ManagerTag).currentState=ssPlaying then
205
// start sources if some capacity remains, and forget the others
206
for i:=Sources.Count-1 downto 0 do if Sources[i].ManagerTag=0 then begin
207
if n<MaxChannels then begin
208
smp:=Sources[i].Sample;
209
if Assigned(smp) and (smp.Data<>nil) then begin
210
wfx:=smp.Data.Sampling.WaveFormat;
211
mmres:=waveOutOpen(@hwo, WAVE_MAPPER, @wfx,
212
Cardinal(@_waveOutCallBack), Integer(Sources[i]),
214
Assert(mmres=MMSYSERR_NOERROR, IntToStr(mmres));
216
FillChar(wh,sizeof(wh),0);
217
wh.dwBufferLength:=smp.LengthInBytes;
218
wh.lpData:=smp.Data.PCMData;
219
wh.dwLoops:=Sources[i].NbLoops;
221
wh.dwFlags:=WHDR_BEGINLOOP+WHDR_ENDLOOP
226
pRec.waveoutdevice:=hwo;
228
pRec.CurrentState:=ssPlaying;
230
mmres:=waveOutPrepareHeader(hwo, @pRec.waveheader, SizeOf(wavehdr));
231
Assert(mmres=MMSYSERR_NOERROR, IntToStr(mmres));
232
Sources[i].ManagerTag:=Integer(prec);
233
mmres:=waveOutWrite(hwo, @pRec.waveheader, SizeOf(wavehdr));
234
Assert(mmres=MMSYSERR_NOERROR, IntToStr(mmres));
246
RegisterClasses([TGLSMWaveOut]);