GLSM.WaveOut.pas 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLSM.WaveOut;
  5. (* Basic sound manager based on WinMM *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.MMSystem,
  10. System.Classes,
  11. System.SysUtils,
  12. GLSound,
  13. GLSoundFileObjects;
  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. // Note: This callback function is called from another thread, from MSDN docs:
  124. // "Applications should not call any system-defined functions from inside a
  125. // callback function, except for EnterCriticalSection, LeaveCriticalSection,
  126. // midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage,
  127. // PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent,
  128. // and timeSetEvent. Calling other wave functions will cause deadlock."
  129. procedure _waveOutCallBack(hwo: HWaveOut; uMsg: Cardinal;
  130. dwInstance, dwParam1, dwParam2: Integer); stdcall;
  131. begin
  132. if uMsg = WOM_DONE then
  133. begin
  134. PWaveOutPlayingRec(TGLSoundSource(dwInstance).ManagerTag).CurrentState :=
  135. ssFinished;
  136. end;
  137. end;
  138. procedure TGLSMWaveOut.UpdateSources;
  139. var
  140. i, n: Integer;
  141. wfx: TWaveFormatEx;
  142. smp: TGLSoundSample;
  143. wh: wavehdr;
  144. mmres: MMRESULT;
  145. hwo: HWaveOut;
  146. pRec: PWaveOutPlayingRec;
  147. begin
  148. // count nb of playing sources and delete done ones
  149. n := 0;
  150. for i := Sources.Count - 1 downto 0 do
  151. if Sources[i].ManagerTag <> 0 then
  152. if PWaveOutPlayingRec(Sources[i].ManagerTag).CurrentState = ssPlaying then
  153. Inc(n)
  154. else
  155. Sources.Delete(i);
  156. // start sources if some capacity remains, and forget the others
  157. for i := Sources.Count - 1 downto 0 do
  158. if Sources[i].ManagerTag = 0 then
  159. begin
  160. if n < MaxChannels then
  161. begin
  162. smp := Sources[i].Sample;
  163. if Assigned(smp) and (smp.Data <> nil) then
  164. begin
  165. wfx := smp.Data.sampling.waveFormat;
  166. mmres := waveOutOpen(@hwo, WAVE_MAPPER, @wfx,
  167. Cardinal(@_waveOutCallBack), Integer(Sources[i]),
  168. CALLBACK_FUNCTION);
  169. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  170. FillChar(wh, SizeOf(wh), 0);
  171. wh.dwBufferLength := smp.lengthInBytes;
  172. wh.lpData := smp.Data.pcmData;
  173. wh.dwLoops := Sources[i].NbLoops;
  174. if wh.dwLoops > 1 then
  175. wh.dwFlags := WHDR_BEGINLOOP + WHDR_ENDLOOP
  176. else
  177. wh.dwFlags := 0;
  178. wh.lpNext := nil;
  179. new(pRec);
  180. pRec.WaveOutDevice := hwo;
  181. pRec.WaveHeader := wh;
  182. pRec.CurrentState := ssPlaying;
  183. mmres := waveOutPrepareHeader(hwo, @pRec.WaveHeader, SizeOf(wavehdr));
  184. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  185. Sources[i].ManagerTag := Integer(pRec);
  186. mmres := waveOutWrite(hwo, @pRec.WaveHeader, SizeOf(wavehdr));
  187. Assert(mmres = MMSYSERR_NOERROR, IntToStr(mmres));
  188. Inc(n);
  189. end
  190. else
  191. Sources.Delete(i);
  192. end
  193. else
  194. Sources.Delete(i);
  195. end;
  196. end;
  197. initialization
  198. RegisterClasses([TGLSMWaveOut]);
  199. end.