GLSound.pas 44 KB

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