This patch is the preliminary result of some work I have been doing in order to add missing functionality to builtin msacm32.dll. I am submitting this to wine-devel rather than wine-patches, and in one big patch rather than several because I would like comments on some choices I made while adding features. The following is the list of features added by the patch:

* Implementation of acmDriverAddW(), and delegation of acmDriverAddA() to acmDriverAddW() * Working implementation of modes of operation of acmDriverAdd[AW]: add driver by new registry entry (ACM_DRIVERADDF_NAME), add (local) driver by combination of hModule/acmDriverProc (ACM_DRIVERADDF_FUNCTION), add notification window for event broadcasts (ACM_DRIVERADDF_NOTIFYHWND) * Implementation of broadcasts to notification windows on driver add/remove, enabling/disabling, and priority changes * Fix for implementation quirks of acmDriverMessage() in order to allow native codecs to display configuration dialogs * Working implementation of acmDriverPriority(), with support of delayed notification broadcasts (for one process only). Includes saving new priorities and enabled status to registry
* Loading of codec priorities and enabled status from registry
* Support for ACM_METRIC_DRIVER_SUPPORT in acmMetrics()

I must note that in order to provide support for acmDriverAddW() in ACM_DRIVERADDF_FUNCTION mode, it is necessary to treat an application-supplied module with an application-supplied driverProc as a full-blown winmm driver. Therefore, the patch includes a new procedure in winmm called wineAddDriver(), that instructs winmm to build a hDrvr from a supplied hModule/driverProc pair, rather than loading both from a DLL, as OpenDriver() does. This allows the rest of the code to continue using SendDriverMessage() as usual.

Alex Villacís Lasso

diff -ur wine-0.9.5-cvs/dlls/msacm/driver.c wine-0.9.5-cvs-patch/dlls/msacm/driver.c
--- wine-0.9.5-cvs/dlls/msacm/driver.c	2005-12-20 10:17:52.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/msacm/driver.c	2006-01-09 21:59:49.000000000 -0500
@@ -40,6 +40,7 @@
 #include "msacmdrv.h"
 #include "wineacm.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
 
@@ -49,6 +50,10 @@
 MMRESULT WINAPI acmDriverAddA(PHACMDRIVERID phadid, HINSTANCE hinstModule,
 			      LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
 {
+    MMRESULT resultW;
+    WCHAR * driverW = NULL;
+    LPARAM lParamW = lParam;
+
     TRACE("(%p, %p, %08lx, %08lx, %08lx)\n",
           phadid, hinstModule, lParam, dwPriority, fdwAdd);
 
@@ -72,30 +77,100 @@
 	return MMSYSERR_INVALFLAG;
     }
 
-    /* FIXME: in fact, should GetModuleFileName(hinstModule) and do a
-     * LoadDriver on it, to be sure we can call SendDriverMessage on the
-     * hDrvr handle.
-     */
-    *phadid = (HACMDRIVERID) MSACM_RegisterDriver(NULL, NULL, hinstModule);
-
-    /* FIXME: lParam, dwPriority and fdwAdd ignored */
+    /* A->W translation of name */
+    if ((fdwAdd & ACM_DRIVERADDF_TYPEMASK) == ACM_DRIVERADDF_NAME) {
+        unsigned long len = strlen((LPSTR)lParam) + 1;
+        driverW = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR));
+        if (!driverW) return MMSYSERR_NOMEM;
+        MultiByteToWideChar(CP_ACP, 0, (LPSTR)lParam, -1, driverW, len);
+        lParamW = (LPARAM)driverW;
+    }
 
