Sync to Wine-20050419:
authorGé van Geldorp <ge@gse.nl>
Fri, 6 May 2005 19:16:10 +0000 (19:16 +0000)
committerGé van Geldorp <ge@gse.nl>
Fri, 6 May 2005 19:16:10 +0000 (19:16 +0000)
Robert Reif <reif@earthlink.net>
- Stub out DRVM_MAPPER_RECONFIGURE support.
- Correctly handle where waveOutGetPosition changes timepos.wType
  because the requested type is not supported.
- Added Jeremy White's waveOutGetPosition fix to waveInGetPosition.
- Fix memory leak in error path.
- Provide default implementation of waveInPrepareHeader and
  waveInUnprepareHeader if driver doesn't support them.
Jose Manuel Ferrer Ortiz <jmfo1982@yahoo.es>
- Spanish translations updated.
Peter Berg Larsen <pebl@math.ku.dk>
- Assorted memleak fixes. Found on Michael Stefaniuc smatch list.
Jeremy White <jwhite@codeweavers.com>
- Do not fallback to defaults if a driver, mapper, or midi is specified
  in the registry; consolidate MMDRV_Init() into a single function.
Robert Reif <reif@earthlink.net>
- Fix memory leak when there are too many drivers.
Vincent Beron <vberon@mecano.gme.usherb.ca>
- Correct and complete some api documentation.
Filip Navara <xnavara@volny.cz>
- Specify correct buffer size in GetPrivateProfileStringW calls.
Jakob Eriksson <jakov@vmlinux.org>
- Get rid of HeapAlloc casts.
Jason Edmeades <us@the-edmeades.demon.co.uk>
- Avoid trap in mixerGetLineControlsA when cControls is uninitialized
  and MIXER_GETLINECONTROLSSF_ONEBYTYPE requested.

svn path=/trunk/; revision=15056

reactos/lib/winmm/driver.c
reactos/lib/winmm/lolvldrv.c
reactos/lib/winmm/mci.c
reactos/lib/winmm/mmio.c
reactos/lib/winmm/mmsystem.c
reactos/lib/winmm/time.c
reactos/lib/winmm/wavemap/wavemap.c
reactos/lib/winmm/winemm.h
reactos/lib/winmm/winmm.c
reactos/lib/winmm/winmm_Es.rc

index 9bda8da..d13a2b9 100644 (file)
@@ -308,13 +308,13 @@ HDRVR WINAPI OpenDriverA(LPCSTR lpDriverName, LPCSTR lpSectionName, LPARAM lPara
     INT                 len;\r
     LPWSTR             dn = NULL;\r
     LPWSTR             sn = NULL;\r
     INT                 len;\r
     LPWSTR             dn = NULL;\r
     LPWSTR             sn = NULL;\r
-    HDRVR              ret;\r
+    HDRVR              ret = 0;\r
 \r
     if (lpDriverName)\r
     {\r
         len = MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, NULL, 0 );\r
         dn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );\r
 \r
     if (lpDriverName)\r
     {\r
         len = MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, NULL, 0 );\r
         dn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );\r
-        if (!dn) return 0;\r
+        if (!dn) goto done;\r
         MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, dn, len );\r
     }\r
 \r
         MultiByteToWideChar( CP_ACP, 0, lpDriverName, -1, dn, len );\r
     }\r
 \r
@@ -322,14 +322,15 @@ HDRVR WINAPI OpenDriverA(LPCSTR lpDriverName, LPCSTR lpSectionName, LPARAM lPara
     {\r
         len = MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, NULL, 0 );\r
         sn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );\r
     {\r
         len = MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, NULL, 0 );\r
         sn = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );\r
-        if (!sn) return 0;\r
+        if (!sn) goto done;\r
         MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, sn, len );\r
     }\r
 \r
     ret = OpenDriver(dn, sn, lParam);\r
 \r
         MultiByteToWideChar( CP_ACP, 0, lpSectionName, -1, sn, len );\r
     }\r
 \r
     ret = OpenDriver(dn, sn, lParam);\r
 \r
-    if (dn) HeapFree(GetProcessHeap(), 0, dn);\r
-    if (sn) HeapFree(GetProcessHeap(), 0, sn);\r
+done:\r
+    HeapFree(GetProcessHeap(), 0, dn);\r
+    HeapFree(GetProcessHeap(), 0, sn);\r
     return ret;\r
 }\r
 \r
     return ret;\r
 }\r
 \r
index 50c029b..335e15d 100644 (file)
@@ -309,6 +309,7 @@ LPWINE_MLD  MMDRV_Alloc(UINT size, UINT type, LPHANDLE hndl, DWORD* dwFlags,
     if (i == MAX_MM_MLDRVS) {\r
        /* the MM_MLDrvs table could be made growable in the future if needed */\r
        ERR("Too many open drivers\n");\r
     if (i == MAX_MM_MLDRVS) {\r
        /* the MM_MLDrvs table could be made growable in the future if needed */\r
        ERR("Too many open drivers\n");\r
+        HeapFree(GetProcessHeap(), 0, mld);\r
        return NULL;\r
     }\r
     MM_MLDrvs[i] = mld;\r
        return NULL;\r
     }\r
     MM_MLDrvs[i] = mld;\r
@@ -714,78 +715,60 @@ static    BOOL    MMDRV_Install(LPCSTR drvRegName, LPCSTR drvFileName, BOOL bIsMapper)
 }\r
 \r
 /**************************************************************************\r
 }\r
 \r
 /**************************************************************************\r
- *                             MMDRV_InitFromRegistry          [internal]\r
+ *                             MMDRV_Init\r
  */\r
  */\r
