1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** $Id: snd_vorbis.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //** Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //** This program is free software; you can redistribute it and/or
15 //** modify it under the terms of the GNU General Public License
16 //** as published by the Free Software Foundation; either version 2
17 //** of the License, or (at your option) any later version.
18 //**
19 //** This program is distributed in the hope that it will be useful,
20 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 //** GNU General Public License for more details.
23 //**
24 //**************************************************************************
25
26 // HEADER FILES ------------------------------------------------------------
27
28 #include <vorbis/codec.h>
29
30 #include "gamedefs.h"
31 #include "snd_local.h"
32
33 // MACROS ------------------------------------------------------------------
34
35 // TYPES -------------------------------------------------------------------
36
37 class VVorbisAudioCodec : public VAudioCodec
38 {
39 public:
40 int InitLevel;
41 VStream* Strm;
42 bool FreeStream;
43 int BytesLeft;
44
45 ogg_sync_state oy;
46 ogg_stream_state os;
47 vorbis_info vi;
48 vorbis_comment vc;
49 vorbis_dsp_state vd;
50 vorbis_block vb;
51
52 bool eos;
53
54 VVorbisAudioCodec(VStream*, bool);
55 ~VVorbisAudioCodec();
56 bool Init();
57 void Cleanup();
58 int Decode(short*, int);
59 int ReadData();
60 bool Finished();
61 void Restart();
62 static VAudioCodec* Create(VStream*);
63 };
64
65 class VVorbisSampleLoader : public VSampleLoader
66 {
67 public:
68 void Load(sfxinfo_t&, VStream&);
69 };
70
71 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
72
73 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
74
75 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
76
77 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
78
79 // PUBLIC DATA DEFINITIONS -------------------------------------------------
80
81 // PRIVATE DATA DEFINITIONS ------------------------------------------------
82
83 IMPLEMENT_AUDIO_CODEC(VVorbisAudioCodec, "Vorbis");
84
85 VVorbisSampleLoader VorbisSampleLoader;
86
87 // CODE --------------------------------------------------------------------
88
89 //==========================================================================
90 //
91 // VVorbisAudioCodec::VVorbisAudioCodec
92 //
93 //==========================================================================
94
VVorbisAudioCodec(VStream * AStrm,bool AFreeStream)95 VVorbisAudioCodec::VVorbisAudioCodec(VStream* AStrm, bool AFreeStream)
96 : Strm(AStrm)
97 , FreeStream(AFreeStream)
98 {
99 guard(VVorbisAudioCodec::VVorbisAudioCodec);
100 BytesLeft = Strm->TotalSize();
101 Strm->Seek(0);
102
103 ogg_sync_init(&oy);
104 unguard;
105 }
106
107 //==========================================================================
108 //
109 // VVorbisAudioCodec::~VVorbisAudioCodec
110 //
111 //==========================================================================
112
~VVorbisAudioCodec()113 VVorbisAudioCodec::~VVorbisAudioCodec()
114 {
115 guard(VVorbisAudioCodec::~VVorbisAudioCodec);
116 if (InitLevel > 0)
117 {
118 Cleanup();
119
120 if (FreeStream)
121 {
122 Strm->Close();
123 delete Strm;
124 }
125 Strm = NULL;
126 }
127 ogg_sync_clear(&oy);
128 unguard;
129 }
130
131 //==========================================================================
132 //
133 // VVorbisAudioCodec::Init
134 //
135 //==========================================================================
136
Init()137 bool VVorbisAudioCodec::Init()
138 {
139 guard(VVorbisAudioCodec::Init);
140 ogg_page og;
141 ogg_packet op;
142
143 eos = false;
144 InitLevel = 0;
145
146 // Read some data.
147 ReadData();
148
149 // Get the first page.
150 if (ogg_sync_pageout(&oy, &og) != 1)
151 {
152 // Not a Vorbis file.
153 return false;
154 }
155
156 ogg_stream_init(&os, ogg_page_serialno(&og));
157 vorbis_info_init(&vi);
158 vorbis_comment_init(&vc);
159 InitLevel = 1;
160
161 if (ogg_stream_pagein(&os, &og) < 0)
162 {
163 // Stream version mismatch perhaps.
164 return false;
165 }
166
167 if (ogg_stream_packetout(&os, &op) != 1)
168 {
169 // No page? Must not be vorbis.
170 return false;
171 }
172
173 if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0)
174 {
175 // Not a vorbis header.
176 return false;
177 }
178
179 // We need 2 more headers.
180 int i = 0;
181 while (i < 2)
182 {
183 int result = ogg_stream_packetout(&os, &op);
184 if (result < 0)
185 {
186 // Corrupt header.
187 return false;
188 }
189 if (result > 0)
190 {
191 if (vorbis_synthesis_headerin(&vi, &vc, &op))
192 {
193 // Corrupt header.
194 return false;
195 }
196 i++;
197 }
198 else if (ogg_sync_pageout(&oy, &og) > 0)
199 {
200 ogg_stream_pagein(&os, &og);
201 }
202 else if (ReadData() == 0 && i < 2)
203 {
204 // Out of data while reading headers.
205 return false;
206 }
207 }
208
209 // Parsed all three headers. Initialise the Vorbis decoder.
210 vorbis_synthesis_init(&vd, &vi);
211 vorbis_block_init(&vd, &vb);
212 SampleRate = vi.rate;
213 InitLevel = 2;
214 return true;
215 unguard;
216 }
217
218 //==========================================================================
219 //
220 // VVorbisAudioCodec::Cleanup
221 //
222 //==========================================================================
223
Cleanup()224 void VVorbisAudioCodec::Cleanup()
225 {
226 guard(VVorbisAudioCodec::Cleanup);
227 if (InitLevel > 0)
228 {
229 ogg_stream_clear(&os);
230 }
231 if (InitLevel > 1)
232 {
233 vorbis_block_clear(&vb);
234 vorbis_dsp_clear(&vd);
235 }
236 if (InitLevel > 0)
237 {
238 vorbis_comment_clear(&vc);
239 vorbis_info_clear(&vi);
240 }
241 InitLevel = 0;
242 unguard;
243 }
244
245 //==========================================================================
246 //
247 // VVorbisAudioCodec::Decode
248 //
249 //==========================================================================
250
Decode(short * Data,int NumSamples)251 int VVorbisAudioCodec::Decode(short* Data, int NumSamples)
252 {
253 guard(VVorbisAudioCodec::Decode);
254 ogg_page og;
255 ogg_packet op;
256 int CurSample = 0;
257
258 while (!eos)
259 {
260 // While we have data ready, read it.
261 float** pcm;
262 int samples = vorbis_synthesis_pcmout(&vd, &pcm);
263 if (samples > 0)
264 {
265 int bout = (CurSample + samples < NumSamples ? samples : NumSamples - CurSample);
266 for (int i = 0; i < 2; i++)
267 {
268 short* ptr = Data + CurSample * 2 + (vi.channels > 1 ? i : 0);
269 float* mono = pcm[vi.channels > 1 ? i : 0];
270 for (int j = 0; j < bout; j++)
271 {
272 int val = int(mono[j] * 32767.f);
273 // clipping
274 if (val > 32767)
275 {
276 val = 32767;
277 }
278 if (val < -32768)
279 {
280 val = -32768;
281 }
282 *ptr = val;
283 ptr += 2;
284 }
285 }
286 // Tell libvorbis how many samples we actually consumed
287 vorbis_synthesis_read(&vd, bout);
288 CurSample += bout;
289 if (CurSample == NumSamples)
290 return CurSample;
291 }
292
293 if (ogg_stream_packetout(&os, &op) > 0)
294 {
295 // We have a packet. Decode it.
296 if (vorbis_synthesis(&vb, &op) == 0)
297 vorbis_synthesis_blockin(&vd, &vb);
298 }
299 else if (ogg_sync_pageout(&oy, &og) > 0)
300 {
301 ogg_stream_pagein(&os, &og);
302 }
303 else if (ReadData() == 0)
304 {
305 eos = true;
306 }
307 }
308 return CurSample;
309 unguard;
310 }
311
312 //==========================================================================
313 //
314 // VVorbisAudioCodec::ReadData
315 //
316 //==========================================================================
317
ReadData()318 int VVorbisAudioCodec::ReadData()
319 {
320 guard(VVorbisAudioCodec::ReadData);
321 if (!BytesLeft)
322 return 0;
323 char* buffer = ogg_sync_buffer(&oy, 4096);
324 int bytes = 4096;
325 if (bytes > BytesLeft)
326 bytes = BytesLeft;
327 Strm->Serialise(buffer, bytes);
328 ogg_sync_wrote(&oy, bytes);
329 BytesLeft -= bytes;
330 return bytes;
331 unguard;
332 }
333
334 //==========================================================================
335 //
336 // VVorbisAudioCodec::Finished
337 //
338 //==========================================================================
339
Finished()340 bool VVorbisAudioCodec::Finished()
341 {
342 guard(VVorbisAudioCodec::Finished);
343 return eos;
344 unguard;
345 }
346
347 //==========================================================================
348 //
349 // VVorbisAudioCodec::Restart
350 //
351 //==========================================================================
352
Restart()353 void VVorbisAudioCodec::Restart()
354 {
355 guard(VVorbisAudioCodec::Restart);
356 Cleanup();
357 Strm->Seek(0);
358 BytesLeft = Strm->TotalSize();
359 Init();
360 unguard;
361 }
362
363 //==========================================================================
364 //
365 // VVorbisAudioCodec::Create
366 //
367 //==========================================================================
368
Create(VStream * InStrm)369 VAudioCodec* VVorbisAudioCodec::Create(VStream* InStrm)
370 {
371 guard(VVorbisAudioCodec::Create);
372 VVorbisAudioCodec* Codec = new VVorbisAudioCodec(InStrm, true);
373 if (!Codec->Init())
374 {
375 Codec->Cleanup();
376 delete Codec;
377 Codec = NULL;
378 return NULL;
379 }
380 return Codec;
381 unguard;
382 }
383
384 //==========================================================================
385 //
386 // VVorbisAudioCodec::Create
387 //
388 //==========================================================================
389
Load(sfxinfo_t & Sfx,VStream & Stream)390 void VVorbisSampleLoader::Load(sfxinfo_t& Sfx, VStream& Stream)
391 {
392 guard(VVorbisSampleLoader::Load);
393 VVorbisAudioCodec* Codec = new VVorbisAudioCodec(&Stream, false);
394 if (!Codec->Init())
395 {
396 Codec->Cleanup();
397 delete Codec;
398 Codec = NULL;
399 return;
400 }
401
402 TArray<short> Data;
403 do
404 {
405 short Buf[16 * 2048];
406 int SamplesDecoded = Codec->Decode(Buf, 16 * 1024);
407 if (SamplesDecoded > 0)
408 {
409 int OldPos = Data.Num();
410 Data.SetNumWithReserve(Data.Num() + SamplesDecoded);
411 for (int i = 0; i < SamplesDecoded; i++)
412 {
413 Data[OldPos + i] = Buf[i * 2];
414 }
415 }
416 }
417 while (!Codec->Finished());
418 if (!Data.Num())
419 {
420 delete Codec;
421 Codec = NULL;
422 return;
423 }
424
425 // Copy parameters.
426 Sfx.SampleRate = Codec->SampleRate;
427 Sfx.SampleBits = Codec->SampleBits;
428
429 // Copy data.
430 Sfx.DataSize = Data.Num() * 2;
431 Sfx.Data = Z_Malloc(Data.Num() * 2);
432 memcpy(Sfx.Data, Data.Ptr(), Data.Num() * 2);
433
434 delete Codec;
435 Codec = NULL;
436 unguard;
437 }
438