Andrew Eikum : winealsa.drv: Remap mmdevapi channels to correct ALSA channels.

Alexandre Julliard julliard at winehq.org
Fri Aug 17 10:37:46 CDT 2012


Module: wine
Branch: master
Commit: d402231e3f148a30ceca15c1223a765f5a388379
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=d402231e3f148a30ceca15c1223a765f5a388379

Author: Andrew Eikum <aeikum at codeweavers.com>
Date:   Thu Aug 16 14:12:04 2012 -0500

winealsa.drv: Remap mmdevapi channels to correct ALSA channels.

---

 dlls/winealsa.drv/mmdevdrv.c |  201 ++++++++++++++++++++++++++++++++++++------
 1 files changed, 173 insertions(+), 28 deletions(-)

diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 0b500ad..3a6bf33 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -108,15 +108,20 @@ struct ACImpl {
     HANDLE event;
     float *vols;
 
+    BOOL need_remapping;
+    int alsa_channels;
+    int alsa_channel_map[32];
+
     BOOL initted, started;
     REFERENCE_TIME mmdev_period_rt;
     UINT64 written_frames, last_pos_frames;
     UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
+    snd_pcm_uframes_t remapping_buf_frames;
     UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
     UINT32 hidden_frames;   /* ALSA reserve to ensure continuous rendering */
 
     HANDLE timer;
-    BYTE *local_buffer, *tmp_buffer;
+    BYTE *local_buffer, *tmp_buffer, *remapping_buf;
     LONG32 getbuf_last; /* <0 when using tmp_buffer */
 
     CRITICAL_SECTION lock;
@@ -885,6 +890,7 @@ static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
         }
         HeapFree(GetProcessHeap(), 0, This->vols);
         HeapFree(GetProcessHeap(), 0, This->local_buffer);
+        HeapFree(GetProcessHeap(), 0, This->remapping_buf);
         HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
         HeapFree(GetProcessHeap(), 0, This->hw_params);
         CoTaskMemFree(This->fmt);
@@ -1071,6 +1077,121 @@ static HRESULT get_audio_session(const GUID *sessionguid,
     return S_OK;
 }
 
+static int alsa_channel_index(DWORD flag)
+{
+    switch(flag){
+    case SPEAKER_FRONT_LEFT:
+        return 0;
+    case SPEAKER_FRONT_RIGHT:
+        return 1;
+    case SPEAKER_BACK_LEFT:
+        return 2;
+    case SPEAKER_BACK_RIGHT:
+        return 3;
+    case SPEAKER_FRONT_CENTER:
+        return 4;
+    case SPEAKER_LOW_FREQUENCY:
+        return 5;
+    case SPEAKER_SIDE_LEFT:
+        return 6;
+    case SPEAKER_SIDE_RIGHT:
+        return 7;
+    }
+    return -1;
+}
+
+static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt)
+{
+    unsigned int i;
+    for(i = 0; i < fmt->nChannels; ++i){
+        if(This->alsa_channel_map[i] != i)
+            return TRUE;
+    }
+    return FALSE;
+}
+
+static DWORD get_channel_mask(unsigned int channels)
+{
+    switch(channels){
+    case 0:
+        return 0;
+    case 1:
+        return KSAUDIO_SPEAKER_MONO;
+    case 2:
+        return KSAUDIO_SPEAKER_STEREO;
+    case 3:
+        return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
+    case 4:
+        return KSAUDIO_SPEAKER_QUAD;    /* not _SURROUND */
+    case 5:
+        return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
+    case 6:
+        return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
+    case 7:
+        return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
+    case 8:
+        return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
+    }
+    FIXME("Unknown speaker configuration: %u\n", channels);
+    return 0;
+}
+
+static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt)
+{
+    if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2){
+        WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
+        DWORD mask, flag = SPEAKER_FRONT_LEFT;
+        UINT i = 0;
+
+        if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+                fmtex->dwChannelMask != 0)
+            mask = fmtex->dwChannelMask;
+        else
+            mask = get_channel_mask(fmt->nChannels);
+
+        This->alsa_channels = 0;
+
+        while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
+            if(mask & flag){
+                This->alsa_channel_map[i] = alsa_channel_index(flag);
+                TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
+                        i, flag, This->alsa_channel_map[i]);
+                if(This->alsa_channel_map[i] >= This->alsa_channels)
+                    This->alsa_channels = This->alsa_channel_map[i] + 1;
+                ++i;
+            }
+            flag <<= 1;
+        }
+
+        while(i < fmt->nChannels){
+            This->alsa_channel_map[i] = This->alsa_channels;
+            TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
+                    i, This->alsa_channel_map[i]);
+            ++This->alsa_channels;
+            ++i;
+        }
+
+        for(i = 0; i < fmt->nChannels; ++i){
+            if(This->alsa_channel_map[i] == -1){
+                This->alsa_channel_map[i] = This->alsa_channels;
+                ++This->alsa_channels;
+                TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
+                        i, This->alsa_channel_map[i]);
+            }
+        }
+
+        This->need_remapping = need_remapping(This, fmt);
+
+        TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
+    }else{
+        This->need_remapping = FALSE;
+        This->alsa_channels = fmt->nChannels;
+        TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
+    }
+
+    return S_OK;
+}
+
 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
         AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
         REFERENCE_TIME period, const WAVEFORMATEX *fmt,