-static BOOL    MMDRV_InitFromRegistry(void)\r
+BOOL   MMDRV_Init(void)\r
 {\r
     HKEY       hKey;\r
 {\r
     HKEY       hKey;\r
-    char       buffer[256];\r
+    char       driver_buffer[256];\r
+    char       mapper_buffer[256];\r
+    char       midi_buffer[256];\r
     char*      p1;\r
     char*      p2;\r
     DWORD      type, size;\r
     BOOL       ret = FALSE;\r
     TRACE("()\n");\r
 \r
     char*      p1;\r
     char*      p2;\r
     DWORD      type, size;\r
     BOOL       ret = FALSE;\r
     TRACE("()\n");\r
 \r
-    if (RegCreateKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\WinMM", &hKey)) {\r
-       TRACE("Cannot open WinMM config key\n");\r
-       return FALSE;\r
-    }\r
+    strcpy(driver_buffer, WINE_DEFAULT_WINMM_DRIVER);\r
+    strcpy(mapper_buffer, WINE_DEFAULT_WINMM_MAPPER);\r
+    strcpy(midi_buffer, WINE_DEFAULT_WINMM_MIDI);\r
 \r
 \r
-    size = sizeof(buffer);\r
-    if (!RegQueryValueExA(hKey, "Drivers", 0, &type, (LPVOID)buffer, &size)) {\r
-       p1 = buffer;\r
-       while (p1) {\r
-           p2 = strchr(p1, ';');\r
-           if (p2) *p2++ = '\0';\r
-           ret |= MMDRV_Install(p1, p1, FALSE);\r
-           p1 = p2;\r
-       }\r
-    }\r
+    if (! RegCreateKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\WinMM", &hKey)) {\r
+        size = sizeof(driver_buffer);\r
+        if (RegQueryValueExA(hKey, "Drivers", 0, &type, (LPVOID)driver_buffer, &size)) \r
+            strcpy(driver_buffer, WINE_DEFAULT_WINMM_DRIVER);\r
 \r
 \r
-    /* finish with mappers */\r
-    size = sizeof(buffer);\r
-    if (!RegQueryValueExA(hKey, "WaveMapper", 0, &type, (LPVOID)buffer, &size))\r
-       ret |= MMDRV_Install("wavemapper", buffer, TRUE);\r
-    size = sizeof(buffer);\r
-    if (!RegQueryValueExA(hKey, "MidiMapper", 0, &type, (LPVOID)buffer, &size))\r
-       ret |= MMDRV_Install("midimapper", buffer, TRUE);\r
+        /* finish with mappers */\r
+        size = sizeof(mapper_buffer);\r
+        if (RegQueryValueExA(hKey, "WaveMapper", 0, &type, (LPVOID)mapper_buffer, &size))\r
+            strcpy(mapper_buffer, WINE_DEFAULT_WINMM_MAPPER);\r
 \r
 \r
-    RegCloseKey(hKey);\r
+        size = sizeof(midi_buffer);\r
+        if (RegQueryValueExA(hKey, "MidiMapper", 0, &type, (LPVOID)midi_buffer, &size))\r
+            strcpy(midi_buffer, WINE_DEFAULT_WINMM_MIDI);\r
 \r
 \r
-    return ret;\r
-}\r
+        RegCloseKey(hKey);\r
+    }\r
 \r
 \r
-/**************************************************************************\r
- *                             MMDRV_InitHardcoded             [internal]\r
- */\r
-static BOOL    MMDRV_InitHardcoded(void)\r
-{\r
-    TRACE("()\n");\r
-    /* first load hardware drivers */\r
 #ifndef __REACTOS__\r
 #ifndef __REACTOS__\r
-    MMDRV_Install("wineoss.drv",       "wineoss.drv",  FALSE);\r
-#endif /* __REACTOS__ */\r
+    p1 = driver_buffer;\r
+    while (p1) {\r
+        p2 = strchr(p1, ';');\r
+        if (p2) *p2++ = '\0';\r
+        ret |= MMDRV_Install(p1, p1, FALSE);\r
+        p1 = p2;\r
+    }\r
+#endif\r
 \r
 #ifdef __REACTOS__\r
     // AG: TESTING:\r
 \r
 #ifdef __REACTOS__\r
     // AG: TESTING:\r
-    MMDRV_Install("mmdrv.dll", "mmdrv.dll", FALSE);\r
+    ret |= MMDRV_Install("mmdrv.dll", "mmdrv.dll", FALSE);\r
 #endif\r
 \r
 #endif\r
 \r
-    /* finish with mappers */\r
-    MMDRV_Install("wavemapper",     "msacm32.dll",    TRUE);\r
-    MMDRV_Install("midimapper",     "midimap.dll",  TRUE);\r
-\r
-    return TRUE;\r
-}\r
+    ret |= MMDRV_Install("wavemapper", mapper_buffer, TRUE);\r
+    ret |= MMDRV_Install("midimapper", midi_buffer, TRUE);\r
+    return ret;\r
 \r
 \r
-/**************************************************************************\r
- *                             MMDRV_Init                      [internal]\r
- */\r
-BOOL   MMDRV_Init(void)\r
-{\r
-    TRACE("()\n");\r
-    /* FIXME: MMDRV_InitFromRegistry shall be MMDRV_Init in a near future */\r
-    return MMDRV_InitFromRegistry() || MMDRV_InitHardcoded();\r
 }\r
 \r
 /******************************************************************\r
 }\r
 \r
 /******************************************************************\r
index 6cf7a63..c46be05 100644 (file)
@@ -1458,7 +1458,11 @@ DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet,
     if (lpstrRet)\r
     {\r
         lpwstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR));\r
     if (lpstrRet)\r
     {\r
         lpwstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR));\r
-        if (!lpwstrRet) return MCIERR_OUT_OF_MEMORY;\r
+        if (!lpwstrRet) {\r
+            WARN("no memory\n");\r
+            HeapFree( GetProcessHeap(), 0, lpwstrCommand );\r
+            return MCIERR_OUT_OF_MEMORY;\r
+        }\r
     }\r
     ret = mciSendStringW(lpwstrCommand, lpwstrRet, uRetLen, hwndCallback);\r
     if (lpwstrRet)\r
     }\r
     ret = mciSendStringW(lpwstrCommand, lpwstrRet, uRetLen, hwndCallback);\r
     if (lpwstrRet)\r
index 9a05f1f..7cf1e11 100644 (file)
@@ -1360,26 +1360,27 @@ MMRESULT WINAPI mmioRenameW(LPCWSTR szFileName, LPCWSTR szNewFileName,
 {\r
     LPSTR      szFn = NULL;\r
     LPSTR      sznFn = NULL;\r
 {\r
     LPSTR      szFn = NULL;\r
     LPSTR      sznFn = NULL;\r
-    UINT       ret;\r
+    UINT       ret = MMSYSERR_NOMEM;\r
     INT         len;\r
 \r
     if (szFileName)\r
     {\r
         len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );\r
         szFn = HeapAlloc( GetProcessHeap(), 0, len );\r
     INT         len;\r
 \r
     if (szFileName)\r
     {\r
         len = WideCharToMultiByte( CP_ACP, 0, szFileName, -1, NULL, 0, NULL, NULL );\r
         szFn = HeapAlloc( GetProcessHeap(), 0, len );\r
-        if (!szFn) return MMSYSERR_NOMEM;\r
+        if (!szFn) goto done;\r
         WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );\r
     }\r
     if (szNewFileName)\r
     {\r
         len = WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, NULL, 0, NULL, NULL );\r
         sznFn = HeapAlloc( GetProcessHeap(), 0, len );\r
         WideCharToMultiByte( CP_ACP, 0, szFileName, -1, szFn, len, NULL, NULL );\r
     }\r
     if (szNewFileName)\r
     {\r
         len = WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, NULL, 0, NULL, NULL );\r
         sznFn = HeapAlloc( GetProcessHeap(), 0, len );\r
-        if (!sznFn) return MMSYSERR_NOMEM;\r
+        if (!sznFn) goto done;\r
         WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, sznFn, len, NULL, NULL );\r
     }\r
 \r
     ret = mmioRenameA(szFn, sznFn, lpmmioinfo, dwFlags);\r
 \r
         WideCharToMultiByte( CP_ACP, 0, szNewFileName, -1, sznFn, len, NULL, NULL );\r
     }\r
 \r
     ret = mmioRenameA(szFn, sznFn, lpmmioinfo, dwFlags);\r
 \r
+done:\r
     HeapFree(GetProcessHeap(),0,szFn);\r
     HeapFree(GetProcessHeap(),0,sznFn);\r
     return ret;\r
     HeapFree(GetProcessHeap(),0,szFn);\r
     HeapFree(GetProcessHeap(),0,sznFn);\r
     return ret;\r
index 1e4e41d..98eb8e1 100644 (file)
@@ -731,7 +731,7 @@ UINT16 WINAPI midiOutGetDevCaps16(UINT16 uDeviceID, LPMIDIOUTCAPS16 lpCaps,
 \r
 /**************************************************************************\r
  *                             midiOutGetErrorText     [MMSYSTEM.203]\r
 \r
 /**************************************************************************\r
  *                             midiOutGetErrorText     [MMSYSTEM.203]\r
- *                             midiInGetErrorText      [MMSYSTEM.203]\r
+ *                             midiInGetErrorText      [MMSYSTEM.303]\r
  */\r
 UINT16 WINAPI midiOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)\r
 {\r
  */\r
 UINT16 WINAPI midiOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)\r
 {\r
@@ -1231,7 +1231,7 @@ UINT16 WINAPI waveOutGetDevCaps16(UINT16 uDeviceID,
 \r
 /**************************************************************************\r
  *                             waveOutGetErrorText     [MMSYSTEM.403]\r
 \r
 /**************************************************************************\r
  *                             waveOutGetErrorText     [MMSYSTEM.403]\r
- *                             waveInGetErrorText      [MMSYSTEM.403]\r
+ *                             waveInGetErrorText      [MMSYSTEM.503]\r
  */\r
 UINT16 WINAPI waveOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)\r
 {\r
  */\r
 UINT16 WINAPI waveOutGetErrorText16(UINT16 uError, LPSTR lpText, UINT16 uSize)\r
 {\r
@@ -2260,7 +2260,7 @@ static WINMM_MapType DRIVER_MapMsg32To16(WORD wMsg, DWORD* lParam1, DWORD* lPara
             LPDRVCONFIGINFO    dci32 = (LPDRVCONFIGINFO)(*lParam2);\r
 \r
            if (dci16) {\r
             LPDRVCONFIGINFO    dci32 = (LPDRVCONFIGINFO)(*lParam2);\r
 \r
            if (dci16) {\r
-               LPSTR str1;\r
+               LPSTR str1 = NULL,str2;\r
                 INT len;\r
                dci16->dwDCISize = sizeof(DRVCONFIGINFO16);\r
 \r
                 INT len;\r
                dci16->dwDCISize = sizeof(DRVCONFIGINFO16);\r
 \r
@@ -2271,6 +2271,7 @@ static WINMM_MapType DRIVER_MapMsg32To16(WORD wMsg, DWORD* lParam1, DWORD* lPara
                         WideCharToMultiByte( CP_ACP, 0, dci32->lpszDCISectionName, -1, str1, len, NULL, NULL );\r
                         dci16->lpszDCISectionName = MapLS( str1 );\r
                     } else {\r
                         WideCharToMultiByte( CP_ACP, 0, dci32->lpszDCISectionName, -1, str1, len, NULL, NULL );\r
                         dci16->lpszDCISectionName = MapLS( str1 );\r
                     } else {\r
+                        HeapFree( GetProcessHeap(), 0, dci16);\r
                         return WINMM_MAP_NOMEM;\r
                     }\r
                } else {\r
                         return WINMM_MAP_NOMEM;\r
                     }\r
                } else {\r
@@ -2278,11 +2279,13 @@ static WINMM_MapType DRIVER_MapMsg32To16(WORD wMsg, DWORD* lParam1, DWORD* lPara
                }\r
                 if (dci32->lpszDCIAliasName) {\r
                     len = WideCharToMultiByte( CP_ACP, 0, dci32->lpszDCIAliasName, -1, NULL, 0, NULL, NULL );\r
                }\r
                 if (dci32->lpszDCIAliasName) {\r
                     len = WideCharToMultiByte( CP_ACP, 0, dci32->lpszDCIAliasName, -1, NULL, 0, NULL, NULL );\r
-                    str1 = HeapAlloc( GetProcessHeap(), 0, len );\r
-                    if (str1) {\r
-                        WideCharToMultiByte( CP_ACP, 0, dci32->lpszDCIAliasName, -1, str1, len, NULL, NULL );\r
-                        dci16->lpszDCIAliasName = MapLS( str1 );\r
+                    str2 = HeapAlloc( GetProcessHeap(), 0, len );\r
+                    if (str2) {\r
+                        WideCharToMultiByte( CP_ACP, 0, dci32->lpszDCIAliasName, -1, str2, len, NULL, NULL );\r
+                        dci16->lpszDCIAliasName = MapLS( str2 );\r
                     } else {\r
                     } else {\r
+                        HeapFree( GetProcessHeap(), 0, str1);\r
+                        HeapFree( GetProcessHeap(), 0, dci16);\r
                         return WINMM_MAP_NOMEM;\r
                     }\r
                } else {\r
                         return WINMM_MAP_NOMEM;\r
                     }\r
                } else {\r
index dbd912e..b97fa19 100644 (file)
@@ -315,11 +315,11 @@ WORD      TIME_SetEventInternal(UINT wDelay, UINT wResol,
 \r
     TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);\r
 \r
 \r
     TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);\r
 \r
-    lpNewTimer = (LPWINE_TIMERENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY));\r
-    if (lpNewTimer == NULL)\r
+    if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)\r
        return 0;\r
 \r
        return 0;\r
 \r
-    if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)\r
+    lpNewTimer = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY));\r
+    if (lpNewTimer == NULL)\r
        return 0;\r
 \r
     TIME_MMTimeStart();\r
        return 0;\r
 \r
     TIME_MMTimeStart();\r
