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