DEADSOFTWARE

simplify TSoundLoader interface
[d2df-sdl.git] / src / engine / e_soundfile_mp3.pas
index 5f60a864bb726d8ed1a1ac9507e8e54de746301d..c49f376b574c01abe207f062ee934890884333c4 100644 (file)
@@ -2,8 +2,7 @@
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,32 +24,29 @@ type
 
   TMP3Loader = class (TSoundLoader)
   public
-    function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
-    function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
-    function SetPosition(Pos: LongWord): Boolean; override;
+    function Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean; override; overload;
+    function Load(FName: string; Loop: Boolean): Boolean; override; overload;
+    function Finished(): Boolean; override;
+    function Restart(): Boolean; override;
     function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
-    function GetAll(var OutPtr: Pointer): LongWord; override;
     procedure Free(); override;
 
   private
     FMPG: pmpg123_handle;
     FData: TStream;
     FBuf: Pointer;
-    FAllSamples: Pointer;
     FOpen: Boolean;
+    FFinished: Boolean;
+    FLooping: Boolean;
 
-    function LoadStream(Stream: TStream; SStreaming: Boolean): Boolean;
+    function LoadStream(Stream: TStream): Boolean;
   end;
 
   TMP3LoaderFactory = class (TSoundLoaderFactory)
   public
-    constructor Create();
-    destructor Destroy(); override;
     function MatchHeader(Data: Pointer; Len: LongWord): Boolean; override;
     function MatchExtension(FName: string): Boolean; override;
     function GetLoader(): TSoundLoader; override;
-  private
-    FMPG: pmpg123_handle; // tester context
   end;
 
 implementation
@@ -89,49 +85,43 @@ end;
 
 (* TMP3LoaderFactory *)
 
-constructor TMP3LoaderFactory.Create();
-begin
-  FMPG := mpg123_new(nil, nil);
-  if FMPG <> nil then
-    mpg123_replace_reader_handle(FMPG, streamRead, streamLSeek, nil);
-end;
-
-destructor TMP3LoaderFactory.Destroy();
-begin
-  if FMPG <> nil then mpg123_delete(FMPG);
-end;
 
 function TMP3LoaderFactory.MatchHeader(Data: Pointer; Len: LongWord): Boolean;
 var
-  ID3: array [0..9] of Byte;
-  HeaderLen: LongInt;
-  S: TSFSMemoryStreamRO;
-  Info: mpg123_frameinfo;
+  P: PByte;
+  N: LongInt;
 begin
   Result := False;
-  if Len < 10 then Exit;
+  if Len < 10 then Exit; // way too short even without an ID3
+
+  P := PByte(Data);
 
-  // try and check for an ID3 header
-  Move(Data^, ID3, 10);
-  if (ID3[0] = Ord('I')) and (ID3[1] = Ord('D')) and (ID3[2] = Ord('3')) then
+  // try to check for an ID3v2 header
+  if ((P+0)^ = $49) and ((P+1)^ = $44) and ((P+2)^ = $33) then // 'ID3'
   begin
-    HeaderLen := ID3[9] + (ID3[8] shl 7) + (ID3[7] shl 14) + (ID3[6] shl 21);
-    Result := Len > (HeaderLen + 10);
+    N := (P+9)^ + ((P+8)^ shl 7) + ((P+7)^ shl 14) + ((P+6)^ shl 21);
+    Result := Len > (N + 10);
     if Result then Exit;
   end;
 
-  // if there isn't one, employ heavier shit
-  if FMPG = nil then Exit;
-
-  S := TSFSMemoryStreamRO.Create(Data, Len);
-
-  if mpg123_open_handle(FMPG, S) = MPG123_OK then
+  // try to read the frame sync word, bits 0-10 should be 1
+  if ((P+0)^ = $FF) and (((P+1)^ and $E0) = $E0) then
   begin
-    Result := mpg123_info(FMPG, @Info) = MPG123_OK;
-    mpg123_close(FMPG);
+    // bits 11-12: mpeg version, can't be 01
+    if (((P+1)^ and $10) = 0) and (((P+1)^ and $08) = $08) then
+      Exit;
+    // bits 13-14: layer: can't be 00
+    if ((P+1)^ and $06) = 0 then
+      Exit;
+    // bits 16-19: bitrate index: can't be 1111 or 0000
+    if (((P+2)^ and $F0) = 0) or (((P+2)^ and $F0) = $F0) then
+      Exit;
+    // bits 20-21: samplerate index: can't be 11
+    if ((P+2)^ and $0C) = $0C then
+      Exit;
+    // this is probably an MP3 then
+    Result := True;
   end;