index 2ff43cd..5a3a72d 100644 (file)
@@ -166,6 +166,7 @@ static      DWORD   wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
     if (dwFlags & WAVE_MAPPED) {\r
        if (lpDesc->uMappedDeviceID >= ndhi) {\r
             WARN("invalid parameter: dwFlags WAVE_MAPPED\n");\r
     if (dwFlags & WAVE_MAPPED) {\r
        if (lpDesc->uMappedDeviceID >= ndhi) {\r
             WARN("invalid parameter: dwFlags WAVE_MAPPED\n");\r
+            HeapFree(GetProcessHeap(), 0, wom);\r
             return MMSYSERR_INVALPARAM;\r
         }\r
        ndlo = lpDesc->uMappedDeviceID;\r
             return MMSYSERR_INVALPARAM;\r
         }\r
        ndlo = lpDesc->uMappedDeviceID;\r
@@ -427,9 +428,10 @@ static     DWORD   wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
     if (lpTime->wType == TIME_MS)\r
         timepos.wType = TIME_BYTES;\r
 \r
     if (lpTime->wType == TIME_MS)\r
         timepos.wType = TIME_BYTES;\r
 \r
+    /* This can change timepos.wType if the requested type is not supported */\r
     val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);\r
 \r
     val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);\r
 \r