-    return MMSYSERR_NOERROR;
-}
+    resultW = acmDriverAddW(phadid, hinstModule, lParamW, dwPriority, fdwAdd);
+    if ((WCHAR *)lParamW != NULL) HeapFree(MSACM_hHeap, 0, driverW);
+    return resultW;
+}			      
 
 /***********************************************************************
  *           acmDriverAddW (MSACM32.@)
- * FIXME
- *   Not implemented
+ *
  */
 MMRESULT WINAPI acmDriverAddW(PHACMDRIVERID phadid, HINSTANCE hinstModule,
 			      LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
 {
-    FIXME("(%p, %p, %ld, %ld, %ld): stub\n",
-	  phadid, hinstModule, lParam, dwPriority, fdwAdd);
+    TRACE("(%p, %p, %08lx, %08lx, %08lx)\n",
+          phadid, hinstModule, lParam, dwPriority, fdwAdd);
+
+    if (!phadid) {
+        WARN("invalid parameter\n");
+	return MMSYSERR_INVALPARAM;
+    }
+
+    /* Check if any unknown flags */
+    if (fdwAdd &
+	~(ACM_DRIVERADDF_FUNCTION|ACM_DRIVERADDF_NOTIFYHWND|
+	  ACM_DRIVERADDF_GLOBAL)) {
+        WARN("invalid flag\n");
+	return MMSYSERR_INVALFLAG;
+    }
 
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return MMSYSERR_ERROR;
+    /* Check if any incompatible flags */
+    if ((fdwAdd & ACM_DRIVERADDF_FUNCTION) &&
+	(fdwAdd & ACM_DRIVERADDF_NOTIFYHWND)) {
+        WARN("invalid flag\n");
+	return MMSYSERR_INVALFLAG;
+    }
+
+    switch (fdwAdd & ACM_DRIVERADDF_TYPEMASK) {
+    case ACM_DRIVERADDF_NAME:
+        /*
+                hInstModule     (unused)
+                lParam          name of value in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32
+                dwPriority      (unused, set to 0)
+         */
+        *phadid = (HACMDRIVERID) MSACM_RegisterDriverFromRegistry((LPCWSTR)lParam);        
+        if (!*phadid) {
+            ERR("Unable to register driver via ACM_DRIVERADDF_NAME\n");
+            return MMSYSERR_INVALPARAM;
+        }
+        break;
+    case ACM_DRIVERADDF_FUNCTION:
+        /*
+                hInstModule     Handle of module which contains driver entry proc
+                lParam          Driver function address
+                dwPriority      (unused, set to 0)
+         */
+        fdwAdd &= ~ACM_DRIVERADDF_TYPEMASK;
+        /* FIXME: fdwAdd ignored */
+        /* Application-supplied acmDriverProc's are placed at the top of the priority unless
+           fdwAdd indicates ACM_DRIVERADDF_GLOBAL
+         */
+        *phadid = (HACMDRIVERID) MSACM_RegisterDriver(NULL, NULL, hinstModule, (DRIVERPROC)lParam, (fdwAdd & ACM_DRIVERADDF_GLOBAL) ? FALSE : TRUE);
+        if (!*phadid) {
+            ERR("Unable to register driver via ACM_DRIVERADDF_FUNCTION\n");
+            return MMSYSERR_INVALPARAM;
+        }
+        break;
+    case ACM_DRIVERADDF_NOTIFYHWND:
+        /*
+                hInstModule     (unused)
+                lParam          Handle of notification window
+                dwPriority      Window message to send for notification broadcasts
+         */
+        *phadid = (HACMDRIVERID) MSACM_RegisterNotificationWindow((HWND)lParam, dwPriority);
+        if (!*phadid) {
+            ERR("Unable to register driver via ACM_DRIVERADDF_NOTIFYHWND\n");
+            return MMSYSERR_INVALPARAM;
+        }
+        break;
+    default:
+        ERR("invalid flag value %lu for fdwAdd\n", fdwAdd & ACM_DRIVERADDF_TYPEMASK);
+	return MMSYSERR_INVALFLAG;
+        break;
+    }
+
+    MSACM_BroadcastNotification();
+    return MMSYSERR_NOERROR;
 }
 
 /***********************************************************************
@@ -250,6 +325,7 @@
     }
 
     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+        if (padid->hNotifyWnd) continue;     /* skip over notification windows */
 	fdwSupport = padid->fdwSupport;
 
 	if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
@@ -298,6 +374,15 @@
 /***********************************************************************
  *           acmDriverMessage (MSACM32.@)
  *
+ * Note: MSDN documentation (July 2001) is incomplete. This function
+ * accepts sending messages to an HACMDRIVERID in addition to the
+ * documented HACMDRIVER. In fact, for DRV_QUERYCONFIGURE and DRV_CONFIGURE, 
+ * this might actually be the required mode of operation.
+ *
+ * Note: For DRV_CONFIGURE, msacm supplies its own DRVCONFIGINFO structure
+ * when the application fails to supply one. Some native drivers depend on
+ * this and refuse to display unless a valid DRVCONFIGINFO structure is
+ * built and supplied.
  */
 LRESULT WINAPI acmDriverMessage(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
 {
@@ -307,12 +392,91 @@
 	uMsg == ACMDM_DRIVER_ABOUT ||
 	uMsg == DRV_QUERYCONFIGURE ||
 	uMsg == DRV_CONFIGURE)
-	return MSACM_Message(had, uMsg, lParam1, lParam2);
-
+    {
+        PWINE_ACMDRIVERID padid;
+        LRESULT lResult;
+        LPDRVCONFIGINFO pConfigInfo = NULL;
+
+        /* Check whether handle is an HACMDRIVERID */
+        padid  = MSACM_GetDriverID((HACMDRIVERID)had);
+        
+        /* If the message is DRV_CONFIGURE, and the application provides no
+           DRVCONFIGINFO structure, msacm must supply its own.
+         */
+        if (uMsg == DRV_CONFIGURE && lParam2 == 0) {
+            LPWSTR pAlias;
+            
+            /* Get the alias from the HACMDRIVERID */
+            if (padid) {
+                pAlias = padid->pszDriverAlias;
+                if (pAlias == NULL) {
+                    WARN("DRV_CONFIGURE: no alias for this driver, cannot self-supply alias\n");
+                }
+            } else {
+                FIXME("DRV_CONFIGURE: reverse lookup HACMDRIVER -> HACMDRIVERID not implemented\n");
+                pAlias = NULL;
+            }
+        
+            if (pAlias != NULL) {
+                unsigned int iStructSize = 16;
+                /* This verification is required because DRVCONFIGINFO is 12 bytes
+                   long, yet native msacm reports a 16-byte structure to codecs.
+                 */
+                if (iStructSize < sizeof(DRVCONFIGINFO)) iStructSize = sizeof(DRVCONFIGINFO);
+                pConfigInfo = HeapAlloc(MSACM_hHeap, 0, iStructSize);
+                if (!pConfigInfo) {
+                    ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
+                } else {
+                    static const WCHAR drivers32[] = {'D','r','i','v','e','r','s','3','2','\0'};
+                    pConfigInfo->dwDCISize = iStructSize;
+                
+                    pConfigInfo->lpszDCISectionName = HeapAlloc(MSACM_hHeap, 0, (strlenW(drivers32) + 1) * sizeof(WCHAR));
+                    if (pConfigInfo->lpszDCISectionName) strcpyW((WCHAR *)pConfigInfo->lpszDCISectionName, drivers32);
+                    pConfigInfo->lpszDCIAliasName = HeapAlloc(MSACM_hHeap, 0, (strlenW(pAlias) + 1) * sizeof(WCHAR));
+                    if (pConfigInfo->lpszDCIAliasName) strcpyW((WCHAR *)pConfigInfo->lpszDCIAliasName, pAlias);
+                    
+                    if (pConfigInfo->lpszDCISectionName == NULL || pConfigInfo->lpszDCIAliasName == NULL) {
+                        if (pConfigInfo->lpszDCIAliasName) HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCIAliasName);
+                        if (pConfigInfo->lpszDCISectionName) HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCISectionName);
+                        HeapFree(MSACM_hHeap, 0, pConfigInfo);
+                        pConfigInfo = NULL;
+                        ERR("OOM while supplying DRVCONFIGINFO for DRV_CONFIGURE, using NULL\n");
+                    }
+                }
+            }
+            
+            lParam2 = (LPARAM)pConfigInfo;
+        }
+        
+        if (padid) {
+            /* Handle is really an HACMDRIVERID, must have an open session to get an HACMDRIVER */
+            if (padid->pACMDriverList != NULL) {
+                lResult = SendDriverMessage(padid->pACMDriverList->hDrvr, uMsg, lParam1, lParam2);
+            } else {
+                MMRESULT mmr = acmDriverOpen(&had, (HACMDRIVERID)padid, 0);
+                if (mmr != MMSYSERR_NOERROR) {
+                    lResult = MMSYSERR_INVALPARAM;
+                } else {
+                    lResult = acmDriverMessage(had, uMsg, lParam1, lParam2);
+                    acmDriverClose(had, 0);
+                }
+            }
+        } else {
+            lResult = MSACM_Message(had, uMsg, lParam1, lParam2);
+        }
+        if (pConfigInfo) {
+            HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCIAliasName);
+            HeapFree(MSACM_hHeap, 0, (void *)pConfigInfo->lpszDCISectionName);
+            HeapFree(MSACM_hHeap, 0, pConfigInfo);
+        }
+        return lResult;
+    }
     WARN("invalid parameter\n");
     return MMSYSERR_INVALPARAM;
 }
 
+HDRVR WINAPI wineAddDriver(HMODULE hModule, DRIVERPROC lpDriverProc, LPARAM lParam2);
+
 /***********************************************************************
  *           acmDriverOpen (MSACM32.@)
  */
