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