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