123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- //
- // The graphics rendering engine GLScene http://glscene.org
- //
- unit GXS.Sounds.OpenAL;
- (*
- OpenAL based sound-manager http://www.openal.org
- OpenAL drivers can be download from the OpenAL site or your soundcard
- manufacturer's website.
- Unsupported feature(s) :
- Accepts only simple *uncompressed* WAV files (8/16 bits, mono/stereo)
- Dynamic loading/unloading
- Global 3D parameters
- Environments
- CPUUsagePercent
- No system in place to limit number of sources playing simultaneously,
- can crash if too playing at once.
- *)
- interface
- uses
- System.Classes,
- System.SysUtils,
- FMX.Forms,
- FMX.Dialogs,
- OpenAL.Import, //al, alut, alTypes
- Stage.VectorTypes,
- GXS.Scene,
- Stage.VectorGeometry,
- GXS.Coordinates,
- GXS.SoundManager,
- GXS.SoundFileObjects;
- type
- TgxSMOpenAL = class(TgxSoundManager)
- private
- FActivated: Boolean;
- protected
- function DoActivate: Boolean; override;
- procedure DoDeActivate; override;
- procedure NotifyMasterVolumeChange; override;
- procedure Notify3DFactorsChanged; override;
- procedure NotifyEnvironmentChanged; override;
- procedure KillSource(aSource: TgxBaseSoundSource); override;
- procedure UpdateSource(aSource: TgxBaseSoundSource); override;
- procedure MuteSource(aSource: TgxBaseSoundSource; muted: Boolean); override;
- procedure PauseSource(aSource: TgxBaseSoundSource; paused: Boolean); override;
- function GetDefaultFrequency(aSource: TgxBaseSoundSource): Integer;
- function GetALFormat(sampling: TgxSoundSampling): Integer;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure UpdateSources; override;
- function EAXSupported: Boolean; override;
- end;
- EOpenALError = Exception;
- // ---------------------------------------------------------------------
- implementation
- // ---------------------------------------------------------------------
- // checks for an error and raises an exception if necessary
- procedure CheckOpenALError;
- var
- error: Integer;
- begin
- error := alGetError;
- if error <> AL_NO_ERROR then
- raise EOpenALError.Create('OpenAL Error #' + IntToStr(error) + ' (HEX: $' + IntToHex(error, 4) + ')');
- end;
- // clears the error-states
- procedure ClearOpenALError;
- begin
- alGetError;
- end;
- // ------------------
- // ------------------ TgxSMOpenAL ------------------
- // ------------------
- constructor TgxSMOpenAL.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- end;
- destructor TgxSMOpenAL.Destroy;
- begin
- inherited Destroy;
- end;
- function TgxSMOpenAL.DoActivate: Boolean;
- var
- dummy: array of PALbyte;
- begin
- Result := false;
- // Setup OpenAL
- if not InitOpenAL() then
- Exit;
- dummy := nil;
- alutInit(nil, dummy);
- CheckOpenALError;
- alDistanceModel(AL_INVERSE_DISTANCE);
- CheckOpenALError;
- ReadOpenALExtensions();
- // Set any global states
- FActivated := true;
- NotifyMasterVolumeChange;
- Notify3DFactorsChanged;
- if Environment <> seDefault then
- NotifyEnvironmentChanged;
- Result := true;
- end;
- procedure TgxSMOpenAL.DoDeActivate;
- var
- i: Integer;
- begin
- FActivated := false;
- for i := 0 to Sources.Count - 1 do
- begin
- Sources[i].Sample.ManagerTag := 0;
- end;
- alutExit;
- end;
- procedure TgxSMOpenAL.NotifyMasterVolumeChange;
- begin
- if FActivated then
- begin
- alListenerf(AL_GAIN, MasterVolume);
- end;
- end;
- procedure TgxSMOpenAL.Notify3DFactorsChanged;
- begin
- if FActivated then
- begin
- alDopplerFactor(DopplerFactor);
- end;
- end;
- procedure TgxSMOpenAL.NotifyEnvironmentChanged;
- begin
- if FActivated then
- begin
- // check extension is available + update
- if EAXSupported then
- begin
- // nothing yet
- end;
- end;
- end;
- procedure TgxSMOpenAL.KillSource(aSource: TgxBaseSoundSource);
- var
- i, currentBufferTag, bufferCount: Integer;
- begin
- if aSource.ManagerTag <> 0 then
- begin
- alSourceStop(aSource.ManagerTag);
- alDeleteSources(1, PALuint(@aSource.ManagerTag));
- aSource.ManagerTag := 0;
- // We can't just delete buffer, because other sources may be using it
- // so we count how many sources are using, then delete if it's the only one
- // using.
- // Same for ASource.Sample.ManagerTag, we set to zero once it's no longer
- // being used by any other sources
- currentBufferTag := aSource.Sample.ManagerTag;
- bufferCount := 0;
- if currentBufferTag <> 0 then
- begin
- for i := 0 to Sources.Count - 1 do
- begin
- if Sources[i].Sample.ManagerTag = currentBufferTag then
- begin
- bufferCount := bufferCount + 1;
- end;
- end;
- if bufferCount = 1 then
- begin
- alDeleteBuffers(1, PALuint(@aSource.Sample.ManagerTag));
- aSource.Sample.ManagerTag := 0;
- end;
- end;
- end;
- end;
- procedure TgxSMOpenAL.UpdateSource(aSource: TgxBaseSoundSource);
- var
- a: TALint;
- begin
- // Clear any errors we may enter into procedure with
- ClearOpenALError;
- // Creates an OpenAL source object if needed, and put ID into aSource.ManagerTag
- if aSource.ManagerTag = 0 then
- begin
- alGenSources(1, PALuint(@aSource.ManagerTag));
- CheckOpenALError;
- end
- else
- begin
- // Check to see if source has stopped, if so free it as limited number of sources allowed
- alGetSourcei(aSource.ManagerTag, AL_SOURCE_STATE, @a);
- CheckOpenALError;
- if a = AL_STOPPED then
- begin
- aSource.Free;
- Exit;
- end;
- end;
- // if sscTransformation in aSource.Changes then begin
- alSourcefv(aSource.ManagerTag, AL_POSITION, PALFloat(aSource.Origin.Position.asAddress));
- CheckOpenALError;
- alSourcefv(aSource.ManagerTag, AL_DIRECTION, PALFloat(aSource.Origin.Direction.asAddress));
- CheckOpenALError;
- // end;
- if aSource.SoundName <> '' then
- begin
- // If the sample doesn't have a reference to an OpenAL buffer
- // we need to create a buffer, and load the sample data into it
- if (aSource.Sample.ManagerTag = 0) and Assigned(aSource.Sample.Data) then
- begin
- alGenBuffers(1, PALuint(@aSource.Sample.ManagerTag));
- CheckOpenALError;
- // fill buffer (once buffer filled, can't fill buffer again, unless no other sources playing)
- alBufferData(aSource.Sample.ManagerTag, GetALFormat(aSource.Sample.sampling), aSource.Sample.Data.PCMData,
- aSource.Sample.Data.LengthInBytes, aSource.Sample.Data.sampling.Frequency);
- CheckOpenALError;
- end;
- if (sscSample in aSource.Changes) and Assigned(aSource.Sample.Data) then
- begin
- // Associate buffer with source, buffer may have either been recently
- // created, or already existing if being used by another source
- alSourcei(aSource.ManagerTag, AL_BUFFER, aSource.Sample.ManagerTag);
- CheckOpenALError;
- // If NbLoops>1 the source will constantly loop the sample, otherwise only play once
- alSourcei(aSource.ManagerTag, AL_LOOPING, Integer(aSource.NbLoops > 1));
- CheckOpenALError;
- // Start the source playing!
- alSourcePlay(aSource.ManagerTag);
- CheckOpenALError;
- end;
- end;
- if sscStatus in aSource.Changes then
- begin
- alSourcef(aSource.ManagerTag, AL_PITCH, 1.0);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_GAIN, 1.0);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_MAX_DISTANCE, aSource.MaxDistance);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_ROLLOFF_FACTOR, 1.0);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_REFERENCE_DISTANCE, aSource.MinDistance);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_CONE_INNER_ANGLE, aSource.InsideConeAngle);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_CONE_OUTER_ANGLE, aSource.OutsideConeAngle);
- CheckOpenALError;
- alSourcef(aSource.ManagerTag, AL_CONE_OUTER_GAIN, aSource.ConeOutsideVolume);
- end;
- inherited UpdateSource(aSource);
- end;
- procedure TgxSMOpenAL.MuteSource(aSource: TgxBaseSoundSource; muted: Boolean);
- begin
- if muted then
- alSourcef(aSource.ManagerTag, AL_MAX_GAIN, 0.0)
- else
- alSourcef(aSource.ManagerTag, AL_MAX_GAIN, 1.0);
- end;
- procedure TgxSMOpenAL.PauseSource(aSource: TgxBaseSoundSource; paused: Boolean);
- begin
- if not paused then
- begin
- alSourceRewind(aSource.ManagerTag);
- alSourcePlay(aSource.ManagerTag);
- end
- else
- alSourcePause(aSource.ManagerTag);
- end;
- procedure TgxSMOpenAL.UpdateSources;
- var
- pos, dir, up, vel: TVector4f;
- DirUp: array [0 .. 5] of TALfloat; // orientation
- begin
- ListenerCoordinates(pos, vel, dir, up);
- alListenerfv(AL_POSITION, PALFloat(@pos));
- alListenerfv(AL_VELOCITY, PALFloat(@vel));
- DirUp[0] := dir.X;
- DirUp[1] := dir.Y;
- DirUp[2] := dir.Z;
- DirUp[3] := up.X;
- DirUp[4] := up.Y;
- DirUp[5] := up.Z;
- alListenerfv(AL_ORIENTATION, PALFloat(@DirUp));
- inherited;
- end;
- function TgxSMOpenAL.EAXSupported: Boolean;
- begin
- Result := alIsExtensionPresent(PAnsiChar('EAX2.0'));
- end;
- function TgxSMOpenAL.GetDefaultFrequency(aSource: TgxBaseSoundSource): Integer;
- begin
- Result := -1;
- end;
- function TgxSMOpenAL.GetALFormat(sampling: TgxSoundSampling): Integer;
- begin
- Result := 0;
- // mono
- if sampling.NbChannels = 1 then
- case sampling.BitsPerSample of
- 8:
- Result := AL_FORMAT_MONO8;
- 16:
- Result := AL_FORMAT_MONO16;
- end
- else
- case sampling.BitsPerSample of // stereo
- 8:
- Result := AL_FORMAT_STEREO8;
- 16:
- Result := AL_FORMAT_STEREO16;
- end;
- end;
- end.
|