1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: snd_wav.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 "gamedefs.h"
29 #include "snd_local.h"
30 
31 // MACROS ------------------------------------------------------------------
32 
33 // TYPES -------------------------------------------------------------------
34 
35 #pragma pack(1)
36 struct FRiffChunkHeader
37 {
38 	char		ID[4];
39 	vuint32		Size;
40 };
41 
42 struct FWavFormatDesc
43 {
44 	vuint16		Format;
45 	vuint16		Channels;
46 	vuint32		Rate;
47 	vuint32		BytesPerSec;
48 	vuint16		BlockAlign;
49 	vuint16		Bits;
50 };
51 #pragma pack()
52 
53 class VWaveSampleLoader : public VSampleLoader
54 {
55 public:
56 	void Load(sfxinfo_t&, VStream&);
57 };
58 
59 class VWavAudioCodec : public VAudioCodec
60 {
61 public:
62 	VStream*		Strm;
63 	int				SamplesLeft;
64 
65 	int				WavChannels;
66 	int				WavBits;
67 	int				BlockAlign;
68 
69 	VWavAudioCodec(VStream* InStrm);
70 	~VWavAudioCodec();
71 	int Decode(short* Data, int NumSamples);
72 	bool Finished();
73 	void Restart();
74 
75 	static VAudioCodec* Create(VStream* InStrm);
76 };
77 
78 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
79 
80 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
81 
82 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
83 
84 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
85 
86 // PUBLIC DATA DEFINITIONS -------------------------------------------------
87 
88 // PRIVATE DATA DEFINITIONS ------------------------------------------------
89 
90 static VWaveSampleLoader		WaveSampleLoader;
91 
92 IMPLEMENT_AUDIO_CODEC(VWavAudioCodec, "Wav");
93 
94 // CODE --------------------------------------------------------------------
95 
96 //==========================================================================
97 //
98 //	FindChunk
99 //
100 //==========================================================================
101 
FindRiffChunk(VStream & Strm,const char * ID)102 static int FindRiffChunk(VStream& Strm, const char* ID)
103 {
104 	guard(VWavAudioCodec::FindChunk);
105 	Strm.Seek(12);
106 	int EndPos = Strm.TotalSize();
107 	while (Strm.Tell() + 8 <= EndPos)
108 	{
109 		FRiffChunkHeader ChunkHdr;
110 		Strm.Serialise(&ChunkHdr, 8);
111 		int ChunkSize = LittleLong(ChunkHdr.Size);
112 		if (!memcmp(ChunkHdr.ID, ID, 4))
113 		{
114 			//	Found chunk.
115 			return ChunkSize;
116 		}
117 		if (Strm.Tell() + ChunkSize > EndPos)
118 		{
119 			//	Chunk goes beyound end of file.
120 			break;
121 		}
122 		Strm.Seek(Strm.Tell() + ChunkSize);
123 	}
124 	return -1;
125 	unguard;
126 }
127 
128 //==========================================================================
129 //
130 //	VWaveSampleLoader::Load
131 //
132 //==========================================================================
133 
Load(sfxinfo_t & Sfx,VStream & Strm)134 void VWaveSampleLoader::Load(sfxinfo_t& Sfx, VStream& Strm)
135 {
136 	guard(VWaveSampleLoader::Load);
137 	//	Check header to see if it's a wave file.
138 	char Header[12];
139 	Strm.Seek(0);
140 	Strm.Serialise(Header, 12);
141 	if (memcmp(Header, "RIFF", 4) || memcmp(Header + 8, "WAVE", 4))
142 	{
143 		//	Not a WAVE.
144 		return;
145 	}
146 
147 	//	Get format settings.
148 	int FmtSize = FindRiffChunk(Strm, "fmt ");
149 	if (FmtSize < 16)
150 	{
151 		//	Format not found or too small.
152 		return;
153 	}
154 	FWavFormatDesc Fmt;
155 	Strm.Serialise(&Fmt, 16);
156 	if (LittleShort(Fmt.Format) != 1)
157 	{
158 		//	Not a PCM format.
159 		return;
160 	}
161 	int SampleRate = LittleLong(Fmt.Rate);
162 	int WavChannels = LittleShort(Fmt.Channels);
163 	int WavBits = LittleShort(Fmt.Bits);
164 	int BlockAlign = LittleShort(Fmt.BlockAlign);
165 	if (WavChannels != 1)
166 	{
167 		GCon->Logf("A stereo sample, taking left channel");
168 	}
169 
170 	//	Find data chunk.
171 	int DataSize = FindRiffChunk(Strm, "data");
172 	if (DataSize == -1)
173 	{
174 		//	Data not found
175 		return;
176 	}
177 
178 	//	Fill in sample info and allocate data.
179 	Sfx.SampleRate = SampleRate;
180 	Sfx.SampleBits = WavBits;
181 	Sfx.DataSize = (DataSize / BlockAlign) * (WavBits/ 8);
182 	Sfx.Data = Z_Malloc(Sfx.DataSize);
183 
184 	//	Read wav data.
185 	void* WavData = Z_Malloc(DataSize);
186 	Strm.Serialise(WavData, DataSize);
187 
188 	//	Copy sample data.
189 	DataSize /= BlockAlign;
190 	if (WavBits == 8)
191 	{
192 		byte* pSrc = (byte*)WavData;
193 		byte* pDst = (byte*)Sfx.Data;
194 		for (int i = 0; i < DataSize; i++, pSrc += BlockAlign, pDst++)
195 		{
196 			*pDst = *pSrc;
197 		}
198 	}
199 	else
200 	{
201 		byte* pSrc = (byte*)WavData;
202 		short* pDst = (short*)Sfx.Data;
203 		for (int i = 0; i < DataSize; i++, pSrc += BlockAlign, pDst++)
204 		{
205 			*pDst = LittleShort(*(short*)pSrc);
206 		}
207 	}
208 	Z_Free(WavData);
209 
210 	unguard;
211 }
212 
213 //==========================================================================
214 //
215 //	VWavAudioCodec::VWavAudioCodec
216 //
217 //==========================================================================
218 
VWavAudioCodec(VStream * InStrm)219 VWavAudioCodec::VWavAudioCodec(VStream* InStrm)
220 : Strm(InStrm)
221 , SamplesLeft(-1)
222 {
223 	guard(VWavAudioCodec::VWavAudioCodec);
224 	int FmtSize = FindRiffChunk(*Strm, "fmt ");
225 	if (FmtSize < 16)
226 	{
227 		//	Format not found or too small.
228 		return;
229 	}
230 	FWavFormatDesc Fmt;
231 	Strm->Serialise(&Fmt, 16);
232 	if (LittleShort(Fmt.Format) != 1)
233 	{
234 		//	Not a PCM format.
235 		return;
236 	}
237 	SampleRate = LittleLong(Fmt.Rate);
238 	WavChannels = LittleShort(Fmt.Channels);
239 	WavBits = LittleShort(Fmt.Bits);
240 	BlockAlign = LittleShort(Fmt.BlockAlign);
241 
242 	SamplesLeft = FindRiffChunk(*Strm, "data");
243 	if (SamplesLeft == -1)
244 	{
245 		//	Data not found
246 		return;
247 	}
248 	SamplesLeft /= BlockAlign;
249 	unguard;
250 }
251 
252 //==========================================================================
253 //
254 //	VWavAudioCodec::~VWavAudioCodec
255 //
256 //==========================================================================
257 
~VWavAudioCodec()258 VWavAudioCodec::~VWavAudioCodec()
259 {
260 	guard(VWavAudioCodec::~VWavAudioCodec);
261 	if (SamplesLeft != -1)
262 	{
263 		Strm->Close();
264 		delete Strm;
265 		Strm = NULL;
266 	}
267 	unguard;
268 }
269 
270 //==========================================================================
271 //
272 //	VWavAudioCodec::Decode
273 //
274 //==========================================================================
275 
Decode(short * Data,int NumSamples)276 int VWavAudioCodec::Decode(short* Data, int NumSamples)
277 {
278 	guard(VWavAudioCodec::Decode);
279 	int CurSample = 0;
280 	byte Buf[1024];
281 	while (SamplesLeft && CurSample < NumSamples)
282 	{
283 		int ReadSamples = 1024 / BlockAlign;
284 		if (ReadSamples > NumSamples - CurSample)
285 			ReadSamples = NumSamples - CurSample;
286 		if (ReadSamples > SamplesLeft)
287 			ReadSamples = SamplesLeft;
288 		Strm->Serialise(Buf, ReadSamples * BlockAlign);
289 		for (int i = 0; i < 2; i++)
290 		{
291 			byte* pSrc = Buf;
292 			if (i && WavChannels > 1)
293 				pSrc += WavBits / 8;
294 			short* pDst = Data + CurSample * 2 + i;
295 			if (WavBits == 8)
296 			{
297 				for (int j = 0; j < ReadSamples; j++, pSrc += BlockAlign, pDst += 2)
298 				{
299 					*pDst = (*pSrc - 127) << 8;
300 				}
301 			}
302 			else
303 			{
304 				for (int j = 0; j < ReadSamples; j++, pSrc += BlockAlign, pDst += 2)
305 				{
306 					*pDst = LittleShort(*(short*)pSrc);
307 				}
308 			}
309 		}
310 		SamplesLeft -= ReadSamples;
311 		CurSample += ReadSamples;
312 	}
313 	return CurSample;
314 	unguard;
315 }
316 
317 //==========================================================================
318 //
319 //	VWavAudioCodec::Finished
320 //
321 //==========================================================================
322 
Finished()323 bool VWavAudioCodec::Finished()
324 {
325 	return !SamplesLeft;
326 }
327 
328 //==========================================================================
329 //
330 //	VWavAudioCodec::Restart
331 //
332 //==========================================================================
333 
Restart()334 void VWavAudioCodec::Restart()
335 {
336 	guard(VWavAudioCodec::Restart);
337 	SamplesLeft = FindRiffChunk(*Strm, "data") / BlockAlign;
338 	unguard;
339 }
340 
341 //==========================================================================
342 //
343 //	VWavAudioCodec::Create
344 //
345 //==========================================================================
346 
Create(VStream * InStrm)347 VAudioCodec* VWavAudioCodec::Create(VStream* InStrm)
348 {
349 	guard(VWavAudioCodec::Create);
350 	char Header[12];
351 	InStrm->Seek(0);
352 	InStrm->Serialise(Header, 12);
353 	if (!memcmp(Header, "RIFF", 4) && !memcmp(Header + 8, "WAVE", 4))
354 	{
355 		//	It's a WAVE file.
356 		VWavAudioCodec* Codec = new VWavAudioCodec(InStrm);
357 		if (Codec->SamplesLeft != -1)
358 		{
359 			return Codec;
360 		}
361 		//	File seams to be broken.
362 		delete Codec;
363 		Codec = NULL;
364 	}
365 	return NULL;
366 	unguard;
367 }
368