GLS.Sounds.WaveOut.pas 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.Sounds.WaveOut;
  5. (* Basic sound manager based on WinMM *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.MMSystem,
  10. System.Classes,
  11. System.SysUtils,
  12. GLS.SoundManager,
  13. GLS.SoundFileObjects;
  14. type
  15. (* Basic sound manager based on WinMM waveOut function.
  16. This manager has NO 3D miximing capacity, this is merely a default manager
  17. that should work on any windows based system, and help showcasing/testing
  18. basic GLSS core functionality.
  19. Apart from 3D, mute, pause, priority and volume are ignored too, and only
  20. sampling conversions supported by the windows ACM driver are supported
  21. (ie. no 4bits samples playback etc.). *)
  22. TGLSMWaveOut = class(TGLSoundManager)
  23. protected
  24. function DoActivate: Boolean; override;
  25. procedure DoDeActivate; override;
  26. procedure KillSource(aSource: TGLBaseSoundSource); override;
  27. public
  28. constructor Create(AOwner: TComponent); override;
  29. destructor Destroy; override;
  30. procedure UpdateSources; override;
  31. published
  32. property MaxChannels default 4;
  33. end;
  34. procedure PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer;
  35. sampling: TGLSoundSampling); overload;
  36. function PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer;
  37. waveFormat: TWaveFormatEx): HWaveOut; overload;
  38. // -------------------------------------------------------------
  39. implementation
  40. // -------------------------------------------------------------
  41. type
  42. TSoundState = (ssPlaying, ssFinished);
  43. TWaveOutPlayingRec = record
  44. CurrentState: TSoundState;
  45. WaveOutDevice: HWaveOut;
  46. WaveHeader: wavehdr;
  47. end;
  48. PWaveOutPlayingRec = ^TWaveOutPlayingRec;
  49. procedure _waveOutCallBack2(hwo: HWaveOut; uMsg: Cardinal;
  50. dwInstance, dwParam1, dwParam2: Integer); stdcall;
  51. begin
  52. if uMsg = WOM_DONE then
  53. waveOutClose(hwo);
  54. end;
  55. function PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer;
  56. waveFormat: TWaveFormatEx): HWaveOut;
  57. var
  58. hwo: HWaveOut;
  59. wh: wavehdr;
  60. mmres: MMRESULT;
  61. begin
  62. mmres := waveOutOpen(@hwo, WAVE_MAPPER, @waveFormat,
  63. Cardinal(@_waveOutCallBack2), 0, CALLBACK_FUNCTION);
  64. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  65. wh.dwBufferLength := lengthInBytes;
  66. wh.lpData := pcmData;
  67. wh.dwFlags := 0;
  68. wh.dwLoops := 1;
  69. wh.lpNext := nil;
  70. mmres := waveOutPrepareHeader(hwo, @wh, SizeOf(wavehdr));
  71. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  72. mmres := waveOutWrite(hwo, @wh, SizeOf(wavehdr));
  73. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  74. Result := hwo;
  75. end;
  76. procedure PlayOnWaveOut(pcmData: Pointer; lengthInBytes: Integer;
  77. sampling: TGLSoundSampling);
  78. var
  79. wfx: TWaveFormatEx;
  80. begin
  81. wfx := sampling.waveFormat;
  82. PlayOnWaveOut(pcmData, lengthInBytes, wfx);
  83. end;
  84. // ------------------
  85. // ------------------ TGLSMWaveOut ------------------
  86. // ------------------
  87. constructor TGLSMWaveOut.Create(AOwner: TComponent);
  88. begin
  89. inherited Create(AOwner);
  90. MaxChannels := 4;
  91. end;
  92. destructor TGLSMWaveOut.Destroy;
  93. begin
  94. inherited Destroy;
  95. end;
  96. function TGLSMWaveOut.DoActivate: Boolean;
  97. begin
  98. Result := True;
  99. end;
  100. procedure TGLSMWaveOut.DoDeActivate;
  101. var
  102. i: Integer;
  103. begin
  104. for i := Sources.Count - 1 downto 0 do
  105. KillSource(Sources[i]);
  106. end;
  107. procedure TGLSMWaveOut.KillSource(aSource: TGLBaseSoundSource);
  108. var
  109. pRec: PWaveOutPlayingRec;
  110. begin
  111. if aSource.ManagerTag <> 0 then
  112. begin
  113. pRec := PWaveOutPlayingRec(aSource.ManagerTag);
  114. if pRec.CurrentState = ssPlaying then
  115. waveOutReset(pRec.WaveOutDevice);
  116. waveOutUnprepareHeader(pRec.WaveOutDevice, @pRec.WaveHeader,
  117. SizeOf(wavehdr));
  118. waveOutClose(pRec.WaveOutDevice);
  119. Dispose(pRec);
  120. aSource.ManagerTag := 0;
  121. end;
  122. end;
  123. (*
  124. Note: This callback function is called from another thread, from MSDN docs:
  125. "Applications should not call any system-defined functions from inside a
  126. callback function, except for EnterCriticalSection, LeaveCriticalSection,
  127. midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage,
  128. PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent,
  129. and timeSetEvent. Calling other wave functions will cause deadlock."
  130. *)
  131. procedure _waveOutCallBack(hwo: HWaveOut; uMsg: Cardinal;
  132. dwInstance, dwParam1, dwParam2: Integer); stdcall;
  133. begin
  134. if uMsg = WOM_DONE then
  135. begin
  136. PWaveOutPlayingRec(TGLSoundSource(dwInstance).ManagerTag).CurrentState :=
  137. ssFinished;
  138. end;
  139. end;
  140. procedure TGLSMWaveOut.UpdateSources;
  141. var
  142. i, n: Integer;
  143. wfx: TWaveFormatEx;
  144. smp: TGLSoundSample;
  145. wh: wavehdr;
  146. mmres: MMRESULT;
  147. hwo: HWaveOut;
  148. pRec: PWaveOutPlayingRec;
  149. begin
  150. // count nb of playing sources and delete done ones
  151. n := 0;
  152. for i := Sources.Count - 1 downto 0 do
  153. if Sources[i].ManagerTag <> 0 then
  154. if PWaveOutPlayingRec(Sources[i].ManagerTag).CurrentState = ssPlaying then
  155. Inc(n)
  156. else
  157. Sources.Delete(i);
  158. // start sources if some capacity remains, and forget the others
  159. for i := Sources.Count - 1 downto 0 do
  160. if Sources[i].ManagerTag = 0 then
  161. begin
  162. if n < MaxChannels then
  163. begin
  164. smp := Sources[i].Sample;
  165. if Assigned(smp) and (smp.Data <> nil) then
  166. begin
  167. wfx := smp.Data.sampling.waveFormat;
  168. mmres := waveOutOpen(@hwo, WAVE_MAPPER, @wfx,
  169. Cardinal(@_waveOutCallBack), Integer(Sources[i]),
  170. CALLBACK_FUNCTION);
  171. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  172. FillChar(wh, SizeOf(wh), 0);
  173. wh.dwBufferLength := smp.lengthInBytes;
  174. wh.lpData := smp.Data.pcmData;
  175. wh.dwLoops := Sources[i].NbLoops;
  176. if wh.dwLoops > 1 then
  177. wh.dwFlags := WHDR_BEGINLOOP + WHDR_ENDLOOP
  178. else
  179. wh.dwFlags := 0;
  180. wh.lpNext := nil;
  181. new(pRec);
  182. pRec.WaveOutDevice := hwo;
  183. pRec.WaveHeader := wh;
  184. pRec.CurrentState := ssPlaying;
  185. mmres := waveOutPrepareHeader(hwo, @pRec.WaveHeader, SizeOf(wavehdr));
  186. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  187. Sources[i].ManagerTag := Integer(pRec);
  188. mmres := waveOutWrite(hwo, @pRec.WaveHeader, SizeOf(wavehdr));
  189. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  190. Inc(n);
  191. end
  192. else
  193. Sources.Delete(i);
  194. end
  195. else
  196. Sources.Delete(i);
  197. end;
  198. end;
  199. initialization
  200. RegisterClasses([TGLSMWaveOut]);
  201. end.