-    if (lpTime->wType == TIME_BYTES || lpTime->wType == TIME_MS)\r
+    if (timepos.wType == TIME_BYTES)\r
     {\r
         DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter;\r
         if (dwInnerSamplesPerOuter > 0)\r
     {\r
         DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter;\r
         if (dwInnerSamplesPerOuter > 0)\r
@@ -463,10 +465,12 @@ static    DWORD   wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
 \r
         /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */\r
         if (lpTime->wType == TIME_MS)\r
 \r
         /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */\r
         if (lpTime->wType == TIME_MS)\r
-            lpTime->u.cb = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter);\r
+            lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter);\r
+        else\r
+            lpTime->wType = TIME_BYTES;\r
     }\r
     }\r
-    else if (lpTime->wType == TIME_SAMPLES)\r
-        lpTime->u.cb = MulDiv(timepos.u.cb, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner);\r
+    else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)\r
+        lpTime->u.sample = MulDiv(timepos.u.sample, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner);\r
     else\r
         /* other time types don't require conversion */\r
         lpTime->u = timepos.u;\r
     else\r
         /* other time types don't require conversion */\r
         lpTime->u = timepos.u;\r
@@ -586,6 +590,13 @@ static  DWORD      wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
     return ret;\r
 }\r
 \r
     return ret;\r
 }\r
 \r
