unit dogg;

interface

implementation

uses
  DEng,
  DMusic,
  dstream,
  doggparser;

type
POggStream = ^TOggStream;
TOggStream = object(TMusicStream)
private
  FOggVorbisFile: TOggVorbisFile;
public
  constructor Init(Stream: PStream);
  destructor Done; virtual;
  function ReadPCM(Data: PByte; Size: LongInt): LongInt; virtual;
  procedure Rewind; virtual;
end;

TOggLoader = object(TMusicLoader)
public
  function Load(Stream: PStream): PMusicStream; virtual;
end;

constructor TOggStream.Init(Stream: PStream);
begin
  inherited Init;
  if ov_open_callbacks(Pointer(Stream), @FOggVorbisFile, nil, 0, OVCallbacks) < 0 then begin
    FReady := False;
    Exit;
  end;
  FChannels := FOggVorbisFile.VI^.Channels;
  FRate := FOggVorbisFile.VI^.Rate;
end;

destructor TOggStream.Done;
begin
  ov_clear(@FOggVorbisFile);
  inherited Done;
end;

function TOggStream.ReadPCM(Data: PByte; Size: LongInt): LongInt;
var
  bitStream, Temp: LongInt;
begin
  Result := 0;
  while Result < Size do begin
    Temp := ov_read(@FOggVorbisFile, @(Data[Result]), Size - Result, 0, 2, 1, @bitStream);
    // TODO Temp<0 means error, check and return it
    if Temp < 0 then begin // error
      FReady := False;
      Exit;
    end;
    if Temp = 0 then begin // EOF
      Exit;
    end;
    Inc(Result, Temp);
  end;
end;

procedure TOggStream.Rewind;
begin
  ov_pcm_seek(@FOggVorbisFile, 0);
end;

function TOggLoader.Load(Stream: PStream): PMusicStream;
begin
  Result := New(POggStream, Init(Stream));
  if not Result^.Ready then begin
    Dispose(Result, Done);
    Result := nil;
  end;
end;

var
  OggLoader: TOggLoader;
initialization
  OggLoader.Init;
  RegisterMusicLoader(OggLoader);
end.
