2 * $Logfile: /Freespace2/code/Sound/acm.cpp $
7 * C file for interface to Audio Compression Manager functions
10 * Revision 1.2 2002/05/07 03:16:52 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:10 root
17 * 2 10/07/98 10:53a Dave
20 * 1 10/07/98 10:51a Dave
22 * 8 6/13/98 1:45p Sandeep
24 * 7 2/18/98 5:49p Lawrance
25 * Even if the ADPCM codec is unavailable, allow game to continue.
27 * 6 11/28/97 2:09p Lawrance
28 * Overhaul how ADPCM conversion works... use much less memory... safer
31 * 5 11/22/97 11:32p Lawrance
32 * decompress ADPCM data into 8 bit (not 16bit) for regular sounds (ie not
35 * 4 9/09/97 3:39p Sandeep
36 * warning level 4 bugs
38 * 3 8/05/97 1:39p Lawrance
39 * support compressed stereo playback
41 * 2 5/29/97 12:03p Lawrance
42 * creation of file to hold AudioCompressionManager specific code
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;
60 //--------------------------------------------------------------------------;
62 // int ACM_enum_callback()
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.
70 // HACMDRIVERID hadid:
74 //--------------------------------------------------------------------------;
76 int CALLBACK ACM_enum_callback(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport)
78 static TCHAR szBogus[] = TEXT("????");
86 hlb = (HWND)(UINT)dwInstance;
88 add.cbStruct = sizeof(add);
89 mmr = acmDriverDetails(hadid, &add, 0L);
90 if (MMSYSERR_NOERROR != mmr)
92 lstrcpy(add.szShortName, szBogus);
93 lstrcpy(add.szLongName, szBogus);
96 dwPriority = (DWORD)-1L;
97 acmMetrics((HACMOBJ)hadid, ACM_METRIC_DRIVER_PRIORITY, &dwPriority);
99 fDisabled = (0 != (ACMDRIVERDETAILS_SUPPORTF_DISABLED & fdwSupport));
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"));
110 return (FALSE); // stop enumerating devices, we've found what we need
113 // return TRUE to continue with enumeration (FALSE will stop the
119 int ACM_stream_open(WAVEFORMATEX *pwfxSrc, WAVEFORMATEX *pwfxDest, void **stream, int dest_bps)
121 Assert( pwfxSrc != NULL );
122 Assert( pwfxDest != NULL );
123 Assert( stream != NULL);
127 if ( ACM_inited == 0 ) {
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;
141 rc = acmStreamOpen((HACMSTREAM*)stream, ghacmdriver, pwfxSrc, pwfxDest, NULL, 0L, 0L, ACM_STREAMOPENF_NONREALTIME);
142 if ( rc != 0 ) return -1;
147 int ACM_stream_close(void *stream)
151 rc = acmStreamClose((HACMSTREAM)stream, 0);
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)
162 ACMSTREAMHEADER hCvtHdr;
164 if ( ACM_inited == 0 ) {
170 memset(&hCvtHdr, 0, sizeof(hCvtHdr));
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;
178 rc = acmStreamPrepareHeader((HACMSTREAM)stream, &hCvtHdr, 0);
179 if ( rc != 0 ) return -1;
181 rc = acmStreamConvert((HACMSTREAM)stream, &hCvtHdr, 0);
182 if ( rc != 0 ) return -1;
184 // Important step, since we need the exact length of the converted data.
185 *dest_len = hCvtHdr.cbDstLengthUsed;
186 *src_bytes_used = hCvtHdr.cbSrcLengthUsed;
188 rc = acmStreamUnprepareHeader((HACMSTREAM)stream, &hCvtHdr, 0);
195 int ACM_query_source_size(void *stream, int dest_len)
198 unsigned long src_size;
200 rc = acmStreamSize((HACMSTREAM)stream, dest_len, &src_size, ACM_STREAMSIZEF_DESTINATION);
201 return (int)src_size;
204 int ACM_query_dest_size(void *stream, int src_len)
207 unsigned long dest_size;
209 rc = acmStreamSize((HACMSTREAM)stream, src_len, &dest_size, ACM_STREAMSIZEF_SOURCE);
210 return (int)dest_size;
213 // =============================================================================
214 // ACM_convert_ADPCM_to_PCM()
216 // Convert an ADPCM wave file to a PCM wave file using the Audio Compression Manager
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
228 // returns: 0 => success
229 // -1 => could not convert wav file
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.
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)
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 );
244 WAVEFORMATEX wfxDest;
246 ACMSTREAMHEADER hCvtHdr;
249 if ( ACM_inited == 0 ) {
255 wfxDest.wFormatTag = WAVE_FORMAT_PCM;
256 wfxDest.nChannels = pwfxSrc->nChannels;
257 wfxDest.nSamplesPerSec = pwfxSrc->nSamplesPerSec;
258 wfxDest.wBitsPerSample = dest_bps;
260 wfxDest.nBlockAlign = (unsigned short)(( wfxDest.nChannels * wfxDest.wBitsPerSample ) / 8);
261 wfxDest.nAvgBytesPerSec = wfxDest.nBlockAlign * wfxDest.nSamplesPerSec;
263 rc = acmStreamOpen(&hStream, ghacmdriver, pwfxSrc, &wfxDest, NULL, 0L, 0L, ACM_STREAMOPENF_NONREALTIME);
264 if ( rc != 0 ) return -1;
266 rc = acmStreamSize(hStream, src_len, (unsigned long *)dest_len, ACM_STREAMSIZEF_SOURCE);
267 if ( rc != 0 ) return -1;
269 if ( *dest == NULL ) {
270 *dest = (ubyte*)malloc(*dest_len);
271 Assert( *dest != NULL );
274 if ( !max_dest_bytes ) {
275 max_dest_bytes = *dest_len;
278 memset(&hCvtHdr, 0, sizeof(hCvtHdr));
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;
286 rc = acmStreamPrepareHeader(hStream, &hCvtHdr, 0);
287 if ( rc != 0 ) return -1;
289 rc = acmStreamConvert(hStream, &hCvtHdr, 0);
290 if ( rc != 0 ) return -1;
292 // Important step, since we need the exact length of the converted data.
293 *dest_len = hCvtHdr.cbDstLengthUsed;
294 *src_bytes_used = hCvtHdr.cbSrcLengthUsed;
296 rc = acmStreamUnprepareHeader(hStream, &hCvtHdr, 0);
297 if ( rc != 0 ) return -1;
299 rc = acmStreamClose(hStream, 0);
300 if ( rc != 0 ) return -1;
305 // =============================================================================
308 // Initializes the Audio Compression Manager components used to convert ADPCM to PCM
310 // returns: 0 => success
311 // -1 => ACM could not be initialized
317 if ( ACM_inited == 1 )
320 ghinstAcm = LoadLibrary(NOX("msacm32.dll"));
321 if (ghinstAcm == NULL) {
324 FreeLibrary(ghinstAcm);
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 ) {
334 rc = acmDriverEnum(ACM_enum_callback, 0L, ACM_DRIVERENUMF_DISABLED);
335 if ( rc != MMSYSERR_NOERROR ) return -1;
337 if ( ghadid == NULL ) {
338 nprintf(("Sound", "SOUND => Unable to locate the Microsoft ADPCM driver\n"));
342 rc = acmDriverOpen(&ghacmdriver, ghadid, 0L);
343 if ( rc != MMSYSERR_NOERROR ) return -1;
345 rc = acmDriverPriority(ghadid,0,0);
346 if ( rc != MMSYSERR_NOERROR ) return -1;
352 // Closes down the Audio Compression Manager components
355 if ( ACM_inited == 0 )
358 acmDriverClose( ghacmdriver, 0L);
362 // Query if the ACM system is initialized