[MSACM32] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / dll / win32 / msacm32 / pcmconverter.c
index 7d041c9..d52662a 100644 (file)
@@ -77,13 +77,16 @@ static      DWORD   PCM_drvClose(DWORD dwDevID)
 /* flags for fdwDriver */
 #define PCM_RESAMPLE   1
 
+typedef void (*PCM_CONVERT_KEEP_RATE)(const unsigned char*, int, unsigned char*);
+
+typedef void (*PCM_CONVERT_CHANGE_RATE)(const DWORD, const unsigned char*, DWORD*, const DWORD, unsigned char*, DWORD*);
+
 /* data used while converting */
 typedef struct tagAcmPcmData {
     /* conversion routine, depending if rate conversion is required */
     union {
-       void (*cvtKeepRate)(const unsigned char*, int, unsigned char*);
-       void (*cvtChangeRate)(DWORD, const unsigned char*, LPDWORD,
-                             DWORD, unsigned char*, LPDWORD);
+        PCM_CONVERT_KEEP_RATE cvtKeepRate;
+        PCM_CONVERT_CHANGE_RATE cvtChangeRate;
     } cvt;
 } AcmPcmData;
 
@@ -95,12 +98,12 @@ static const struct {
     int                nBits;
     int                rate;
 } PCM_Formats[] = {
-    {1,  8,  8000}, {2,  8,  8000}, {1, 16,  8000}, {2, 16,  8000},
-    {1,  8, 11025}, {2,  8, 11025}, {1, 16, 11025}, {2, 16, 11025},
-    {1,  8, 22050}, {2,  8, 22050}, {1, 16, 22050}, {2, 16, 22050},
-    {1,  8, 44100}, {2,  8, 44100}, {1, 16, 44100}, {2, 16, 44100},
-    {1,  8, 48000}, {2,  8, 48000}, {1, 16, 48000}, {2, 16, 48000},
-    {1,  8, 96000}, {2,  8, 96000}, {1, 16, 96000}, {2, 16, 96000}
+    {1,  8,  8000}, {2,  8,  8000}, {1, 16,  8000}, {2, 16,  8000}, {1, 24,  8000}, {2, 24,  8000},
+    {1,  8, 11025}, {2,  8, 11025}, {1, 16, 11025}, {2, 16, 11025}, {1, 24, 11025}, {2, 24, 11025},
+    {1,  8, 22050}, {2,  8, 22050}, {1, 16, 22050}, {2, 16, 22050}, {1, 24, 22050}, {2, 24, 22050},
+    {1,  8, 44100}, {2,  8, 44100}, {1, 16, 44100}, {2, 16, 44100}, {1, 24, 44100}, {2, 24, 44100},
+    {1,  8, 48000}, {2,  8, 48000}, {1, 16, 48000}, {2, 16, 48000}, {1, 24, 48000}, {2, 24, 48000},
+    {1,  8, 96000}, {2,  8, 96000}, {1, 16, 96000}, {2, 16, 96000}, {1, 24, 96000}, {2, 24, 96000},
 };
 
 /***********************************************************************
@@ -152,20 +155,52 @@ static inline unsigned char C168(short s)
     return HIBYTE(s) ^ (unsigned char)0x80;
 }
 
+/***********************************************************************
+ *           C248
+ *
+ * Converts a 24 bit sample to a 8 bit one (data loss !!)
+ */
+static inline unsigned char C248(int s)
+{
+    return HIBYTE(HIWORD(s)) ^ (unsigned char)0x80;
+}
+
+/***********************************************************************
+ *           C2416
+ *
+ * Converts a 24 bit sample to a 16 bit one (data loss !!)
+ */
+static inline short C2416(int s)
+{
+    return HIWORD(s);
+}
+
 /***********************************************************************
  *           R16
  *
- * Read a 16 bit sample (correctly handles endianess)
+ * Read a 16 bit sample (correctly handles endianness)
  */
 static inline short  R16(const unsigned char* src)
 {
     return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
 }
 
+/***********************************************************************
+ *           R24
+ *
+ * Read a 24 bit sample (correctly handles endianness)
+ * Note, to support signed arithmetic, the values are shifted high in the int
+ * and low 8 bytes are unused.
+ */
+static inline int R24(const unsigned char* src)
+{
+    return ((int)src[0] | (int)src[1] << 8 | (int)src[2] << 16) << 8;
+}
+
 /***********************************************************************
  *           W16
  *
- * Write a 16 bit sample (correctly handles endianess)
+ * Write a 16 bit sample (correctly handles endianness)
  */
 static inline void  W16(unsigned char* dst, short s)
 {
@@ -173,6 +208,37 @@ static inline void  W16(unsigned char* dst, short s)
     dst[1] = HIBYTE(s);
 }
 
