2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Sound/acm.cpp $
15 * C file for interface to Audio Compression Manager functions
18 * Revision 1.3 2002/06/09 04:41:26 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:52 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:10 root
28 * 2 10/07/98 10:53a Dave
31 * 1 10/07/98 10:51a Dave
33 * 8 6/13/98 1:45p Sandeep
35 * 7 2/18/98 5:49p Lawrance
36 * Even if the ADPCM codec is unavailable, allow game to continue.
38 * 6 11/28/97 2:09p Lawrance
39 * Overhaul how ADPCM conversion works... use much less memory... safer
42 * 5 11/22/97 11:32p Lawrance
43 * decompress ADPCM data into 8 bit (not 16bit) for regular sounds (ie not
46 * 4 9/09/97 3:39p Sandeep
47 * warning level 4 bugs
49 * 3 8/05/97 1:39p Lawrance
50 * support compressed stereo playback
52 * 2 5/29/97 12:03p Lawrance
53 * creation of file to hold AudioCompressionManager specific code
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;
71 //--------------------------------------------------------------------------;
73 // int ACM_enum_callback()
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.
81 // HACMDRIVERID hadid:
85 //--------------------------------------------------------------------------;
87 int CALLBACK ACM_enum_callback(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport)
89 static TCHAR szBogus[] = TEXT("????");
97 hlb = (HWND)(UINT)dwInstance;
99 add.cbStruct = sizeof(add);
100 mmr = acmDriverDetails(hadid, &add, 0L);
101 if (MMSYSERR_NOERROR != mmr)
103 lstrcpy(add.szShortName, szBogus);
104 lstrcpy(add.szLongName, szBogus);
107 dwPriority = (DWORD)-1L;
108 acmMetrics((HACMOBJ)hadid, ACM_METRIC_DRIVER_PRIORITY, &dwPriority);
110 fDisabled = (0 != (ACMDRIVERDETAILS_SUPPORTF_DISABLED & fdwSupport));
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"));
121 return (FALSE); // stop enumerating devices, we've found what we need
124 // return TRUE to continue with enumeration (FALSE will stop the
130 int ACM_stream_open(WAVEFORMATEX *pwfxSrc, WAVEFORMATEX *pwfxDest, void **stream, int dest_bps)
132 Assert( pwfxSrc != NULL );
133 Assert( pwfxDest != NULL );
134 Assert( stream != NULL);
138 if ( ACM_inited == 0 ) {
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;
152 rc = acmStreamOpen((HACMSTREAM*)stream, ghacmdriver, pwfxSrc, pwfxDest, NULL, 0L, 0L, ACM_STREAMOPENF_NONREALTIME);
153 if ( rc != 0 ) return -1;
158 int ACM_stream_close(void *stream)
162 rc = acmStreamClose((HACMSTREAM)stream, 0);
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)
173 ACMSTREAMHEADER hCvtHdr;
175 if ( ACM_inited == 0 ) {
181 memset(&hCvtHdr, 0, sizeof(hCvtHdr));
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;
189 rc = acmStreamPrepareHeader((HACMSTREAM)stream, &hCvtHdr, 0);
190 if ( rc != 0 ) return -1;
192 rc = acmStreamConvert((HACMSTREAM)stream, &hCvtHdr, 0);
193 if ( rc != 0 ) return -1;
195 // Important step, since we need the exact length of the converted data.
196 *dest_len = hCvtHdr.cbDstLengthUsed;
197 *src_bytes_used = hCvtHdr.cbSrcLengthUsed;
199 rc = acmStreamUnprepareHeader((HACMSTREAM)stream, &hCvtHdr, 0);
206 int ACM_query_source_size(void *stream, int dest_len)
209 unsigned long src_size;
211 rc = acmStreamSize((HACMSTREAM)stream, dest_len, &src_size, ACM_STREAMSIZEF_DESTINATION);
212 return (int)src_size;
215 int ACM_query_dest_size(void *stream, int src_len)
218 unsigned long dest_size;
220 rc = acmStreamSize((HACMSTREAM)stream, src_len, &dest_size, ACM_STREAMSIZEF_SOURCE);
221 return (int)dest_size;
224 // =============================================================================
225 // ACM_convert_ADPCM_to_PCM()
227 // Convert an ADPCM wave file to a PCM wave file using the Audio Compression Manager
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
239 // returns: 0 => success
240 // -1 => could not convert wav file
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.
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)
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 );
255 WAVEFORMATEX wfxDest;
257 ACMSTREAMHEADER hCvtHdr;
260 if ( ACM_inited == 0 ) {
266 wfxDest.wFormatTag = WAVE_FORMAT_PCM;
267 wfxDest.nChannels = pwfxSrc->nChannels;
268 wfxDest.nSamplesPerSec = pwfxSrc->nSamplesPerSec;
269 wfxDest.wBitsPerSample = dest_bps;
271 wfxDest.nBlockAlign = (unsigned short)(( wfxDest.nChannels * wfxDest.wBitsPerSample ) / 8);
272 wfxDest.nAvgBytesPerSec = wfxDest.nBlockAlign * wfxDest.nSamplesPerSec;
274 rc = acmStreamOpen(&hStream, ghacmdriver, pwfxSrc, &wfxDest, NULL, 0L, 0L, ACM_STREAMOPENF_NONREALTIME);
275 if ( rc != 0 ) return -1;
277 rc = acmStreamSize(hStream, src_len, (unsigned long *)dest_len, ACM_STREAMSIZEF_SOURCE);
278 if ( rc != 0 ) return -1;
280 if ( *dest == NULL ) {
281 *dest = (ubyte*)malloc(*dest_len);
282 Assert( *dest != NULL );
285 if ( !max_dest_bytes ) {
286 max_dest_bytes = *dest_len;
289 memset(&hCvtHdr, 0, sizeof(hCvtHdr));
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;
297 rc = acmStreamPrepareHeader(hStream, &hCvtHdr, 0);
298 if ( rc != 0 ) return -1;
300 rc = acmStreamConvert(hStream, &hCvtHdr, 0);
301 if ( rc != 0 ) return -1;
303 // Important step, since we need the exact length of the converted data.
304 *dest_len = hCvtHdr.cbDstLengthUsed;
305 *src_bytes_used = hCvtHdr.cbSrcLengthUsed;
307 rc = acmStreamUnprepareHeader(hStream, &hCvtHdr, 0);
308 if ( rc != 0 ) return -1;
310 rc = acmStreamClose(hStream, 0);
311 if ( rc != 0 ) return -1;
316 // =============================================================================
319 // Initializes the Audio Compression Manager components used to convert ADPCM to PCM
321 // returns: 0 => success
322 // -1 => ACM could not be initialized
328 if ( ACM_inited == 1 )
331 ghinstAcm = LoadLibrary(NOX("msacm32.dll"));
332 if (ghinstAcm == NULL) {
335 FreeLibrary(ghinstAcm);
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 ) {
345 rc = acmDriverEnum(ACM_enum_callback, 0L, ACM_DRIVERENUMF_DISABLED);
346 if ( rc != MMSYSERR_NOERROR ) return -1;
348 if ( ghadid == NULL ) {
349 nprintf(("Sound", "SOUND => Unable to locate the Microsoft ADPCM driver\n"));
353 rc = acmDriverOpen(&ghacmdriver, ghadid, 0L);
354 if ( rc != MMSYSERR_NOERROR ) return -1;
356 rc = acmDriverPriority(ghadid,0,0);
357 if ( rc != MMSYSERR_NOERROR ) return -1;
363 // Closes down the Audio Compression Manager components
366 if ( ACM_inited == 0 )
369 acmDriverClose( ghacmdriver, 0L);
373 // Query if the ACM system is initialized