@@ -382,6 +546,29 @@
             goto gotError;
         }
     }
+    else
+    {
+        ACMDRVOPENDESCW	adod;
+
+        pad->hDrvr = NULL;
+
+        adod.cbStruct = sizeof(adod);
+        adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
+        adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
+        adod.dwVersion = acmGetVersion();
+        adod.dwFlags = fdwOpen;
+        adod.dwError = 0;
+        adod.pszSectionName = NULL;
+        adod.pszAliasName = NULL;
+        adod.dnDevNode = 0;
+
+        pad->hDrvr = wineAddDriver(padid->hInstModule, padid->lpDriverProc, (DWORD)&adod);
+        if (!pad->hDrvr)
+        {
+            ret = adod.dwError;
+            goto gotError;
+        }
+    }
 
     /* insert new pad at beg of list */
     pad->pNextACMDriver = padid->pACMDriverList;
@@ -404,22 +591,9 @@
  */
 MMRESULT WINAPI acmDriverPriority(HACMDRIVERID hadid, DWORD dwPriority, DWORD fdwPriority)
 {
-    PWINE_ACMDRIVERID padid;
-    CHAR szSubKey[17];
-    CHAR szBuffer[256];
-    LONG lBufferLength = sizeof(szBuffer);
-    LONG lError;
-    HKEY hPriorityKey;
-    DWORD dwPriorityCounter;
 
     TRACE("(%p, %08lx, %08lx)\n", hadid, dwPriority, fdwPriority);
 
-    padid = MSACM_GetDriverID(hadid);
-    if (!padid) {
-        WARN("invalid handle\n");
-	return MMSYSERR_INVALHANDLE;
-    }
-
     /* Check for unknown flags */
     if (fdwPriority &
 	~(ACM_DRIVERPRIORITYF_ENABLE|ACM_DRIVERPRIORITYF_DISABLE|
@@ -441,33 +615,91 @@
         WARN("invalid flag\n");
 	return MMSYSERR_INVALFLAG;
     }
+    
+    /* According to MSDN, ACM_DRIVERPRIORITYF_BEGIN and ACM_DRIVERPRIORITYF_END 
+       may only appear by themselves, and in addition, hadid and dwPriority must
+       both be zero */
+    if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) ||
+	(fdwPriority & ACM_DRIVERPRIORITYF_END)) {
+	if (fdwPriority & ~(ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
+	    WARN("ACM_DRIVERPRIORITYF_[BEGIN|END] cannot be used with any other flags\n");
+	    return MMSYSERR_INVALPARAM;
+	}
+	if (dwPriority) {
+	    WARN("priority invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
+	    return MMSYSERR_INVALPARAM;
+	}
+	if (hadid) {
+	    WARN("non-null hadid invalid with ACM_DRIVERPRIORITYF_[BEGIN|END]\n");
+	    return MMSYSERR_INVALPARAM;
+	}
+	/* FIXME: MSDN wording suggests that deferred notification should be 
+	   implemented as a system-wide lock held by a calling task, and that 
+	   re-enabling notifications should broadcast them across all processes.
+	   This implementation uses a simple BOOL flag. One consequence of the
+	   current implementation is that applications will never see 
+	   MMSYSERR_ALLOCATED as a return error.
+	 */
+	if (fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) {
+	    MSACM_DisableNotifications();
+	} else if (fdwPriority & ACM_DRIVERPRIORITYF_END) {
+	    MSACM_EnableNotifications();
+	}
+	return MMSYSERR_NOERROR;
+    } else {
+        PWINE_ACMDRIVERID padid;
+        BOOL bPerformBroadcast = FALSE;
+
+        /* Fetch driver ID */
+        padid = MSACM_GetDriverID(hadid);
+        if (!padid) {
+            WARN("invalid handle\n");
+	    return MMSYSERR_INVALHANDLE;
+        }
+        
+        /* Check whether driver ID is appropriate for requested op */
+        if (dwPriority) {
+            if (padid->hNotifyWnd || (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL)) {
+                return MMSYSERR_NOTSUPPORTED;
+            }
+            if (dwPriority != 1 && dwPriority != -1) {
+                FIXME("unexpected priority %ld, using sign only\n", dwPriority);
+                if (dwPriority < 0) dwPriority = -1;
+                if (dwPriority > 0) dwPriority = 1;
+            }
+            
+            if (dwPriority == 1 && (padid->pPrevACMDriverID == NULL || 
+                (padid->pPrevACMDriverID->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL))) {
+                /* do nothing - driver is first of list, or first after last
+                   local driver */
+            } else if (dwPriority == -1 && padid->pNextACMDriverID == NULL) {
+                /* do nothing - driver is last of list */
+            } else {
+                MSACM_RePositionDriver(padid, dwPriority);
+                bPerformBroadcast = TRUE;
+            }
+        }
 
-    lError = RegOpenKeyA(HKEY_CURRENT_USER,
-			 "Software\\Microsoft\\Multimedia\\"
-			 "Audio Compression Manager\\Priority v4.00",
-			 &hPriorityKey
-			 );
-    /* FIXME: Create key */
-    if (lError != ERROR_SUCCESS) {
-        WARN("RegOpenKeyA failed\n");
-	return MMSYSERR_ERROR;
-    }
-
-    for (dwPriorityCounter = 1; ; dwPriorityCounter++)	{
-	snprintf(szSubKey, 17, "Priority%ld", dwPriorityCounter);
-	lError = RegQueryValueA(hPriorityKey, szSubKey, szBuffer, &lBufferLength);
-	if (lError != ERROR_SUCCESS)
-	    break;
-
-	FIXME("(%p, %ld, %ld): stub (partial)\n",
-	      hadid, dwPriority, fdwPriority);
-	break;
+        /* Check whether driver ID should be enabled or disabled */
+        if (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE) {
+            if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
+                padid->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
+                bPerformBroadcast = TRUE;
+            }
+        } else if (fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) {
+            if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
+                padid->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
+                bPerformBroadcast = TRUE;
+            }
+        }
+        
+        /* Perform broadcast of changes */
+        if (bPerformBroadcast) {
+            MSACM_WriteCurrentPriorities();
+            MSACM_BroadcastNotification();
+        }
+        return MMSYSERR_NOERROR;
     }
-
-    RegCloseKey(hPriorityKey);
-
-    WARN("RegQueryValueA failed\n");
-    return MMSYSERR_ERROR;
 }
 
 /***********************************************************************
@@ -491,6 +723,7 @@
     }
 
     MSACM_UnregisterDriver(padid);
+    MSACM_BroadcastNotification();
 
     return MMSYSERR_NOERROR;
 }
diff -ur wine-0.9.5-cvs/dlls/msacm/format.c wine-0.9.5-cvs-patch/dlls/msacm/format.c
--- wine-0.9.5-cvs/dlls/msacm/format.c	2005-09-12 10:35:53.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/msacm/format.c	2006-01-09 21:17:07.000000000 -0500
@@ -527,6 +527,7 @@
 	return MMSYSERR_NOERROR;
     }
     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+            if (padid->hNotifyWnd) continue;     /* skip over notification windows */
 	    /* should check for codec only */
 	    if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
 		acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