+static  DWORD   wodMapperReconfigure(WAVEMAPDATA* wom, DWORD dwParam1, DWORD dwParam2)\r
+{\r
+    FIXME("(%p %08lx %08lx) stub!\n", wom, dwParam1, dwParam2);\r
+\r
+    return MMSYSERR_NOERROR;\r
+}\r
+\r
 /**************************************************************************\r
  *                             wodMessage (MSACM.@)\r
  */\r
 /**************************************************************************\r
  *                             wodMessage (MSACM.@)\r
  */\r
@@ -621,6 +632,7 @@ DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
     case WODM_RESTART:         return wodRestart       ((WAVEMAPDATA*)dwUser);\r
     case WODM_RESET:           return wodReset         ((WAVEMAPDATA*)dwUser);\r
     case WODM_MAPPER_STATUS:   return wodMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);\r
     case WODM_RESTART:         return wodRestart       ((WAVEMAPDATA*)dwUser);\r
     case WODM_RESET:           return wodReset         ((WAVEMAPDATA*)dwUser);\r
     case WODM_MAPPER_STATUS:   return wodMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);\r
+    case DRVM_MAPPER_RECONFIGURE: return wodMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);\r
     /* known but not supported */\r
     case DRV_QUERYDEVICEINTERFACESIZE:\r
     case DRV_QUERYDEVICEINTERFACE:\r
     /* known but not supported */\r
     case DRV_QUERYDEVICEINTERFACESIZE:\r
     case DRV_QUERYDEVICEINTERFACE:\r
@@ -961,15 +973,62 @@ static    DWORD   widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwPara
 static DWORD   widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)\r
 {\r
     DWORD       val;\r
 static DWORD   widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)\r
 {\r
     DWORD       val;\r
-\r
+    MMTIME      timepos;\r
     TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);\r
 \r
     TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);\r
 \r
-    val = waveInGetPosition(wim->u.in.hInnerWave, lpTime, dwParam2);\r
-    if (lpTime->wType == TIME_BYTES)\r
-        lpTime->u.cb = MulDiv(lpTime->u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);\r
-    if (lpTime->wType == TIME_SAMPLES)\r
-        lpTime->u.cb = MulDiv(lpTime->u.cb, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);\r
-    /* other time types don't require conversion */\r
+    memcpy(&timepos, lpTime, sizeof(timepos));\r
+\r
+    /* For TIME_MS, we're going to recalculate using TIME_BYTES */\r
+    if (lpTime->wType == TIME_MS)\r
+        timepos.wType = TIME_BYTES;\r
+\r
+    /* This can change timepos.wType if the requested type is not supported */\r
+    val = waveInGetPosition(wim->u.in.hInnerWave, &timepos, dwParam2);\r
+\r
+    if (timepos.wType == TIME_BYTES)\r
+    {\r
+        DWORD dwInnerSamplesPerOuter = wim->nSamplesPerSecInner / wim->nSamplesPerSecOuter;\r
+        if (dwInnerSamplesPerOuter > 0)\r
+        {\r
+            DWORD dwInnerBytesPerSample = wim->avgSpeedInner / wim->nSamplesPerSecInner;\r
+            DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;\r
+            DWORD remainder = 0;\r
+\r
+            /* If we are up sampling (going from lower sample rate to higher),\r
+            **   we need to make a special accomodation for times when we've\r
+            **   written a partial output sample.  This happens frequently\r
+            **   to us because we use msacm to do our up sampling, and it\r
+            **   will up sample on an unaligned basis.\r
+            ** For example, if you convert a 2 byte wide 8,000 'outer'\r
+            **   buffer to a 2 byte wide 48,000 inner device, you would\r
+            **   expect 2 bytes of input to produce 12 bytes of output.\r
+            **   Instead, msacm will produce 8 bytes of output.\r
+            **   But reporting our position as 1 byte of output is\r
+            **   nonsensical; the output buffer position needs to be\r
+            **   aligned on outer sample size, and aggressively rounded up.\r
+            */\r
+            remainder = timepos.u.cb % dwInnerBytesPerOuterSample;\r
+            if (remainder > 0)\r
+            {\r
+                timepos.u.cb -= remainder;\r
+                timepos.u.cb += dwInnerBytesPerOuterSample;\r
+            }\r
+        }\r
+\r
+        lpTime->u.cb = MulDiv(timepos.u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);\r
+\r
+        /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */\r
+        if (lpTime->wType == TIME_MS)\r
+            lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wim->avgSpeedOuter);\r
+        else\r
+            lpTime->wType = TIME_BYTES;\r
+    }\r
+    else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)\r
+        lpTime->u.sample = MulDiv(timepos.u.sample, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);\r
+    else\r
+        /* other time types don't require conversion */\r
+        lpTime->u = timepos.u;\r
+\r
     return val;\r
 }\r
 \r
     return val;\r
 }\r
 \r
