GXS.SoundManager.pas 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.SoundManager;
  5. (* Base classes and interface for GLScene Sound System *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. System.Classes,
  10. System.SysUtils,
  11. System.Types,
  12. Stage.VectorTypes,
  13. Stage.VectorGeometry,
  14. GXS.BaseClasses,
  15. GXS.XCollection,
  16. GXS.SoundFileObjects,
  17. GXS.Scene,
  18. GXS.Cadencer,
  19. Stage.Utils,
  20. GXS.ImageUtils;
  21. type
  22. // Stores a single PCM coded sound sample.
  23. TgxSoundSample = class(TCollectionItem)
  24. private
  25. FName: string;
  26. FData: TgxSoundFile;
  27. FTag: Integer;
  28. protected
  29. procedure DefineProperties(Filer: TFiler); override;
  30. procedure ReadData(Stream: TStream); virtual;
  31. procedure WriteData(Stream: TStream); virtual;
  32. function GetDisplayName: string; override;
  33. procedure SetData(const val: TgxSoundFile);
  34. public
  35. constructor Create(Collection: TCollection); override;
  36. destructor Destroy; override;
  37. procedure Assign(Source: TPersistent); override;
  38. procedure LoadFromFile(const fileName: string);
  39. procedure PlayOnWaveOut;
  40. function Sampling: TgxSoundSampling;
  41. function LengthInBytes: Integer;
  42. function LengthInSamples: Integer;
  43. function LengthInSec: Single;
  44. // This Tag is reserved for sound manager use only
  45. property ManagerTag: Integer read FTag write FTag;
  46. published
  47. property Name: string read FName write FName;
  48. property Data: TgxSoundFile read FData write SetData stored False;
  49. end;
  50. TgxSoundSamples = class(TCollection)
  51. protected
  52. owner: TComponent;
  53. function GetOwner: TPersistent; override;
  54. procedure SetItems(index: Integer; const val: TgxSoundSample);
  55. function GetItems(index: Integer): TgxSoundSample;
  56. public
  57. constructor Create(AOwner: TComponent);
  58. function Add: TgxSoundSample;
  59. function FindItemID(ID: Integer): TgxSoundSample;
  60. property Items[index: Integer]: TgxSoundSample read GetItems write SetItems;
  61. default;
  62. function GetByName(const aName: string): TgxSoundSample;
  63. function AddFile(const fileName: string; const sampleName: string = ''):
  64. TgxSoundSample;
  65. end;
  66. TgxSoundLibrary = class(TComponent)
  67. private
  68. FSamples: TgxSoundSamples;
  69. protected
  70. procedure SetSamples(const val: TgxSoundSamples);
  71. procedure Notification(AComponent: TComponent; Operation: TOperation);
  72. override;
  73. public
  74. constructor Create(AOwner: TComponent); override;
  75. destructor Destroy; override;
  76. published
  77. property Samples: TgxSoundSamples read FSamples write SetSamples;
  78. end;
  79. TgxSoundSourceChange = (sscTransformation, sscSample, sscStatus);
  80. TgxSoundSourceChanges = set of TgxSoundSourceChange;
  81. TgxBSoundEmitter = class;
  82. // Base class for origin of sound playback.
  83. TgxBaseSoundSource = class(TCollectionItem)
  84. private
  85. FBehaviourToNotify: TgxBSoundEmitter;
  86. // private only, NOT persistent, not assigned
  87. FPriority: Integer;
  88. FOrigin: TgxBaseSceneObject; // NOT persistent
  89. FVolume: Single;
  90. FMinDistance, FMaxDistance: Single;
  91. FInsideConeAngle, FOutsideConeAngle: Single;
  92. FConeOutsideVolume: Single;
  93. FSoundLibraryName: string; // used for persistence
  94. FSoundLibrary: TgxSoundLibrary; // persistence via name
  95. FSoundName: string;
  96. FMute: Boolean;
  97. FPause: Boolean;
  98. FChanges: TgxSoundSourceChanges; // NOT persistent, not assigned
  99. FNbLoops: Integer;
  100. FTag: Cardinal; // NOT persistent, not assigned
  101. FFrequency: Integer;
  102. protected
  103. procedure WriteToFiler(writer: TWriter);
  104. procedure ReadFromFiler(reader: TReader);
  105. function GetDisplayName: string; override;
  106. procedure SetPriority(const val: Integer);
  107. procedure SetOrigin(const val: TgxBaseSceneObject);
  108. procedure SetVolume(const val: Single);
  109. procedure SetMinDistance(const val: Single);
  110. procedure SetMaxDistance(const val: Single);
  111. procedure SetInsideConeAngle(const val: Single);
  112. procedure SetOutsideConeAngle(const val: Single);
  113. procedure SetConeOutsideVolume(const val: Single);
  114. function GetSoundLibrary: TgxSoundLibrary;
  115. procedure SetSoundLibrary(const val: TgxSoundLibrary);
  116. procedure SetSoundName(const val: string);
  117. procedure SetMute(const val: Boolean);
  118. procedure SetPause(const val: Boolean);
  119. procedure SetNbLoops(const val: Integer);
  120. procedure SetFrequency(const val: Integer);
  121. public
  122. constructor Create(Collection: TCollection); override;
  123. destructor Destroy; override;
  124. procedure Assign(Source: TPersistent); override;
  125. property Changes: TgxSoundSourceChanges read FChanges;
  126. function Sample: TgxSoundSample;
  127. // This Tag is reserved for sound manager use only
  128. property ManagerTag: Cardinal read FTag write FTag;
  129. (*
  130. Origin object for the sound sources.
  131. Absolute object position/orientation are taken into account, the
  132. object's TgxBInertia is considered if any.
  133. If origin is nil, the source is assumed to be static at the origin.
  134. Note : since TCollectionItem do not support the "Notification"
  135. scheme, it is up to the Origin object to take care of updating this
  136. property prior to release/destruction.
  137. *)
  138. property Origin: TgxBaseSceneObject read FOrigin write SetOrigin;
  139. published
  140. property SoundLibrary: TgxSoundLibrary read GetSoundLibrary write
  141. SetSoundLibrary;
  142. property SoundName: string read FSoundName write SetSoundName;
  143. // Volume of the source, [0.0; 1.0] range
  144. property Volume: Single read FVolume write SetVolume;
  145. // Nb of playing loops.
  146. property NbLoops: Integer read FNbLoops write SetNbLoops default 1;
  147. property Mute: Boolean read FMute write SetMute default False;
  148. property Pause: Boolean read FPause write SetPause default False;
  149. (*
  150. Sound source priority, the higher the better.
  151. When maximum number of sound sources is reached, only the sources
  152. with the highest priority will continue to play, however, even
  153. non-playing sources should be tracked by the manager, thus allowing
  154. an "unlimited" amount of sources from the application point of view.
  155. *)
  156. property Priority: Integer read FPriority write SetPriority default 0;
  157. // Min distance before spatial attenuation occurs. 1.0 by default
  158. property MinDistance: Single read FMinDistance write SetMinDistance;
  159. { Max distance, if source is further away, it will not be heard.
  160. 100.0 by default }
  161. property MaxDistance: Single read FMaxDistance write SetMaxDistance;
  162. { Inside cone angle, [0°; 360°].
  163. Sound volume is maximal within this cone.
  164. See DirectX SDK for details. }
  165. property InsideConeAngle: Single read FInsideConeAngle write
  166. SetInsideConeAngle;
  167. { Outside cone angle, [0°; 360°].
  168. Between inside and outside cone, sound volume decreases between max
  169. and cone outside volume.
  170. See DirectX SDK for details. }
  171. property OutsideConeAngle: Single read FOutsideConeAngle write
  172. SetOutsideConeAngle;
  173. { Cone outside volume, [0.0; 1.0] range.
  174. See DirectX SDK for details. }
  175. property ConeOutsideVolume: Single read FConeOutsideVolume write
  176. SetConeOutsideVolume;
  177. { Sample custom playback frequency.
  178. Values null or negative are interpreted as 'default frequency'. }
  179. property Frequency: Integer read FFrequency write SetFrequency default -1;
  180. end;
  181. (*
  182. Origin of sound playback. Just publishes the 'Origin' property.
  183. Note: "orientation" is the the source's Direction, ie. the "Z" vector.
  184. *)
  185. TgxSoundSource = class(TgxBaseSoundSource)
  186. public
  187. destructor Destroy; override;
  188. published
  189. property Origin;
  190. end;
  191. TgxSoundSources = class(TCollection)
  192. protected
  193. owner: TComponent;
  194. function GetOwner: TPersistent; override;
  195. procedure SetItems(index: Integer; const val: TgxSoundSource);
  196. function GetItems(index: Integer): TgxSoundSource;
  197. function Add: TgxSoundSource;
  198. function FindItemID(ID: Integer): TgxSoundSource;
  199. public
  200. constructor Create(AOwner: TComponent);
  201. property Items[index: Integer]: TgxSoundSource read GetItems write SetItems;
  202. default;
  203. end;
  204. // EAX standard sound environments
  205. TgxSoundEnvironment = (seDefault, sePaddedCell, seRoom, seBathroom,
  206. seLivingRoom, seStoneroom, seAuditorium,
  207. seConcertHall, seCave, seArena, seHangar,
  208. seCarpetedHallway, seHallway, seStoneCorridor,
  209. seAlley, seForest, seCity, seMountains, seQuarry,
  210. sePlain, seParkingLot, seSewerPipe, seUnderWater,
  211. seDrugged, seDizzy, sePsychotic);
  212. (*
  213. Base class for sound manager components.
  214. The sound manager component is the interface to a low-level audio API
  215. (like DirectSound), there can only be one active manager at any time
  216. (this class takes care of this).
  217. Subclass should override the DoActivate and DoDeActivate protected methods
  218. to "initialize/unitialize" their sound layer, actual data releases should
  219. occur in destructor however.
  220. *)
  221. TgxSoundManager = class(TgxCadenceAbleComponent)
  222. private
  223. FActive: Boolean;
  224. FMute: Boolean;
  225. FPause: Boolean;
  226. FMasterVolume: Single;
  227. FListener: TgxBaseSceneObject;
  228. FLastListenerPosition: TVector4f;
  229. FSources: TgxSoundSources;
  230. FMaxChannels: Integer;
  231. FOutputFrequency: Integer;
  232. FUpdateFrequency: Single;
  233. FDistanceFactor: Single;
  234. FRollOffFactor: Single;
  235. FDopplerFactor: Single;
  236. FSoundEnvironment: TgxSoundEnvironment;
  237. FLastUpdateTime, FLastDeltaTime: Single;
  238. // last time UpdateSources was fired, not persistent
  239. FCadencer: TgxCadencer;
  240. procedure SetActive(const val: Boolean);
  241. procedure SetMute(const val: Boolean);
  242. procedure SetPause(const val: Boolean);
  243. procedure WriteDoppler(writer: TWriter);
  244. procedure ReadDoppler(reader: TReader);
  245. protected
  246. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  247. procedure SetSources(const val: TgxSoundSources);
  248. procedure SetMasterVolume(const val: Single);
  249. procedure SetListener(const val: TgxBaseSceneObject);
  250. procedure SetMaxChannels(const val: Integer);
  251. procedure SetOutputFrequency(const val: Integer);
  252. procedure SetUpdateFrequency(const val: Single);
  253. function StoreUpdateFrequency: Boolean;
  254. procedure SetCadencer(const val: TgxCadencer);
  255. procedure SetDistanceFactor(const val: Single);
  256. function StoreDistanceFactor: Boolean;
  257. procedure SetRollOffFactor(const val: Single);
  258. function StoreRollOffFactor: Boolean;
  259. procedure SetDopplerFactor(const val: Single);
  260. procedure SetSoundEnvironment(const val: TgxSoundEnvironment);
  261. procedure Loaded; override;
  262. procedure DefineProperties(Filer: TFiler); override;
  263. procedure ListenerCoordinates(var position, velocity, direction, up:
  264. TVector4f);
  265. function DoActivate: Boolean; virtual;
  266. // Invoked AFTER all sources have been stopped
  267. procedure DoDeActivate; virtual;
  268. { Effect mute of all sounds.
  269. Default implementation call MuteSource for all non-muted sources
  270. with "True" as parameter. }
  271. function DoMute: Boolean; virtual;
  272. { Effect un-mute of all sounds.
  273. Default implementation call MuteSource for all non-muted sources
  274. with "False" as parameter. }
  275. procedure DoUnMute; virtual;
  276. { Effect pause of all sounds.
  277. Default implementation call PauseSource for all non-paused sources
  278. with "True" as parameter. }
  279. function DoPause: Boolean; virtual;
  280. { Effect un-pause of all sounds.
  281. Default implementation call PauseSource for all non-paused sources
  282. with "True" as parameter. }
  283. procedure DoUnPause; virtual;
  284. procedure NotifyMasterVolumeChange; virtual;
  285. procedure Notify3DFactorsChanged; virtual;
  286. procedure NotifyEnvironmentChanged; virtual;
  287. // Called when a source will be freed
  288. procedure KillSource(aSource: TgxBaseSoundSource); virtual;
  289. { Request to update source's data in low-level sound API.
  290. Default implementation just clears the "Changes" flags. }
  291. procedure UpdateSource(aSource: TgxBaseSoundSource); virtual;
  292. procedure MuteSource(aSource: TgxBaseSoundSource; muted: Boolean); virtual;
  293. procedure PauseSource(aSource: TgxBaseSoundSource; paused: Boolean);
  294. virtual;
  295. public
  296. constructor Create(AOwner: TComponent); override;
  297. destructor Destroy; override;
  298. { Manual request to update all sources to reflect changes.
  299. Default implementation invokes UpdateSource for all known sources. }
  300. procedure UpdateSources; virtual;
  301. { Stop and free all sources. }
  302. procedure StopAllSources;
  303. { Progress notification for time synchronization.
  304. This method will call UpdateSources depending on the last time
  305. it was performed and the value of the UpdateFrequency property. }
  306. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  307. { Sound manager API reported CPU Usage.
  308. Returns -1 when unsupported. }
  309. function CPUUsagePercent: Single; virtual;
  310. { True if EAX is supported. }
  311. function EAXSupported: Boolean; virtual;
  312. published
  313. { Activation/deactivation of the low-level sound API }
  314. property Active: Boolean read FActive write SetActive default False;
  315. { Maximum number of sound output channels.
  316. While some drivers will just ignore this value, others cannot
  317. dynamically adjust the maximum number of channels (you need to
  318. de-activate and re-activate the manager for this property to be
  319. taken into account). }
  320. property MaxChannels: Integer read FMaxChannels write SetMaxChannels default
  321. 8;
  322. { Sound output mixing frequency.
  323. Commonly used values ar 11025, 22050 and 44100.
  324. Note that most driver cannot dynamically adjust the output frequency
  325. (you need to de-ativate and re-activate the manager for this property
  326. to be taken into account). }
  327. property OutputFrequency: Integer read FOutputFrequency write
  328. SetOutputFrequency default 44100;
  329. { Request to mute all sounds.
  330. All sound requests should be handled as if sound is unmuted though,
  331. however drivers should try to take a CPU advantage of mute over
  332. MasterVolume=0 }
  333. property Mute: Boolean read FMute write SetMute default False;
  334. { Request to pause all sound, sound output should be muted too.
  335. When unpausing, all sound should resume at the point they were paused. }
  336. property Pause: Boolean read FPause write SetPause default False;
  337. { Master Volume adjustement in the [0.0; 1.0] range.
  338. Driver should take care of properly clamping the master volume. }
  339. property MasterVolume: Single read FMasterVolume write SetMasterVolume;
  340. { Scene object that materializes the listener.
  341. The sceneobject's AbsolutePosition and orientation are used to define
  342. the listener coordinates, velocity is automatically calculated
  343. (if you're using DoProgress or connected the manager to a cadencer).
  344. If this property is nil, the listener is assumed to be static at
  345. the NullPoint coordinate, facing Z axis, with up being Y (ie. the
  346. default orientation). }
  347. property Listener: TgxBaseSceneObject read FListener write SetListener;
  348. { Currently active and playing sound sources. }
  349. property Sources: TgxSoundSources read FSources write SetSources;
  350. { Update frequency for time-based control (DoProgress).
  351. Default value is 10 Hz (frequency is clamped in the 1Hz-60Hz range). }
  352. property UpdateFrequency: Single read FUpdateFrequency write
  353. SetUpdateFrequency stored StoreUpdateFrequency;
  354. { Cadencer for time-based control. }
  355. property Cadencer: TgxCadencer read FCadencer write SetCadencer;
  356. { Engine relative distance factor, compared to 1.0 meters.
  357. Equates to 'how many units per meter' your engine has. }
  358. property DistanceFactor: Single read FDistanceFactor write SetDistanceFactor
  359. stored StoreDistanceFactor;
  360. { Sets the global attenuation rolloff factor.
  361. Normally volume for a sample will scale at 1 / distance.
  362. This gives a logarithmic attenuation of volume as the source gets
  363. further away (or closer).
  364. Setting this value makes the sound drop off faster or slower.
  365. The higher the value, the faster volume will fall off. }
  366. property RollOffFactor: Single read FRollOffFactor write SetRollOffFactor
  367. stored StoreRollOffFactor;
  368. { Engine relative Doppler factor, compared to 1.0 meters.
  369. Equates to 'how many units per meter' your engine has. }
  370. property DopplerFactor: Single read FDopplerFactor write SetDopplerFactor
  371. stored False;
  372. { Sound environment (requires EAX compatible soundboard). }
  373. property Environment: TgxSoundEnvironment read FSoundEnvironment write
  374. SetSoundEnvironment default seDefault;
  375. end;
  376. { A sound emitter behaviour, plug it on any object to make it noisy.
  377. This behaviour is just an interface to a TgxSoundSource, for editing convenience. }
  378. TgxBSoundEmitter = class(TgxBehaviour)
  379. private
  380. FPlaying: Boolean; // used at design-time ONLY
  381. FSource: TgxBaseSoundSource;
  382. FPlayingSource: TgxSoundSource;
  383. protected
  384. procedure WriteToFiler(writer: TWriter); override;
  385. procedure ReadFromFiler(reader: TReader); override;
  386. procedure Loaded; override;
  387. procedure SetSource(const val: TgxBaseSoundSource);
  388. procedure SetPlaying(const val: Boolean);
  389. function GetPlaying: Boolean;
  390. procedure NotifySourceDestruction(aSource: TgxSoundSource);
  391. public
  392. constructor Create(aOwner: TXCollection); override;
  393. destructor Destroy; override;
  394. procedure Assign(Source: TPersistent); override;
  395. class function FriendlyName: string; override;
  396. class function FriendlyDescription: string; override;
  397. class function UniqueItem: Boolean; override;
  398. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  399. property PlayingSource: TgxSoundSource read FPlayingSource;
  400. published
  401. property Source: TgxBaseSoundSource read FSource write SetSource;
  402. property Playing: Boolean read GetPlaying write SetPlaying default False;
  403. end;
  404. function ActiveSoundManager: TgxSoundManager;
  405. function GetSoundLibraryByName(const aName: string): TgxSoundLibrary;
  406. function GetOrCreateSoundEmitter(behaviours: TgxBehaviours): TgxBSoundEmitter; overload;
  407. function GetOrCreateSoundEmitter(obj: TgxBaseSceneObject): TgxBSoundEmitter; overload;
  408. var
  409. // If this variable is true, errors in GLSM may be displayed to the user
  410. vVerboseGLSMErrors: Boolean = True;
  411. implementation // -----------------------------------------------------------
  412. var
  413. vActiveSoundManager: TgxSoundManager;
  414. vSoundLibraries: TList;
  415. function ActiveSoundManager: TgxSoundManager;
  416. begin
  417. Result := vActiveSoundManager;
  418. end;
  419. function GetSoundLibraryByName(const aName: string): TgxSoundLibrary;
  420. var
  421. i: Integer;
  422. begin
  423. Result := nil;
  424. if Assigned(vSoundLibraries) then
  425. for i := 0 to vSoundLibraries.Count - 1 do
  426. if TgxSoundLibrary(vSoundLibraries[i]).Name = aName then
  427. begin
  428. Result := TgxSoundLibrary(vSoundLibraries[i]);
  429. Break;
  430. end;
  431. end;
  432. function GetOrCreateSoundEmitter(behaviours: TgxBehaviours): TgxBSoundEmitter;
  433. var
  434. i: Integer;
  435. begin
  436. i := behaviours.IndexOfClass(TgxBSoundEmitter);
  437. if i >= 0 then
  438. Result := TgxBSoundEmitter(behaviours[i])
  439. else
  440. Result := TgxBSoundEmitter.Create(behaviours);
  441. end;
  442. function GetOrCreateSoundEmitter(obj: TgxBaseSceneObject): TgxBSoundEmitter;
  443. begin
  444. Result := GetOrCreateSoundEmitter(obj.Behaviours);
  445. end;
  446. // ------------------
  447. // ------------------ TgxSoundSample ------------------
  448. // ------------------
  449. constructor TgxSoundSample.Create(Collection: TCollection);
  450. begin
  451. inherited Create(Collection);
  452. end;
  453. destructor TgxSoundSample.Destroy;
  454. begin
  455. FData.Free;
  456. inherited Destroy;
  457. end;
  458. procedure TgxSoundSample.Assign(Source: TPersistent);
  459. begin
  460. if Source is TgxSoundSample then
  461. begin
  462. FName := TgxSoundSample(Source).Name;
  463. FData.Free;
  464. FData := TgxSoundFile(TgxSoundSample(Source).Data.CreateCopy(Self));
  465. end
  466. else
  467. inherited Assign(Source); // Assign error
  468. end;
  469. procedure TgxSoundSample.DefineProperties(Filer: TFiler);
  470. begin
  471. Filer.DefineBinaryProperty('BinData', ReadData, WriteData, Assigned(FData));
  472. end;
  473. procedure TgxSoundSample.ReadData(Stream: TStream);
  474. var
  475. n: Integer;
  476. clName: AnsiString;
  477. begin
  478. with Stream do
  479. begin
  480. Read(n, SizeOf(Integer));
  481. SetLength(clName, n);
  482. if n > 0 then
  483. Read(clName[1], n);
  484. FData := TgxSoundFileClass(FindClass(string(clName))).Create(Self);
  485. FData.LoadFromStream(Stream);
  486. end;
  487. end;
  488. procedure TgxSoundSample.WriteData(Stream: TStream);
  489. var
  490. n: Integer;
  491. buf: AnsiString;
  492. begin
  493. with Stream do
  494. begin
  495. n := Length(FData.ClassName);
  496. Write(n, SizeOf(Integer));
  497. buf := AnsiString(FData.ClassName);
  498. if n > 0 then
  499. Write(buf[1], n);
  500. FData.SaveToStream(Stream);
  501. end;
  502. end;
  503. function TgxSoundSample.GetDisplayName: string;
  504. var
  505. s: string;
  506. begin
  507. if Assigned(FData) then
  508. begin
  509. if Data.Sampling.NbChannels > 1 then
  510. s := 's'
  511. else
  512. s := '';
  513. Result := Format('%s (%d Hz, %d bits, %d channel%s, %.2f sec)',
  514. [Name, Data.Sampling.Frequency,
  515. Data.Sampling.BitsPerSample,
  516. Data.Sampling.NbChannels, s, LengthInSec])
  517. end
  518. else
  519. Result := Format('%s (empty)', [Name]);
  520. end;
  521. procedure TgxSoundSample.LoadFromFile(const fileName: string);
  522. var
  523. sfc: TgxSoundFileClass;
  524. begin
  525. FData.Free;
  526. sfc := GetGLSoundFileFormats.FindExt(ExtractFileExt(fileName));
  527. if Assigned(sfc) then
  528. begin
  529. FData := sfc.Create(Self);
  530. FData.LoadFromFile(fileName);
  531. end
  532. else
  533. FData := nil;
  534. Assert(Data <> nil, 'Could not load ' + fileName +
  535. ', make sure you include the unit required to load this format in your uses clause.');
  536. Name := ExtractFileName(fileName);
  537. end;
  538. procedure TgxSoundSample.PlayOnWaveOut;
  539. begin
  540. if Assigned(FData) then
  541. FData.PlayOnWaveOut;
  542. end;
  543. // TgxSoundSample
  544. //
  545. function TgxSoundSample.Sampling: TgxSoundSampling;
  546. begin
  547. if Assigned(FData) then
  548. Result := FData.Sampling
  549. else
  550. Result := nil;
  551. end;
  552. function TgxSoundSample.LengthInBytes: Integer;
  553. begin
  554. if Assigned(FData) then
  555. Result := FData.LengthInBytes
  556. else
  557. Result := 0;
  558. end;
  559. function TgxSoundSample.LengthInSamples: Integer;
  560. begin
  561. if Assigned(FData) then
  562. Result := FData.LengthInSamples
  563. else
  564. Result := 0;
  565. end;
  566. function TgxSoundSample.LengthInSec: Single;
  567. begin
  568. if Assigned(FData) then
  569. Result := FData.LengthInSec
  570. else
  571. Result := 0;
  572. end;
  573. procedure TgxSoundSample.SetData(const val: TgxSoundFile);
  574. begin
  575. FData.Free;
  576. if Assigned(val) then
  577. FData := TgxSoundFile(val.CreateCopy(Self))
  578. else
  579. FData := nil;
  580. end;
  581. // ------------------
  582. // ------------------ TgxSoundSamples ------------------
  583. // ------------------
  584. constructor TgxSoundSamples.Create(AOwner: TComponent);
  585. begin
  586. Owner := AOwner;
  587. inherited Create(TgxSoundSample);
  588. end;
  589. function TgxSoundSamples.GetOwner: TPersistent;
  590. begin
  591. Result := Owner;
  592. end;
  593. procedure TgxSoundSamples.SetItems(index: Integer; const val: TgxSoundSample);
  594. begin
  595. inherited Items[index] := val;
  596. end;
  597. function TgxSoundSamples.GetItems(index: Integer): TgxSoundSample;
  598. begin
  599. Result := TgxSoundSample(inherited Items[index]);
  600. end;
  601. function TgxSoundSamples.Add: TgxSoundSample;
  602. begin
  603. Result := (inherited Add) as TgxSoundSample;
  604. end;
  605. function TgxSoundSamples.FindItemID(ID: Integer): TgxSoundSample;
  606. begin
  607. Result := (inherited FindItemID(ID)) as TgxSoundSample;
  608. end;
  609. function TgxSoundSamples.GetByName(const aName: string): TgxSoundSample;
  610. var
  611. i: Integer;
  612. begin
  613. Result := nil;
  614. for i := 0 to Count - 1 do
  615. if CompareText(Items[i].Name, aName) = 0 then
  616. begin
  617. Result := Items[i];
  618. Break;
  619. end;
  620. end;
  621. function TgxSoundSamples.AddFile(const fileName: string; const sampleName: string
  622. = ''): TgxSoundSample;
  623. begin
  624. Result := Add;
  625. Result.LoadFromFile(fileName);
  626. if sampleName <> '' then
  627. Result.Name := sampleName;
  628. end;
  629. // ------------------
  630. // ------------------ TgxSoundLibrary ------------------
  631. // ------------------
  632. constructor TgxSoundLibrary.Create(AOwner: TComponent);
  633. begin
  634. inherited Create(AOwner);
  635. FSamples := TgxSoundSamples.Create(Self);
  636. vSoundLibraries.Add(Self);
  637. end;
  638. destructor TgxSoundLibrary.Destroy;
  639. begin
  640. vSoundLibraries.Remove(Self);
  641. FSamples.Free;
  642. inherited Destroy;
  643. end;
  644. procedure TgxSoundLibrary.Notification(AComponent: TComponent; Operation:
  645. TOperation);
  646. begin
  647. inherited;
  648. end;
  649. procedure TgxSoundLibrary.SetSamples(const val: TgxSoundSamples);
  650. begin
  651. FSamples.Assign(val);
  652. end;
  653. // ------------------
  654. // ------------------ TgxBaseSoundSource ------------------
  655. // ------------------
  656. constructor TgxBaseSoundSource.Create(Collection: TCollection);
  657. begin
  658. inherited Create(Collection);
  659. FChanges := [sscTransformation, sscSample, sscStatus];
  660. FVolume := 1.0;
  661. FMinDistance := 1.0;
  662. FMaxDistance := 100.0;
  663. FInsideConeAngle := 360;
  664. FOutsideConeAngle := 360;
  665. FConeOutsideVolume := 0.0;
  666. FNbLoops := 1;
  667. FFrequency := -1;
  668. end;
  669. destructor TgxBaseSoundSource.Destroy;
  670. begin
  671. inherited Destroy;
  672. end;
  673. function TgxBaseSoundSource.GetDisplayName: string;
  674. begin
  675. Result := Format('%s', [FSoundName]);
  676. end;
  677. procedure TgxBaseSoundSource.Assign(Source: TPersistent);
  678. begin
  679. if Source is TgxBaseSoundSource then
  680. begin
  681. FPriority := TgxBaseSoundSource(Source).FPriority;
  682. FOrigin := TgxBaseSoundSource(Source).FOrigin;
  683. FVolume := TgxBaseSoundSource(Source).FVolume;
  684. FMinDistance := TgxBaseSoundSource(Source).FMinDistance;
  685. FMaxDistance := TgxBaseSoundSource(Source).FMaxDistance;
  686. FInsideConeAngle := TgxBaseSoundSource(Source).FInsideConeAngle;
  687. FOutsideConeAngle := TgxBaseSoundSource(Source).FOutsideConeAngle;
  688. FConeOutsideVolume := TgxBaseSoundSource(Source).FConeOutsideVolume;
  689. FSoundLibraryName := TgxBaseSoundSource(Source).FSoundLibraryName;
  690. FSoundLibrary := TgxBaseSoundSource(Source).FSoundLibrary;
  691. FSoundName := TgxBaseSoundSource(Source).FSoundName;
  692. FMute := TgxBaseSoundSource(Source).FMute;
  693. FPause := TgxBaseSoundSource(Source).FPause;
  694. FChanges := [sscTransformation, sscSample, sscStatus];
  695. FNbLoops := TgxBaseSoundSource(Source).FNbLoops;
  696. FFrequency := TgxBaseSoundSource(Source).FFrequency;
  697. end
  698. else
  699. inherited Assign(Source);
  700. end;
  701. procedure TgxBaseSoundSource.WriteToFiler(writer: TWriter);
  702. begin
  703. inherited;
  704. with writer do
  705. begin
  706. WriteInteger(0); // Archive Version 0
  707. WriteInteger(FPriority);
  708. WriteFloat(FVolume);
  709. WriteFloat(FMinDistance);
  710. WriteFloat(FMaxDistance);
  711. WriteFloat(FInsideConeAngle);
  712. WriteFloat(FOutsideConeAngle);
  713. WriteFloat(FConeOutsideVolume);
  714. if Assigned(FSoundLibrary) then
  715. WriteString(FSoundLibrary.Name)
  716. else
  717. WriteString(FSoundLibraryName);
  718. WriteString(FSoundName);
  719. WriteBoolean(FMute);
  720. WriteBoolean(FPause);
  721. WriteInteger(FNbLoops);
  722. // WriteInteger(FFrequency);
  723. end;
  724. end;
  725. procedure TgxBaseSoundSource.ReadFromFiler(reader: TReader);
  726. begin
  727. inherited;
  728. with reader do
  729. begin
  730. ReadInteger; // ignore archiveVersion
  731. FPriority := ReadInteger;
  732. FVolume := ReadFloat;
  733. FMinDistance := ReadFloat;
  734. FMaxDistance := ReadFloat;
  735. FInsideConeAngle := ReadFloat;
  736. FOutsideConeAngle := ReadFloat;
  737. FConeOutsideVolume := ReadFloat;
  738. FSoundLibraryName := ReadString;
  739. FSoundLibrary := nil;
  740. FSoundName := ReadString;
  741. FMute := ReadBoolean;
  742. FPause := ReadBoolean;
  743. FChanges := [sscTransformation, sscSample, sscStatus];
  744. FNbLoops := ReadInteger;
  745. // FFrequency:=ReadInteger;
  746. end;
  747. end;
  748. function TgxBaseSoundSource.Sample: TgxSoundSample;
  749. begin
  750. if SoundLibrary <> nil then
  751. Result := FSoundLibrary.Samples.GetByName(FSoundName)
  752. else
  753. Result := nil;
  754. end;
  755. procedure TgxBaseSoundSource.SetPriority(const val: Integer);
  756. begin
  757. if val <> FPriority then
  758. begin
  759. FPriority := val;
  760. Include(FChanges, sscStatus);
  761. end;
  762. end;
  763. procedure TgxBaseSoundSource.SetOrigin(const val: TgxBaseSceneObject);
  764. begin
  765. if val <> FOrigin then
  766. begin
  767. FOrigin := val;
  768. Include(FChanges, sscTransformation);
  769. end;
  770. end;
  771. procedure TgxBaseSoundSource.SetVolume(const val: Single);
  772. begin
  773. if val <> FVolume then
  774. begin
  775. FVolume := ClampValue(val, 0, 1);
  776. Include(FChanges, sscStatus);
  777. end;
  778. end;
  779. procedure TgxBaseSoundSource.SetMinDistance(const val: Single);
  780. begin
  781. if val <> FMinDistance then
  782. begin
  783. FMinDistance := ClampValue(val, 0);
  784. Include(FChanges, sscStatus);
  785. end;
  786. end;
  787. procedure TgxBaseSoundSource.SetMaxDistance(const val: Single);
  788. begin
  789. if val <> FMaxDistance then
  790. begin
  791. FMaxDistance := ClampValue(val, 0);
  792. Include(FChanges, sscStatus);
  793. end;
  794. end;
  795. procedure TgxBaseSoundSource.SetInsideConeAngle(const val: Single);
  796. begin
  797. if val <> FInsideConeAngle then
  798. begin
  799. FInsideConeAngle := ClampValue(val, 0, 360);
  800. Include(FChanges, sscStatus);
  801. end;
  802. end;
  803. procedure TgxBaseSoundSource.SetOutsideConeAngle(const val: Single);
  804. begin
  805. if val <> FOutsideConeAngle then
  806. begin
  807. FOutsideConeAngle := ClampValue(val, 0, 360);
  808. Include(FChanges, sscStatus);
  809. end;
  810. end;
  811. procedure TgxBaseSoundSource.SetConeOutsideVolume(const val: Single);
  812. begin
  813. if val <> FConeOutsideVolume then
  814. begin
  815. FConeOutsideVolume := ClampValue(val, 0, 1);
  816. Include(FChanges, sscStatus);
  817. end;
  818. end;
  819. function TgxBaseSoundSource.GetSoundLibrary: TgxSoundLibrary;
  820. begin
  821. if (FSoundLibrary = nil) and (FSoundLibraryName <> '') then
  822. FSoundLibrary := GetSoundLibraryByName(FSoundLibraryName);
  823. Result := FSoundLibrary;
  824. end;
  825. procedure TgxBaseSoundSource.SetSoundLibrary(const val: TgxSoundLibrary);
  826. begin
  827. if val <> FSoundLibrary then
  828. begin
  829. FSoundLibrary := val;
  830. if Assigned(FSoundLibrary) then
  831. FSoundLibraryName := FSoundLibrary.Name
  832. else
  833. FSoundLibraryName := '';
  834. Include(FChanges, sscSample);
  835. end;
  836. end;
  837. procedure TgxBaseSoundSource.SetSoundName(const val: string);
  838. begin
  839. if val <> FSoundName then
  840. begin
  841. FSoundName := val;
  842. Include(FChanges, sscSample);
  843. end;
  844. end;
  845. procedure TgxBaseSoundSource.SetPause(const val: Boolean);
  846. begin
  847. if val <> FPause then
  848. begin
  849. FPause := val;
  850. if Collection <> nil then
  851. TgxSoundManager(TgxSoundSources(Collection).owner).PauseSource(Self,
  852. FPause);
  853. end;
  854. end;
  855. procedure TgxBaseSoundSource.SetNbLoops(const val: Integer);
  856. begin
  857. if val <> FNbLoops then
  858. begin
  859. FNbLoops := val;
  860. Include(FChanges, sscSample);
  861. end;
  862. end;
  863. procedure TgxBaseSoundSource.SetFrequency(const val: integer);
  864. begin
  865. if val <> FFrequency then
  866. begin
  867. FFrequency := val;
  868. Include(FChanges, sscStatus);
  869. end;
  870. end;
  871. procedure TgxBaseSoundSource.SetMute(const val: Boolean);
  872. begin
  873. if val <> FMute then
  874. begin
  875. FMute := val;
  876. if Collection <> nil then
  877. TgxSoundManager(TgxSoundSources(Collection).owner).MuteSource(Self,
  878. FMute);
  879. end;
  880. end;
  881. // ------------------
  882. // ------------------ TgxSoundSource ------------------
  883. // ------------------
  884. destructor TgxSoundSource.Destroy;
  885. begin
  886. if Assigned(FBehaviourToNotify) then
  887. FBehaviourToNotify.NotifySourceDestruction(Self);
  888. if Collection <> nil then
  889. ((Collection as TgxSoundSources).Owner as TgxSoundManager).KillSource(Self);
  890. inherited;
  891. end;
  892. // ------------------
  893. // ------------------ TgxSoundSources ------------------
  894. // ------------------
  895. constructor TgxSoundSources.Create(AOwner: TComponent);
  896. begin
  897. Owner := AOwner;
  898. inherited Create(TgxSoundSource);
  899. end;
  900. function TgxSoundSources.GetOwner: TPersistent;
  901. begin
  902. Result := Owner;
  903. end;
  904. procedure TgxSoundSources.SetItems(index: Integer; const val: TgxSoundSource);
  905. begin
  906. inherited Items[index] := val;
  907. end;
  908. function TgxSoundSources.GetItems(index: Integer): TgxSoundSource;
  909. begin
  910. Result := TgxSoundSource(inherited Items[index]);
  911. end;
  912. function TgxSoundSources.Add: TgxSoundSource;
  913. begin
  914. Result := (inherited Add) as TgxSoundSource;
  915. end;
  916. function TgxSoundSources.FindItemID(ID: Integer): TgxSoundSource;
  917. begin
  918. Result := (inherited FindItemID(ID)) as TgxSoundSource;
  919. end;
  920. // ------------------
  921. // ------------------ TgxSoundManager ------------------
  922. // ------------------
  923. constructor TgxSoundManager.Create(AOwner: TComponent);
  924. begin
  925. inherited Create(AOwner);
  926. FSources := TgxSoundSources.Create(Self);
  927. FMasterVolume := 1.0;
  928. FOutputFrequency := 44100;
  929. FMaxChannels := 8;
  930. FUpdateFrequency := 10;
  931. FLastUpdateTime := -1e30;
  932. FDistanceFactor := 1.0;
  933. FRollOffFactor := 1.0;
  934. FDopplerFactor := 1.0;
  935. end;
  936. destructor TgxSoundManager.Destroy;
  937. begin
  938. Active := False;
  939. Listener := nil;
  940. FSources.Free;
  941. inherited Destroy;
  942. end;
  943. procedure TgxSoundManager.Notification(AComponent: TComponent; Operation:
  944. TOperation);
  945. begin
  946. if Operation = opRemove then
  947. begin
  948. if AComponent = FListener then
  949. Listener := nil;
  950. if AComponent = FCadencer then
  951. Cadencer := nil;
  952. end;
  953. inherited;
  954. end;
  955. procedure TgxSoundManager.SetActive(const val: Boolean);
  956. begin
  957. if (csDesigning in ComponentState) or (csLoading in ComponentState) then
  958. FActive := val
  959. else if val <> FActive then
  960. begin
  961. if val then
  962. begin
  963. if Assigned(vActiveSoundManager) then
  964. vActiveSoundManager.Active := False;
  965. if DoActivate then
  966. begin
  967. FActive := True;
  968. vActiveSoundManager := Self;
  969. end;
  970. end
  971. else
  972. begin
  973. try
  974. StopAllSources;
  975. DoDeActivate;
  976. finally
  977. FActive := val;
  978. vActiveSoundManager := nil;
  979. end;
  980. end;
  981. end;
  982. end;
  983. function TgxSoundManager.DoActivate: Boolean;
  984. begin
  985. Result := True;
  986. end;
  987. procedure TgxSoundManager.DoDeActivate;
  988. begin
  989. StopAllSources;
  990. end;
  991. procedure TgxSoundManager.SetMute(const val: Boolean);
  992. begin
  993. if val <> FMute then
  994. begin
  995. if val then
  996. begin
  997. if DoMute then
  998. FMute := True
  999. end
  1000. else
  1001. begin
  1002. DoUnMute;
  1003. FMute := False;
  1004. end;
  1005. end;
  1006. end;
  1007. function TgxSoundManager.DoMute: Boolean;
  1008. var
  1009. i: Integer;
  1010. begin
  1011. for i := 0 to Sources.Count - 1 do
  1012. if not Sources[i].Mute then
  1013. MuteSource(Sources[i], True);
  1014. Result := True;
  1015. end;
  1016. procedure TgxSoundManager.DoUnMute;
  1017. var
  1018. i: Integer;
  1019. begin
  1020. for i := 0 to Sources.Count - 1 do
  1021. if not Sources[i].Mute then
  1022. MuteSource(Sources[i], False);
  1023. end;
  1024. procedure TgxSoundManager.SetPause(const val: Boolean);
  1025. begin
  1026. if val <> FPause then
  1027. begin
  1028. if val then
  1029. begin
  1030. if DoPause then
  1031. FPause := True
  1032. end
  1033. else
  1034. begin
  1035. DoUnPause;
  1036. FPause := False;
  1037. end;
  1038. end;
  1039. end;
  1040. procedure TgxSoundManager.Loaded;
  1041. begin
  1042. inherited;
  1043. if Active and (not (csDesigning in ComponentState)) then
  1044. begin
  1045. FActive := False;
  1046. Active := True;
  1047. end;
  1048. end;
  1049. procedure TgxSoundManager.DefineProperties(Filer: TFiler);
  1050. begin
  1051. inherited;
  1052. Filer.DefineProperty('Doppler', ReadDoppler, WriteDoppler, (DopplerFactor <>
  1053. 1));
  1054. end;
  1055. procedure TgxSoundManager.WriteDoppler(writer: TWriter);
  1056. begin
  1057. writer.WriteFloat(DopplerFactor);
  1058. end;
  1059. procedure TgxSoundManager.ReadDoppler(reader: TReader);
  1060. begin
  1061. FDopplerFactor := reader.ReadFloat;
  1062. end;
  1063. function TgxSoundManager.DoPause: Boolean;
  1064. var
  1065. i: Integer;
  1066. begin
  1067. for i := 0 to Sources.Count - 1 do
  1068. if not Sources[i].Pause then
  1069. PauseSource(Sources[i], True);
  1070. Result := True;
  1071. end;
  1072. procedure TgxSoundManager.DoUnPause;
  1073. var
  1074. i: Integer;
  1075. begin
  1076. for i := 0 to Sources.Count - 1 do
  1077. if not Sources[i].Pause then
  1078. PauseSource(Sources[i], False);
  1079. end;
  1080. procedure TgxSoundManager.SetMasterVolume(const val: Single);
  1081. begin
  1082. if val < 0 then
  1083. FMasterVolume := 0
  1084. else if val > 1 then
  1085. FMasterVolume := 1
  1086. else
  1087. FMasterVolume := val;
  1088. NotifyMasterVolumeChange;
  1089. end;
  1090. procedure TgxSoundManager.SetMaxChannels(const val: Integer);
  1091. begin
  1092. if val <> FMaxChannels then
  1093. begin
  1094. if val < 1 then
  1095. FMaxChannels := 1
  1096. else
  1097. FMaxChannels := val;
  1098. end;
  1099. end;
  1100. procedure TgxSoundManager.SetOutputFrequency(const val: Integer);
  1101. begin
  1102. if val <> FOutputFrequency then
  1103. begin
  1104. if val < 11025 then
  1105. FOutputFrequency := 11025
  1106. else
  1107. FOutputFrequency := val;
  1108. end;
  1109. end;
  1110. procedure TgxSoundManager.SetUpdateFrequency(const val: Single);
  1111. begin
  1112. FUpdateFrequency := ClampValue(val, 1, 60);
  1113. end;
  1114. function TgxSoundManager.StoreUpdateFrequency: Boolean;
  1115. begin
  1116. Result := (FUpdateFrequency <> 10);
  1117. end;
  1118. procedure TgxSoundManager.SetCadencer(const val: TgxCadencer);
  1119. begin
  1120. if val <> FCadencer then
  1121. begin
  1122. if Assigned(FCadencer) then
  1123. FCadencer.UnSubscribe(Self);
  1124. FCadencer := val;
  1125. if Assigned(FCadencer) then
  1126. FCadencer.Subscribe(Self);
  1127. end;
  1128. end;
  1129. procedure TgxSoundManager.SetDistanceFactor(const val: Single);
  1130. begin
  1131. if val <= 0 then
  1132. FDistanceFactor := 1
  1133. else
  1134. FDistanceFactor := val;
  1135. Notify3DFactorsChanged;
  1136. end;
  1137. function TgxSoundManager.StoreDistanceFactor: Boolean;
  1138. begin
  1139. Result := (FDistanceFactor <> 1);
  1140. end;
  1141. procedure TgxSoundManager.SetRollOffFactor(const val: Single);
  1142. begin
  1143. if val <= 0 then
  1144. FRollOffFactor := 1
  1145. else
  1146. FRollOffFactor := val;
  1147. Notify3DFactorsChanged;
  1148. end;
  1149. function TgxSoundManager.StoreRollOffFactor: Boolean;
  1150. begin
  1151. Result := (FRollOffFactor <> 1);
  1152. end;
  1153. procedure TgxSoundManager.SetDopplerFactor(const val: Single);
  1154. begin
  1155. if val < 0 then
  1156. FDopplerFactor := 0
  1157. else if val > 10 then
  1158. FDopplerFactor := 10
  1159. else
  1160. FDopplerFactor := val;
  1161. Notify3DFactorsChanged;
  1162. end;
  1163. procedure TgxSoundManager.SetSoundEnvironment(const val: TgxSoundEnvironment);
  1164. begin
  1165. if val <> FSoundEnvironment then
  1166. begin
  1167. FSoundEnvironment := val;
  1168. NotifyEnvironmentChanged;
  1169. end;
  1170. end;
  1171. procedure TgxSoundManager.ListenerCoordinates(var position, velocity, direction,
  1172. up: TVector4f);
  1173. var
  1174. right: TVector4f;
  1175. begin
  1176. if Listener <> nil then
  1177. begin
  1178. position := Listener.AbsolutePosition;
  1179. if FLastDeltaTime <> 0 then
  1180. begin
  1181. velocity := VectorSubtract(position, FLastListenerPosition);
  1182. ScaleVector(velocity, 1 / FLastDeltaTime);
  1183. end;
  1184. FLastListenerPosition := position;
  1185. if (Listener is TgxCamera) and (TgxCamera(Listener).TargetObject <> nil)
  1186. then
  1187. begin
  1188. // special case of the camera targeting something
  1189. direction := TgxCamera(Listener).AbsoluteVectorToTarget;
  1190. NormalizeVector(direction);
  1191. up := Listener.AbsoluteYVector;
  1192. right := VectorCrossProduct(direction, up);
  1193. up := VectorCrossProduct(right, direction);
  1194. end
  1195. else
  1196. begin
  1197. direction := Listener.AbsoluteZVector;
  1198. up := Listener.AbsoluteYVector;
  1199. end;
  1200. end
  1201. else
  1202. begin
  1203. position := NullHmgPoint;
  1204. velocity := NullHmgVector;
  1205. direction := ZHmgVector;
  1206. up := YHmgVector;
  1207. end;
  1208. end;
  1209. procedure TgxSoundManager.NotifyMasterVolumeChange;
  1210. begin
  1211. // nothing
  1212. end;
  1213. procedure TgxSoundManager.Notify3DFactorsChanged;
  1214. begin
  1215. // nothing
  1216. end;
  1217. procedure TgxSoundManager.NotifyEnvironmentChanged;
  1218. begin
  1219. // nothing
  1220. end;
  1221. procedure TgxSoundManager.SetListener(const val: TgxBaseSceneObject);
  1222. begin
  1223. if Assigned(FListener) then
  1224. FListener.RemoveFreeNotification(Self);
  1225. FListener := val;
  1226. if Assigned(FListener) then
  1227. FListener.FreeNotification(Self);
  1228. end;
  1229. procedure TgxSoundManager.SetSources(const val: TgxSoundSources);
  1230. begin
  1231. FSources.Assign(val);
  1232. end;
  1233. procedure TgxSoundManager.KillSource(aSource: TgxBaseSoundSource);
  1234. begin
  1235. // nothing
  1236. end;
  1237. procedure TgxSoundManager.UpdateSource(aSource: TgxBaseSoundSource);
  1238. begin
  1239. aSource.FChanges := [];
  1240. end;
  1241. procedure TgxSoundManager.MuteSource(aSource: TgxBaseSoundSource; muted:
  1242. Boolean);
  1243. begin
  1244. // nothing
  1245. end;
  1246. procedure TgxSoundManager.PauseSource(aSource: TgxBaseSoundSource; paused:
  1247. Boolean);
  1248. begin
  1249. // nothing
  1250. end;
  1251. procedure TgxSoundManager.UpdateSources;
  1252. var
  1253. i: Integer;
  1254. begin
  1255. for i := Sources.Count - 1 downto 0 do
  1256. UpdateSource(Sources[i]);
  1257. end;
  1258. procedure TgxSoundManager.StopAllSources;
  1259. var
  1260. i: Integer;
  1261. begin
  1262. for i := Sources.Count - 1 downto 0 do
  1263. Sources.Delete(i);
  1264. end;
  1265. procedure TgxSoundManager.DoProgress(const progressTime: TgxProgressTimes);
  1266. begin
  1267. if not Active then
  1268. Exit;
  1269. with progressTime do
  1270. if newTime - FLastUpdateTime > 1 / FUpdateFrequency then
  1271. begin
  1272. FLastDeltaTime := newTime - FLastUpdateTime;
  1273. FLastUpdateTime := newTime;
  1274. UpdateSources;
  1275. end;
  1276. end;
  1277. function TgxSoundManager.CPUUsagePercent: Single;
  1278. begin
  1279. Result := -1;
  1280. end;
  1281. function TgxSoundManager.EAXSupported: Boolean;
  1282. begin
  1283. Result := False;
  1284. end;
  1285. // ------------------
  1286. // ------------------ TgxBSoundEmitter ------------------
  1287. // ------------------
  1288. constructor TgxBSoundEmitter.Create(aOwner: TXCollection);
  1289. begin
  1290. inherited Create(aOwner);
  1291. FSource := TgxSoundSource.Create(nil);
  1292. end;
  1293. destructor TgxBSoundEmitter.Destroy;
  1294. begin
  1295. if Assigned(FPlayingSource) then
  1296. FPlayingSource.Free;
  1297. FSource.Free;
  1298. inherited Destroy;
  1299. end;
  1300. procedure TgxBSoundEmitter.Assign(Source: TPersistent);
  1301. begin
  1302. if Source is TgxBSoundEmitter then
  1303. begin
  1304. FSource.Assign(TgxBSoundEmitter(Source).FSource);
  1305. end;
  1306. inherited Assign(Source);
  1307. end;
  1308. procedure TgxBSoundEmitter.WriteToFiler(writer: TWriter);
  1309. begin
  1310. inherited;
  1311. with writer do
  1312. begin
  1313. WriteInteger(0); // Archive Version 0
  1314. FSource.WriteToFiler(writer);
  1315. WriteBoolean(FPlaying);
  1316. end;
  1317. end;
  1318. procedure TgxBSoundEmitter.ReadFromFiler(reader: TReader);
  1319. begin
  1320. inherited;
  1321. with reader do
  1322. begin
  1323. ReadInteger; // ignore archiveVersion
  1324. FSource.ReadFromFiler(reader);
  1325. FPlaying := ReadBoolean;
  1326. end;
  1327. end;
  1328. procedure TgxBSoundEmitter.Loaded;
  1329. begin
  1330. inherited;
  1331. if not (csDesigning in OwnerBaseSceneObject.ComponentState) then
  1332. SetPlaying(FPlaying);
  1333. end;
  1334. class function TgxBSoundEmitter.FriendlyName: string;
  1335. begin
  1336. Result := 'Sound Emitter';
  1337. end;
  1338. class function TgxBSoundEmitter.FriendlyDescription: string;
  1339. begin
  1340. Result := 'A simple sound emitter behaviour';
  1341. end;
  1342. class function TgxBSoundEmitter.UniqueItem: Boolean;
  1343. begin
  1344. Result := False;
  1345. end;
  1346. procedure TgxBSoundEmitter.DoProgress(const progressTime: TgxProgressTimes);
  1347. begin
  1348. // nothing, yet
  1349. end;
  1350. procedure TgxBSoundEmitter.SetSource(const val: TgxBaseSoundSource);
  1351. begin
  1352. FSource.Assign(val);
  1353. end;
  1354. procedure TgxBSoundEmitter.SetPlaying(const val: Boolean);
  1355. begin
  1356. if csDesigning in OwnerBaseSceneObject.ComponentState then
  1357. FPlaying := val
  1358. else if ActiveSoundManager <> nil then
  1359. begin
  1360. if val <> Playing then
  1361. begin
  1362. if val then
  1363. begin
  1364. FPlayingSource := ActiveSoundManager.Sources.Add;
  1365. FPlayingSource.FBehaviourToNotify := Self;
  1366. FPlayingSource.Assign(FSource);
  1367. FPlayingSource.Origin := OwnerBaseSceneObject;
  1368. end
  1369. else
  1370. FPlayingSource.Free;
  1371. end;
  1372. end
  1373. else if vVerboseGLSMErrors then
  1374. InformationDlg('No Active Sound Manager.'#13#10'Make sure manager is created before emitter');
  1375. end;
  1376. function TgxBSoundEmitter.GetPlaying: Boolean;
  1377. begin
  1378. if csDesigning in OwnerBaseSceneObject.ComponentState then
  1379. Result := FPlaying
  1380. else
  1381. Result := Assigned(FPlayingSource);
  1382. end;
  1383. procedure TgxBSoundEmitter.NotifySourceDestruction(aSource: TgxSoundSource);
  1384. begin
  1385. Assert(FPlayingSource = aSource);
  1386. FPlayingSource := nil;
  1387. end;
  1388. initialization // ---------------------------------------------------------
  1389. RegisterClasses([TgxSoundLibrary]);
  1390. RegisterXCollectionItemClass(TgxBSoundEmitter);
  1391. vSoundLibraries := TList.Create;
  1392. finalization // -----------------------------------------------------------
  1393. if Assigned(vActiveSoundManager) then
  1394. vActiveSoundManager.Active := False;
  1395. vSoundLibraries.Free;
  1396. vSoundLibraries := nil;
  1397. UnregisterXCollectionItemClass(TgxBSoundEmitter);
  1398. end.