convert line ends
[canaan.git] / prj / tech / libsrc / sound / sndfmt.cpp
blob9fd4495378683f09c6e7423b3e08d50a718d13ea
1 ////////////////////////////////////////////////////////////////////////
2 // $Source: x:/prj/tech/libsrc/sound/RCS/sndfmt.cpp $
3 // $Author: PATMAC $
4 // $Date: 1998/03/20 12:55:54 $
5 // $Revision: 1.12 $
6 //
7 // (c) 1996 Looking Glass Technologies Inc.
8 // Pat McElhatton (from JohnB)
9 //
10 // Module name: sound formats
11 // File name: sndfmt.cpp
13 // Description: Implementation of sound file/resource format handling
15 ////////////////////////////////////////////////////////////////////////
17 #include <windows.h>
18 #include <lg.h>
19 #include <mmsystem.h>
20 #include <mmreg.h>
22 #include <dsound.h>
23 #include <lgsndi.h>
24 #include <sndfmt.h>
26 #include <sndvoc.h>
27 #include <mprintf.h>
29 static int
30 openWaveHeader(
31 HMMIO hmmio,
32 MMIOINFO *pMmio,
33 void **ppPCMData,
34 uint32 *pPCMLen,
35 uint32 *pNSamples,
36 sSndAttribs *pAttribs
39 MMCKINFO riff;
40 MMCKINFO chunk;
41 WAVEFORMATEX waveFmt;
42 int nBytes, bytesPerSamp;
43 WORD sampsPerBlock;
44 uint32 maxSamples, nBlocks, partialBlockLen;
46 // get the next riff chunk
47 if(mmioDescend(hmmio,&riff, NULL, 0))
48 return TRUE;
50 // make sure its a wave file
51 if((riff.ckid != FOURCC_RIFF) || (riff.fccType != mmioFOURCC('W','A','V','E')))
52 return TRUE;
54 /*----------------------------
55 * process format chunk
56 *---------------------------*/
57 // get the format chunk
58 chunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
59 if (mmioDescend(hmmio, &chunk, &riff, MMIO_FINDCHUNK))
60 return TRUE;
62 // make sure the chunk is the right size, otherwise we dont know what
63 // kind of wave file it is
64 if(chunk.cksize < (long)sizeof(PCMWAVEFORMAT))
65 return TRUE;
67 // now grab the format
68 nBytes = mmioRead(hmmio, (HPSTR) &waveFmt, (long) sizeof(waveFmt));
69 if ( waveFmt.wFormatTag == WAVE_FORMAT_DVI_ADPCM ) {
70 if ( nBytes != sizeof(waveFmt) )
71 return TRUE;
72 if ( waveFmt.cbSize < sizeof(sampsPerBlock) )
73 return TRUE;
74 nBytes = mmioRead(hmmio, (HPSTR) &sampsPerBlock, (long) sizeof(sampsPerBlock));
75 pAttribs->samplesPerBlock = sampsPerBlock;
78 // get back out of the format chunk
79 if(mmioAscend(hmmio, &chunk, 0))
80 return TRUE;
82 /*----------------------------------------
83 * process fact chunk, or calc #samples
84 *---------------------------------------*/
85 // for ADPCM, get the fact chunk, which holds the number of samples
86 if ( waveFmt.wFormatTag == WAVE_FORMAT_DVI_ADPCM ) {
87 // get the format chunk
88 chunk.ckid = mmioFOURCC('f', 'a', 'c', 't');
89 // go back to beginning of riff file to handle out of order chunks
90 mmioSeek(hmmio, riff.dwDataOffset + 4, SEEK_SET);
91 if (mmioDescend(hmmio, &chunk, &riff, MMIO_FINDCHUNK))
92 return TRUE;
93 if(chunk.cksize < sizeof(uint32) )
94 return TRUE;
95 nBytes = mmioRead(hmmio, (HPSTR) pNSamples, sizeof(long) );
96 if(mmioAscend(hmmio, &chunk, 0))
97 return TRUE;
98 pAttribs->dataType = kSndDataIMAADPCM;
101 /*----------------------------
102 * find data chunk
103 *---------------------------*/
104 // Find the data subchunk. The current file position should be at
105 // the beginning of the data chunk; however, you should not make
106 // this assumption. Use mmioDescend to locate the data chunk.
107 // go back to beginning of riff file to handle out of order chunks
108 mmioSeek(hmmio, riff.dwDataOffset + 4, SEEK_SET);
109 chunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
110 if (mmioDescend(hmmio, &chunk, &riff, MMIO_FINDCHUNK))
111 return TRUE;
113 if (chunk.cksize == 0L)
114 return TRUE;
116 // now we look to see where we are
117 if(mmioGetInfo(hmmio, pMmio, 0))
118 return TRUE;
120 *ppPCMData = (uint8 *)pMmio->pchNext;
121 *pPCMLen = chunk.cksize;
122 pAttribs->sampleRate = waveFmt.nSamplesPerSec;
123 pAttribs->bitsPerSample = waveFmt.wBitsPerSample;
124 pAttribs->nChannels = waveFmt.nChannels;
125 pAttribs->bytesPerBlock = waveFmt.nBlockAlign;
127 switch( waveFmt.wFormatTag ) {
129 case WAVE_FORMAT_PCM:
130 bytesPerSamp = ((pAttribs->nChannels * pAttribs->bitsPerSample) / 8);
131 pAttribs->dataType = kSndDataPCM;
132 // for PCM data types, calculate the #sample from #bytes
133 *pNSamples = chunk.cksize / bytesPerSamp;
134 pAttribs->samplesPerBlock = pAttribs->bytesPerBlock / bytesPerSamp;
135 break;
137 case WAVE_FORMAT_DVI_ADPCM:
138 // the nSamples field in the fact chunk is often (usually!) wrong
139 // so if nSamples is larger than the max # of samples in the data
140 // chunk, limit nSamples to that
141 nBlocks = *pPCMLen / pAttribs->bytesPerBlock;
142 partialBlockLen = *pPCMLen % pAttribs->bytesPerBlock;
143 maxSamples = nBlocks * pAttribs->samplesPerBlock;
144 if ( partialBlockLen >= 4 ) {
145 // there is a partial block, account for any samples in it
146 // the block has a 4 byte header with 1 sample in it, and
147 // 2 samples in each byte after the header
148 maxSamples += ( 1 + (2 * (partialBlockLen - 4)) );
150 if ( *pNSamples > maxSamples ) {
151 *pNSamples = maxSamples;
153 break;
155 default:
156 // we don't handle other formats
157 return TRUE;
160 pAttribs->numSamples = *pNSamples;
161 return FALSE;
166 // extract useful info from RIFF WAVE file image header
167 // return TRUE if an error occurs
169 static BOOL
170 SndCrackWaveHeader(
171 void *pRezData,
172 uint32 rezLen,
173 void **ppData,
174 uint32 *pDataLen,
175 uint32 *pNumSamples,
176 sSndAttribs *pAttribs )
178 HMMIO hmmio;
179 MMIOINFO mmio;
180 BOOL bad;
182 // set up to read from a memory file. This is becuase
183 // we have the whole file in memory at this point...
184 memset(&mmio, 0, sizeof(MMIOINFO));
186 mmio.pIOProc = NULL;
187 mmio.fccIOProc = FOURCC_MEM;
188 mmio.pchBuffer = (char *) pRezData;
189 mmio.cchBuffer = rezLen;
191 hmmio = mmioOpen(NULL, &mmio, MMIO_READWRITE);
192 if(hmmio==NULL)
193 return TRUE;
195 // and point to all that good sound data
196 bad = openWaveHeader( hmmio, &mmio, ppData, pDataLen,
197 pNumSamples, pAttribs );
198 mmioClose(hmmio, 0);
200 return bad;
204 // extract useful info from VOC file image header
205 // return TRUE if an error occurs
207 static BOOL
208 SndCrackVocHeader(
209 void *pRezData,
210 uint32 rezLen,
211 void **ppData,
212 uint32 *pDataLen,
213 uint32 *pNumSamples,
214 sSndAttribs *pAttribs )
216 uint8 blockType;
217 uint8 *pVoc = (uint8 *) pRezData;
218 uint8 *pEndRez = pVoc + rezLen;
219 BOOL voiceBlockFound = FALSE;
220 uint32 sampleRate;
221 int16 bits;
222 int16 channels;
224 sVocHeader *hdr = (sVocHeader *) pVoc;
226 sVocDataBlock *dataBlock;
227 // voc_continue_block *contBlock;
228 sVocStereoBlock *stereoBlock;
229 sVocExtendedBlock *extendedBlock;
231 pVoc += hdr->offData; // fast forward to data
233 while ( !voiceBlockFound ) {
234 if ( pVoc >= pEndRez ) {
235 // got to the end of VOC without finding a data block
236 break;
239 blockType = *pVoc; // just read the id off the top.
241 switch(blockType)
243 case VOC_BLOCK_TERM:
244 return TRUE;
245 case VOC_BLOCK_DATA:
246 dataBlock = (sVocDataBlock *)pVoc;
248 channels = 1;
249 bits = 8;
250 sampleRate = 1000000L / (256 - dataBlock->timeConstant);
252 *ppData = pVoc + sizeof(sVocDataBlock);
253 *pDataLen = VOC_BLOCK_LEN(pVoc) - 2;
254 voiceBlockFound = TRUE;
255 break;
256 case VOC_BLOCK_STEREO:
257 stereoBlock = (sVocStereoBlock *)pVoc;
258 if(stereoBlock->voiceMode)
260 bits = 8;
261 channels = 2;
262 sampleRate =
263 128000000L / (65536L - stereoBlock->timeConstant);
265 else
267 bits = 8;
268 channels = 1;
269 sampleRate =
270 256000000L / (65536L - stereoBlock->timeConstant);
272 pVoc += VOC_BLOCK_LEN(pVoc) + 4; // go to the end of the block
274 *ppData = pVoc + sizeof(sVocDataBlock);
275 *pDataLen = VOC_BLOCK_LEN(pVoc) - 2;
276 voiceBlockFound = TRUE;
277 break;
279 case VOC_BLOCK_EXTENDED:
280 extendedBlock = (sVocExtendedBlock *)pVoc;
281 *ppData = pVoc + sizeof(sVocExtendedBlock);
282 *pDataLen = VOC_BLOCK_LEN(pVoc) - 12;
283 sampleRate = extendedBlock->sampleRate;
284 channels = extendedBlock->channels;
286 if(extendedBlock->format == 0)
287 bits = 8;
288 else if(extendedBlock->format == 4)
289 bits = 16;
290 voiceBlockFound = TRUE;
291 break;
292 default:
293 pVoc += VOC_BLOCK_LEN(pVoc) + 4;
294 break;
296 } // end while !voiceBlockFound
298 if ( voiceBlockFound == TRUE ) {
299 // fill in sound attributes & samples-in-resource
300 pAttribs->nChannels = channels;
301 pAttribs->bitsPerSample = bits;
302 pAttribs->sampleRate = sampleRate;
303 pAttribs->bytesPerBlock = channels * (bits / 8);
304 pAttribs->dataType = kSndDataPCM;
305 pAttribs->samplesPerBlock = 1;
306 *pNumSamples = *pDataLen / pAttribs->bytesPerBlock;
308 return !voiceBlockFound;
312 // get header info from sound resource (mem-resident sound file image)
313 // return TRUE if failure occurs
315 BOOL
316 SndCrackRezHeader(
317 void *pRezData,
318 uint32 rezLen,
319 void **ppData,
320 uint32 *pDataLen,
321 uint32 *pNumSamples,
322 sSndAttribs *pAttribs )
324 char *buf = (char *) pRezData;
325 BOOL bad;
327 // determine the file image type (WAVE or VOC)
328 // extract sound attribs from rez header
329 // find start of audio data
330 if ( (strncmp( buf, "RIFF", 4) == 0 )
331 && (strncmp( buf + 8, "WAVE", 4) == 0) ) {
332 // sound resource is a wave file image
333 bad = SndCrackWaveHeader( pRezData, rezLen, ppData, pDataLen,
334 pNumSamples, pAttribs );
335 if ( bad ) mprintf( "SndCrackWaveHeader returned error\n");
336 TLOG3("SndCrackWaveHeader %ld bytes, %ld samples %d badFlag",
337 rezLen, *pNumSamples, bad );
338 } else if ( strncmp( buf, "Creative Voice File", 19 ) == 0 ) {
339 // sound resource is a voc file image
340 //TBD!
341 bad = SndCrackVocHeader( pRezData, rezLen, ppData, pDataLen,
342 pNumSamples, pAttribs );
343 if ( bad ) mprintf( "SndCrackVocHeader returned error\n");
344 TLOG3("SndCrackVocHeader %ld bytes, %ld samples %d badFlag",
345 rezLen, *pNumSamples, bad );
346 } else {
347 // Error - Rez is not a recognized sound file image type
348 mprintf("Unrecognizable sound file type\n");
349 bad = TRUE;
352 return bad;