diff -ur wine-0.9.5-cvs/dlls/msacm/internal.c wine-0.9.5-cvs-patch/dlls/msacm/internal.c
--- wine-0.9.5-cvs/dlls/msacm/internal.c	2005-03-24 16:01:38.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/msacm/internal.c	2006-01-09 22:35:31.000000000 -0500
@@ -23,6 +23,7 @@
 
 #include <stdarg.h>
 #include <string.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -45,6 +46,8 @@
 HANDLE MSACM_hHeap = NULL;
 PWINE_ACMDRIVERID MSACM_pFirstACMDriverID = NULL;
 PWINE_ACMDRIVERID MSACM_pLastACMDriverID = NULL;
+BOOL MSACM_suspendBroadcast = FALSE;
+BOOL MSACM_pendingBroadcast = FALSE;
 
 #if 0
 /***********************************************************************
@@ -237,17 +240,20 @@
  *           MSACM_RegisterDriver()
  */
 PWINE_ACMDRIVERID MSACM_RegisterDriver(LPCWSTR pszDriverAlias, LPCWSTR pszFileName,
-				       HINSTANCE hinstModule)
+				       HINSTANCE hinstModule, DRIVERPROC lpDriverProc,
+				       BOOL bLocalDriver)
 {
     PWINE_ACMDRIVERID	padid;
 
-    TRACE("(%s, %s, %p)\n", 
-          debugstr_w(pszDriverAlias), debugstr_w(pszFileName), hinstModule);
+    TRACE("(%s, %s, %p, %p, %s)\n", 
+          debugstr_w(pszDriverAlias), debugstr_w(pszFileName), hinstModule, lpDriverProc, bLocalDriver ? "(local)" : "(global)");
 
     padid = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVERID));
     padid->obj.dwType = WINE_ACMOBJ_DRIVERID;
     padid->obj.pACMDriverID = padid;
     padid->pszDriverAlias = NULL;
+    padid->hNotifyWnd = NULL;
+    padid->dwNotifyMsg = 0;
     if (pszDriverAlias)
     {
         padid->pszDriverAlias = HeapAlloc( MSACM_hHeap, 0, (strlenW(pszDriverAlias)+1) * sizeof(WCHAR) );
@@ -260,6 +266,54 @@
         strcpyW( padid->pszFileName, pszFileName );
     }
     padid->hInstModule = hinstModule;
+    padid->lpDriverProc = lpDriverProc;
+
+    padid->pACMDriverList = NULL;
+    if (bLocalDriver) {
+        padid->pPrevACMDriverID = NULL;
+        padid->pNextACMDriverID = MSACM_pFirstACMDriverID;
+        if (MSACM_pFirstACMDriverID)
+            MSACM_pFirstACMDriverID->pPrevACMDriverID = padid;
+        MSACM_pFirstACMDriverID = padid;
+        if (!MSACM_pLastACMDriverID)
+            MSACM_pLastACMDriverID = padid;
+    } else {
+        padid->pNextACMDriverID = NULL;
+        padid->pPrevACMDriverID = MSACM_pLastACMDriverID;
+        if (MSACM_pLastACMDriverID)
+	    MSACM_pLastACMDriverID->pNextACMDriverID = padid;
+        MSACM_pLastACMDriverID = padid;
+        if (!MSACM_pFirstACMDriverID)
+	    MSACM_pFirstACMDriverID = padid;
+    }
+    /* disable the driver if we cannot load the cache */
+    if ((bLocalDriver || !MSACM_ReadCache(padid)) && !MSACM_FillCache(padid)) {
+	WARN("Couldn't load cache for ACM driver (%s)\n", debugstr_w(pszFileName));
+	MSACM_UnregisterDriver(padid);
+	return NULL;
+    }
+    if (bLocalDriver) padid->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_LOCAL;
+    return padid;
+}
+
+/***********************************************************************
+ *           MSACM_RegisterNotificationWindow()
+ */
+PWINE_ACMDRIVERID MSACM_RegisterNotificationWindow(HWND hNotifyWnd, DWORD dwNotifyMsg)
+{
+    PWINE_ACMDRIVERID	padid;
+
+    TRACE("(%p,0x%08lx)\n", 
+          hNotifyWnd, dwNotifyMsg);
+
+    padid = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVERID));
+    padid->obj.dwType = WINE_ACMOBJ_DRIVERID;
+    padid->obj.pACMDriverID = padid;
+    padid->pszDriverAlias = NULL;
+    padid->hNotifyWnd = hNotifyWnd;
+    padid->dwNotifyMsg = dwNotifyMsg;
+    padid->pszFileName = NULL;
+    padid->hInstModule = 0;
 
     padid->pACMDriverList = NULL;
     padid->pNextACMDriverID = NULL;
@@ -269,15 +323,142 @@
     MSACM_pLastACMDriverID = padid;
     if (!MSACM_pFirstACMDriverID)
 	MSACM_pFirstACMDriverID = padid;
