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