@@ -1058,6 +1117,13 @@ static  DWORD    widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
     return ret;\r
 }\r
 \r
     return ret;\r
 }\r
 \r
+static  DWORD   widMapperReconfigure(WAVEMAPDATA* wim, DWORD dwParam1, DWORD dwParam2)\r
+{\r
+    FIXME("(%p %08lx %08lx) stub!\n", wim, dwParam1, dwParam2);\r
+\r
+    return MMSYSERR_NOERROR;\r
+}\r
+\r
 /**************************************************************************\r
  *                             widMessage (MSACM.@)\r
  */\r
 /**************************************************************************\r
  *                             widMessage (MSACM.@)\r
  */\r
@@ -1088,6 +1154,7 @@ DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
     case WIDM_START:           return widStart         ((WAVEMAPDATA*)dwUser);\r
     case WIDM_STOP:            return widStop          ((WAVEMAPDATA*)dwUser);\r
     case WIDM_MAPPER_STATUS:   return widMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);\r
     case WIDM_START:           return widStart         ((WAVEMAPDATA*)dwUser);\r
     case WIDM_STOP:            return widStop          ((WAVEMAPDATA*)dwUser);\r
     case WIDM_MAPPER_STATUS:   return widMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);\r
+    case DRVM_MAPPER_RECONFIGURE: return widMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);\r
     /* known but not supported */\r
     case DRV_QUERYDEVICEINTERFACESIZE:\r
     case DRV_QUERYDEVICEINTERFACE:\r
     /* known but not supported */\r
     case DRV_QUERYDEVICEINTERFACESIZE:\r
     case DRV_QUERYDEVICEINTERFACE:\r
index b26a5c1..4363543 100644 (file)
 #include "winbase.h"\r
 #include "mmddk.h"\r
 \r
 #include "winbase.h"\r
 #include "mmddk.h"\r
 \r
+#define WINE_DEFAULT_WINMM_DRIVER     "wineoss.drv"\r
+#define WINE_DEFAULT_WINMM_MAPPER     "msacm.drv"\r
+#define WINE_DEFAULT_WINMM_MIDI       "midimap.drv"\r
+\r
 typedef DWORD (WINAPI *MessageProc16)(UINT16 wDevID, UINT16 wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);\r
 typedef DWORD (WINAPI *MessageProc32)(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);\r
 \r
 typedef DWORD (WINAPI *MessageProc16)(UINT16 wDevID, UINT16 wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);\r
 typedef DWORD (WINAPI *MessageProc32)(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);\r
 \r
index 76e4ef7..17f304e 100644 (file)
@@ -441,7 +441,7 @@ UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA
            if (lpmcdA->u.cMultipleItems != 0) {\r
                size *= lpmcdA->u.cMultipleItems;\r
            }\r
            if (lpmcdA->u.cMultipleItems != 0) {\r
                size *= lpmcdA->u.cMultipleItems;\r
            }\r
-           pDetailsW = (MIXERCONTROLDETAILS_LISTTEXTW *)HeapAlloc(GetProcessHeap(), 0, size);\r
+           pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);\r
             lpmcdA->paDetails = pDetailsW;\r
             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);\r
            /* set up lpmcd->paDetails */\r
             lpmcdA->paDetails = pDetailsW;\r
             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);\r
            /* set up lpmcd->paDetails */\r
@@ -492,7 +492,15 @@ UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
     mlcW.dwLineID = lpmlcA->dwLineID;\r
     mlcW.u.dwControlID = lpmlcA->u.dwControlID;\r
     mlcW.u.dwControlType = lpmlcA->u.dwControlType;\r
     mlcW.dwLineID = lpmlcA->dwLineID;\r
     mlcW.u.dwControlID = lpmlcA->u.dwControlID;\r
     mlcW.u.dwControlType = lpmlcA->u.dwControlType;\r