-    /* disable the driver if we cannot load the cache */
-    if (!MSACM_ReadCache(padid) && !MSACM_FillCache(padid)) {
-	WARN("Couldn't load cache for ACM driver (%s)\n", debugstr_w(pszFileName));
-	MSACM_UnregisterDriver(padid);
-	return NULL;
+
+
+    padid->aFormatTag = NULL;
+    padid->cFormatTags = 0;
+    padid->cFilterTags = 0;
+    padid->fdwSupport  = 0;
+
+    return padid;
+}
+
+/***********************************************************************
+ *           MSACM_BroadcastNotification()
+ */
+void MSACM_BroadcastNotification(void)
+{
+    if (!MSACM_suspendBroadcast) {
+        PWINE_ACMDRIVERID padid;
+
+        for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+            if (padid->hNotifyWnd && !(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
+                SendMessageW(padid->hNotifyWnd, padid->dwNotifyMsg, 0, 0);
+            }
+        }
+    } else {
+        MSACM_pendingBroadcast = TRUE;
+    }
+}
+
+/***********************************************************************
+ *           MSACM_DisableNotifications()
+ */
+void MSACM_DisableNotifications(void)
+{
+    MSACM_suspendBroadcast = TRUE;
+}
+
+/***********************************************************************
+ *           MSACM_EnableNotifications()
+ */
+void MSACM_EnableNotifications(void)
+{
+    MSACM_suspendBroadcast = FALSE;
+    if (MSACM_pendingBroadcast) {
+        MSACM_pendingBroadcast = FALSE;
+        MSACM_BroadcastNotification();
+    }
+}
+
+/***********************************************************************
+ *           MSACM_RePositionDriver()
+ */
+void MSACM_RePositionDriver(PWINE_ACMDRIVERID padid, DWORD dwPriority)
+{
+    PWINE_ACMDRIVERID pTargetPosition = NULL;
+                
+    /* Remove selected driver from linked list */
+    if (MSACM_pFirstACMDriverID == padid) {
+        MSACM_pFirstACMDriverID = padid->pNextACMDriverID;
+    }
+    if (MSACM_pLastACMDriverID == padid) {
+        MSACM_pLastACMDriverID = padid->pPrevACMDriverID;
+    }
+    if (padid->pPrevACMDriverID != NULL) {
+        padid->pPrevACMDriverID->pNextACMDriverID = padid->pNextACMDriverID;
+    }
+    if (padid->pNextACMDriverID != NULL) {
+        padid->pNextACMDriverID->pPrevACMDriverID = padid->pPrevACMDriverID;
+    }
+    
+    /* Look up position where selected driver should be */
+    if (dwPriority == 1) {
+        pTargetPosition = padid->pPrevACMDriverID;
+        while (pTargetPosition->pPrevACMDriverID != NULL &&
+            !(pTargetPosition->pPrevACMDriverID->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL)) {
+            pTargetPosition = pTargetPosition->pPrevACMDriverID;
+        }
+    } else if (dwPriority == -1) {
+        pTargetPosition = padid->pNextACMDriverID;
+        while (pTargetPosition->pNextACMDriverID != NULL) {
+            pTargetPosition = pTargetPosition->pNextACMDriverID;
+        }
+    }
+    
+    /* Place selected driver in selected position */
+    padid->pPrevACMDriverID = pTargetPosition->pPrevACMDriverID;
+    padid->pNextACMDriverID = pTargetPosition;
+    if (padid->pPrevACMDriverID != NULL) {
+        padid->pPrevACMDriverID->pNextACMDriverID = padid;
+    } else {
+        MSACM_pFirstACMDriverID = padid;
+    }
+    if (padid->pNextACMDriverID != NULL) {
+        padid->pNextACMDriverID->pPrevACMDriverID = padid;
+    } else {
+        MSACM_pLastACMDriverID = padid;
+    }
+}
+
+/***********************************************************************
+ *           MSACM_RegisterDriverFromRegistry()
+ */
+PWINE_ACMDRIVERID MSACM_RegisterDriverFromRegistry(LPCWSTR pszRegEntry)
+{
+    static const WCHAR msacmW[] = {'M','S','A','C','M','.'};
+    static const WCHAR drvkey[] = {'S','o','f','t','w','a','r','e','\\',
+				   'M','i','c','r','o','s','o','f','t','\\',
+				   'W','i','n','d','o','w','s',' ','N','T','\\',
+				   'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+				   'D','r','i','v','e','r','s','3','2','\0'};
+    WCHAR buf[2048];
+    DWORD bufLen, lRet;
+    HKEY hKey;
+    PWINE_ACMDRIVERID padid = NULL;
+    
+    /* The requested registry entry must have the format msacm.XXXXX in order to
+       be recognized in any future sessions of msacm
+     */
+    if (0 == strncmpiW(buf, msacmW, sizeof(msacmW)/sizeof(WCHAR))) {
+        lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, drvkey, 0, KEY_QUERY_VALUE, &hKey);
+        if (lRet != ERROR_SUCCESS) {
+            WARN("unable to open registry key - 0x%08lx\n", lRet);
+        } else {
+            bufLen = sizeof(buf) / sizeof(WCHAR);
+            lRet = RegQueryValueExW(hKey, pszRegEntry, NULL, NULL, (LPBYTE)buf, (LPDWORD)&bufLen);
+            if (lRet != ERROR_SUCCESS) {
+                WARN("unable to query requested subkey %s - 0x%08lx\n", debugstr_w(pszRegEntry), lRet);
+            } else {
+                MSACM_RegisterDriver(pszRegEntry, buf, 0, NULL, FALSE);
+            }
+            RegCloseKey( hKey );
+        }
     }
     return padid;
 }
 
+static void MSACM_ReorderDriversByPriority(void);
 /***********************************************************************
  *           MSACM_RegisterAllDrivers()
  */
@@ -311,7 +492,7 @@
 	    if (strncmpiW(buf, msacmW, sizeof(msacmW)/sizeof(msacmW[0]))) continue;
 	    if (!(name = strchrW(buf, '='))) continue;
 	    *name = 0;
-	    MSACM_RegisterDriver(buf, name + 1, 0);
+	    MSACM_RegisterDriver(buf, name + 1, 0, NULL, FALSE);
 	}
     	RegCloseKey( hKey );
     }
@@ -323,12 +504,202 @@
 	    if (strncmpiW(s, msacmW, sizeof(msacmW)/sizeof(msacmW[0]))) continue;
 	    if (!(name = strchrW(s, '='))) continue;
 	    *name = 0;
-	    MSACM_RegisterDriver(s, name + 1, 0);
+	    MSACM_RegisterDriver(s, name + 1, 0, NULL, FALSE);
 	    *name = '=';
 	}
     }
+    MSACM_ReorderDriversByPriority();
+    MSACM_RegisterDriver(msacm32, msacm32, 0, NULL, FALSE);
+}
 
