unit acs_mpeg; 

{$ifdef fpc}
{$mode delphi}
{$endif}

interface

uses
  Classes, SysUtils,ACS_file,ACS_classes, smpeg;

type
  TMPEGIn = class(TACSCustomFileIn)
  private
    _M : Pointer;
  protected
    procedure OpenFile; override;
    procedure CloseFile; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetData(Buffer : Pointer; BufferSize : Integer): Integer; override;
    procedure Init; override;
  end;


implementation

  constructor TMPEGIn.Create;
  begin
    inherited Create(AOwner);
    BufferSize := $2000;
  end;

  destructor TMPEGIn.Destroy;
  begin
    inherited Destroy;
  end;

  procedure TMPEGIn.Init;
  begin
    inherited Init;
    SMPEG_play(_M);
  end;

  procedure TMPEGIn.OpenFile;
  var
    info : SMPEG_info;
    spec : SDL_AudioSpec;
  begin
    if FOpened = 0 then
    begin
      (* the next call is needed just to make sure
        the SDL library is loaded *)
      _M := SMPEG_new(PChar(FFileName), info, 1);
      SMPEG_delete(_M);
      FValid := True;
      _M := SMPEG_new(PChar(FFileName), info, 0);
      if info.has_audio <> 1 then
      begin
        FValid := False;
        Exit;
      end;
      FTime := Round(info.total_time);
      SMPEG_wantedSpec(_M, spec);
      FSR := spec.freq;
      FBPS := 16;
      FChan := spec.channels;
      FSize := FTime*2*FChan*FSR;
    end;
    Inc(FOpened);
  end;

  procedure TMPEGIn.CloseFile;
  begin
    if FOpened = 1 then
    begin
      if SMPEG_status(_M) = SMPEG_PLAYING then
      SMPEG_stop(_M);
      SMPEG_delete(_M);
    end;
    if FOpened > 0 then Dec(FOpened);
  end;

  function TMPEGIn.GetData(Buffer : Pointer; BufferSize : Integer): Integer;
  var
    l, offs : Integer;
    tmp : Single;
  begin
    if not Busy then  raise EACSException.Create('The Stream is not opened');
    if BufStart > BufEnd then
    begin
      if FOffset <> 0 then
      begin
        offs := Round((FOffset/100)*FSize);
        FPosition := FPosition + offs;
        if FPosition < 0 then FPosition := 0
        else if FPosition > FSize then FPosition := FSize;
        if FOffset < 0 then
        begin
          SMPEG_rewind(_M);
          SMPEG_play(_M);
          tmp := (FPosition/FSize)*FTime;
          SMPEG_skip(_M, tmp);
        end;
        tmp := (FOffset/100)*FTime;
        SMPEG_skip(_M, tmp);
        FOffset := 0;
      end;
      BufStart := 1;
      FillChar(FBuffer, BufferSize, 0);
      l := Buffersize;
      l := SMPEG_playAudio(_M, @FBuffer[BufEnd + 1], BufferSize - BufEnd);
      if l = 0 then
      begin
        if FLoop then
        begin
          Flush;
          Init;
          SMPEG_rewind(_M);
          SMPEG_play(_M);
          FPosition := 0;
          l := SMPEG_playAudio(_M, @FBuffer[BufEnd + 1], BufferSize - BufEnd);
        end else
        begin
          Result := 0;
          Exit;
        end;
      end;
      BufEnd := l;
    end;
    if BufferSize < (BufEnd - BufStart + 1)
    then Result := BufferSize
    else Result := BufEnd - BufStart + 1;
    Move(FBuffer[BufStart], Buffer^, Result);
    Inc(BufStart, Result);
    Inc(FPosition, Result);
  end;

initialization
  LoadMPEGLibrary;
  if LibsmpegLoaded then
    begin
      FileFormats.Add('mp3','Mpeg Audio Layer III',TMPEGIn);
      FileFormats.Add('mp2','Mpeg Audio Layer II',TMPEGIn);
      FileFormats.Add('mpeg','Mpeg Audio',TMPEGIn);
    end;

finalization
  UnLoadMPEGLibrary;

end.

