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