-    MSACM_RegisterDriver(msacm32, msacm32, 0);
+/***********************************************************************
+ *           MSACM_ReorderDriversByPriority()
+ * Reorders all drivers based on the priority list indicated by the registry key:
+ * HKCU\\Software\\Microsoft\\Multimedia\\Audio Compression Manager\\Priority v4.00
+ */
+static void MSACM_ReorderDriversByPriority(void)
+{
+    PWINE_ACMDRIVERID	padid;
+    unsigned int iNumDrivers;
+    
+    TRACE("\n");
+    
+    /* Count drivers && alloc corresponding memory for list */
+    iNumDrivers = 0;
+    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID, iNumDrivers++);
+    if (iNumDrivers > 1)
+    {
+        PWINE_ACMDRIVERID * driverList;
+        
+        driverList = HeapAlloc(MSACM_hHeap, 0, iNumDrivers * sizeof(PWINE_ACMDRIVERID));
+        if (!driverList)
+        {
+            ERR("out of memory\n");
+        }
+        else
+        {
+            LONG lError;
+            HKEY hPriorityKey;
+            static const WCHAR basePriorityKey[] = {
+                'S','o','f','t','w','a','r','e','\\',
+                'M','i','c','r','o','s','o','f','t','\\',
+                'M','u','l','t','i','m','e','d','i','a','\\',
+                'A','u','d','i','o',' ','C','o','m','p','r','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
+                'P','r','i','o','r','i','t','y',' ','v','4','.','0','0','\0'
+            };
+
+            lError = RegOpenKeyW(HKEY_CURRENT_USER,
+                        basePriorityKey,
+			 &hPriorityKey);
+            if (lError != ERROR_SUCCESS) {
+                TRACE("RegOpenKeyW failed, possibly key does not exist yet\n");
+            } else {
+                unsigned int i;
+                LONG lBufferLength;
+                WCHAR szBuffer[256];
+                
+                /* Copy drivers into list to simplify linked list modification */
+                for (i = 0, padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID, i++)
+                {
+                    /* at startup, all three conditions are true for all drivers in list */
+                    assert(padid->hNotifyWnd == NULL);
+                    assert(padid->hInstModule == NULL);
+                    assert(padid->lpDriverProc == NULL);
+                    driverList[i] = padid;
+                }
+
+                /* Query each of the priorities in turn. Alias key is in lowercase. 
+                   The general form of the priority record is the following:
+                   "PriorityN" --> "1, msacm.driveralias"
+                   where N is an integer, and the value is a string of the driver
+                   alias, prefixed by "1, " for an enabled driver, or "0, " for a
+                   disabled driver.
+                 */
+                for (i = 0; i < iNumDrivers; i++)
+                {
+                    static const WCHAR priorityTmpl[] = {'P','r','i','o','r','i','t','y','%','l','d','\0'};
+                    WCHAR szSubKey[17];
+                    
+                    snprintfW(szSubKey, 17, priorityTmpl, i + 1);
+                    lBufferLength = sizeof(szBuffer);
+                    lError = RegQueryValueExW(hPriorityKey, szSubKey, NULL, NULL, (LPBYTE)szBuffer, (LPDWORD)&lBufferLength);
+                    if (lError == ERROR_SUCCESS) {
+                        unsigned int iTargetPosition = i;
+                        unsigned int iCurrentPosition;
+                        WCHAR * pAlias;
+                        static const WCHAR sPrefix[] = {'m','s','a','c','m','.','\0'};
+                        
+                        /* Locate driver alias */
+                        pAlias = strstrW(szBuffer, sPrefix);
+                        if (pAlias != NULL) {
+                            for (iCurrentPosition = 0; iCurrentPosition < iNumDrivers; iCurrentPosition++) {
+                                if (strcmpiW(driverList[iCurrentPosition]->pszDriverAlias, pAlias) == 0) 
+                                    break;
+                            }
+                            if (iCurrentPosition < iNumDrivers && iTargetPosition != iCurrentPosition) {
+                                padid = driverList[iTargetPosition];
+                                driverList[iTargetPosition] = driverList[iCurrentPosition];
+                                driverList[iCurrentPosition] = padid;
+
+                                /* Locate enabled status */
+                                if (szBuffer[0] == '1') {
+                                    driverList[iTargetPosition]->fdwSupport &= ~ACMDRIVERDETAILS_SUPPORTF_DISABLED;
+                                } else if (szBuffer[0] == '0') {
+                                    driverList[iTargetPosition]->fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                RegCloseKey(hPriorityKey);
+                
+                /* Re-assign pointers so that linked list traverses the ordered array */
+                for (i = 0; i < iNumDrivers; i++) {
+                    driverList[i]->pPrevACMDriverID = (i > 0) ? driverList[i - 1] : NULL;
+                    driverList[i]->pNextACMDriverID = (i < iNumDrivers - 1) ? driverList[i + 1] : NULL;
+                }
+                MSACM_pFirstACMDriverID = driverList[0];
+                MSACM_pLastACMDriverID = driverList[iNumDrivers - 1];
+            }
+
+            HeapFree(MSACM_hHeap, 0, driverList);
+        }
+    }
+}
+
+/***********************************************************************
+ *           MSACM_WriteCurrentPriorities()
+ * Writes out current order of driver priorities to registry key:
+ * HKCU\\Software\\Microsoft\\Multimedia\\Audio Compression Manager\\Priority v4.00
+ */
+void MSACM_WriteCurrentPriorities(void)
+{
+    LONG lError;
+    HKEY hPriorityKey;
+    static const WCHAR basePriorityKey[] = {
+        'S','o','f','t','w','a','r','e','\\',
+        'M','i','c','r','o','s','o','f','t','\\',
+        'M','u','l','t','i','m','e','d','i','a','\\',
+        'A','u','d','i','o',' ','C','o','m','p','r','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
+        'P','r','i','o','r','i','t','y',' ','v','4','.','0','0','\0'
+    };
+    PWINE_ACMDRIVERID padid;
+    DWORD dwPriorityCounter;
+    static const WCHAR priorityTmpl[] = {'P','r','i','o','r','i','t','y','%','l','d','\0'};
+    static const WCHAR valueTmpl[] = {'%','c',',',' ','%','s','\0'};
+    static const WCHAR converterAlias[] = {'I','n','t','e','r','n','a','l',' ','P','C','M',' ','C','o','n','v','e','r','t','e','r','\0'};
+    WCHAR szSubKey[17];
+    WCHAR szBuffer[256];
+
+    /* Delete ACM priority key and create it anew */
+    lError = RegDeleteKeyW(HKEY_CURRENT_USER, basePriorityKey);
+    if (lError != ERROR_SUCCESS && lError != ERROR_FILE_NOT_FOUND) {
+        ERR("unable to remove current key %s (0x%08lx) - priority changes won't persist past application end.\n",
+            debugstr_w(basePriorityKey), lError);
+        return;
+    }
+    lError = RegCreateKeyW(HKEY_CURRENT_USER, basePriorityKey, &hPriorityKey);
+    if (lError != ERROR_SUCCESS) {
+        ERR("unable to create key %s (0x%08lx) - priority changes won't persist past application end.\n",
+            debugstr_w(basePriorityKey), lError);
+        return;
+    }
+    
+    /* Write current list of priorities */
+    for (dwPriorityCounter = 0, padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {        
+        if (padid->hNotifyWnd) continue;
+        if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL) continue;
+        if (padid->pszDriverAlias == NULL) continue;    /* internal PCM converter is last */
+
+        /* Build required value name */
+        dwPriorityCounter++;
+        snprintfW(szSubKey, 17, priorityTmpl, dwPriorityCounter);
+        
+        /* Value has a 1 in front for enabled drivers and 0 for disabled drivers */
+        snprintfW(szBuffer, 256, valueTmpl, (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ? '0' : '1', padid->pszDriverAlias);
+        strlwrW(szBuffer);
+        
+        lError = RegSetValueExW(hPriorityKey, szSubKey, 0, REG_SZ, (BYTE *)szBuffer, (strlenW(szBuffer) + 1) * sizeof(WCHAR));
+        if (lError != ERROR_SUCCESS) {
+            ERR("unable to write value for %s under key %s (0x%08lx)\n",
+                debugstr_w(padid->pszDriverAlias), debugstr_w(basePriorityKey), lError);
+        }
+    }
+    
+    /* Build required value name */
+    dwPriorityCounter++;
+    snprintfW(szSubKey, 17, priorityTmpl, dwPriorityCounter);
+        
+    /* Value has a 1 in front for enabled drivers and 0 for disabled drivers */
+    snprintfW(szBuffer, 256, valueTmpl, '1', converterAlias);
+        
+    lError = RegSetValueExW(hPriorityKey, szSubKey, 0, REG_SZ, (BYTE *)szBuffer, (strlenW(szBuffer) + 1) * sizeof(WCHAR));
+    if (lError != ERROR_SUCCESS) {
+        ERR("unable to write value for %s under key %s (0x%08lx)\n",
+            debugstr_w(converterAlias), debugstr_w(basePriorityKey), lError);
+    }
+    RegCloseKey(hPriorityKey);
 }
 
 /***********************************************************************
diff -ur wine-0.9.5-cvs/dlls/msacm/msacm32_main.c wine-0.9.5-cvs-patch/dlls/msacm/msacm32_main.c
--- wine-0.9.5-cvs/dlls/msacm/msacm32_main.c	2006-01-03 10:17:18.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/msacm/msacm32_main.c	2006-01-09 21:17:07.000000000 -0500
@@ -123,7 +123,8 @@
 	if (hao) return MMSYSERR_INVALHANDLE;
         if (!pMetric) return MMSYSERR_INVALPARAM;
 	for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID)
-	    if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && CheckLocal(padid))
+	    if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && CheckLocal(padid)
+	       && !(!bLocal && ((padid)->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL)))
 		val++;
 	*(LPDWORD)pMetric = val;
 	break;
@@ -235,10 +236,23 @@
         }
         break;
         
+    case ACM_METRIC_DRIVER_SUPPORT:
+        /* Return fdwSupport for driver */
+        if (!hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+        mmr = MMSYSERR_INVALHANDLE;
+        for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+            if (padid == (PWINE_ACMDRIVERID)hao) {
+                *(LPDWORD)pMetric = padid->fdwSupport;
+                mmr = MMSYSERR_NOERROR;
+                break;
+            }
+        }
+        break;
+
     case ACM_METRIC_HARDWARE_WAVE_INPUT:
     case ACM_METRIC_HARDWARE_WAVE_OUTPUT:
     case ACM_METRIC_MAX_SIZE_FILTER:
-    case ACM_METRIC_DRIVER_SUPPORT:
     default:
 	FIXME("(%p, %d, %p): stub\n", hao, uMetric, pMetric);
 	mmr = MMSYSERR_NOTSUPPORTED;
diff -ur wine-0.9.5-cvs/dlls/msacm/pcmconverter.c wine-0.9.5-cvs-patch/dlls/msacm/pcmconverter.c
--- wine-0.9.5-cvs/dlls/msacm/pcmconverter.c	2005-11-28 15:57:25.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/msacm/pcmconverter.c	2006-01-09 21:17:07.000000000 -0500
@@ -945,12 +945,20 @@
     switch (adss->fdwSize) {
     case ACM_STREAMSIZEF_DESTINATION:
 	/* cbDstLength => cbSrcLength */
+	if (!adsi->pwfxDst->nAvgBytesPerSec) {
+	    ERR("adsi->pwfxDst->nAvgBytesPerSec == %lu\n", adsi->pwfxDst->nAvgBytesPerSec);
+	    return MMSYSERR_INVALPARAM;
+	}
 	adss->cbSrcLength = PCM_round(adss->cbDstLength & dstMask,
 				      adsi->pwfxSrc->nAvgBytesPerSec,
 				      adsi->pwfxDst->nAvgBytesPerSec) & srcMask;
 	break;
     case ACM_STREAMSIZEF_SOURCE:
 	/* cbSrcLength => cbDstLength */
+	if (!adsi->pwfxSrc->nAvgBytesPerSec) {
+	    ERR("adsi->pwfxSrc->nAvgBytesPerSec == %lu\n", adsi->pwfxSrc->nAvgBytesPerSec);
+	    return MMSYSERR_INVALPARAM;
+	}
 	adss->cbDstLength =  PCM_round(adss->cbSrcLength & srcMask,
 				       adsi->pwfxDst->nAvgBytesPerSec,
 				       adsi->pwfxSrc->nAvgBytesPerSec) & dstMask;
diff -ur wine-0.9.5-cvs/dlls/msacm/wineacm.h wine-0.9.5-cvs-patch/dlls/msacm/wineacm.h
--- wine-0.9.5-cvs/dlls/msacm/wineacm.h	2004-12-13 16:19:02.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/msacm/wineacm.h	2006-01-09 21:58:12.000000000 -0500
@@ -319,6 +319,12 @@
     LPWSTR		pszDriverAlias;
     LPWSTR              pszFileName;
     HINSTANCE		hInstModule;          /* NULL if global */
+    DRIVERPROC          lpDriverProc;        /* acmDriverProc supplied by application, if any */
+
+    /* fields used in the case of ACM_DRIVERADDF_NOTIFYHWND */
+    HWND                hNotifyWnd;          /* Window to notify on ACM events: driver add, driver removal, priority change */
+    DWORD               dwNotifyMsg;         /* Notification message to send to window */
+
     PWINE_ACMDRIVER     pACMDriverList;
     PWINE_ACMDRIVERID   pNextACMDriverID;
     PWINE_ACMDRIVERID	pPrevACMDriverID;
@@ -337,7 +343,8 @@
 extern PWINE_ACMDRIVERID MSACM_pFirstACMDriverID;
 extern PWINE_ACMDRIVERID MSACM_pLastACMDriverID;
 extern PWINE_ACMDRIVERID MSACM_RegisterDriver(LPCWSTR pszDriverAlias, LPCWSTR pszFileName,
-					      HINSTANCE hinstModule);
+					      HINSTANCE hinstModule, DRIVERPROC lpDriverProc,
+					      BOOL bLocalDriver);
 extern void MSACM_RegisterAllDrivers(void);
 extern PWINE_ACMDRIVERID MSACM_UnregisterDriver(PWINE_ACMDRIVERID p);
 extern void MSACM_UnregisterAllDrivers(void);
@@ -348,6 +355,16 @@
 extern MMRESULT MSACM_Message(HACMDRIVER, UINT, LPARAM, LPARAM);
 extern BOOL MSACM_FindFormatTagInCache(WINE_ACMDRIVERID*, DWORD, LPDWORD);
 
+extern PWINE_ACMDRIVERID MSACM_RegisterNotificationWindow(HWND hNotifyWnd, DWORD dwNotifyMsg);
+extern void MSACM_BroadcastNotification(void);
+extern void MSACM_DisableNotifications(void);
+extern void MSACM_EnableNotifications(void);
+
+extern void MSACM_RePositionDriver(PWINE_ACMDRIVERID, DWORD);
+extern void MSACM_WriteCurrentPriorities(void);
+
+extern PWINE_ACMDRIVERID MSACM_RegisterDriverFromRegistry(LPCWSTR pszRegEntry);
+
 /* From msacm32.c */
 extern HINSTANCE MSACM_hInstance32;
 
diff -ur wine-0.9.5-cvs/dlls/winmm/driver.c wine-0.9.5-cvs-patch/dlls/winmm/driver.c
--- wine-0.9.5-cvs/dlls/winmm/driver.c	2005-12-19 09:54:38.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/winmm/driver.c	2006-01-09 21:17:08.000000000 -0500
@@ -308,6 +308,80 @@
 }
 
 /**************************************************************************
+ *				wineAddDriver		[WINE specific]
+ *
+ * Tries to create a driver from an existing hModule, with an specified DriverProc
+ */
+HDRVR WINAPI wineAddDriver(HMODULE hModule, DRIVERPROC lpDriverProc, LPARAM lParam2)
+{
+    LPWINE_DRIVER 	lpDrv = NULL;
+    LPWSTR		ptr = NULL;
+    LPCSTR		cause = 0;
+    HMODULE             hModuleRef2 = NULL;
+
+    TRACE("(%p, %p, %08lX);\n", hModule, lpDriverProc, lParam2);
+
+    lpDrv = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_DRIVER));
+    if (lpDrv == NULL) {cause = "OOM"; goto exit;}
+
+    lpDrv->d.d32.lpDrvProc = lpDriverProc;
+    if (lpDrv->d.d32.lpDrvProc == NULL) {cause = "no DriverProc"; goto exit;}
+
+    lpDrv->dwFlags          = 0;
+    lpDrv->d.d32.hModule    = hModule;
+    lpDrv->d.d32.dwDriverID = 0;
+
+    /* Roundabout way to increment the reference count for the module */
+    ptr = HeapAlloc(GetProcessHeap(), 0, 1024 * sizeof(WCHAR));
+    if (lpDrv == NULL) {cause = "OOM"; goto exit;}
+    if (!GetModuleFileNameW(hModule, ptr, 1024 * sizeof(WCHAR)))
+    {
+        cause = "GetModuleFileNameW failed"; goto exit;
+    }
+    if ((hModuleRef2 = LoadLibraryW(ptr)) == NULL)
+    {
+        cause = "LoadLibraryW failed"; goto exit;
+    }
+
+    /* Win32 installable drivers must support a two phase opening scheme:
+     * + first open with NULL as lParam2 (session instance),
+     * + then do a second open with the real non null lParam2)
+     */
+    if (DRIVER_GetNumberOfModuleRefs(lpDrv->d.d32.hModule, NULL) == 0 && lParam2)
+    {
+        LPWINE_DRIVER   ret;
+
+        if (!DRIVER_AddToList(lpDrv, (LPARAM)ptr, 0L))
+        {
+            cause = "load0 failed";
+            goto exit;
+        }
+        ret = (LPWINE_DRIVER)wineAddDriver(hModule, lpDriverProc, lParam2);
+        if (!ret)
+        {
+            CloseDriver((HDRVR)lpDrv, 0L, 0L);
+            cause = "load1 failed";
+            goto exit;
+        }
+        lpDrv->dwFlags |= WINE_GDF_SESSION;
+        return (HDRVR)ret;
+    }
+
+    if (!DRIVER_AddToList(lpDrv, (LPARAM)ptr, lParam2))
+    {cause = "load failed"; goto exit;}
+
+    if (ptr != NULL) HeapFree(GetProcessHeap(), 0, ptr);
+    TRACE("=> %p\n", lpDrv);
+    return (HDRVR)lpDrv;
+ exit:
+    if (hModuleRef2 != NULL) FreeLibrary(hModuleRef2);
+    if (ptr != NULL) HeapFree(GetProcessHeap(), 0, ptr);
+    HeapFree(GetProcessHeap(), 0, lpDrv);
+    ERR("Unable to load 32 bit module %p: %s\n", hModule, cause);
+    return NULL;
+}
+
+/**************************************************************************
  *				OpenDriverA		        [EMAIL PROTECTED]
  *				DrvOpenA			[EMAIL PROTECTED]
  * (0,1,DRV_LOAD  ,0       ,0)
diff -ur wine-0.9.5-cvs/dlls/winmm/winmm.spec wine-0.9.5-cvs-patch/dlls/winmm/winmm.spec
--- wine-0.9.5-cvs/dlls/winmm/winmm.spec	2005-06-22 13:38:23.000000000 -0500
+++ wine-0.9.5-cvs-patch/dlls/winmm/winmm.spec	2006-01-09 21:17:08.000000000 -0500
@@ -209,3 +209,4 @@
 # winmmDbgOut
 # winmmSetDebugLevel
 # wod32Message
+@ stdcall wineAddDriver(long long long)
diff -ur wine-0.9.5-cvs/include/msacm.h wine-0.9.5-cvs-patch/include/msacm.h
--- wine-0.9.5-cvs/include/msacm.h	2003-09-05 18:15:44.000000000 -0500
+++ wine-0.9.5-cvs-patch/include/msacm.h	2006-01-09 21:17:08.000000000 -0500
@@ -40,6 +40,7 @@
 #define MM_ACM_CLOSE MM_STREAM_CLOSE
 #define MM_ACM_DONE  MM_STREAM_DONE
 
+#define ACM_DRIVERADDF_NAME       0x00000001L
 #define ACM_DRIVERADDF_FUNCTION   0x00000003L
 #define ACM_DRIVERADDF_NOTIFYHWND 0x00000004L
 #define ACM_DRIVERADDF_TYPEMASK   0x00000007L


Reply via email to