]> icculus.org git repositories - taylor/freespace2.git/blob - src/sound/acm.cpp
patch from mailing list (courtesy of fatty) to allow compling with both OpenAL 1...
[taylor/freespace2.git] / src / sound / acm.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Sound/acm.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C file for interface to Audio Compression Manager functions
16  *
17  * $Log$
18  * Revision 1.3  2002/06/09 04:41:26  relnev
19  * added copyright header
20  *
21  * Revision 1.2  2002/05/07 03:16:52  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:10  root
25  * Initial import.
26  *
27  * 
28  * 2     10/07/98 10:53a Dave
29  * Initial checkin.
30  * 
31  * 1     10/07/98 10:51a Dave
32  * 
33  * 8     6/13/98 1:45p Sandeep
34  * 
35  * 7     2/18/98 5:49p Lawrance
36  * Even if the ADPCM codec is unavailable, allow game to continue.
37  * 
38  * 6     11/28/97 2:09p Lawrance
39  * Overhaul how ADPCM conversion works... use much less memory... safer
40  * too.
41  * 
42  * 5     11/22/97 11:32p Lawrance
43  * decompress ADPCM data into 8 bit (not 16bit) for regular sounds (ie not
44  * music)
45  * 
46  * 4     9/09/97 3:39p Sandeep
47  * warning level 4 bugs
48  * 
49  * 3     8/05/97 1:39p Lawrance
50  * support compressed stereo playback
51  * 
52  * 2     5/29/97 12:03p Lawrance
53  * creation of file to hold AudioCompressionManager specific code
54  *
55  * $NoKeywords: $
56  *
57  */
58
59 #include "pstypes.h"
60 #include <windows.h>
61 #include <mmreg.h>
62 #include "acm.h"
63
64
65 // variables global to file for Audio Compression Manager (ACM) conversion
66 static HACMDRIVERID     ghadid = NULL;
67 static HINSTANCE                ghinstAcm;
68 static HACMDRIVER               ghacmdriver;
69 static int                              ACM_inited = 0;
70
71 //--------------------------------------------------------------------------;
72 //  
73 //  int ACM_enum_callback()
74 //  
75 //  This function is called by acmDriverEnum() to go through the installed
76 //  audio codecs.  This function will locate the Microsoft ADPCM codec and
77 //  set the global value ghadid (type HACMDRIVERID), which we need when 
78 //  converting audio from ADPCM to PCM format.
79 //
80 //  Arguments:
81 //      HACMDRIVERID hadid:
82 //      DWORD dwInstance:
83 //      DWORD fdwSupport:
84 //  
85 //--------------------------------------------------------------------------;
86
87 int CALLBACK ACM_enum_callback(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport)
88 {
89     static TCHAR    szBogus[]       = TEXT("????");
90
91     MMRESULT            mmr;
92     HWND                hlb;
93     ACMDRIVERDETAILS    add;
94     BOOL                fDisabled;
95     DWORD               dwPriority;
96
97     hlb = (HWND)(UINT)dwInstance;
98
99     add.cbStruct = sizeof(add);
100     mmr = acmDriverDetails(hadid, &add, 0L);
101     if (MMSYSERR_NOERROR != mmr)
102     {
103         lstrcpy(add.szShortName, szBogus);
104         lstrcpy(add.szLongName,  szBogus);
105     }
106
107     dwPriority = (DWORD)-1L;
108     acmMetrics((HACMOBJ)hadid, ACM_METRIC_DRIVER_PRIORITY, &dwPriority);
109
110     fDisabled = (0 != (ACMDRIVERDETAILS_SUPPORTF_DISABLED & fdwSupport));
111
112         
113          if ( stricmp(NOX("MS-ADPCM"),add.szShortName) == 0 ) {
114                 if ( fDisabled != 0 ) {
115                         nprintf(("Sound", "SOUND => The Microsoft ADPCM driver is disabled, unable to convert ADPCM to PCM\n"));
116                         ghadid = NULL;
117                 }
118                 else {
119                         ghadid = hadid;
120                 }
121                 return (FALSE); // stop enumerating devices, we've found what we need
122          }
123     //
124     //  return TRUE to continue with enumeration (FALSE will stop the
125     //  enumerator)
126     //
127     return (TRUE);
128
129
130 int ACM_stream_open(WAVEFORMATEX *pwfxSrc, WAVEFORMATEX *pwfxDest, void **stream, int dest_bps) 
131 {
132         Assert( pwfxSrc != NULL );
133         Assert( pwfxDest != NULL );
134         Assert( stream != NULL);
135
136         int rc;
137
138         if ( ACM_inited == 0 ) {
139                 rc = ACM_init();
140                 if ( rc != 0 )
141                         return -1;
142         }
143                 
144         pwfxDest->wFormatTag                    = WAVE_FORMAT_PCM;
145         pwfxDest->nChannels                     = pwfxSrc->nChannels;
146         pwfxDest->nSamplesPerSec        = pwfxSrc->nSamplesPerSec;
147         pwfxDest->wBitsPerSample        = (unsigned short)dest_bps;
148         pwfxDest->cbSize                                = 0;
149         pwfxDest->nBlockAlign           = (unsigned short)(( pwfxDest->nChannels * pwfxDest->wBitsPerSample ) / 8);
150         pwfxDest->nAvgBytesPerSec       = pwfxDest->nBlockAlign * pwfxDest->nSamplesPerSec;
151
152         rc = acmStreamOpen((HACMSTREAM*)stream, ghacmdriver, pwfxSrc, pwfxDest, NULL, 0L, 0L, ACM_STREAMOPENF_NONREALTIME);
153         if ( rc != 0 ) return -1;
154
155         return 0;
156 }
157
158 int ACM_stream_close(void *stream)
159 {
160         int rc;
161
162         rc = acmStreamClose((HACMSTREAM)stream, 0);
163         if ( rc != 0 )
164                 return -1;
165
166         return 0;
167 }
168
169
170 int ACM_convert(void *stream, ubyte *src, int src_len, ubyte *dest, int max_dest_bytes, unsigned int *dest_len, unsigned int *src_bytes_used)
171 {
172         int rc;
173         ACMSTREAMHEADER hCvtHdr;
174
175         if ( ACM_inited == 0 ) {
176                 rc = ACM_init();
177                 if ( rc != 0 )
178                         return -1;
179         }
180
181         memset(&hCvtHdr, 0, sizeof(hCvtHdr));
182
183         hCvtHdr.cbStruct                = sizeof(hCvtHdr);
184         hCvtHdr.pbSrc                   = (unsigned char *)src;
185         hCvtHdr.cbSrcLength     = src_len;
186         hCvtHdr.pbDst                   = (unsigned char*)dest;
187         hCvtHdr.cbDstLength     = max_dest_bytes;
188         
189         rc = acmStreamPrepareHeader((HACMSTREAM)stream, &hCvtHdr, 0);
190         if ( rc != 0 ) return -1;
191
192         rc = acmStreamConvert((HACMSTREAM)stream, &hCvtHdr, 0);
193         if ( rc != 0 ) return -1;
194
195         // Important step, since we need the exact length of the converted data.
196         *dest_len = hCvtHdr.cbDstLengthUsed;
197         *src_bytes_used = hCvtHdr.cbSrcLengthUsed;
198
199         rc = acmStreamUnprepareHeader((HACMSTREAM)stream, &hCvtHdr, 0);
200         if ( rc != 0 )
201                 return -1;
202
203         return rc;
204 }
205
206 int ACM_query_source_size(void *stream, int dest_len)
207 {
208         int     rc;
209         unsigned long   src_size;
210
211         rc = acmStreamSize((HACMSTREAM)stream, dest_len, &src_size, ACM_STREAMSIZEF_DESTINATION);
212         return (int)src_size;
213 }
214
215 int ACM_query_dest_size(void *stream, int src_len)
216 {
217         int     rc;
218         unsigned long   dest_size;
219
220         rc = acmStreamSize((HACMSTREAM)stream, src_len, &dest_size, ACM_STREAMSIZEF_SOURCE);
221         return (int)dest_size;
222 }
223
224 // =============================================================================
225 // ACM_convert_ADPCM_to_PCM()
226 //
227 // Convert an ADPCM wave file to a PCM wave file using the Audio Compression Manager
228 //
229 //      parameters:    *pwfxSrc   => address of WAVEFORMATEX structure describing the source wave
230 //                *src       => pointer to raw source wave data
231 //                src_len    => num bytes of source wave data
232 //                **dest     => pointer to pointer to dest buffer for wave data
233 //                              (mem is allocated in this function if *dest is NULL)
234 //                                              max_dest_bytes          => Maximum memory allocated to dest
235 //                *dest_len                     => returns num bytes of wave data in converted form (OUTPUT PARAMETER)
236 //                                              *src_bytes_used =>      returns num bytes of src actually used in the conversion
237 //                                              dest_bps                                => bits per sample that data should be uncompressed to
238 //
239 // returns:       0 => success
240 //               -1 => could not convert wav file
241 //
242 //
243 // NOTES:
244 // 1. Storage for the decompressed audio will be allocated in this function if *dest in NULL.
245 //    The caller is responsible for freeing this memory later.
246 //
247 int ACM_convert_ADPCM_to_PCM(WAVEFORMATEX *pwfxSrc, ubyte *src, int src_len, ubyte **dest, int max_dest_bytes, int *dest_len, unsigned int *src_bytes_used, unsigned short dest_bps)
248 {
249         Assert( pwfxSrc != NULL );
250         Assert( pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM );
251         Assert( src != NULL );
252         Assert( src_len > 0 );
253         Assert( dest_len != NULL );
254
255         WAVEFORMATEX wfxDest;
256         HACMSTREAM hStream;
257         ACMSTREAMHEADER hCvtHdr;
258         int rc;
259
260         if ( ACM_inited == 0 ) {
261                 rc = ACM_init();
262                 if ( rc != 0 )
263                         return -1;
264         }
265                 
266         wfxDest.wFormatTag = WAVE_FORMAT_PCM;
267         wfxDest.nChannels = pwfxSrc->nChannels;
268         wfxDest.nSamplesPerSec = pwfxSrc->nSamplesPerSec;
269         wfxDest.wBitsPerSample = dest_bps;
270         wfxDest.cbSize = 0;
271         wfxDest.nBlockAlign = (unsigned short)(( wfxDest.nChannels * wfxDest.wBitsPerSample ) / 8);
272         wfxDest.nAvgBytesPerSec = wfxDest.nBlockAlign * wfxDest.nSamplesPerSec;
273
274         rc = acmStreamOpen(&hStream, ghacmdriver, pwfxSrc, &wfxDest, NULL, 0L, 0L, ACM_STREAMOPENF_NONREALTIME);
275         if ( rc != 0 ) return -1;
276
277         rc = acmStreamSize(hStream, src_len, (unsigned long *)dest_len, ACM_STREAMSIZEF_SOURCE);
278         if ( rc != 0 ) return -1;
279
280         if ( *dest == NULL ) {
281                 *dest = (ubyte*)malloc(*dest_len);
282                 Assert( *dest != NULL );
283         }
284
285         if ( !max_dest_bytes ) {
286                 max_dest_bytes = *dest_len;
287         }
288
289         memset(&hCvtHdr, 0, sizeof(hCvtHdr));
290
291         hCvtHdr.cbStruct                = sizeof(hCvtHdr);
292         hCvtHdr.pbSrc                   = (unsigned char *)src;
293         hCvtHdr.cbSrcLength     = src_len;
294         hCvtHdr.pbDst                   = (unsigned char *)*dest;
295         hCvtHdr.cbDstLength     = max_dest_bytes;
296         
297         rc = acmStreamPrepareHeader(hStream, &hCvtHdr, 0);
298         if ( rc != 0 ) return -1;
299
300         rc = acmStreamConvert(hStream, &hCvtHdr, 0);
301         if ( rc != 0 ) return -1;
302
303         // Important step, since we need the exact length of the converted data.
304         *dest_len = hCvtHdr.cbDstLengthUsed;
305         *src_bytes_used = hCvtHdr.cbSrcLengthUsed;
306
307         rc = acmStreamUnprepareHeader(hStream, &hCvtHdr, 0);
308         if ( rc != 0 ) return -1;
309
310         rc = acmStreamClose(hStream, 0);
311         if ( rc != 0 ) return -1;
312
313         return 0;
314 }
315
316 // =============================================================================
317 // ACM_init()
318 //
319 // Initializes the Audio Compression Manager components used to convert ADPCM to PCM
320 //
321 // returns:       0 => success
322 //               -1 => ACM could not be initialized
323 //
324 int ACM_init()
325 {
326         int rc;
327
328         if ( ACM_inited == 1 )
329                 return 0;
330
331         ghinstAcm = LoadLibrary(NOX("msacm32.dll"));
332         if (ghinstAcm == NULL) {
333                 return -1;
334         }
335         FreeLibrary(ghinstAcm);
336
337         long dwVersion;
338         dwVersion = acmGetVersion();
339         nprintf(("Sound", "ACM Version number: %u.%.02u\n", HIWORD(dwVersion) >> 8, HIWORD(dwVersion) & 0x00FF)); 
340         // ACM must be version 3.5 or higher
341         if ( dwVersion < 0x03320000 )  {
342                 return -1;
343         }
344
345         rc = acmDriverEnum(ACM_enum_callback,   0L, ACM_DRIVERENUMF_DISABLED);
346         if ( rc != MMSYSERR_NOERROR ) return -1;
347
348         if ( ghadid == NULL ) {
349                 nprintf(("Sound", "SOUND => Unable to locate the Microsoft ADPCM driver\n"));
350                 return -1;
351         }
352
353         rc = acmDriverOpen(&ghacmdriver, ghadid, 0L);
354         if ( rc != MMSYSERR_NOERROR ) return -1;
355
356         rc = acmDriverPriority(ghadid,0,0);
357         if ( rc != MMSYSERR_NOERROR ) return -1;
358
359         ACM_inited = 1;
360         return 0;
361 }
362
363 // Closes down the Audio Compression Manager components
364 void ACM_close()
365 {
366         if ( ACM_inited == 0 )
367                 return;
368
369         acmDriverClose( ghacmdriver, 0L);
370         ACM_inited = 0;
371 }
372
373 // Query if the ACM system is initialized
374 int ACM_is_inited()
375 {
376         return ACM_inited;
377 }