procedure alutUnloadWAV(format: TALenum; data: TALvoid; size: TALsizei; freq: TALsizei);
begin
  //Clean up
  if data<>nil then freemem(data);
end;

procedure alutLoadWAVFile(fname: string; var format: TALenum; var data: TALvoid; var size: TALsizei; var freq: TALsizei; var loop: TALint);
var
  Stream: TStreamFile;
begin
  Stream.Init(fname, FM_READ);
  LoadWavStream(@Stream, format, data, size, freq, loop);
  Stream.Free;
end;

constructor TSoundHolder.Init(_Man: PSoundMan; const Parameters: TSoundParameters);
var
  I: Integer;
  Format: LongInt;
  Size: TALsizei;
  Data: Pointer;
  Freq: TALsizei;
  Loop: TALint;
  Zero: TVec4f;
begin
  inherited Init;
  IncRef;
  Man := _Man;
  Key := Parameters.GetKey;

  alGenBuffers(1, @Buffer);
  if alGetError() <> AL_NO_ERROR then
    exit;
  Log('TAudio: loading ' + Parameters.GetKey);
  LoadWavStream(Parameters.Data.Stream, Format, Data, Size, Freq, Loop);
  Parameters.Data.Done;
  alBufferData(Buffer, Format, Data, Size, Freq);
  alutUnloadWAV(Format, Data, Size, Freq);
  alGenSources (1, @Id);
  alSourcef (Id, AL_PITCH, 1.0);
  alSourcef (Id, AL_GAIN, 1.0);
  Zero := Vec4f(0.0, 0.0, 0.0, 0.0);
  alSourcefv(Id, AL_POSITION, @Zero);
  alSourcefv(Id, AL_VELOCITY, @Zero);
  alSourcei(Id, AL_LOOPING, 0);
  alSourcei(Id, AL_BUFFER, Buffer);
end;

destructor TSoundHolder.Done;
begin
  alDeleteSources(1, @Id);
  alDeleteBuffers(1, @Buffer);
  Key := '';
  inherited Done;
end;

procedure TSoundSource.Init(Eng: Pointer);
begin
  alGenSources(1, @FId);
end;

procedure TSoundSource.Done;
begin
  Stop;
  alDeleteSources(1, @FId);
end;

procedure TSoundSource.Play(const Sound: TSound);
begin
  Stop;
  alSourcei(FId, AL_BUFFER, Sound.Holder^.Buffer);
  alSourcePlay(FId);
end;

procedure TSoundSource.Stop;
begin
  alSourceStop(FId);
end;

procedure TSoundSource.SetPos(const Pos: TVec3f);
begin
  alSourcefv(FId, AL_POSITION, @Pos);
end;

procedure TSoundSource.SetDir(const Dir: TVec3f);
begin
  alSourcefv(FId, AL_DIRECTION, @Dir);
end;

procedure TSoundSource.SetVel(const Vel: TVec3f);
begin
  alSourcefv(FId, AL_VELOCITY, @Vel);
end;

function TSoundSource.IsPlaying: Boolean;
var
  Param: ALint;
begin
  alGetSourcei(FId, AL_SOURCE_STATE, @Param);
  Result := Param = AL_PLAYING;
end;

function TSoundSource.GetLooping: Boolean;
var
  Param: ALint;
begin
  alGetSourcei(FId, AL_SOURCE_STATE, @Param);
  Result := Param = AL_TRUE;
end;

procedure TSoundSource.SetLooping(Looping: Boolean);
var
  Param: ALint;
begin
  if Looping then begin
    Param := AL_TRUE;
  end else
    Param := AL_FALSE;
  alSourcei(FId, AL_LOOPING, Param);
end;

procedure TSoundSource.SetVolume(Volume: LongInt);
begin
  alSourcef(FId, AL_GAIN, Volume / 255);
end;

procedure TSoundSource.SetPitch(Pitch: Single);
begin
  alSourcef(FId, AL_PITCH, Pitch);
end;

procedure TAudio.OnInit;
{$IF Defined(ANDROID)}
var
  DeviceName: PAnsiChar;
{$ENDIF}
begin
  if FReady then
    Exit;

