GXS.Sounds.BASS.pas 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.Sounds.BASS;
  5. (*
  6. BASS based sound-manager (http://www.un4seen.com/music/, free for freeware).
  7. Unsupported feature(s) :
  8. sound source velocity
  9. looping (sounds are played either once or forever)
  10. source priorities (not relevant, channels are not limited)
  11. *)
  12. interface
  13. uses
  14. Winapi.Windows,
  15. System.Classes,
  16. System.SysUtils,
  17. FMX.Forms,
  18. Stage.VectorTypes,
  19. GXS.SoundManager,
  20. GXS.Scene,
  21. Stage.VectorGeometry,
  22. BASS.Import;
  23. type
  24. TBASS3DAlgorithm = (algDefault, algOff, algFull, algLight);
  25. TgxSMBASS = class(TgxSoundManager)
  26. private
  27. FActivated: Boolean;
  28. FAlgorithm3D: TBASS3DAlgorithm;
  29. protected
  30. function DoActivate: Boolean; override;
  31. procedure DoDeActivate; override;
  32. procedure NotifyMasterVolumeChange; override;
  33. procedure Notify3DFactorsChanged; override;
  34. procedure NotifyEnvironmentChanged; override;
  35. procedure KillSource(aSource: TgxBaseSoundSource); override;
  36. procedure UpdateSource(aSource: TgxBaseSoundSource); override;
  37. procedure MuteSource(aSource: TgxBaseSoundSource; muted: Boolean); override;
  38. procedure PauseSource(aSource: TgxBaseSoundSource;
  39. paused: Boolean); override;
  40. function GetDefaultFrequency(aSource: TgxBaseSoundSource): Integer;
  41. public
  42. constructor Create(AOwner: TComponent); override;
  43. destructor Destroy; override;
  44. procedure UpdateSources; override;
  45. function CPUUsagePercent: Single; override;
  46. function EAXSupported: Boolean; override;
  47. published
  48. property Algorithm3D: TBASS3DAlgorithm read FAlgorithm3D write FAlgorithm3D
  49. default algDefault;
  50. end;
  51. // ---------------------------------------------------------------------
  52. implementation
  53. // ---------------------------------------------------------------------
  54. type
  55. TBASSInfo = record
  56. channel: HCHANNEL;
  57. sample: HSAMPLE;
  58. end;
  59. PBASSInfo = ^TBASSInfo;
  60. // VectorToBASSVector
  61. //
  62. procedure VectorToBASSVector(const aVector: TVector4f;
  63. var aBASSVector: BASS_3DVECTOR);
  64. begin
  65. aBASSVector.x := aVector.x;
  66. aBASSVector.y := aVector.y;
  67. aBASSVector.z := -aVector.z;
  68. end;
  69. // ------------------
  70. // ------------------ TgxSMBASS ------------------
  71. // ------------------
  72. constructor TgxSMBASS.Create(AOwner: TComponent);
  73. begin
  74. inherited Create(AOwner);
  75. BASS_Load(bassdll);
  76. MaxChannels := 32;
  77. end;
  78. destructor TgxSMBASS.Destroy;
  79. begin
  80. inherited Destroy;
  81. BASS_UnLoad;
  82. end;
  83. function TgxSMBASS.DoActivate: Boolean;
  84. const
  85. c3DAlgo: array [algDefault .. algLight] of Integer = (BASS_3DALG_DEFAULT,
  86. BASS_3DALG_OFF, BASS_3DALG_FULL, BASS_3DALG_LIGHT);
  87. var
  88. AHWND: HWND;
  89. begin
  90. Assert(bass_isloaded, 'BASS DLL is not present');
  91. if not BASS_Init(1, OutputFrequency, BASS_DEVICE_3D, AHWND, nil) then
  92. begin
  93. Result := False;
  94. Exit;
  95. end;
  96. if not BASS_Start then
  97. begin
  98. Result := False;
  99. Exit;
  100. end;
  101. FActivated := True;
  102. BASS_SetConfig(BASS_CONFIG_3DALGORITHM, c3DAlgo[FAlgorithm3D]);
  103. NotifyMasterVolumeChange;
  104. Notify3DFactorsChanged;
  105. if Environment <> seDefault then
  106. NotifyEnvironmentChanged;
  107. Result := True;
  108. end;
  109. procedure TgxSMBASS.DoDeActivate;
  110. begin
  111. FActivated := False;
  112. BASS_Stop;
  113. BASS_Free;
  114. end;
  115. procedure TgxSMBASS.NotifyMasterVolumeChange;
  116. begin
  117. if FActivated then
  118. BASS_SetVolume(Round(MasterVolume * 100));
  119. end;
  120. procedure TgxSMBASS.Notify3DFactorsChanged;
  121. begin
  122. if FActivated then
  123. BASS_Set3DFactors(DistanceFactor, RollOffFactor, DopplerFactor);
  124. end;
  125. procedure TgxSMBASS.NotifyEnvironmentChanged;
  126. const
  127. cEnvironmentToBASSConstant: array [seDefault .. sePsychotic] of Integer =
  128. (EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_PADDEDCELL, EAX_ENVIRONMENT_ROOM,
  129. EAX_ENVIRONMENT_BATHROOM, EAX_ENVIRONMENT_LIVINGROOM,
  130. EAX_ENVIRONMENT_STONEROOM, EAX_ENVIRONMENT_AUDITORIUM,
  131. EAX_ENVIRONMENT_CONCERTHALL, EAX_ENVIRONMENT_CAVE, EAX_ENVIRONMENT_ARENA,
  132. EAX_ENVIRONMENT_HANGAR, EAX_ENVIRONMENT_CARPETEDHALLWAY,
  133. EAX_ENVIRONMENT_HALLWAY, EAX_ENVIRONMENT_STONECORRIDOR,
  134. EAX_ENVIRONMENT_ALLEY, EAX_ENVIRONMENT_FOREST, EAX_ENVIRONMENT_CITY,
  135. EAX_ENVIRONMENT_MOUNTAINS, EAX_ENVIRONMENT_QUARRY, EAX_ENVIRONMENT_PLAIN,
  136. EAX_ENVIRONMENT_PARKINGLOT, EAX_ENVIRONMENT_SEWERPIPE,
  137. EAX_ENVIRONMENT_UNDERWATER, EAX_ENVIRONMENT_DRUGGED, EAX_ENVIRONMENT_DIZZY,
  138. EAX_ENVIRONMENT_PSYCHOTIC);
  139. begin
  140. if FActivated and EAXSupported then
  141. BASS_SetEAXParameters(cEnvironmentToBASSConstant[Environment], -1, -1, -1);
  142. end;
  143. procedure TgxSMBASS.KillSource(aSource: TgxBaseSoundSource);
  144. var
  145. p: PBASSInfo;
  146. begin
  147. if aSource.ManagerTag <> 0 then
  148. begin
  149. p := PBASSInfo(aSource.ManagerTag);
  150. if p.channel <> 0 then
  151. if not BASS_ChannelStop(p.channel) then
  152. Assert(False);
  153. BASS_SampleFree(p.sample);
  154. FreeMem(p);
  155. aSource.ManagerTag := 0;
  156. end;
  157. end;
  158. procedure TgxSMBASS.UpdateSource(aSource: TgxBaseSoundSource);
  159. var
  160. i: Integer;
  161. p: PBASSInfo;
  162. objPos, objOri, objVel: TVector4f;
  163. position, orientation, velocity: BASS_3DVECTOR;
  164. res: Boolean;
  165. begin
  166. if (sscSample in aSource.Changes) then
  167. begin
  168. KillSource(aSource);
  169. end;
  170. if (aSource.sample = nil) or (aSource.sample.Data = nil) or
  171. (aSource.sample.Data.WAVDataSize = 0) then
  172. Exit;
  173. if aSource.ManagerTag <> 0 then
  174. begin
  175. p := PBASSInfo(aSource.ManagerTag);
  176. if BASS_ChannelIsActive(p.channel) = 0 then
  177. begin
  178. p.channel := 0;
  179. aSource.Free;
  180. Exit;
  181. end;
  182. end
  183. else
  184. begin
  185. p := AllocMem(SizeOf(TBASSInfo));
  186. p.channel := 0;
  187. i := BASS_SAMPLE_VAM + BASS_SAMPLE_3D + BASS_SAMPLE_OVER_DIST;
  188. if aSource.NbLoops > 1 then
  189. i := i + BASS_SAMPLE_LOOP;
  190. p.sample := BASS_SampleLoad(True, aSource.sample.Data.WAVData, 0,
  191. aSource.sample.Data.WAVDataSize, MaxChannels, i);
  192. Assert(p.sample <> 0, 'BASS Error ' + IntToStr(Integer(BASS_ErrorGetCode)));
  193. aSource.ManagerTag := Integer(p);
  194. if aSource.Frequency <= 0 then
  195. aSource.Frequency := -1;
  196. end;
  197. if aSource.Origin <> nil then
  198. begin
  199. objPos := aSource.Origin.AbsolutePosition;
  200. objOri := aSource.Origin.AbsoluteZVector;
  201. objVel := NullHmgVector;
  202. end
  203. else
  204. begin
  205. objPos := NullHmgPoint;
  206. objOri := ZHmgVector;
  207. objVel := NullHmgVector;
  208. end;
  209. VectorToBASSVector(objPos, position);
  210. VectorToBASSVector(objVel, velocity);
  211. VectorToBASSVector(objOri, orientation);
  212. if p.channel = 0 then
  213. begin
  214. p.channel := BASS_SampleGetChannel(p.sample, False);
  215. Assert(p.channel <> 0);
  216. BASS_ChannelSet3DPosition(p.channel, position, orientation, velocity);
  217. BASS_ChannelSet3DAttributes(p.channel, BASS_3DMODE_NORMAL,
  218. aSource.MinDistance, aSource.MaxDistance, Round(aSource.InsideConeAngle),
  219. Round(aSource.OutsideConeAngle), Round(aSource.ConeOutsideVolume * 100));
  220. if not aSource.Pause then
  221. BASS_ChannelPlay(p.channel, True);
  222. end
  223. else
  224. BASS_ChannelSet3DPosition(p.channel, position, orientation, velocity);
  225. if p.channel <> 0 then
  226. begin
  227. res := BASS_ChannelSetAttribute(p.channel, BASS_ATTRIB_FREQ, 0);
  228. Assert(res);
  229. if aSource.Mute then
  230. res := BASS_ChannelSetAttribute(p.channel, BASS_ATTRIB_VOL, 0)
  231. else
  232. res := BASS_ChannelSetAttribute(p.channel, BASS_ATTRIB_VOL,
  233. aSource.Volume);
  234. Assert(res);
  235. end
  236. else
  237. aSource.Free;
  238. inherited UpdateSource(aSource);
  239. end;
  240. procedure TgxSMBASS.MuteSource(aSource: TgxBaseSoundSource; muted: Boolean);
  241. var
  242. p: PBASSInfo;
  243. res: Boolean;
  244. begin
  245. if aSource.ManagerTag <> 0 then
  246. begin
  247. p := PBASSInfo(aSource.ManagerTag);
  248. if muted then
  249. res := BASS_ChannelSetAttribute(p.channel, BASS_ATTRIB_VOL, 0)
  250. else
  251. res := BASS_ChannelSetAttribute(p.channel, BASS_ATTRIB_VOL,
  252. aSource.Volume);
  253. Assert(res);
  254. end;
  255. end;
  256. procedure TgxSMBASS.PauseSource(aSource: TgxBaseSoundSource; paused: Boolean);
  257. var
  258. p: PBASSInfo;
  259. begin
  260. if aSource.ManagerTag <> 0 then
  261. begin
  262. p := PBASSInfo(aSource.ManagerTag);
  263. if paused then
  264. BASS_ChannelPause(p.channel)
  265. else
  266. BASS_ChannelPlay(p.channel, False);
  267. end;
  268. end;
  269. procedure TgxSMBASS.UpdateSources;
  270. var
  271. objPos, objVel, objDir, objUp: TVector4f;
  272. position, velocity, fwd, top: BASS_3DVECTOR;
  273. begin
  274. // update listener
  275. ListenerCoordinates(objPos, objVel, objDir, objUp);
  276. VectorToBASSVector(objPos, position);
  277. VectorToBASSVector(objVel, velocity);
  278. VectorToBASSVector(objDir, fwd);
  279. VectorToBASSVector(objUp, top);
  280. if not BASS_Set3DPosition(position, velocity, fwd, top) then
  281. Assert(False);
  282. // update sources
  283. inherited;
  284. { if not } BASS_Apply3D; { then Assert(False); }
  285. end;
  286. function TgxSMBASS.CPUUsagePercent: Single;
  287. begin
  288. Result := BASS_GetCPU * 100;
  289. end;
  290. function TgxSMBASS.EAXSupported: Boolean;
  291. var
  292. c: Cardinal;
  293. s: Single;
  294. begin
  295. Result := BASS_GetEAXParameters(c, s, s, s);
  296. end;
  297. function TgxSMBASS.GetDefaultFrequency(aSource: TgxBaseSoundSource): Integer;
  298. var
  299. p: PBASSInfo;
  300. sampleInfo: BASS_Sample;
  301. begin
  302. try
  303. p := PBASSInfo(aSource.ManagerTag);
  304. BASS_SampleGetInfo(p.sample, sampleInfo);
  305. Result := sampleInfo.freq;
  306. except
  307. Result := -1;
  308. end;
  309. end;
  310. end.