-    mlcW.cControls = lpmlcA->cControls;\r
+\r
+    /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,\r
+       the control count is assumed to be 1 - This is relied upon by a game,\r
+       "Dynomite Deluze"                                                    */\r
+    if (MIXER_GETLINECONTROLSF_ONEBYTYPE == fdwControls) {\r
+        mlcW.cControls = 1;\r
+    } else {\r
+        mlcW.cControls = lpmlcA->cControls;\r
+    }\r
     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);\r
     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,\r
                              mlcW.cControls * mlcW.cbmxctrl);\r
     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);\r
     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,\r
                              mlcW.cControls * mlcW.cbmxctrl);\r
@@ -2580,6 +2588,7 @@ UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
                                UINT uSize)\r
 {\r
     LPWINE_MLD         wmld;\r
                                UINT uSize)\r
 {\r
     LPWINE_MLD         wmld;\r
+    UINT                result;\r
 \r
     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
 \r
 \r
     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
 \r
@@ -2589,9 +2598,18 @@ UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
        return MMSYSERR_INVALHANDLE;\r
 \r
     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
        return MMSYSERR_INVALHANDLE;\r
 \r
+    if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,\r
+                                uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)\r
+        return result;\r
+\r
+    if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)\r
+        return WAVERR_STILLPLAYING;\r
+\r
+    lpWaveInHdr->dwFlags |= WHDR_PREPARED;\r
+    lpWaveInHdr->dwFlags &= ~WHDR_DONE;\r
     lpWaveInHdr->dwBytesRecorded = 0;\r
 \r
     lpWaveInHdr->dwBytesRecorded = 0;\r
 \r
-    return MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);\r
+    return MMSYSERR_NOERROR;\r
 }\r
 \r
 /**************************************************************************\r
 }\r
 \r
 /**************************************************************************\r
@@ -2601,20 +2619,30 @@ UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
                                  UINT uSize)\r
 {\r
     LPWINE_MLD         wmld;\r
                                  UINT uSize)\r
 {\r
     LPWINE_MLD         wmld;\r
+    UINT                result;\r
 \r
     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
 \r
     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))\r
        return MMSYSERR_INVALPARAM;\r
 \r
 \r
     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
 \r
     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))\r
        return MMSYSERR_INVALPARAM;\r
 \r
-    if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {\r
+    if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))\r
        return MMSYSERR_NOERROR;\r
        return MMSYSERR_NOERROR;\r
-    }\r
 \r
     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
        return MMSYSERR_INVALHANDLE;\r
 \r
 \r
     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
        return MMSYSERR_INVALHANDLE;\r
 \r
-    return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);\r
+    if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,\r
+                                uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)\r
+        return result;\r
+\r
+    if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)\r
+        return WAVERR_STILLPLAYING;\r
+\r
+    lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;\r
+    lpWaveInHdr->dwFlags |= WHDR_DONE;\r
+\r
+    return MMSYSERR_NOERROR;\r
 }\r
 \r
 /**************************************************************************\r
 }\r
 \r
 /**************************************************************************\r
index 7c4712c..934c7c9 100644 (file)
@@ -1,5 +1,6 @@
 /*\r
  * Copyright 1999 Julio Cesar Gazquez\r
 /*\r
  * Copyright 1999 Julio Cesar Gazquez\r
+ * Copyright 2005 José Manuel Ferrer Ortiz\r
  *\r
  * This library is free software; you can redistribute it and/or\r
  * modify it under the terms of the GNU Lesser General Public\r
  *\r
  * This library is free software; you can redistribute it and/or\r
  * modify it under the terms of the GNU Lesser General Public\r
  */\r
 \r
 STRINGTABLE LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL\r
  */\r
 \r
 STRINGTABLE LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL\r
-   BEGIN\r
+BEGIN\r
 \r
 \r
-   /* MMSYS errors */\r
+/* MMSYS errors */\r
    MMSYSERR_NOERROR, "El comando especificado fue ejecutado."\r
    MMSYSERR_ERROR, "Error externo indefinido."\r
    MMSYSERR_BADDEVICEID, "Un identificador de dispositivo que ha sido usado está fuera de rango para su sistema."\r
    MMSYSERR_NOERROR, "El comando especificado fue ejecutado."\r
    MMSYSERR_ERROR, "Error externo indefinido."\r
    MMSYSERR_BADDEVICEID, "Un identificador de dispositivo que ha sido usado está fuera de rango para su sistema."\r
-   MMSYSERR_NOTENABLED, "El controlador no fue activado."\r
+   MMSYSERR_NOTENABLED, "El manejador no fue activado."\r
    MMSYSERR_ALLOCATED, "El dispositivo especificado aún está en uso. Espere hasta que esté libre e intente nuevamente."\r
    MMSYSERR_INVALHANDLE, "El handle de dispositivo especificado es inválido."\r
    MMSYSERR_ALLOCATED, "El dispositivo especificado aún está en uso. Espere hasta que esté libre e intente nuevamente."\r
    MMSYSERR_INVALHANDLE, "El handle de dispositivo especificado es inválido."\r