+/***********************************************************************
+ *           W24
+ *
+ * Write a 24 bit sample (correctly handles endianness)
+ */
+static inline void  W24(unsigned char* dst, int s)
+{
+    dst[0] = HIBYTE(LOWORD(s));
+    dst[1] = LOBYTE(HIWORD(s));
+    dst[2] = HIBYTE(HIWORD(s));
+}
+
+/***********************************************************************
+ *           M24
+ *
+ * Convert the (l,r) 24 bit stereo sample into a 24 bit mono
+ * (takes the sum of the two values)
+ */
+static inline int M24(int l, int r)
+{
+    LONGLONG sum = l + r;
+
+    /* clip sum to saturation */
+    if (sum > 0x7fffff00)
+        sum = 0x7fffff00;
+    else if (sum < -0x7fffff00)
+        sum = -0x7fffff00;
+
+    return sum;
+}
+
 /***********************************************************************
  *           M16
  *
@@ -374,14 +440,100 @@ static   void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst)
     }
 }
 
+static void cvtMS248K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    unsigned char v;
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       v = C248(R24(src));     src += 3;
+       *dst++ = v;
+       *dst++ = v;
+    }
+}
+
+static void cvtSM248K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = C248(M24(R24(src), R24(src + 3)));
+       src += 6;
+    }
+}
+
+static void cvtMM248K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = C248(R24(src));        src += 3;
+    }
+}
+
+static void cvtSS248K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = C248(R24(src));        src += 3;
+       *dst++ = C248(R24(src));        src += 3;
+    }
+}
+
+static void cvtMS2416K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    short v;
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       v = C2416(R24(src));    src += 3;
+       W16(dst, v);    dst += 2;
+       W16(dst, v);    dst += 2;
+    }
+}
+
+static void cvtSM2416K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       W16(dst, C2416(M24(R24(src), R24(src + 3))));
+       dst += 2;
+       src += 6;
+    }
+}
+
+static void cvtMM2416K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       W16(dst, C2416(R24(src)));      dst += 2; src += 3;
+    }
+}
+
+static void cvtSS2416K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       W16(dst, C2416(R24(src)));      dst += 2; src += 3;
+       W16(dst, C2416(R24(src)));      dst += 2; src += 3;
+    }
+}
 
-typedef void (*PCM_CONVERT_KEEP_RATE)(const unsigned char*, int, unsigned char*);
 
-static const PCM_CONVERT_KEEP_RATE PCM_ConvertKeepRate[16] = {
+static const PCM_CONVERT_KEEP_RATE PCM_ConvertKeepRate[] = {
     cvtSS88K,  cvtSM88K,   cvtMS88K,   cvtMM88K,
     cvtSS816K, cvtSM816K,  cvtMS816K,  cvtMM816K,
+    NULL, NULL, NULL, NULL, /* TODO: 8->24 */
     cvtSS168K, cvtSM168K,  cvtMS168K,  cvtMM168K,
     cvtSS1616K, cvtSM1616K, cvtMS1616K, cvtMM1616K,
+    NULL, NULL, NULL, NULL, /* TODO: 16->24 */
+    cvtSS248K, cvtSM248K, cvtMS248K, cvtMM248K,
+    cvtSS2416K, cvtSM2416K, cvtMS2416K, cvtMM2416K,
+    NULL, NULL, NULL, NULL, /* TODO: 24->24 */
 };
 
 /* the conversion routines with rate conversion are labelled cvt<X><Y><N><M>C
@@ -392,325 +544,427 @@ static const PCM_CONVERT_KEEP_RATE PCM_ConvertKeepRate[16] = {
  * <M> is the number of bits of output channel (8 or 16)
  *
  */
-static void cvtSS88C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                     DWORD dstRate, unsigned char* dst, LPDWORD ndst)
-{
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = *src;
-        *dst++ = *src;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc == 0)
+
+static void cvtSS88C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                     const DWORD dstRate, unsigned char *dst, DWORD *ndst)
+{
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = src[0];
+            *dst++ = src[1];
         }
+        src += 2;
     }
 }
 
-static void cvtSM88C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                     DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSM88C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                     const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = M8(src[0], src[1]);
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc == 0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = M8(src[0], src[1]);
         }
+        src += 2;
     }
 }
 
-static void cvtMS88C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                     DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMS88C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                     const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = *src;
-        *dst++ = *src;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src++;
-            (*nsrc)--;
-            if (*nsrc == 0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = src[0];
+            *dst++ = src[0];
         }
+        src += 1;
     }
 }
 
-static void cvtMM88C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                     DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMM88C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                     const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = *src;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src++;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = src[0];
         }
+        src += 1;
     }
 }
 
-static void cvtSS816C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSS816C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-       W16(dst, C816(src[0])); dst += 2;
-       W16(dst, C816(src[1])); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, C816(src[0])); dst += 2;
+            W16(dst, C816(src[1])); dst += 2;
         }
+        src += 2;
     }
 }
 
-static void cvtSM816C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSM816C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        W16(dst, M16(C816(src[0]), C816(src[1]))); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, M16(C816(src[0]), C816(src[1]))); dst += 2;
         }
+        src += 2;
     }
 }
 
-static void cvtMS816C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMS816C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        W16(dst, C816(*src)); dst += 2;
-        W16(dst, C816(*src)); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src++;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, C816(src[0])); dst += 2;
+            W16(dst, C816(src[0])); dst += 2;
         }
+        src += 1;
     }
 }
 
-static void cvtMM816C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMM816C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        W16(dst, C816(*src)); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src++;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, C816(src[0])); dst += 2;
         }
+        src += 1;
     }
 }
 
-static void cvtSS168C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSS168C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = C168(R16(src));
-        *dst++ = C168(R16(src + 2));
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 4;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = C168(R16(src));
+            *dst++ = C168(R16(src + 2));
         }
+        src += 4;
     }
 }
 
-static void cvtSM168C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSM168C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-       *dst++ = C168(M16(R16(src), R16(src + 2)));
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 4;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = C168(M16(R16(src), R16(src + 2)));
         }
+        src += 4;
     }
 }
 
-static void cvtMS168C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMS168C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = C168(R16(src));
-        *dst++ = C168(R16(src));
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc==0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = C168(R16(src));
+            *dst++ = C168(R16(src));
         }
+        src += 2;
     }
 }
 
-static void cvtMM168C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                      DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMM168C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                      const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        *dst++ = C168(R16(src));
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc == 0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            *dst++ = C168(R16(src));
         }
+        src += 2;
     }
 }
 
-static void cvtSS1616C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                       DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSS1616C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
-
-    while ((*ndst)--) {
-        W16(dst, R16(src)); dst += 2;
-        W16(dst, R16(src)); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 4;
-            (*nsrc)--;
-            if (*nsrc == 0)
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, R16(src)); dst += 2;
+            W16(dst, R16(src + 2)); dst += 2;
         }
+        src += 4;
     }
 }
 
-static void cvtSM1616C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                       DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSM1616C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
+                return;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, M16(R16(src), R16(src + 2))); dst += 2;
+        }
+        src += 4;
+    }
+}
 
-    while ((*ndst)--) {
-        W16(dst, M16(R16(src), R16(src + 2))); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 4;
-            (*nsrc)--;
-            if (*nsrc == 0)
+static void cvtMS1616C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
+{
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W16(dst, R16(src)); dst += 2;
+            W16(dst, R16(src)); dst += 2;
         }
+        src += 2;
     }
 }
 
-static void cvtMS1616C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                       DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtMM1616C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
+                return;
+            (*ndst)++;
+            error -= srcRate;
 
-    while((*ndst)--) {
-        W16(dst, R16(src)); dst += 2;
-        W16(dst, R16(src)); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc == 0)
+            W16(dst, R16(src)); dst += 2;
+        }
+        src += 2;
+    }
+}
+
+static void cvtSS2424C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
+{
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W24(dst, R24(src)); dst += 3;
+            W24(dst, R24(src + 3)); dst += 3;
         }
+        src += 6;
     }
 }
 
-static void cvtMM1616C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
-                       DWORD dstRate, unsigned char* dst, LPDWORD ndst)
+static void cvtSM2424C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
 {
-    DWORD error = dstRate / 2;
-    TRACE("(%d, %p, %p, %d, %p, %p)\n", srcRate, src, nsrc, dstRate, dst, ndst);
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
+                return;
+            (*ndst)++;
+            error -= srcRate;
 
-    while ((*ndst)--) {
-        W16(dst, R16(src)); dst += 2;
-        error = error + srcRate;
-        while (error > dstRate) {
-            src += 2;
-            (*nsrc)--;
-            if (*nsrc == 0)
+            W24(dst, M24(R24(src), R24(src + 3))); dst += 3;
+        }
+        src += 6;
+    }
+}
+
+static void cvtMS2424C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
+{
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
                 return;
-            error = error - dstRate;
+            (*ndst)++;
+            error -= srcRate;
+
+            W24(dst, R24(src)); dst += 3;
+            W24(dst, R24(src)); dst += 3;
         }
+        src += 3;
     }
 }
 
-typedef void (*PCM_CONVERT_CHANGE_RATE)(DWORD, const unsigned char*, LPDWORD, DWORD, unsigned char*, LPDWORD);
+static void cvtMM2424C(const DWORD srcRate, const unsigned char *src, DWORD *nsrc,
+                       const DWORD dstRate, unsigned char *dst, DWORD *ndst)
+{
+    DWORD error = srcRate / 2;
+    DWORD maxSrc = *nsrc, maxDst = *ndst;
+    *ndst = 0;
+    for (*nsrc = 0; *nsrc < maxSrc; (*nsrc)++) {
+        error += dstRate;
+        while (error > srcRate) {
+            if (*ndst == maxDst)
+                return;
+            (*ndst)++;
+            error -= srcRate;
 
-static const PCM_CONVERT_CHANGE_RATE PCM_ConvertChangeRate[16] = {
+            W24(dst, R24(src)); dst += 3;
+        }
+        src += 3;
+    }
+}
+
+static const PCM_CONVERT_CHANGE_RATE PCM_ConvertChangeRate[] = {
     cvtSS88C,   cvtSM88C,   cvtMS88C,   cvtMM88C,
     cvtSS816C, cvtSM816C,  cvtMS816C,  cvtMM816C,
+    NULL, NULL, NULL, NULL, /* TODO: 8->24 */
     cvtSS168C, cvtSM168C,  cvtMS168C,  cvtMM168C,
     cvtSS1616C, cvtSM1616C, cvtMS1616C, cvtMM1616C,
+    NULL, NULL, NULL, NULL, /* TODO: 16->24 */
+    NULL, NULL, NULL, NULL, /* TODO: 24->8 */
+    NULL, NULL, NULL, NULL, /* TODO: 24->16 */
+    cvtSS2424C, cvtSM2424C, cvtMS2424C, cvtMM2424C,
 };
 
 /***********************************************************************
@@ -723,15 +977,15 @@ static    LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
 
     add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
     add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
-    add->wMid = 0xFF;
-    add->wPid = 0x00;
+    add->wMid = MM_MICROSOFT;
+    add->wPid = MM_MSFT_ACM_PCM;
     add->vdwACM = 0x01000000;
     add->vdwDriver = 0x01000000;
     add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
     add->cFormatTags = 1;
     add->cFilterTags = 0;
     add->hicon = NULL;
-    MultiByteToWideChar( CP_ACP, 0, "WINE-PCM", -1,
+    MultiByteToWideChar( CP_ACP, 0, "MS-PCM", -1,
                          add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
     MultiByteToWideChar( CP_ACP, 0, "Wine PCM converter", -1,
                          add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
@@ -839,28 +1093,33 @@ static   LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
 
     /* some tests ... */
     if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
-       adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
-       PCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) {
+        adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
+        PCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) {
             WARN("not possible\n");
             return ACMERR_NOTPOSSIBLE;
     }
 
     /* is no suggestion for destination, then copy source value */
     if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) {
-       adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
+        adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
     }
     if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) {
-       adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
+        adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
     }
     if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) {
-       adfs->pwfxDst->wBitsPerSample = adfs->pwfxSrc->wBitsPerSample;
+        adfs->pwfxDst->wBitsPerSample = adfs->pwfxSrc->wBitsPerSample;
     }
     if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) {
-       if (adfs->pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) {
-            WARN("not possible\n");
+        if (adfs->pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) {
+            WARN("source format 0x%x not supported\n", adfs->pwfxSrc->wFormatTag);
+            return ACMERR_NOTPOSSIBLE;
+        }
+        adfs->pwfxDst->wFormatTag = adfs->pwfxSrc->wFormatTag;
+    } else {
+        if (adfs->pwfxDst->wFormatTag != WAVE_FORMAT_PCM) {
+            WARN("destination format 0x%x not supported\n", adfs->pwfxDst->wFormatTag);
             return ACMERR_NOTPOSSIBLE;
         }
-       adfs->pwfxDst->wFormatTag = adfs->pwfxSrc->wFormatTag;
     }
     /* check if result is ok */
     if (PCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) {
@@ -881,34 +1140,78 @@ static   LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
  */
 static LRESULT PCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
 {
-    AcmPcmData*        apd;
-    int                idx = 0;
+    AcmPcmData* apd;
+    int idx;
+    DWORD flags;
 
     TRACE("(%p)\n", adsi);
 
     assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
 
-    apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData));
-    if (apd == 0) {
-        WARN("no memory\n");
-        return MMSYSERR_NOMEM;
+    switch(adsi->pwfxSrc->wBitsPerSample){
+    case 8:
+        idx = 0;
+        break;
+    case 16:
+        idx = 12;
+        break;
+    case 24:
+        if (adsi->pwfxSrc->nBlockAlign != 3 * adsi->pwfxSrc->nChannels) {
+            FIXME("Source: 24-bit samples must be packed\n");
+            return MMSYSERR_NOTSUPPORTED;
+        }
+        idx = 24;
+        break;
+    default:
+        FIXME("Unsupported source bit depth: %u\n", adsi->pwfxSrc->wBitsPerSample);
+        return MMSYSERR_NOTSUPPORTED;
     }
 
-    adsi->dwDriver = (DWORD_PTR)apd;
-    adsi->fdwDriver = 0;
+    switch(adsi->pwfxDst->wBitsPerSample){
+    case 8:
+        break;
+    case 16:
+        idx += 4;
+        break;
+    case 24:
+        if (adsi->pwfxDst->nBlockAlign != 3 * adsi->pwfxDst->nChannels) {
+            FIXME("Destination: 24-bit samples must be packed\n");
+            return MMSYSERR_NOTSUPPORTED;
+        }
+        idx += 8;
+        break;
+    default:
+        FIXME("Unsupported destination bit depth: %u\n", adsi->pwfxDst->wBitsPerSample);
+        return MMSYSERR_NOTSUPPORTED;
+    }
 
-    if (adsi->pwfxSrc->wBitsPerSample == 16) idx += 8;
-    if (adsi->pwfxDst->wBitsPerSample == 16) idx += 4;
     if (adsi->pwfxSrc->nChannels      == 1)  idx += 2;
+
     if (adsi->pwfxDst->nChannels      == 1)  idx += 1;
 
+    apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData));
+    if (!apd)
+        return MMSYSERR_NOMEM;
+
     if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) {
-       apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx];
+        flags = 0;
+        apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx];
     } else {
-       adsi->fdwDriver |= PCM_RESAMPLE;
-       apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx];
+        flags = PCM_RESAMPLE;
+        apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx];
+    }
+
+    if(!apd->cvt.cvtChangeRate && !apd->cvt.cvtKeepRate){
+        FIXME("Unimplemented conversion from %u -> %u bps\n",
+            adsi->pwfxSrc->wBitsPerSample,
+            adsi->pwfxDst->wBitsPerSample);
+        HeapFree(GetProcessHeap(), 0, apd);
+        return MMSYSERR_NOTSUPPORTED;
     }
 
+    adsi->dwDriver = (DWORD_PTR)apd;
+    adsi->fdwDriver = flags;
+
     return MMSYSERR_NOERROR;
 }
 
@@ -1004,12 +1307,8 @@ static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER
 
     /* do the job */
     if (adsi->fdwDriver & PCM_RESAMPLE) {
-       DWORD   nsrc2 = nsrc;
-       DWORD   ndst2 = ndst;
-       apd->cvt.cvtChangeRate(adsi->pwfxSrc->nSamplesPerSec, adsh->pbSrc, &nsrc2,
-                              adsi->pwfxDst->nSamplesPerSec, adsh->pbDst, &ndst2);
-       nsrc -= nsrc2;
-       ndst -= ndst2;
+        apd->cvt.cvtChangeRate(adsi->pwfxSrc->nSamplesPerSec, adsh->pbSrc, &nsrc,
+                               adsi->pwfxDst->nSamplesPerSec, adsh->pbDst, &ndst);
     } else {
        if (nsrc < ndst) ndst = nsrc; else nsrc = ndst;