Updated.
[wine] / dlls / msacm / format.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  *      MSACM32 library
5  *
6  *      Copyright 1998  Patrik Stridvall
7  *                2000  Eric Pouech
8  */
9
10 #include <string.h>
11 #include "winbase.h"
12 #include "winnls.h"
13 #include "winerror.h"
14 #include "windef.h"
15 #include "wingdi.h"
16 #include "winuser.h"
17 #include "wine/unicode.h"
18 #include "debugtools.h"
19 #include "mmsystem.h"
20 #include "msacm.h"
21 #include "msacmdrv.h"
22 #include "wineacm.h"
23
24 DEFAULT_DEBUG_CHANNEL(msacm);
25
26 static  PACMFORMATCHOOSEA       afc;
27
28 struct MSACM_FillFormatData {
29     HWND                hWnd;
30 #define WINE_ACMFF_TAG          0
31 #define WINE_ACMFF_FORMAT       1
32 #define WINE_ACMFF_WFX          2
33     int                 mode;
34     char                szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
35     PACMFORMATCHOOSEA   afc;
36     DWORD               ret;
37 };
38
39 static BOOL CALLBACK MSACM_FillFormatTagsCB(HACMDRIVERID hadid, 
40                                             PACMFORMATTAGDETAILSA paftd,
41                                             DWORD dwInstance, DWORD fdwSupport)
42 {
43     struct MSACM_FillFormatData*        affd = (struct MSACM_FillFormatData*)dwInstance;
44
45     switch (affd->mode) {
46     case WINE_ACMFF_TAG:
47         if (SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 
48                                 CB_FINDSTRINGEXACT, 
49                                 (WPARAM)-1, (LPARAM)paftd->szFormatTag) == CB_ERR)
50             SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 
51                                 CB_ADDSTRING, 0, (DWORD)paftd->szFormatTag);
52         break;
53     case WINE_ACMFF_FORMAT:
54         if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) {
55             HACMDRIVER          had;
56
57             if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
58                 ACMFORMATDETAILSA       afd;
59                 int                     i, idx;
60                 MMRESULT                mmr;
61                 char                    buffer[64];
62
63                 afd.cbStruct = sizeof(afd);
64                 afd.dwFormatTag = paftd->dwFormatTag;
65                 afd.pwfx = HeapAlloc(GetProcessHeap(), 0, paftd->cbFormatSize);
66                 afd.pwfx->wFormatTag = paftd->dwFormatTag;
67                 afd.pwfx->cbSize = paftd->cbFormatSize;
68                 afd.cbwfx = paftd->cbFormatSize;
69
70                 for (i = 0; i < paftd->cStandardFormats; i++) {
71                     afd.dwFormatIndex = i;
72                     mmr = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX);
73                     if (mmr == MMSYSERR_NOERROR) {
74                         strcpy(buffer, afd.szFormat);
75                         for (idx = strlen(buffer); 
76                              idx < ACMFORMATTAGDETAILS_FORMATTAG_CHARS; idx++)
77                             buffer[idx] = ' ';
78                         wsprintfA(buffer + ACMFORMATTAGDETAILS_FORMATTAG_CHARS, 
79                                   "%d Ko/s", 
80                                   (afd.pwfx->nAvgBytesPerSec + 512) / 1024);
81                         SendDlgItemMessageA(affd->hWnd, 
82                                             IDD_ACMFORMATCHOOSE_CMB_FORMAT, 
83                                             CB_ADDSTRING, 0, (DWORD)buffer);
84                     }
85                 }
86                 acmDriverClose(had, 0);
87                 SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, 
88                                     CB_SETCURSEL, 0, 0);
89             } 
90         }
91         break;
92     case WINE_ACMFF_WFX:
93         if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) {
94             HACMDRIVER          had;
95
96             if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
97                 ACMFORMATDETAILSA       afd;
98
99                 afd.cbStruct = sizeof(afd);
100                 afd.dwFormatTag = paftd->dwFormatTag;
101                 afd.pwfx = affd->afc->pwfx;
102                 afd.cbwfx = affd->afc->cbwfx;
103
104                 afd.dwFormatIndex = SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_GETCURSEL, 0, 0);;
105                 affd->ret = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX);
106                 acmDriverClose(had, 0);
107                 return TRUE;
108             } 
109         }
110         break;
111     default:
112         FIXME("Unknown mode (%d)\n", affd->mode);
113         break;
114     }
115     return TRUE;
116 }
117
118 static BOOL MSACM_FillFormatTags(HWND hWnd)
119 {
120     ACMFORMATTAGDETAILSA        aftd;
121     struct MSACM_FillFormatData affd;
122
123     memset(&aftd, 0, sizeof(aftd));
124     aftd.cbStruct = sizeof(aftd);
125
126     affd.hWnd = hWnd;
127     affd.mode = WINE_ACMFF_TAG;
128
129     acmFormatTagEnumA((HACMDRIVER)0, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
130     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, CB_SETCURSEL, 0, 0);
131     return TRUE;
132 }
133
134 static BOOL MSACM_FillFormat(HWND hWnd)
135 {
136     ACMFORMATTAGDETAILSA        aftd;
137     struct MSACM_FillFormatData affd;
138
139     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_RESETCONTENT, 0, 0);
140
141     memset(&aftd, 0, sizeof(aftd));
142     aftd.cbStruct = sizeof(aftd);
143
144     affd.hWnd = hWnd;
145     affd.mode = WINE_ACMFF_FORMAT;
146     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 
147                         CB_GETLBTEXT,
148                         SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 
149                                             CB_GETCURSEL, 0, 0),
150                         (DWORD)affd.szFormatTag);
151     
152     acmFormatTagEnumA((HACMDRIVER)0, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
153     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_SETCURSEL, 0, 0);
154     return TRUE;
155 }
156
157 static MMRESULT MSACM_GetWFX(HWND hWnd, PACMFORMATCHOOSEA afc)
158 {
159     ACMFORMATTAGDETAILSA        aftd;
160     struct MSACM_FillFormatData affd;
161
162     memset(&aftd, 0, sizeof(aftd));
163     aftd.cbStruct = sizeof(aftd);
164
165     affd.hWnd = hWnd;
166     affd.mode = WINE_ACMFF_WFX;
167     affd.afc = afc;
168     affd.ret = MMSYSERR_NOERROR;
169
170     acmFormatTagEnumA((HACMDRIVER)0, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
171     return affd.ret;
172 }
173
174 static BOOL WINAPI FormatChooseDlgProc(HWND hWnd, UINT msg, 
175                                        WPARAM wParam, LPARAM lParam)
176 {
177     
178     TRACE("hwnd=%i msg=%i 0x%08x 0x%08lx\n", hWnd,  msg, wParam, lParam );
179     
180     switch (msg) {
181     case WM_INITDIALOG:
182         afc = (PACMFORMATCHOOSEA)lParam;
183         MSACM_FillFormatTags(hWnd);
184         MSACM_FillFormat(hWnd);
185         if ((afc->fdwStyle & ~(ACMFORMATCHOOSE_STYLEF_CONTEXTHELP|
186                                ACMFORMATCHOOSE_STYLEF_SHOWHELP)) != 0)
187             FIXME("Unsupported style %08lx\n", ((PACMFORMATCHOOSEA)lParam)->fdwStyle);
188         if (!(afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP))
189             ShowWindow(GetDlgItem(hWnd, IDD_ACMFORMATCHOOSE_BTN_HELP), SW_HIDE);
190         return TRUE;
191         
192     case WM_COMMAND:
193         switch (LOWORD(wParam)) {
194         case IDOK:
195             EndDialog(hWnd, MSACM_GetWFX(hWnd, afc));
196             return TRUE;
197         case IDCANCEL:
198             EndDialog(hWnd, ACMERR_CANCELED);
199             return TRUE;
200         case IDD_ACMFORMATCHOOSE_CMB_FORMATTAG:
201             switch (HIWORD(wParam)) {
202             case CBN_SELCHANGE:
203                 MSACM_FillFormat(hWnd);
204                 break;
205             default:
206                 TRACE("Dropped dlgNotif (fmtTag): 0x%08x 0x%08lx\n", 
207                       HIWORD(wParam), lParam);
208                 break;
209             }
210             break;
211         case IDD_ACMFORMATCHOOSE_BTN_HELP:
212             if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP)
213                 SendMessageA(afc->hwndOwner, 
214                              RegisterWindowMessageA(ACMHELPMSGSTRINGA), 0L, 0L);
215             break;
216             
217         default:
218             TRACE("Dropped dlgCmd: ctl=%d ntf=0x%04x 0x%08lx\n", 
219                   LOWORD(wParam), HIWORD(wParam), lParam);
220             break;
221         }
222         break;
223     case WM_CONTEXTMENU:
224         if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
225             SendMessageA(afc->hwndOwner, 
226                          RegisterWindowMessageA(ACMHELPMSGCONTEXTMENUA), 
227                          wParam, lParam);
228         break;
229 #if defined(WM_CONTEXTHELP)
230     case WM_CONTEXTHELP:
231         if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
232             SendMessageA(afc->hwndOwner, 
233                          RegisterWindowMessageA(ACMHELPMSGCONTEXTHELPA), 
234                          wParam, lParam);
235         break;
236 #endif       
237     default:
238         TRACE("Dropped dlgMsg: hwnd=%i msg=%i 0x%08x 0x%08lx\n", 
239               hWnd,  msg, wParam, lParam );
240         break;
241     }
242     return FALSE;
243 }
244
245 /***********************************************************************
246  *           acmFormatChooseA (MSACM32.23)
247  */
248 MMRESULT WINAPI acmFormatChooseA(PACMFORMATCHOOSEA pafmtc)
249 {
250     return DialogBoxParamA(MSACM_hInstance32, MAKEINTRESOURCEA(DLG_ACMFORMATCHOOSE_ID),
251                            pafmtc->hwndOwner, FormatChooseDlgProc, (INT)pafmtc);
252 }
253
254 /***********************************************************************
255  *           acmFormatChooseW (MSACM32.24)
256  */
257 MMRESULT WINAPI acmFormatChooseW(PACMFORMATCHOOSEW pafmtc)
258 {
259     FIXME("(%p): stub\n", pafmtc);
260     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
261     return MMSYSERR_ERROR;
262 }
263
264 /***********************************************************************
265  *           acmFormatDetailsA (MSACM32.25)
266  */
267 MMRESULT WINAPI acmFormatDetailsA(HACMDRIVER had, PACMFORMATDETAILSA pafd, 
268                                   DWORD fdwDetails)
269 {
270     ACMFORMATDETAILSW   afdw;
271     MMRESULT            mmr;
272
273     memset(&afdw, 0, sizeof(afdw));
274     afdw.cbStruct = sizeof(afdw);
275     afdw.dwFormatIndex = pafd->dwFormatIndex;
276     afdw.dwFormatTag = pafd->dwFormatTag; 
277     afdw.pwfx = pafd->pwfx; 
278     afdw.cbwfx = pafd->cbwfx; 
279
280     mmr = acmFormatDetailsW(had, &afdw, fdwDetails);
281     if (mmr == MMSYSERR_NOERROR) {
282         pafd->dwFormatTag = afdw.dwFormatTag; 
283         pafd->fdwSupport = afdw.fdwSupport; 
284         WideCharToMultiByte( CP_ACP, 0, afdw.szFormat, -1,
285                              pafd->szFormat, sizeof(pafd->szFormat), NULL, NULL );
286     }
287     return mmr;
288 }
289
290 /***********************************************************************
291  *           acmFormatDetailsW (MSACM32.26)
292  */
293 MMRESULT WINAPI acmFormatDetailsW(HACMDRIVER had, PACMFORMATDETAILSW pafd, 
294                                   DWORD fdwDetails)
295 {
296     MMRESULT                    mmr;
297     static WCHAR                fmt1[] = {'%','d',' ','H','z',0};
298     static WCHAR                fmt2[] = {';',' ','%','d',' ','b','i','t','s',0};
299     ACMFORMATTAGDETAILSA        aftd;
300
301     TRACE("(0x%08x, %p, %ld)\n", had, pafd, fdwDetails);
302
303     memset(&aftd, 0, sizeof(aftd));
304     aftd.cbStruct = sizeof(aftd);
305
306     if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
307         
308     switch (fdwDetails) {
309     case ACM_FORMATDETAILSF_FORMAT:
310         if (pafd->dwFormatTag != pafd->pwfx->wFormatTag) {
311             mmr = MMSYSERR_INVALPARAM;
312             break;
313         }
314         if (had == (HACMDRIVER)NULL) {
315             PWINE_ACMDRIVERID           padid;
316
317             mmr = ACMERR_NOTPOSSIBLE;
318             for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
319                 /* should check for codec only */
320                 if (padid->bEnabled && 
321                     acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
322                     mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS,
323                                         (LPARAM)pafd, (LPARAM)fdwDetails);
324                     acmDriverClose(had, 0);
325                     if (mmr == MMSYSERR_NOERROR) break;
326                 }
327             }               
328         } else {
329             mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS,
330                                 (LPARAM)pafd, (LPARAM)fdwDetails);
331         }
332         break;
333     case ACM_FORMATDETAILSF_INDEX:
334         /* should check pafd->dwFormatIndex < aftd->cStandardFormats */
335         mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS,
336                             (LPARAM)pafd, (LPARAM)fdwDetails);
337         break;
338     default:
339         WARN("Unknown fdwDetails %08lx\n", fdwDetails);
340         mmr = MMSYSERR_INVALFLAG;
341         break;
342     }
343
344     if (mmr == MMSYSERR_NOERROR && pafd->szFormat[0] == (WCHAR)0) {
345         wsprintfW(pafd->szFormat, fmt1, pafd->pwfx->nSamplesPerSec);
346         if (pafd->pwfx->wBitsPerSample) {
347             wsprintfW(pafd->szFormat + lstrlenW(pafd->szFormat), fmt2, 
348                       pafd->pwfx->wBitsPerSample);
349         }
350         MultiByteToWideChar( CP_ACP, 0, (pafd->pwfx->nChannels == 1) ? "; Mono" : "; Stereo", -1,
351                              pafd->szFormat + strlenW(pafd->szFormat),
352                              sizeof(pafd->szFormat)/sizeof(WCHAR) - strlenW(pafd->szFormat) );
353     }
354
355     TRACE("=> %d\n", mmr);
356     return mmr;
357 }
358
359 struct MSACM_FormatEnumWtoA_Instance {
360     PACMFORMATDETAILSA  pafda;
361     DWORD               dwInstance;
362     ACMFORMATENUMCBA    fnCallback;
363 };
364
365 static BOOL CALLBACK MSACM_FormatEnumCallbackWtoA(HACMDRIVERID hadid,
366                                                   PACMFORMATDETAILSW pafdw,  
367                                                   DWORD dwInstance,             
368                                                   DWORD fdwSupport)
369 {
370     struct MSACM_FormatEnumWtoA_Instance* pafei;
371
372     pafei = (struct MSACM_FormatEnumWtoA_Instance*)dwInstance;
373
374     pafei->pafda->dwFormatIndex = pafdw->dwFormatIndex; 
375     pafei->pafda->dwFormatTag = pafdw->dwFormatTag; 
376     pafei->pafda->fdwSupport = pafdw->fdwSupport; 
377     WideCharToMultiByte( CP_ACP, 0, pafdw->szFormat, -1,
378                          pafei->pafda->szFormat, sizeof(pafei->pafda->szFormat), NULL, NULL );
379
380     return (pafei->fnCallback)(hadid, pafei->pafda, 
381                                pafei->dwInstance, fdwSupport);
382 }
383
384 /***********************************************************************
385  *           acmFormatEnumA (MSACM32.27)
386  */
387 MMRESULT WINAPI acmFormatEnumA(HACMDRIVER had, PACMFORMATDETAILSA pafda,
388                                ACMFORMATENUMCBA fnCallback, DWORD dwInstance, 
389                                DWORD fdwEnum)
390 {
391     ACMFORMATDETAILSW           afdw;
392     struct MSACM_FormatEnumWtoA_Instance afei;
393
394     memset(&afdw, 0, sizeof(afdw));
395     afdw.cbStruct = sizeof(afdw);
396     afdw.dwFormatIndex = pafda->dwFormatIndex;
397     afdw.dwFormatTag = pafda->dwFormatTag;
398     afdw.pwfx = pafda->pwfx;
399     afdw.cbwfx = pafda->cbwfx;
400
401     afei.pafda = pafda;
402     afei.dwInstance = dwInstance;
403     afei.fnCallback = fnCallback;
404
405     return acmFormatEnumW(had, &afdw, MSACM_FormatEnumCallbackWtoA, 
406                           (DWORD)&afei, fdwEnum);
407 }
408
409 /***********************************************************************
410  *           acmFormatEnumW (MSACM32.28)
411  */
412 static BOOL MSACM_FormatEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had, 
413                                    PACMFORMATDETAILSW pafd, PWAVEFORMATEX pwfxRef, 
414                                    ACMFORMATENUMCBW fnCallback, DWORD dwInstance,  
415                                    DWORD fdwEnum)
416 {
417     ACMDRIVERDETAILSW           add;
418     ACMFORMATTAGDETAILSW        aftd;
419     int                         i, j;
420
421     add.cbStruct = sizeof(add);
422     
423     if (acmDriverDetailsW((HACMDRIVERID)padid, &add, 0) != MMSYSERR_NOERROR) return FALSE;
424
425     for (i = 0; i < add.cFormatTags; i++) {
426         memset(&aftd, 0, sizeof(aftd));
427         aftd.cbStruct = sizeof(aftd);
428         aftd.dwFormatTagIndex = i;
429         if (acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR)
430             continue;
431         
432         if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) && aftd.dwFormatTag != pwfxRef->wFormatTag)
433             continue;
434         
435         for (j = 0; j < aftd.cStandardFormats; j++) {
436             pafd->dwFormatIndex = j;
437             pafd->dwFormatTag = aftd.dwFormatTag;
438             if (acmFormatDetailsW(had, pafd, ACM_FORMATDETAILSF_INDEX) != MMSYSERR_NOERROR) 
439                 continue;
440             
441             if ((fdwEnum & ACM_FORMATENUMF_NCHANNELS) && 
442                 pafd->pwfx->nChannels != pwfxRef->nChannels)
443                 continue;
444             if ((fdwEnum & ACM_FORMATENUMF_NSAMPLESPERSEC) && 
445                 pafd->pwfx->nSamplesPerSec != pwfxRef->nSamplesPerSec)
446                 continue;
447             if ((fdwEnum & ACM_FORMATENUMF_WBITSPERSAMPLE) && 
448                 pafd->pwfx->wBitsPerSample != pwfxRef->wBitsPerSample)
449                 continue;
450             if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
451                 !(pafd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_HARDWARE))
452                 continue;
453             
454             /* more checks to be done on fdwEnum */
455
456             if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, add.fdwSupport))
457                 return FALSE; 
458         }
459         /* the "formats" used by the filters are also reported */
460     }
461     return TRUE;
462 }
463
464 /**********************************************************************/
465
466 MMRESULT WINAPI acmFormatEnumW(HACMDRIVER had, PACMFORMATDETAILSW pafd,
467                                ACMFORMATENUMCBW fnCallback, DWORD dwInstance,  
468                                DWORD fdwEnum)
469 {
470     PWINE_ACMDRIVERID           padid;
471     WAVEFORMATEX                wfxRef;
472     BOOL                        ret;
473
474     TRACE("(0x%08x, %p, %p, %ld, %ld)\n",
475           had, pafd, fnCallback, dwInstance, fdwEnum);
476
477     if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
478
479     if (fdwEnum & (ACM_FORMATENUMF_WFORMATTAG|ACM_FORMATENUMF_NCHANNELS|
480                    ACM_FORMATENUMF_NSAMPLESPERSEC|ACM_FORMATENUMF_WBITSPERSAMPLE|
481                    ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST))
482         wfxRef = *pafd->pwfx;
483
484     if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) && 
485         !(fdwEnum & (ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT)))
486         return MMSYSERR_INVALPARAM;
487
488     if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) &&
489         (pafd->dwFormatTag != pafd->pwfx->wFormatTag))
490         return MMSYSERR_INVALPARAM;
491
492     if (fdwEnum & (ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST|
493                    ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT))
494         FIXME("Unsupported fdwEnum values %08lx\n", fdwEnum);
495
496     if (had) {
497         HACMDRIVERID    hadid;
498
499         if (acmDriverID(had, &hadid, 0) != MMSYSERR_NOERROR)
500             return MMSYSERR_INVALHANDLE;
501         MSACM_FormatEnumHelper(MSACM_GetDriverID(hadid), had, pafd, &wfxRef, 
502                                fnCallback, dwInstance, fdwEnum);
503         return MMSYSERR_NOERROR;
504     }
505     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
506             /* should check for codec only */
507             if (!padid->bEnabled || acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
508                 continue;
509             ret = MSACM_FormatEnumHelper(padid, had, pafd, &wfxRef, 
510                                          fnCallback, dwInstance, fdwEnum);
511             acmDriverClose(had, 0);
512             if (!ret) break;
513     }
514     return MMSYSERR_NOERROR;
515 }
516
517 /***********************************************************************
518  *           acmFormatSuggest (MSACM32.29)
519  */
520 MMRESULT WINAPI acmFormatSuggest(HACMDRIVER had, PWAVEFORMATEX pwfxSrc, 
521                                  PWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest)
522 {
523     ACMDRVFORMATSUGGEST adfg;
524     MMRESULT            mmr;
525
526     TRACE("(0x%08x, %p, %p, %ld, %ld)\n", 
527           had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest);
528
529     if (fdwSuggest & ~(ACM_FORMATSUGGESTF_NCHANNELS|ACM_FORMATSUGGESTF_NSAMPLESPERSEC|
530                        ACM_FORMATSUGGESTF_WBITSPERSAMPLE|ACM_FORMATSUGGESTF_WFORMATTAG))
531         return MMSYSERR_INVALFLAG;
532
533     adfg.cbStruct = sizeof(adfg);
534     adfg.fdwSuggest = fdwSuggest;
535     adfg.pwfxSrc = pwfxSrc;
536     adfg.cbwfxSrc = (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) ?
537         sizeof(WAVEFORMATEX) : pwfxSrc->cbSize;
538     adfg.pwfxDst = pwfxDst;
539     adfg.cbwfxDst = cbwfxDst;
540
541     if (had == (HACMDRIVER)NULL) {
542         PWINE_ACMDRIVERID       padid;
543         
544         /* MS doc says: ACM finds the best suggestion. 
545          * Well, first found will be the "best"
546          */
547         mmr = ACMERR_NOTPOSSIBLE;
548         for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
549             /* should check for codec only */
550             if (!padid->bEnabled ||
551                 acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
552                 continue;
553             
554             if (MSACM_Message(had, ACMDM_FORMAT_SUGGEST, 
555                               (LPARAM)&adfg, 0L) == MMSYSERR_NOERROR) {
556                 mmr = MMSYSERR_NOERROR;
557                 break;
558             }
559             acmDriverClose(had, 0);
560         }
561     } else {
562         mmr = MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L);
563     }
564     return mmr;
565 }
566
567 /***********************************************************************
568  *           acmFormatTagDetailsA (MSACM32.30)
569  */
570 MMRESULT WINAPI acmFormatTagDetailsA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda, 
571                                      DWORD fdwDetails)
572 {
573     ACMFORMATTAGDETAILSW        aftdw;
574     MMRESULT                    mmr;
575
576     memset(&aftdw, 0, sizeof(aftdw));
577     aftdw.cbStruct = sizeof(aftdw);
578     aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
579     aftdw.dwFormatTag = paftda->dwFormatTag;
580
581     mmr = acmFormatTagDetailsW(had, &aftdw, fdwDetails);
582     if (mmr == MMSYSERR_NOERROR) {
583         paftda->dwFormatTag = aftdw.dwFormatTag; 
584         paftda->dwFormatTagIndex = aftdw.dwFormatTagIndex;
585         paftda->cbFormatSize = aftdw.cbFormatSize; 
586         paftda->fdwSupport = aftdw.fdwSupport; 
587         paftda->cStandardFormats = aftdw.cStandardFormats; 
588         WideCharToMultiByte( CP_ACP, 0, aftdw.szFormatTag, -1, paftda->szFormatTag,
589                              sizeof(paftda->szFormatTag), NULL, NULL );
590     }
591     return mmr;
592 }
593
594 /***********************************************************************
595  *           acmFormatTagDetailsW (MSACM32.31)
596  */
597 MMRESULT WINAPI acmFormatTagDetailsW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd, 
598                                      DWORD fdwDetails)
599 {
600     PWINE_ACMDRIVERID   padid;
601     MMRESULT            mmr;
602
603     TRACE("(0x%08x, %p, %ld)\n", had, paftd, fdwDetails);
604
605     if (fdwDetails & ~(ACM_FORMATTAGDETAILSF_FORMATTAG|ACM_FORMATTAGDETAILSF_INDEX|
606                        ACM_FORMATTAGDETAILSF_LARGESTSIZE))
607         return MMSYSERR_INVALFLAG;
608
609     switch (fdwDetails) {
610     case ACM_FORMATTAGDETAILSF_FORMATTAG:
611         if (had == (HACMDRIVER)NULL) {
612             mmr = ACMERR_NOTPOSSIBLE;
613             for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
614                 /* should check for codec only */
615                 if (padid->bEnabled && acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
616                     mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
617                                         (LPARAM)paftd, (LPARAM)fdwDetails);
618                     acmDriverClose(had, 0);
619                     if (mmr == MMSYSERR_NOERROR) break;
620                 }
621             }
622         } else {
623             mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
624                                 (LPARAM)paftd, (LPARAM)fdwDetails);
625         }
626         break;
627
628     case ACM_FORMATTAGDETAILSF_INDEX:
629         /* FIXME should check paftd->dwFormatTagIndex < add.cFormatTags */
630         mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
631                             (LPARAM)paftd, (LPARAM)fdwDetails);
632         break;
633
634     case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
635         if (had == (HACMDRIVER)NULL) {
636             ACMFORMATTAGDETAILSW        tmp;
637             DWORD                       ft = paftd->dwFormatTag;
638
639             mmr = ACMERR_NOTPOSSIBLE;
640             for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
641                 /* should check for codec only */
642                 if (padid->bEnabled && 
643                     acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
644
645                     memset(&tmp, 0, sizeof(tmp));
646                     tmp.cbStruct = sizeof(tmp);
647                     tmp.dwFormatTag = ft;
648
649                     if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
650                                       (LPARAM)&tmp, 
651                                       (LPARAM)fdwDetails) == MMSYSERR_NOERROR) {
652                         if (mmr == ACMERR_NOTPOSSIBLE ||
653                             paftd->cbFormatSize < tmp.cbFormatSize) {
654                             *paftd = tmp;
655                             mmr = MMSYSERR_NOERROR;
656                         } 
657                     }
658                     acmDriverClose(had, 0);
659                 }
660             }
661         } else {
662             mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
663                                 (LPARAM)paftd, (LPARAM)fdwDetails);
664         }
665         break;
666
667     default:
668         WARN("Unsupported fdwDetails=%08lx\n", fdwDetails);
669         mmr = MMSYSERR_ERROR;
670     }
671
672     if (mmr == MMSYSERR_NOERROR && 
673         paftd->dwFormatTag == WAVE_FORMAT_PCM && paftd->szFormatTag[0] == 0)
674         MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
675                              sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
676
677     return mmr;
678 }
679
680 struct MSACM_FormatTagEnumWtoA_Instance {
681     PACMFORMATTAGDETAILSA       paftda;
682     DWORD                       dwInstance;
683     ACMFORMATTAGENUMCBA         fnCallback;
684 };
685
686 static BOOL CALLBACK MSACM_FormatTagEnumCallbackWtoA(HACMDRIVERID hadid,
687                                                      PACMFORMATTAGDETAILSW paftdw,  
688                                                      DWORD dwInstance,             
689                                                      DWORD fdwSupport)
690 {
691     struct MSACM_FormatTagEnumWtoA_Instance* paftei;
692
693     paftei = (struct MSACM_FormatTagEnumWtoA_Instance*)dwInstance;
694
695     paftei->paftda->dwFormatTagIndex = paftdw->dwFormatTagIndex; 
696     paftei->paftda->dwFormatTag = paftdw->dwFormatTag; 
697     paftei->paftda->cbFormatSize = paftdw->cbFormatSize; 
698     paftei->paftda->fdwSupport = paftdw->fdwSupport; 
699     paftei->paftda->cStandardFormats = paftdw->cStandardFormats; 
700     WideCharToMultiByte( CP_ACP, 0, paftdw->szFormatTag, -1, paftei->paftda->szFormatTag,
701                          sizeof(paftei->paftda->szFormatTag), NULL, NULL );
702
703     return (paftei->fnCallback)(hadid, paftei->paftda, 
704                                 paftei->dwInstance, fdwSupport);
705 }
706
707 /***********************************************************************
708  *           acmFormatTagEnumA (MSACM32.32)
709  */
710 MMRESULT WINAPI acmFormatTagEnumA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
711                                   ACMFORMATTAGENUMCBA fnCallback, DWORD dwInstance, 
712                                   DWORD fdwEnum)
713 {
714     ACMFORMATTAGDETAILSW        aftdw;
715     struct MSACM_FormatTagEnumWtoA_Instance aftei;
716
717     memset(&aftdw, 0, sizeof(aftdw));
718     aftdw.cbStruct = sizeof(aftdw);
719     aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
720     aftdw.dwFormatTag = paftda->dwFormatTag;
721
722     aftei.paftda = paftda;
723     aftei.dwInstance = dwInstance;
724     aftei.fnCallback = fnCallback;
725
726     return acmFormatTagEnumW(had, &aftdw, MSACM_FormatTagEnumCallbackWtoA, 
727                              (DWORD)&aftei, fdwEnum);
728 }
729
730 /***********************************************************************
731  *           acmFormatTagEnumW (MSACM32.33)
732  */
733 MMRESULT WINAPI acmFormatTagEnumW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
734                                   ACMFORMATTAGENUMCBW fnCallback, DWORD dwInstance, 
735                                   DWORD fdwEnum)
736 {
737     PWINE_ACMDRIVERID           padid;
738     ACMDRIVERDETAILSW           add;
739     int                         i;
740     BOOL                        bPcmDone = FALSE;
741
742     TRACE("(0x%08x, %p, %p, %ld, %ld)\n",
743           had, paftd, fnCallback, dwInstance, fdwEnum); 
744
745     if (paftd->cbStruct < sizeof(*paftd)) return MMSYSERR_INVALPARAM;
746
747     if (had) FIXME("had != NULL, not supported\n");
748     
749     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
750         /* should check for codec only */
751         if (padid->bEnabled && acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) {
752             add.cbStruct = sizeof(add);
753
754             if (acmDriverDetailsW((HACMDRIVERID)padid, &add, 0) == MMSYSERR_NOERROR) {
755                 for (i = 0; i < add.cFormatTags; i++) {
756                     paftd->dwFormatTagIndex = i;
757                     if (acmFormatTagDetailsW(had, paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
758                         if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
759                             /* FIXME (EPP): I'm not sure this is the correct 
760                              * algorithm (should make more sense to apply the same
761                              * for all already loaded formats, but this will do 
762                              * for now
763                              */
764                             if (bPcmDone) continue;
765                             bPcmDone = TRUE;
766                         }
767                         if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, 
768                                           add.fdwSupport)) {
769                             padid = NULL;
770                             break;
771                         }
772                     }
773                 }
774             }
775         }
776         acmDriverClose(had, 0);
777     }
778     return MMSYSERR_NOERROR;
779 }