-
-  S.Destroy();
 end;
 
 function TMP3LoaderFactory.MatchExtension(FName: string): Boolean;
@@ -149,9 +139,10 @@ end;
 
 (* TMP3Loader *)
 
-function TMP3Loader.LoadStream(Stream: TStream; SStreaming: Boolean): Boolean;
+function TMP3Loader.LoadStream(Stream: TStream): Boolean;
 var
-  SRate, SEnc, SChans: LongInt;
+  SRate: clong;
+  SEnc, SChans: LongInt;
 begin
   FMPG := mpg123_new(nil, nil);
   if FMPG = nil then
@@ -192,12 +183,13 @@ begin
   FFormat.SampleRate := SRate;
   FFormat.SampleBits := 16;
   FFormat.Channels := SChans;
-  FStreaming := SStreaming;
+  FStreaming := True;
+  FFinished := False;
 
   Result := True;
 end;
 
-function TMP3Loader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
+function TMP3Loader.Load(Data: Pointer; Len: LongWord; Loop: Boolean): Boolean;
 var
   S: TStream;
 begin
@@ -210,7 +202,8 @@ begin
   Move(Data^, FBuf^, Len);
 
   S := TSFSMemoryStreamRO.Create(FBuf, Len{, True});
-  Result := LoadStream(S, SStreaming);
+  Result := LoadStream(S);
+  FLooping := Loop;
 
   if not Result and (S <> nil) then
   begin
@@ -220,7 +213,7 @@ begin
   end;
 end;
 
-function TMP3Loader.Load(FName: string; SStreaming: Boolean): Boolean;
+function TMP3Loader.Load(FName: string; Loop: Boolean): Boolean;
 var
   S: TStream = nil;
 begin
@@ -228,21 +221,28 @@ begin
 
   try
     S := openDiskFileRO(FName);
-    Result := LoadStream(S, SStreaming);
+    Result := LoadStream(S);
+    FLooping := Loop;
   except
     on E: Exception do
-      e_LogWritefln('ModPlug: ERROR: could not read file `%s`: %s', [FName, E.Message]);
+      e_LogWritefln('MPG123: ERROR: could not read file `%s`: %s', [FName, E.Message]);
   end;
 
   if not Result and (S <> nil) then
     S.Destroy();
 end;
 
-function TMP3Loader.SetPosition(Pos: LongWord): Boolean;
+function TMP3Loader.Finished(): Boolean;
+begin
+  Result := FFinished;
+end;
+
+function TMP3Loader.Restart(): Boolean;
 begin
   Result := False;
   if FMPG = nil then Exit;
-  Result := mpg123_seek(FMPG, Pos, 0) = MPG123_OK;
+  FFinished := False;
+  Result := mpg123_seek(FMPG, 0, 0) = MPG123_OK;
 end;
 
 function TMP3Loader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
@@ -253,18 +253,19 @@ begin
   Result := 0;
   Got := 0;
   if FMPG = nil then Exit;
+
   Ret := mpg123_read(FMPG, Buf, Len, @Got);
-  if FLooping and (Ret = MPG123_DONE) then
-    mpg123_seek(FMPG, 0, 0); // loop
-  Result := Got;
-end;
 
-function TMP3Loader.GetAll(var OutPtr: Pointer): LongWord;
-begin
-  Result := 0;
-  if FMPG = nil then Exit;
-  if FStreaming then Exit;
-  // TODO
+  if (Ret = MPG123_DONE) or (Got = 0) then
+  begin
+    if FLooping then
+      Ret := mpg123_seek(FMPG, 0, 0) // loop
+    else
+      FFinished := True;
+  end;
+
+  if (Ret = MPG123_OK) or FFinished then
+    Result := Got;
 end;
 
 procedure TMP3Loader.Free();
@@ -273,12 +274,12 @@ begin
   if FMPG <> nil then mpg123_delete(FMPG);
   if FData <> nil then FData.Destroy();
   if FBuf <> nil then FreeMem(FBuf);
-  if FAllSamples <> nil then FreeMem(FAllSamples);
   FOpen := False;
+  FFinished := False;
+  FLooping := False;
   FMPG := nil;
   FData := nil;
   FBuf := nil;
-  FAllSamples := nil;
 end;
 
 initialization