1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
16 unit e_soundfile_vorbis
;
20 uses e_soundfile
, vorbis
, classes
;
25 TVorbisLoader
= class (TSoundLoader
)
27 function Load(Data
: Pointer; Len
: LongWord; SStreaming
: Boolean): Boolean; override; overload
;
28 function Load(FName
: string; SStreaming
: Boolean): Boolean; override; overload
;
29 function SetPosition(Pos
: LongWord): Boolean; override;
30 function FillBuffer(Buf
: Pointer; Len
: LongWord): LongWord; override;
31 function GetAll(var OutPtr
: Pointer): LongWord; override;
32 procedure Free(); override;
41 function LoadStream(Stream
: TStream
; SStreaming
: Boolean): Boolean;
42 function LoadEntireStream(): Pointer;
45 TVorbisLoaderFactory
= class (TSoundLoaderFactory
)
47 function MatchHeader(Data
: Pointer; Len
: LongWord): Boolean; override;
48 function MatchExtension(FName
: string): Boolean; override;
49 function GetLoader(): TSoundLoader
; override;
54 uses sysutils
, utils
, e_log
, xstreams
, ogg
, ctypes
;
56 (* Reader functions for ov_callbacks *)
58 function streamSeek(h
: Pointer; off
: ogg_int64_t
; whence
: cint
): cint
; cdecl;
67 0: s
.Seek(off
, soBeginning
); // SEEK_SET
68 1: s
.Seek(off
, soCurrent
); // SEEK_CUR
69 2: s
.Seek(off
, soEnd
); // SEEK_END
77 function streamRead(buf
: Pointer; sz
, nmemb
: csize_t
; h
: Pointer): csize_t
; cdecl;
85 Result
:= S
.Read(buf
^, sz
*nmemb
) div sz
;
91 function streamTell(h
: Pointer): clong
; cdecl;
102 oggIO
: ov_callbacks
= (
105 close
: nil; // the loader's gonna handle that
109 (* TVorbisLoaderFactory *)
111 function TVorbisLoaderFactory
.MatchHeader(Data
: Pointer; Len
: LongWord): Boolean;
113 OGG_HEADER
= $5367674F; // 'OggS'
120 if Len
< 27 then // header is at least 27 bytes
122 if PLongWord(Data
)^ <> OGG_HEADER
then
125 // now we gotta check that this is indeed a vorbis file and not an opus file
127 S
:= TSFSMemoryStreamRO
.Create(Data
, Len
);
128 Result
:= ov_test_callbacks(S
, F
, nil, 0, oggIO
) = 0;
129 if Result
then ov_clear(F
);
133 function TVorbisLoaderFactory
.MatchExtension(FName
: string): Boolean;
135 Result
:= GetFilenameExt(FName
) = '.ogg';
138 function TVorbisLoaderFactory
.GetLoader(): TSoundLoader
;
140 Result
:= TVorbisLoader
.Create();
145 function TVorbisLoader
.LoadEntireStream(): Pointer;
147 Samples
: ogg_int64_t
;
152 Samples
:= ov_pcm_total(FOgg
, -1);
153 if Samples
< 0 then Exit
;
155 FTotal
:= Samples
* 2 * FFormat
.Channels
;
156 Result
:= GetMem(FTotal
);
157 if Result
= nil then Exit
;
159 Ret
:= ov_read_ext(FOgg
, Result
, FTotal
, False, 2, True);
169 function TVorbisLoader
.LoadStream(Stream
: TStream
; SStreaming
: Boolean): Boolean;
177 Ret
:= ov_open_callbacks(Stream
, FOgg
, nil, 0, oggIO
);
180 e_LogWriteln('OGG: Load(Data) failed: ov_open_callbacks failed');
184 Info
:= ov_info(FOgg
, -1);
187 e_LogWriteln('OGG: Load(Data) failed: ov_info returned NULL');
192 FFormat
.SampleRate
:= Info
^.rate
;
193 FFormat
.Channels
:= Info
^.channels
;
194 FFormat
.SampleBits
:= 16;
196 if not SStreaming
then
198 FullBuf
:= LoadEntireStream();
200 if FullBuf
= nil then
202 e_LogWriteln('OGG: Load(Data) failed: couldn''t allocate for non-streaming chunk');
221 FStreaming
:= SStreaming
;
225 function TVorbisLoader
.Load(Data
: Pointer; Len
: LongWord; SStreaming
: Boolean): Boolean;
231 // TODO: have to make a dupe here because Data gets deallocated after loading
232 // this is obviously very shit
234 if FBuf
= nil then Exit
;
235 Move(Data
^, FBuf
^, Len
);
237 S
:= TSFSMemoryStreamRO
.Create(FBuf
, Len
{, True});
238 Result
:= LoadStream(S
, SStreaming
);
240 if not Result
and (S
<> nil) then
248 function TVorbisLoader
.Load(FName
: string; SStreaming
: Boolean): Boolean;
255 S
:= openDiskFileRO(FName
);
256 Result
:= LoadStream(S
, SStreaming
);
259 e_LogWritefln('OGG: ERROR: could not read file `%s`: %s', [FName
, E
.Message]);
262 if not Result
and (S
<> nil) then
266 function TVorbisLoader
.SetPosition(Pos
: LongWord): Boolean;
269 if not FOpen
or (ov_seekable(FOgg
) = 0) then Exit
;
270 Result
:= ov_pcm_seek(FOgg
, Pos
) = 0;
273 function TVorbisLoader
.FillBuffer(Buf
: Pointer; Len
: LongWord): LongWord;
278 if not FOpen
or not FStreaming
then Exit
;
279 Ret
:= ov_read_ext(FOgg
, Buf
, Len
, False, 2, True);
280 if Ret
< 0 then Exit
;
281 if FLooping
and (Ret
= 0) then
282 ov_pcm_seek(FOgg
, 0);
286 function TVorbisLoader
.GetAll(var OutPtr
: Pointer): LongWord;
289 if FStreaming
or (FTotal
= 0) then Exit
;
294 procedure TVorbisLoader
.Free();
310 e_AddSoundLoader(TVorbisLoaderFactory
.Create());