@@ -1135,6 +1256,12 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
 
     dump_fmt(fmt);
 
+    if(FAILED(map_channels(This, fmt))){
+        WARN("map_channels failed\n");
+        hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
+        goto exit;
+    }
+
     if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
         WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
@@ -1174,7 +1301,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
     }
 
     if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
-                fmt->nChannels)) < 0){
+                This->alsa_channels)) < 0){
         WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
                 snd_strerror(err));
         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
@@ -1428,32 +1555,6 @@ static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
     return S_OK;
 }
 
-static DWORD get_channel_mask(unsigned int channels)
-{
-    switch(channels){
-    case 0:
-        return 0;
-    case 1:
-        return KSAUDIO_SPEAKER_MONO;
-    case 2:
-        return KSAUDIO_SPEAKER_STEREO;
-    case 3:
-        return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
-    case 4:
-        return KSAUDIO_SPEAKER_QUAD;    /* not _SURROUND */
-    case 5:
-        return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
-    case 6:
-        return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
-    case 7:
-        return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
-    case 8:
-        return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
-    }
-    FIXME("Unknown speaker configuration: %u\n", channels);
-    return 0;
-}
-
 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
         AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
         WAVEFORMATEX **out)
@@ -1550,6 +1651,16 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
         closest->nChannels = min;
     }
 
+    if(FAILED(map_channels(This, fmt))){
+        hr = AUDCLNT_E_DEVICE_INVALIDATED;
+        WARN("map_channels failed\n");
+        goto exit;
+    }
+    if(This->alsa_channels > max){
+        hr = S_FALSE;
+        closest->nChannels = max;
+    }
+
     if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
         ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
 
@@ -1706,6 +1817,38 @@ static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
     return S_OK;
 }
 
+static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
+{
+    snd_pcm_uframes_t i;
+    UINT c;
+    UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
+
+    if(!This->need_remapping)
+        return buf;
+
+    if(!This->remapping_buf){
+        This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
+                (This->fmt->wBitsPerSample / 8) * This->alsa_channels * frames);
+        This->remapping_buf_frames = frames;
+    }else if(This->remapping_buf_frames < frames){
+        This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
+                (This->fmt->wBitsPerSample / 8) * This->alsa_channels * frames);
+        This->remapping_buf_frames = frames;
+    }
+
+    snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
+            frames * This->alsa_channels);
+
+    for(i = 0; i < frames; ++i){
+        for(c = 0; c < This->fmt->nChannels; ++c){
+            memcpy(&This->remapping_buf[(i * This->alsa_channels + This->alsa_channel_map[c]) * bytes_per_sample],
+                    &buf[(i * This->fmt->nChannels + c) * bytes_per_sample], bytes_per_sample);
+        }
+    }
+
+    return This->remapping_buf;
+}
+
 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
         snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
 {
@@ -1719,6 +1862,8 @@ static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
                     snd_strerror(err));
     }
 
+    buf = remap_channels(This, buf, frames);
+
     written = snd_pcm_writei(handle, buf, frames);
     if(written < 0){
         int ret;




More information about the wine-cvs mailing list