{$IF Defined(ANDROID)}
  if not InitOpenAL(_AndroidState^.activity^.vm, _AndroidState^.activity^.env) then begin
{$ELSE}
  if not InitOpenAL then begin
{$ENDIF}
    Log('TAudio: OpenAL loading functions failed');
    Exit;
  end else
    Log('TAudio: OpenAL functions loaded');

  if {$IFNDEF ANDROID}@{$ENDIF}alcOpenDevice = nil then begin
    Log('TAudio: no alcOpenDevice procedure');
    Exit;
  end;
  if {$IFNDEF ANDROID}@{$ENDIF}alcCreateContext = nil then begin
    Log('TAudio: no alcCreateContext procedure');
    Exit;
  end;
  if {$IFNDEF ANDROID}@{$ENDIF}alcMakeContextCurrent = nil then begin
    Log('TAudio: no alcCreateContext procedure');
    Exit;
  end;

  {$IF Defined(ANDROID)}
  DeviceName := alcGetString(nil, ALC_DEFAULT_DEVICE_SPECIFIER);
  Log('TAudio: ALC_DEFAULT_DEVICE_SPECIFIER: ' + AnsiString(DeviceName));
  //DeviceName := nil;
  FDevice := alcOpenDevice(DeviceName);
  {$ELSE}
  Log('TAudio: open device');
  FDevice := alcOpenDevice(nil);
  {$ENDIF}
  if FDevice = nil then begin
    Log('TAudio: cannot open openal device');
    Exit;
  end;
  Log('TAudio: create context');
  FContext := alcCreateContext(FDevice, nil);
  Log('TAudio: set current context');
  alcMakeContextCurrent(FContext);
  Log('TAudio: OpenAL initialized');
  FReady := True;
end;

procedure TAudio.OnDone;
begin
  if FReady then begin
    alcMakeContextCurrent(Nil);
    alcDestroyContext(FContext);
    alcCloseDevice(FDevice);
    Log('TAudio: OpenAL finalized');
  end;
end;

procedure TAudio.PlayFile(const Filename: String);
var
  Id: TSoundDeprecated;
begin
  // FIXME is there a resource leak?
  Id := LoadSound(FileName);
  Play(Id);
end;

function TAudio.LoadSound(const Filename: String): TSoundDeprecated;
var
  I: Integer;
  Format: TALenum;
  Size: TALsizei;
  Data: Pointer;
  Freq: TALsizei;
  Loop: TALint;
  Zero: TVec4f;
begin
  alGenBuffers(1, @Result.Buffer);
  if alGetError() <> AL_NO_ERROR then
    exit;
  // TODO
  alutLoadWAVFile (FileName, Format, Data, Size, Freq, Loop);
  alBufferData    (Result.Buffer, Format, Data, Size, Freq);
  alutUnloadWAV   (Format, Data, Size, Freq);
  alGenSources (1, @Result.Id);
  ////alSourcef (Id, AL_PITCH, 1.0);
  ////alSourcef (Id, AL_GAIN, 1.0);
  //Zero := Vec4f(0.0, 0.0, 0.0, 0.0);
  ////alSourcefv(Id, AL_POSITION, @Zero);
  ////alSourcefv(Id, AL_VELOCITY, @Zero);
  alSourcei(Result.Id, AL_LOOPING, 0);
  alSourcei(Result.Id, AL_BUFFER, Result.Buffer);
end;

procedure TAudio.FreeSound(Sound: TSoundDeprecated);
begin
  alDeleteSources(1, @Sound.Id);
  alDeleteBuffers(1, @Sound.Buffer);
end;

procedure TAudio.Play(Sound: TSoundDeprecated);
begin
  alSourcePlay(Sound.Id);
end;

procedure TAudio.SetSoundVolume(Volume: Single);
begin
  alListenerf(AL_GAIN, Volume);
end;

function TAudio.IsPlaying(Sound: TSoundDeprecated): Boolean;
var
  Param: LongInt;
begin
  alGetSourcei(Sound.Id, AL_PLAYING, @Param);
  Result := Param > 0;
end;

procedure TAudio.SetListenerPos(const Pos: TVec3f);
begin
  alListenerfv(AL_POSITION, @Pos);
end;

procedure TAudio.SetListenerDir(const Dir, Up: TVec3f);
var
  Orientation: array[0..1] of TVec3f;
begin
  Orientation[0] := Dir;
  Orientation[1] := Up;
  alListenerfv(AL_ORIENTATION, @Orientation[0]);
end;

procedure TAudio.SetListenerVel(const Vel: TVec3f);
begin
  alListenerfv(AL_VELOCITY, @Vel);
end;

procedure TSound.Play;
begin
  alSourcePlay(Id);
end;

procedure TSound.Stop;
begin
  alSourceStop(Id);
end;