-   MMSYSERR_NODRIVER, "No hay un controlador instalado en su sistema !\n"\r
-   MMSYSERR_NOMEM, "No hay suficiente memoria disponible para esta tarea. Cierre una o más aplicaciones para aumentar la memoria disponible e intente nuevamente."\r
-   MMSYSERR_NOTSUPPORTED, "Esta función no está soportada. Use la función Capacidades para determinar que funciones y mensajes soporta el controlador."\r
+   MMSYSERR_NODRIVER, "¡No hay un manejador instalado en su sistema!\n"\r
+   MMSYSERR_NOMEM, "No hay suficiente memoria disponible para esta tarea. Cierre una o más aplicaciones para aumentar la memoria disponible e inténtelo de nuevo."\r
+   MMSYSERR_NOTSUPPORTED, "Esta función no está soportada. Use la función Capacidades para determinar qué funciones y mensajes soporta el manejador."\r
    MMSYSERR_BADERRNUM, "Se ha especificado un número de error que no está definido en el sistema."\r
    MMSYSERR_BADERRNUM, "Se ha especificado un número de error que no está definido en el sistema."\r
-   MMSYSERR_INVALFLAG, "Se ha pasado un flag no válido a una función del sistema."\r
+   MMSYSERR_INVALFLAG, "Se ha pasado una bandera no válida a una función del sistema."\r
    MMSYSERR_INVALPARAM, "Se ha pasado un parámetro no válido a una función del sistema."\r
 \r
    MMSYSERR_INVALPARAM, "Se ha pasado un parámetro no válido a una función del sistema."\r
 \r
-   /* WAVE errors */\r
+/* WAVE errors */\r
    WAVERR_BADFORMAT, "El formato especificado no está soportado o no puede ser traducido. Use la función Capacidades para determinar los formatos soportados."\r
    WAVERR_STILLPLAYING, "Esta operación no puede ejecutarse mientras continúa la reproducción. Reinicie el dispositivo, o espere hasta que la reproducción termine."\r
    WAVERR_BADFORMAT, "El formato especificado no está soportado o no puede ser traducido. Use la función Capacidades para determinar los formatos soportados."\r
    WAVERR_STILLPLAYING, "Esta operación no puede ejecutarse mientras continúa la reproducción. Reinicie el dispositivo, o espere hasta que la reproducción termine."\r
-   WAVERR_UNPREPARED, "La cabecera del forma de onda no está preparada. Use la función Preparar para preparar la cabecera, e intente nuevamente."\r
-   WAVERR_SYNC, "No puede abrirse el dispositivo sin usar el flag WAVE_ALLOWSYNC. Use el flag, e intente nuevamente."\r
+   WAVERR_UNPREPARED, "La cabecera de onda no está preparada. Use la función Preparar para prepararla, e inténtelo de nuevo."\r
+   WAVERR_SYNC, "No puede abrirse el dispositivo sin usar la bandera WAVE_ALLOWSYNC. Utilícela, e inténtelo de nuevo."\r
 \r
 \r
-   /* MIDI errors */\r
-   MIDIERR_UNPREPARED, "La cabecera MIDI no está preparada. Use la función Preparar para preparar la cabecera, e intente nuevamente."\r
+/* MIDI errors */\r
+   MIDIERR_UNPREPARED, "La cabecera MIDI no está preparada. Use la función Preparar para prepararla, e inténtelo de nuevo."\r
    MIDIERR_STILLPLAYING, "Esta operación no puede ejecutarse mientras se continúa tocando. Reinicie el dispositivo, o espera hasta que termine de tocar."\r
    MIDIERR_NOMAP, "No se encontró un mapa MIDI. Puede haber un problema con el controlador, el el fichero MIDIMAP.CFG puede faltar o estar corrupto."\r
    MIDIERR_NOTREADY, "El puerto está transmitiendo datos al dispositivo. Espera hasta que los datos hayan sido transmitidos, e intente nuevamente."\r
    MIDIERR_STILLPLAYING, "Esta operación no puede ejecutarse mientras se continúa tocando. Reinicie el dispositivo, o espera hasta que termine de tocar."\r
    MIDIERR_NOMAP, "No se encontró un mapa MIDI. Puede haber un problema con el controlador, el el fichero MIDIMAP.CFG puede faltar o estar corrupto."\r
    MIDIERR_NOTREADY, "El puerto está transmitiendo datos al dispositivo. Espera hasta que los datos hayan sido transmitidos, e intente nuevamente."\r
@@ -123,4 +124,4 @@ STRINGTABLE LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
    MCIERR_SEQ_PORTUNSPECIFIED, "El sistema no tiene actualmente un puerto MIDI especificado."\r
    MCIERR_SEQ_TIMER, "Todos los temporizadores de multimedia están siendo usados por otras aplicaciones. Cierre una de esas aplicaciones e intente nuevamente."\r
 \r
    MCIERR_SEQ_PORTUNSPECIFIED, "El sistema no tiene actualmente un puerto MIDI especificado."\r
    MCIERR_SEQ_TIMER, "Todos los temporizadores de multimedia están siendo usados por otras aplicaciones. Cierre una de esas aplicaciones e intente nuevamente."\r
 \r
-   END\r
+END\r