DEADSOFTWARE

Sound: OpenAL: Add Opus support
[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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit e_soundfile_opus;
19 interface
21 uses e_soundfile, opus, classes;
23 type
24 // Opus loader
26 TOpusLoader = class (TSoundLoader)
27 public
28 function Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean; override; overload;
29 function Load(FName: string; SStreaming: Boolean): Boolean; override; overload;
30 function SetPosition(Pos: LongWord): Boolean; override;
31 function FillBuffer(Buf: Pointer; Len: LongWord): LongWord; override;
32 function GetAll(var OutPtr: Pointer): LongWord; override;
33 procedure Free(); override;
34 private
35 FOpus: POggOpusFile;
36 FBuf: Pointer;
37 end;
39 TOpusLoaderFactory = class (TSoundLoaderFactory)
40 public
41 function MatchHeader(Data: Pointer; Len: LongWord): Boolean; override;
42 function MatchExtension(FName: string): Boolean; override;
43 function GetLoader(): TSoundLoader; override;
44 end;
46 implementation
48 uses sysutils, utils, e_log, xstreams, ogg, ctypes;
50 function TOpusLoaderFactory.MatchHeader(Data: Pointer; Len: LongWord): Boolean;
51 const
52 OGG_HEADER = $5367674F; // 'OggS'
53 var
54 F: POggOpusFile = nil;
55 begin
56 Result := False;
58 if Len < 27 then // header is at least 27 bytes
59 Exit;
60 if PLongWord(Data)^ <> OGG_HEADER then
61 Exit;
63 // now we gotta check that this is indeed an opus file and not a vorbis file
65 F := op_test_memory(Data, Len, nil);
66 Result := F <> nil;
67 if Result then op_free(F);
68 end;
70 function TOpusLoaderFactory.MatchExtension(FName: string): Boolean;
71 begin
72 Result := GetFilenameExt(FName) = '.opus';
73 end;
75 function TOpusLoaderFactory.GetLoader(): TSoundLoader;
76 begin
77 Result := TOpusLoader.Create();
78 end;
80 (* TOpusLoader *)
82 function TOpusLoader.Load(Data: Pointer; Len: LongWord; SStreaming: Boolean): Boolean;
83 begin
84 Result := False;
86 FBuf := GetMem(Len);
87 if FBuf = nil then
88 begin
89 e_LogWriteln('Opus: Load(Data) failed: out of memory on copy');
90 Exit;
91 end;
92 Move(Data^, FBuf^, Len);
94 FOpus := op_open_memory(FBuf, Len, nil);
95 if FOpus = nil then
96 begin
97 Free();
98 e_LogWriteln('Opus: Load(Data) failed: op_open_memory failed');
99 Exit;
100 end;
102 FFormat.Channels := 2; // we use ov_read_stereo
103 FFormat.SampleBits := 16;
104 FFormat.SampleRate := 48000; // is this even correct?
105 FStreaming := True; // opus is always streaming
107 Result := True;
108 end;
110 function TOpusLoader.Load(FName: string; SStreaming: Boolean): Boolean;
111 begin
112 Result := False;
114 FOpus := op_open_file(PChar(FName), nil);
115 if FOpus = nil then
116 begin
117 e_LogWritefln('Opus: Load(%s) failed: op_open_file failed', [FName]);
118 Exit;
119 end;
121 FFormat.Channels := 2; // we use ov_read_stereo
122 FFormat.SampleBits := 16;
123 FFormat.SampleRate := 48000; // is this even correct?
124 FStreaming := True; // opus is always streaming
126 Result := True;
127 end;
129 function TOpusLoader.SetPosition(Pos: LongWord): Boolean;
130 begin
131 Result := False;
132 if (FOpus = nil) or (op_seekable(FOpus) = 0) then Exit;
133 Result := op_pcm_seek(FOpus, Pos) = 0;
134 end;
136 function TOpusLoader.FillBuffer(Buf: Pointer; Len: LongWord): LongWord;
137 var
138 Ret: cint;
139 Rx: Integer;
140 begin
141 Result := 0;
142 if FOpus = nil then Exit;
144 Rx := 0;
146 while Rx < Len do
147 begin
148 Ret := op_read_stereo(FOpus, Buf + Rx, (Len - Rx) div 2);
149 if Ret = OP_HOLE then continue;
150 if Ret < 0 then break;
151 if FLooping and (Ret = 0) then op_pcm_seek(FOpus, 0); // loop
152 Rx := Rx + Ret * 4;
153 end;
155 Result := Rx;
156 end;
158 function TOpusLoader.GetAll(var OutPtr: Pointer): LongWord;
159 begin
160 Result := 0; // always streaming
161 end;
163 procedure TOpusLoader.Free();
164 begin
165 if FOpus <> nil then
166 op_free(FOpus);
167 if FBuf <> nil then
168 FreeMem(FBuf);
169 FOpus := nil;
170 FBuf := nil;
171 FStreaming := False;
172 end;
174 initialization
175 e_AddSoundLoader(TOpusLoaderFactory.Create());
176 end.