/usr/src/castle-game-engine-5.2.0/audio/castlevorbisdecoder.pas is in castle-game-engine-src 5.2.0-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | { OggVorbis decoder. }
unit CastleVorbisDecoder;
interface
uses SysUtils, Classes, CastleOpenAL;
type
EVorbisLoadError = class(Exception);
EVorbisFileError = class(EVorbisLoadError);
{ OggVorbis decoder using vorbisfile library and working on
ObjectPascal TStream objects.
This checks VorbisFileInited at the beginning, so you don't have to
worry about it.
Note: this only uses some constants from OpenAL unit. It doesn't
actually require OpenAL library to be available and initialized.
@raises EReadError If Stream cannot be read (e.g. ended prematurely.)
@raises EVorbisLoadError If decoding OggVorbis stream failed. }
function VorbisDecode(Stream: TStream; out DataFormat: TALuint;
out Frequency: LongWord): TMemoryStream;
implementation
uses CastleUtils, CastleVorbisFile, CastleVorbisCodec, CTypes;
{$I vorbisfile_conf.inc}
{ VorbisDecoder_ callbacks code based on Noeska code from
[http://www.noeska.com/doal/tutorials.aspx].
I (Michalis) heavily modified it (e.g. to allow exceptions raising in case of
stream errors (instead of silencing these exceptions), to check (ReadCount mod
Size) in read_func and whence in seek_func, close does nothing),
but still the idea remains.
I made my own CastleVorbisFile header, and later realized that actually
someone already did something similar...
But from the first glance, JEDI CastleVorbisFile
header shows some problems --- as usual, the JEDI guys don't have a clue
about FPC or any OS that isn't Windows. Besides, my CastleVorbisFile will not
cause exception at initialization if vorbisfile will not be installled ---
this is crucial for me. So I will continue using my own CastleVorbisFile unit. }
function VorbisDecoder_read_func(ptr: Pointer;
Size: TSizeT; nmemb: TSizeT; DataSource: Pointer): TSizeT; libvorbisfile_decl;
{ Returns amount of items completely read successfully, returns indeterminate
value on error. The value of a partially read item cannot be determined. Does
not lead to valid feof or ferror responses, because they are not possible to
supply to VorbisFile }
var
ReadCount: Int64;
begin
if (size = 0) or (nmemb = 0) then
begin
Result := 0;
Exit;
end;
ReadCount := TStream(DataSource).Read(ptr^, Size * nmemb);
Assert(ReadCount mod Size = 0);
Result := ReadCount div Size;
end;
function VorbisDecoder_seek_func(DataSource: Pointer;
offset: Int64; whence: CInt): CInt; libvorbisfile_decl;
const
SEEK_SET = 0;
SEEK_CUR = 1;
SEEK_END = 2;
begin
try
case whence of
SEEK_CUR: TStream(DataSource).Seek(offset, soFromCurrent);
SEEK_END: TStream(DataSource).Seek(offset, soFromEnd);
SEEK_SET: TStream(DataSource).Seek(offset, soFromBeginning);
else raise EInternalError.CreateFmt('Invalid VorbisDecoder_seek_func ' +
'whence param: %d', [whence]);
end;
Result := 0;
except
{ If the Stream is unseekable, vorbisfile allows us to return here -1. }
Result := -1;
end;
end;
function VorbisDecoder_close_func(DataSource: Pointer): CInt;
libvorbisfile_decl;
begin
Result := 0;
end;
function VorbisDecoder_tell_func(DataSource: Pointer): CLong;
libvorbisfile_decl;
begin
Result := TStream(DataSource).Position;
end;
{ Simple encoding of OggVorbis is implemented based on
[http://www.gamedev.net/reference/articles/article2031.asp].
I reworked it a lot (I have to use callbacks to handle Pascal streams,
I check for errors). See [http://xiph.org/vorbis/doc/vorbisfile/index.html]
for vorbisfile overview. }
function VorbisDecode(Stream: TStream; out DataFormat: TALuint;
out Frequency: LongWord): TMemoryStream;
procedure CheckVorbisFile(Err: CInt; const Event: string);
var
ErrDescription: string;
begin
{ Errors list and ErrDescription values based on
[http://xiph.org/vorbis/doc/vorbisfile/return.html] }
case Err of
OV_FALSE: ErrDescription := 'No data available';
OV_HOLE: ErrDescription := 'Vorbisfile encountered missing or corrupt data in the bitstream'; {. Recovery is normally automatic and this return code is for informational purposes only. }
OV_EREAD: ErrDescription := 'Read error while fetching compressed data for decode';
OV_EFAULT: ErrDescription := 'Internal inconsistency in decode state'; {. Continuing is likely not possible. }
OV_EIMPL: ErrDescription := 'Feature not implemented';
OV_EINVAL: ErrDescription := 'Either an invalid argument, or incompletely initialized argument passed to libvorbisfile call';
OV_ENOTVORBIS: ErrDescription := 'The given file/data was not recognized as Ogg Vorbis data';
OV_EBADHEADER: ErrDescription := 'The file/data is apparently an Ogg Vorbis stream, but contains a corrupted or undecipherable header';
OV_EVERSION: ErrDescription := 'The bitstream format revision of the given stream is not supported';
OV_EBADLINK: ErrDescription := 'The given link exists in the Vorbis data stream, but is not decipherable due to garbacge or corruption';
OV_ENOSEEK: ErrDescription := 'The given stream is not seekable';
else ErrDescription := '(unknown vorbisfile error code)';
end;
if Err <> 0 then
raise EVorbisFileError.CreateFmt('VorbisFile error %d at "%s": %s',
[Err, Event, ErrDescription]);
end;
const
{ Noone uses ogg vorbis with small files, so it's sensible to make
this buffer at least 1 MB. Above this, increasing BufSize doesn't
seem to make loading OggVorbis faster. }
BufSize = 1000 * 1000;
var
OggFile: TOggVorbis_File;
OggInfo: Pvorbis_info;
Callbacks: Tov_callbacks;
ReadCount: CLong;
Buffer: Pointer;
BitStream: CInt;
begin
if not VorbisFileInited then
raise EVorbisLoadError.Create('vorbisfile library is not available, ' +
'cannot decode OggVorbis file');
Result := TMemoryStream.Create;
try
Callbacks.read_func := @VorbisDecoder_read_func;
Callbacks.seek_func := @VorbisDecoder_seek_func;
Callbacks.close_func := @VorbisDecoder_close_func;
Callbacks.tell_func := @VorbisDecoder_tell_func;
CheckVorbisFile(ov_open_callbacks(Stream, @OggFile, nil, 0, Callbacks),
'ov_open_callbacks');
OggInfo := ov_info(@OggFile, -1);
if OggInfo^.channels = 1 then
DataFormat := AL_FORMAT_MONO16 else
DataFormat := AL_FORMAT_STEREO16;
Frequency := OggInfo^.rate;
Buffer := GetMem(BufSize);
try
repeat
ReadCount := ov_read(@OggFile, Buffer^, BufSize,
{ OpenAL always wants little endian ? } 0,
{ Always give us 16 bits } 2, 1, @BitStream);
if ReadCount < 0 then
CheckVorbisFile(ReadCount, 'ov_read');
Result.WriteBuffer(Buffer^, ReadCount);
until ReadCount <= 0;
finally FreeMemNiling(Buffer) end;
ov_clear(@OggFile);
except
FreeAndNil(Result);
raise;
end;
end;
end.
|