Commit bdbb8591 authored by Huw Davies's avatar Huw Davies Committed by Alexandre Julliard

winecoreaudio: Move get_mix_format to the unixlib.

parent 129c7faf
......@@ -703,9 +703,255 @@ static NTSTATUS release_stream( void *args )
return STATUS_SUCCESS;
}
static DWORD ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
{
int i;
DWORD mask = 0;
for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
switch (layout->mChannelDescriptions[i].mChannelLabel) {
default: FIXME("Unhandled channel 0x%x\n",
(unsigned int)layout->mChannelDescriptions[i].mChannelLabel); break;
case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
case kAudioChannelLabel_Mono:
case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
}
}
return mask;
}
/* For most hardware on Windows, users must choose a configuration with an even
* number of channels (stereo, quad, 5.1, 7.1). Users can then disable
* channels, but those channels are still reported to applications from
* GetMixFormat! Some applications behave badly if given an odd number of
* channels (e.g. 2.1). Here, we find the nearest configuration that Windows
* would report for a given channel layout. */
static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
{
DWORD ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
if (ca_layout->mNumberChannelDescriptions == 1)
{
fmt->Format.nChannels = 1;
fmt->dwChannelMask = ca_mask;
return;
}
/* compare against known configurations and find smallest configuration
* which is a superset of the given speakers */
if (ca_layout->mNumberChannelDescriptions <= 2 &&
(ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
{
fmt->Format.nChannels = 2;
fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
return;
}
if (ca_layout->mNumberChannelDescriptions <= 4 &&
(ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
{
fmt->Format.nChannels = 4;
fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
return;
}
if (ca_layout->mNumberChannelDescriptions <= 4 &&
(ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
{
fmt->Format.nChannels = 4;
fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
return;
}
if (ca_layout->mNumberChannelDescriptions <= 6 &&
(ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
{
fmt->Format.nChannels = 6;
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
return;
}
if (ca_layout->mNumberChannelDescriptions <= 6 &&
(ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
{
fmt->Format.nChannels = 6;
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
return;
}
if (ca_layout->mNumberChannelDescriptions <= 8 &&
(ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
{
fmt->Format.nChannels = 8;
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
return;
}
if (ca_layout->mNumberChannelDescriptions <= 8 &&
(ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
{
fmt->Format.nChannels = 8;
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
return;
}
/* oddball format, report truthfully */
fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
fmt->dwChannelMask = ca_mask;
}
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 NTSTATUS get_mix_format(void *args)
{
struct get_mix_format_params *params = args;
AudioObjectPropertyAddress addr;
AudioChannelLayout *layout;
AudioBufferList *buffers;
Float64 rate;
UInt32 size;
OSStatus sc;
int i;
params->fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
addr.mScope = get_scope(params->flow);
addr.mElement = 0;
addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
sc = AudioObjectGetPropertyDataSize(params->dev_id, &addr, 0, NULL, &size);
if(sc == noErr){
layout = malloc(size);
sc = AudioObjectGetPropertyData(params->dev_id, &addr, 0, NULL, &size, layout);
if(sc == noErr){
TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
(unsigned int)layout->mChannelLayoutTag, (unsigned int)layout->mChannelBitmap,
(unsigned int)layout->mNumberChannelDescriptions);
if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
convert_channel_layout(layout, params->fmt);
}else{
WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n",
(unsigned int)layout->mChannelLayoutTag);
params->fmt->Format.nChannels = 0;
}
}else{
TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
params->fmt->Format.nChannels = 0;
}
free(layout);
}else{
TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
params->fmt->Format.nChannels = 0;
}
if(params->fmt->Format.nChannels == 0){
addr.mScope = get_scope(params->flow);
addr.mElement = 0;
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
sc = AudioObjectGetPropertyDataSize(params->dev_id, &addr, 0, NULL, &size);
if(sc != noErr){
WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
params->result = osstatus_to_hresult(sc);
return STATUS_SUCCESS;
}
buffers = malloc(size);
if(!buffers){
params->result = E_OUTOFMEMORY;
return STATUS_SUCCESS;
}
sc = AudioObjectGetPropertyData(params->dev_id, &addr, 0, NULL, &size, buffers);
if(sc != noErr){
free(buffers);
WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
params->result = osstatus_to_hresult(sc);
return STATUS_SUCCESS;
}
for(i = 0; i < buffers->mNumberBuffers; ++i)
params->fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
free(buffers);
params->fmt->dwChannelMask = get_channel_mask(params->fmt->Format.nChannels);
}
addr.mSelector = kAudioDevicePropertyNominalSampleRate;
size = sizeof(Float64);
sc = AudioObjectGetPropertyData(params->dev_id, &addr, 0, NULL, &size, &rate);
if(sc != noErr){
WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
params->result = osstatus_to_hresult(sc);
return STATUS_SUCCESS;
}
params->fmt->Format.nSamplesPerSec = rate;
params->fmt->Format.wBitsPerSample = 32;
params->fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
params->fmt->Format.nBlockAlign = (params->fmt->Format.wBitsPerSample *
params->fmt->Format.nChannels) / 8;
params->fmt->Format.nAvgBytesPerSec = params->fmt->Format.nSamplesPerSec *
params->fmt->Format.nBlockAlign;
params->fmt->Samples.wValidBitsPerSample = params->fmt->Format.wBitsPerSample;
params->fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
params->result = S_OK;
return STATUS_SUCCESS;
}
unixlib_entry_t __wine_unix_call_funcs[] =
{
get_endpoint_ids,
create_stream,
release_stream,
get_mix_format,
};
......@@ -75,11 +75,20 @@ struct release_stream_params
HRESULT result;
};
struct get_mix_format_params
{
EDataFlow flow;
DWORD dev_id;
WAVEFORMATEXTENSIBLE *fmt;
HRESULT result;
};
enum unix_funcs
{
unix_get_endpoint_ids,
unix_create_stream,
unix_release_stream,
unix_get_mix_format,
};
extern unixlib_handle_t coreaudio_handle;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment