[PATCH] msacm32: Support conversion from 24-bit PCM

Andrew Eikum aeikum at codeweavers.com
Mon Dec 5 11:28:25 CST 2016


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---

This should fix Bug 41599.

 dlls/msacm32/pcmconverter.c | 320 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 299 insertions(+), 21 deletions(-)

diff --git a/dlls/msacm32/pcmconverter.c b/dlls/msacm32/pcmconverter.c
index f56d155..47861d8 100644
--- a/dlls/msacm32/pcmconverter.c
+++ b/dlls/msacm32/pcmconverter.c
@@ -95,12 +95,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,6 +152,26 @@ 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
  *
@@ -162,6 +182,18 @@ 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
  *
@@ -173,6 +205,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 +437,102 @@ 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
@@ -696,6 +847,84 @@ static	void cvtMM1616C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
         error = error + srcRate;
         while (error > dstRate) {
             src += 2;
+            (*nsrc)--;
+            if (*nsrc == 0)
+                return;
+            error = error - dstRate;
+        }
+    }
+}
+
+static	void cvtSS2424C(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)--) {
+        W24(dst, R24(src)); dst += 3;
+        W24(dst, R24(src)); dst += 3;
+        error = error + srcRate;
+        while (error > dstRate) {
+            src += 6;
+            (*nsrc)--;
+            if (*nsrc == 0)
+                return;
+            error = error - dstRate;
+        }
+    }
+}
+
+static	void cvtSM2424C(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)--) {
+        W24(dst, M24(R24(src), R24(src + 3))); dst += 3;
+        error = error + srcRate;
+        while (error > dstRate) {
+            src += 6;
+            (*nsrc)--;
+            if (*nsrc == 0)
+                return;
+            error = error - dstRate;
+        }
+    }
+}
+
+static	void cvtMS2424C(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)--) {
+        W24(dst, R24(src)); dst += 3;
+        W24(dst, R24(src)); dst += 3;
+        error = error + srcRate;
+        while (error > dstRate) {
+            src += 3;
+            (*nsrc)--;
+            if (*nsrc == 0)
+                return;
+            error = error - dstRate;
+        }
+    }
+}
+
+static	void cvtMM2424C(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)--) {
+        W24(dst, R24(src)); dst += 3;
+        error = error + srcRate;
+        while (error > dstRate) {
+            src += 3;
             (*nsrc)--;
             if (*nsrc == 0)
                 return;
@@ -706,11 +935,16 @@ static	void cvtMM1616C(DWORD srcRate, const unsigned char* src, LPDWORD nsrc,
 
 typedef void (*PCM_CONVERT_CHANGE_RATE)(DWORD, const unsigned char*, LPDWORD, DWORD, unsigned char*, LPDWORD);
 
-static const PCM_CONVERT_CHANGE_RATE PCM_ConvertChangeRate[16] = {
+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,
 };
 
 /***********************************************************************
@@ -886,34 +1120,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->pwfxSrc->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;
 }
 
-- 
2.10.2




More information about the wine-patches mailing list