GXS.Sounds.WaveOut.pas 6.4 KB

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