DEADSOFTWARE

ab799510d62777515048b071d79121e671cd84e7
[d2df-sdl.git] / src / engine / e_soundfile_opus.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
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.
6 *
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.
11 *
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/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit e_soundfile_opus;
18 interface
20 uses e_soundfile, opus, classes;
22 type
23 // Opus loader
25 TOpusLoader = class (TSoundLoader)
26 public
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;
33 private
34 FOpus: POggOpusFile;
35 FBuf: Pointer;
36 end;
38 TOpusLoaderFactory = class (TSoundLoaderFactory)
39 public
40 function MatchHeader(Data: Pointer; Len: LongWord): Boolean; override;
41 function MatchExtension(FName: string): Boolean; override;
42 function GetLoader(): TSoundLoader; override;
43 end;
45 implementation
47 uses sysutils, utils, e_log, xstreams, ogg, ctypes;
49 function TOpusLoaderFactory.MatchHeader(Data: Pointer; Len: LongWord): Boolean;
50 const
51 OGG_HEADER = $5367674F; // 'OggS'
52 var
53 F: POggOpusFile = nil;
54 begin
55 Result := False;
57 if Len < 27 then // header is at least 27 bytes
58 Exit;
59 if PLongWord(Data)^ <> OGG_HEADER then
60 Exit;
62 // now we gotta check that this is indeed an opus file and not a vorbis file
64 F := op_test_memory(Data, Len, nil);
65 Result := F <> nil;
66 if Result then op_free(F);
67 end;
69 function TOpusLoaderFactory.MatchExtension(FName: string): Boolean;
70 begin
71 Result := GetFilenameExt(FName) = '.opus';
72 end;
74 function TOpusLoaderFactory.GetLoader(): TSoundLoader;
75 begin
76 Result := TOpusLoader.Create();
77 end;
79 (* TOpusLoader *)
81 function TOpusLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
82 begin
83 Result := False;
85 FBuf := GetMem(Len);
86 if FBuf = nil then
87 begin
88 e_LogWriteln('Opus: Load(Data) failed: out of memory on copy');
89 Exit;
90 end;
91 Move(Data^, FBuf^, Len);
93 FOpus := op_open_memory(FBuf, Len, nil);
94 if FOpus = nil then
95 begin
96 Free();
97 e_LogWriteln('Opus: Load(Data) failed: op_open_memory failed');
98 Exit;
99 end;
101 FFormat.Channels := 2; // we use ov_read_stereo
102 FFormat.SampleBits := 16;
103 FFormat.SampleRate := 48000; // is this even correct?
104 FStreaming := True; // opus is always streaming
106 Result := True;
107 end;
109 function TOpusLoader.Load(FName: string; SStreaming: Boolean): Boolean;
110 begin
111 Result := False;
113 FOpus := op_open_file(PChar(FName), nil);
114 if FOpus = nil then
115 begin
116 e_LogWritefln('Opus: Load(%s) failed: op_open_file failed', [FName]);
117 Exit;
118 end;
120 FFormat.Channels := 2; // we use ov_read_stereo
121 FFormat.SampleBits := 16;
122 FFormat.SampleRate := 48000; // is this even correct?
123 FStreaming := True; // opus is always streaming
125 Result := True;
126 end;
128 function TOpusLoader.SetPosition(Pos: LongWord): Boolean;
129 begin
130 Result := False;
131 if (FOpus = nil) or (op_seekable(FOpus) = 0) then Exit;
132 Result := op_pcm_seek(FOpus, Pos) = 0;
133 end;
135 function TOpusLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
136 var
137 Ret: cint;
138 Rx: Integer;
139 begin
140 Result := 0;
141 if FOpus = nil then Exit;
143 Rx := 0;
145 while Rx < Len do
146 begin
147 Ret := op_read_stereo(FOpus, Buf + Rx, (Len - Rx) div 2);
148 if Ret = OP_HOLE then continue;
149 if Ret < 0 then break;
150 if FLooping and (Ret = 0) then op_pcm_seek(FOpus, 0); // loop
151 Rx := Rx + Ret * 4;
152 end;
154 Result := Rx;
155 end;
157 function TOpusLoader.GetAll(var OutPtr: Pointer): LongWord;
158 begin
159 Result := 0; // always streaming
160 end;
162 procedure TOpusLoader.Free();
163 begin
164 if FOpus <> nil then
165 op_free(FOpus);
166 if FBuf <> nil then
167 FreeMem(FBuf);
168 FOpus := nil;
169 FBuf := nil;
170 FStreaming := False;
171 end;
173 initialization
174 e_AddSoundLoader(TOpusLoaderFactory.Create());
175 end.