GLS.SoundManager.pas 43 KB

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